Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-core,metadata): split the form-view predicate root vocabulary per surface so a field-level current_user test is not false-flagged - #13005
Conversation
📓 Docs Drift CheckThis PR changes 2 package(s): 28 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 11 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 1de1916065f64e3e3a1424b0d63a4c1b49af4e47 && git checkout 1de1916065f64e3e3a1424b0d63a4c1b49af4e47
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin feab4bff9d0c01e0dbf7db52f5b6e57eb3b03f2b 330e4b85bf23470017a120ad44f3b9f9a5b20b42 && git checkout -B drift-repro feab4bff9d0c01e0dbf7db52f5b6e57eb3b03f2b && git merge --no-ff 330e4b85bf23470017a120ad44f3b9f9a5b20b42
node scripts/docs-audit/affected-docs.mjs --json feab4bff9d0c01e0dbf7db52f5b6e57eb3b03f2b
|
Uh oh!
There was an error while loading. Please reload this page.
Part of #12915 — a same-day correction to the boot notice that landed in PR #12989. The card stays open (scope A remains deferred and recorded on it).
What was wrong
#12989 judged every form-view predicate against one vocabulary —
record/previous/parent/data— sourced faithfully from the contract prose. For the field-level slot that prose was stale.current_user, and its ADR-0068 alias roots (user,ctx.user,os.user), resolve on a field-levelvisibleWhensince objectui#6010. Three spec text sites still said otherwise until PR #12930 re-measured them, and one of those sites was the exact sentence the policy was written against — it landed onmainat 12:47, after the fresh read and while #12989 was in flight.Consequence in the shipped detector: a legacy artifact carrying a legitimate
current_user.role == "admin"field predicate was reported as faulting open. That is the cry-wolf class the card explicitly forbids, and the module header additionally stated a measurably false contract claim.The fix
The vocabulary is now per surface, which is what the contract says. Verified by fresh-reading current
origin/mainrather than taking the hand-off at its word:BOUND_FORM_FIELD_PREDICATE_ROOTS: the shared base pluscurrent_user,user,ctx,os. Silent on all of them.BOUND_FORM_VIEW_PREDICATE_ROOTS, unchanged in both name and value: the base alone.current_useris still flagged there, becauseFormSectionSchema.visibleWhenstates it is unbound at that level and the predicate faults open.The traversal already knew which slot it was scanning, so the split is a parameter rather than a second scanner; findings now carry a
surfacefield.Two limits of the field binding deliberately do not change the answer, and the module says why: it is a rendering rule rather than authorization (an authoring hazard, not a version-drift one), and the host scope is empty on the console's public
/f/:slugroute — equally true of a freshly built current artifact, so it says nothing about the artifact's ERA, which is the only thing this notice claims to detect.Warn-line wording. Printing one flat list would now either understate the field vocabulary (reading to an operator as "your legitimate
current_userpredicate is broken") or quote a section rule at someone with no section findings. It now prints the bound roots per surface, and only for the surfaces the findings actually implicate.Stale quotes corrected in the module header, the vocabulary docblock and the door method's docblock, each citing #12930 and objectui#6010, and noting the mid-flight drift so the next reader knows why a merged PR needed a same-day correction.
unboundRootsInCelSourcetakes the vocabulary as an optional second argument. Its default is unchanged (the stricter base), so any existing caller behaves exactly as before; the default is deliberately the strict one, so a caller that forgets to say gets a findable false positive rather than a silent miss.Verification
Union re-run on the final commit
330e4b85b.pnpm --filter @objectstack/metadata-core --filter @objectstack/metadata test— 15/15 files, 260/260 tests (was 255: +5 new); 37/37 files, 674/674 tests (was 672: +2 new). Every pre-existing pin unchanged and still green.current_user/user/ctx/osproduce zero notices, at both the policy and the door level; a section-levelcurrent_userpredicate is still flagged; findings carry the deciding surface; the same predicate is judged differently per surface; the warn line prints only the implicated surface's vocabulary.pnpm --filter @objectstack/metadata-core typecheckexit 0, withtsc -p tsconfig.test.json --listFilesconfirming it still reaches both changed metadata-core files (1 hit each).packages/metadatahas notypecheckscript (DEBT ledger entry, dts build is its type gate):pnpm --filter @objectstack/metadata buildexit 0.node scripts/check-type-check-coverage.mjs --re-measureunderNODE_OPTIONS=--max-old-space-size=4096—none above its recorded number,surplus: none.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackand diffed against the previous round: the family set is identical, so no newly-implicated gate went unrun. All 21 path-matched plus the 6 convention-triggered families exit 0;check:dispatcher-error-vocabularyexit 0 (no new SCREAMING_SNAKE code literal).pnpm lint— full repo, exit 0.node scripts/pm/check-half-states.mjsexits 3 withPREREQUISITE NOT MET — the anonymous API rate limit is exhausted for this egress IP. A transport refusal, not a red gate; the form CI runs (check:pm-half-states=--self-test) exits 0.Ablation, targeting exactly what this patch adds: collapse the field vocabulary back onto the section base — reintroducing the defect — rebuild, and confirm the new pins go red.
packages/metadata/vitest.config.tscarries no alias for@objectstack/metadata-core, so the door suite resolves it throughdist/; both legs therefore rebuilt and both proved what reacheddist/. Predicted direction was stated before running (the field-levelcurrent_usersilence pins go red). Observed: door2 failed | 7 passed, policy4 failed | 22 passed, and every failure is a per-surface-split pin — no incidental collateral. Mutation confirmed on disk by counting the injected marker (1) and the replaced anchor (0) before the rebuild, then indist/byscripts/ablation-dist-preflight.mjs(present in 2 built files). Restore leg:git checkout HEAD -- PATH,git diff HEADempty, restored blob byte-identical to the HEAD blobdcc5963d525c5769dc05835a30564ab16d11d8ad, rebuild, preflight--absentclean, both suites green again at 9/9 and 26/26.Declared narrowing — verification ran UNLOCKED.
scripts/pm/os-verify-lock.shcould not take the shared verify lock on this host: no usableflock. The shared verify lock is declared Linux-only (flockis util-linux, and a stock macOS does not ship it), so the commands were run directly, without the lock — a declared narrowing, not a silent one. No serialization guarantee held for these runs, nor for any sibling agent in this container while they ran.Generated by Claude Code