Repository navigation
feat(spec): a field group's requiresFeature hides the group where a deployment feature is off; Edit organization hides Parent Organization and Sort Order on one organization - #30810
Conversation
…en; sys_organization hides group structure on one organization Claude-Session: https://claude.ai/code/session_016WV4cyCnzVPr9GuD38DdGt Co-authored-by: Claude <noreply@anthropic.com>
…; book it in the feature registry Claude-Session: https://claude.ai/code/session_016WV4cyCnzVPr9GuD38DdGt Co-authored-by: Claude <noreply@anthropic.com>
…dle; regenerate spec references; changeset Claude-Session: https://claude.ai/code/session_016WV4cyCnzVPr9GuD38DdGt Co-authored-by: Claude <noreply@anthropic.com>
…it on the object form, regenerate api-surface and export-origins Claude-Session: https://claude.ai/code/session_016WV4cyCnzVPr9GuD38DdGt Co-authored-by: Claude <noreply@anthropic.com>
…unt the new requiresFeature row Claude-Session: https://claude.ai/code/session_016WV4cyCnzVPr9GuD38DdGt Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 30 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 16 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 170 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9e414459fa4556f71c88a815fc6e87c16caafbdd && git checkout 9e414459fa4556f71c88a815fc6e87c16caafbdd
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2038c56ec99a60e2bf3cb6ee56ef70a5c368ed32 559cf50443f54565dd625a39bc3aff0e6fb1a759 && git checkout -B drift-repro 2038c56ec99a60e2bf3cb6ee56ef70a5c368ed32 && git merge --no-ff 559cf50443f54565dd625a39bc3aff0e6fb1a759
node scripts/docs-audit/affected-docs.mjs --json 2038c56ec99a60e2bf3cb6ee56ef70a5c368ed32
|
|
domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7 Contract reviewServed-tier: Inputs: card #30692 (body, triage 6671686007, claims 6675467698 / 6675514573 / 6675727155, dev report 6677017944), ruling 6670372892 on #30210 (row 4 A), PR #30810 body and file list (28 files, +533/-33), the net diff against merge-base 2d04cd2, and the check-runs on the head. Nothing was built, run or re-run locally. ① Derived judgments
② Semver level
③ Boundary flagsThe dev report lists no open questions. Deviations, each judged:
④ Design judgmentpackages/spec/src/data/object.zod.ts — HOLDS against the ruling (row 4 A: a field-group-level packages/spec/src/data/object.form.ts — HOLDS. One row on the packages/spec/src/kernel/public-auth-features.ts — HOLDS. The slot argument defaults to Findings:
No must-fix. The Check-runs on the head at the time of this read: no gate-carrying run red; Implemented-by: VERDICT: PASS |
…s; state and pin a field group's fail-closed fault direction The slot argument is now a type parameter, and the input's constraint is keyed on it: a shape may carry that slot and must not carry the other one. Lowering an action into 'visibleWhen', or a field group into the default 'visible', fails to compile, and the cast on the slot read is gone. The action call sites are unchanged. A field group's visibleWhen is fail-closed, where an action's visible is fail-soft. A features scope that does not carry the flag yet makes the gate fault (CEL "No such key"), so the group stays hidden until the host publishes the flag, even for a default-on flag. The semantics table, the gate docs and the object comment say so, and the sys_organization pin holds the empty-scope reading. Claude-Session: https://claude.ai/code/session_016WV4cyCnzVPr9GuD38DdGt Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7 Contract reviewServed-tier: Delta record: one commit 559cf50 on top of 24375ec (6 files, +105/-23), judged against the tree. The PASS of record 6677172123 on 24375ec is carried forward for everything this commit did not touch: object.form.ts, validate-expressions.ts and its tests, the feature-gate guard, the generated surfaces, the translation bundles and the echo pins are byte-identical between the two heads. Nothing was built, run or re-run locally. ① Derived judgments
② Semver levelUnchanged: ③ Boundary flags
④ Design judgmentpackages/spec/src/kernel/public-auth-features.ts — HOLDS. The constraint refuses the mismatch at compile time instead of writing into a key the schema never reads; the read is cast-free; the semantics prose now tells the truth about both slots. Tried to break it: an action shape with the per-option packages/spec/src/data/object.zod.ts — HOLDS. Docblock only; the key, the transform and the output shape are unchanged from 24375ec. No finding. packages/spec/src/data/object.form.ts — untouched by the delta; PASS carried forward. No must-fix, no should-fix on this delta. Record 6677172123's should-fix 1 and 2 are resolved by this commit; should-fix 3 is carried by #30812. Check-runs on head 559cf50 at the time of this read: none red; Implemented-by: VERDICT: PASS |
|
domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7 席位核对 · ACCEPTPR #30810 · head 维护者速读(席位校正)The body's six lines stand as written. One consequence is added, because the delta made it explicit: the Group Structure group is fail-closed. On a multi-organization deployment it stays hidden in the console until 席位核对
|
Requested by Elon · project thread
Fixes #30692
Clause-②: yes
Metadata-protocol: added
Skills-docs: follow-up #30812 — skills/objectstack-data/references/examples-objects.md (its Field Groups section teaches
fieldGroupsand should namevisibleWhenandrequiresFeaturebesidecollapse); kept out of this diff so it does not become a governedskills/**change维护者速读
requiresFeature(与动作上的同名键同一套特性名),解析时降成该分组的visibleWhen并从输出中去掉;组织对象声明了四个字段分组,「上级组织」「排序」所在的「集团结构」组要求multiOrgEnabled。packages/spec/src/data/object.zod.ts(新键与降级 transform);packages/lint/src/validate-expressions.ts的withoutLoweredFeatureGate(lint 只放行降级出来的那一个门);packages/platform-objects/src/identity/sys-organization.object.ts(四个分组)。pnpm --filter @objectstack/platform-objects exec vitest run src/identity/sys-organization-group-structure-gate.test.ts,预期 4 条全绿:multiOrgEnabled关时两字段不出现,开时出现。What changes
@objectstack/spec:fieldGroups[].requiresFeature. The field-group twin of the action-levelrequiresFeature, from the samePUBLIC_AUTH_FEATURESvocabulary, so an unknown flag is refused at parse (invalid_valueatrequiresFeature). It is lowered at parse into the group'svisibleWhenby the same helper the action key uses,lowerRequiresFeature, which now takes an optional third argument naming the slot ('visible'by default,'visibleWhen'here); its refusals name that slot. The slot is a type parameter and the input's constraint is keyed on it, so lowering an action into'visibleWhen', or a field group into the default'visible', fails to compile; the action call sites are unchanged. The key is stripped from the output.visibleWhen: "record.kind == 'child'"+requiresFeature: 'multiOrgEnabled'parses to(record.kind == 'child') && features.multiOrgEnabled != false. A non-CELvisibleWhenrefuses the pair, as for actions.PUBLIC_AUTH_FEATURES.multiOrgEnabled.gatedInputsbookssys_organization.fieldGroups.group_structure; thegatedInputspath grammar admitsOBJECT.fieldGroups.KEY, and the platform-objects completeness guard walks field groups in both directions.object.form.tsoffers the key on thefieldGroupsrepeater (label and help in en / zh-CN / ja-JP / es-ES). Liveness:object/fieldGroups.requiresFeatureislive.@objectstack/lint: the lowered gate is accepted. The field-groupvisibleWhenroot verdict now judges the predicate with the trailing gate arequiresFeaturelowers to taken off (withoutLoweredFeatureGate): the gate alone, or(AUTHORED) && GATEwhere the authored part parses on its own. Any otherfeaturesread in a group predicate keeps its root verdict, and its message now points atrequiresFeature. The finding still quotes the whole predicate.@objectstack/platform-objects:sys_organizationdeclaresfieldGroups. Identity, Branding, Configuration, Group Structure, in the fields' own order, so no field moves;id/created_at/updated_atkeepgroup: 'System', which is not declared, so they render last in the ungrouped bucket as before. Group Structure (parent_organization_id,sort_order) carriesrequiresFeature: 'multiOrgEnabled'. Section labels are translated in zh-CN / ja-JP / es-ES.The ui is unchanged, as the ruling says: the form's section gate and the record page's
record:details(ui/packages/plugin-detail/src/renderers/record-details.tsxsectionPredicateVisible) both evaluate a group'svisibleWhenwithusePredicateScope(), which carries the host'sfeatures.Tests
main(1371b9d8, the object before this PR, tests from this PR):sys-organization-group-structure-gate.test.ts3 failed / 1 passed; withmultiOrgEnabled: falsethe flat layout still rendersparent_organization_id.packages/spec/src/data/field-group-requires-feature.test.ts(lowered and stripped; an opt-in flag gets its own gate; unknown flag refused atrequiresFeature; both-present AND-composition from a string and from an envelope; unchanged without the key; insideObjectSchema.create; and two@ts-expect-errorcompile pins: an action lowered into'visibleWhen'and a field group lowered into the default slot),packages/platform-objects/src/identity/sys-organization-group-structure-gate.test.ts(off hides, on shows, emptyfeaturesfaults and hides, only that group gated, declaration order kept), and a#30692describe in the lint harnessruntime-gate.object-field-group-visibility-writes.test.ts(three lowered shapes publish clean at the door and the build; the unlowered key is clean; five hand-writtenfeaturesshapes keep the verdict; the prescription namesrequiresFeature).ab3d3a94:@objectstack/spec818/818 files, 25833 tests;@objectstack/lint195/195, 8226;@objectstack/platform-objects117/117, 1585; the three typechecks exit 0;check:generatedexit 0.Ablations (one per changed behaviour)
dist/:scripts/ablation-replace.mjsreplaced the transform with one that keeps the group and plantsABLATION_30692(anchor 1 → 0, blob47ddc173→c0bfefc3); spec rebuilt;ablation-dist-preflight.mjsfound the marker in 10 built files. Red: spec pin 4 failed / 2 passed; platform-objects 4 failed (the three group-structure cells and the guard's forward rowsys_organization.fieldGroups.group_structure is gated on multiOrgEnabled). Restored to the HEAD blob withgit diff HEADempty; spec rebuilt; preflight--absentclean over 592 files and the tree; the same two files green, 118/118.rawinstead ofwithoutLoweredFeatureGate(raw)(anchor 1 → 0, blob7b9a6ec1→85289f1e). Red: the three lowered-shape CONTROL cells; restored to the HEAD blob,git diff HEADempty.Acceptance notes
featuresroot is not opened on group predicates generally (ruling row 4 B was not chosen): only the exact trailing gate the lowering emits is accepted. A hand-written predicate byte-identical to that lowered form is accepted too, because it is the same gate.visibleWhenis fail-closed, where an action'svisibleis fail-soft. Reading a key thatfeaturesdoes not carry is a CEL fault (No such key), notnull, so withfeatures = {}the default-on gate faults and the group is hidden. The console holdsfeatures = {}until/auth/configresolves (AppContent.tsx), and keeps it if that fetch fails. So on a multi-organization deployment Group Structure is hidden for the pre-fetch window and appears when the config arrives; if the fetch fails it stays hidden for that session.getPublicConfig()in plugin-auth always publishesmultiOrgEnabled. A host that never publishesfeatureswould hide the group everywhere. The semantics table inpublic-auth-features.ts, the field-group docblock and the changeset say so, andsys-organization-group-structure-gate.test.tspins the empty-scope reading.multiOrgEnabledis on.id,created_atandupdated_atkeep their undeclaredgroup: 'System'(thefield-group-undeclaredadvisory fires for those three, where before it fired for all twelve fields); declaring a System group would surfacecreated_at/updated_aton the record page, a change the card did not ask for.Verification
Head
559cf504(review follow-up on24375ec4; merges cleanly withorigin/main2038c56e), with spec and platform-objects rebuilt:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 105 families on559cf504; all 105 exit 0, each code captured to a file before any pipe.--ran: 105 derived, 105 run, 0 NOT-MEASURED, 0 UNRUN. Its STALE TREE note names onlyscripts/pm/**fleet tooling that moved on main, none on this diff's surface.pnpm lint,pnpm check:cli-command-ids,pnpm check:adr-symbol-anchors,node scripts/check-i18n-bundles.mjs: exit 0.packages/speccheck:generated(incl. api-surface and test-typecheck): exit 0.vitest runin each package):@objectstack/spec908/908 files, 27374 passed + 1 todo;@objectstack/lint195/195, 8235;@objectstack/platform-objects117/117, 1590.WithRequiresFeaturereplaced with an inert key;tsc -p tsconfig.test.jsonthen reports both@ts-expect-errorpins as unused (TS2578); restored from the saved copy, marker count 0.!in the title, no narrowing arm).packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.ts; this PR touches the*.objects.generated.tsand*.metadata-forms.generated.tsbundles beside them, not those files.🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cyCnzVPr9GuD38DdGt