fix(react): stop advertising app as a bound expression-scope root — the #8164 post-merge audit residue, swept as a class - #8867
Conversation
objectui#8164 removed `app` from `buildExpressionScope` (the objectui#8155
option-B ruling) on three faces inside app-shell, but never asked which OTHER
surfaces state the same fact. The post-merge tier audit found the residue.
The blocking one: `@object-ui/react`'s `SCOPE_TIER_ADVICE['app-shell']` — the
paragraph printed in production when a predicate cannot be evaluated — still
read "plus `app` and `features`". It is the line an author sees at exactly the
moment a stale `app.*` predicate faults, so it answered "why did this not
resolve?" by naming the root that is the reason, and its own byte-pin
(`expect(msg).toContain('`app`')`) held the false sentence in place.
Swept as a class, not as those two coordinates: every place in the tree that
stated `app` was a bound expression-scope root — diagnostic copy, ambient-scope
docblocks in react / core / components / plugin-detail / plugin-form / app-shell
/ console, a README, and fourteen test fixtures that transcribed the old bag —
is corrected. The two app-shell fixtures now call `buildExpressionScope` instead
of transcribing it, so that pair cannot drift again.
Also corrects two false sentences in the release-bound changeset:
objectstack#16420 was closed `not_planned` 2026-09-07 (not left open), and the
consequence of a stale `app.*` predicate is not uniformly "fails open" — it is
per surface, and the seven directions are now measured and listed.
Nothing unrelated to `app` moved: no export line added or removed anywhere in
the diff, no barrel or package manifest touched, and the `app` prop and React
context field on `ExpressionProvider` (never CEL roots) are untouched.
Part of #8155
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
PM 评审 —— PR #8867(卡 #8155,post-merge 审计残留):通过,已翻 ready、auto-merge 已武装
⭐ F1 按类做了,而不是按我给的那两个坐标我的派发原话是「你的第一项工作不是改这两行,是枚举」。交付的正是这个:先扫,再修 —— 1 个阻塞成员 + 19 个其他成员,三条互相独立的探针形状(scope-bag 字面量带 我在 PR 头上复核了阻塞那一个:
⭐ 而这一半最有价值的不是那 20 处,是两处 app-shell fixture 从「手抄那个 bag」改成了「调用 ⭐⭐ F3:两边都对,而且是被测出来的,不是从文本里挑边的审计说 conditional-formatting fail-closed;卡上 5600986967 那条说 visibility predicate fail-open。我的派发要求「⛔ 不要从文本里挑边,逐表面测量」。交付回来的是一张七行表,每一行都在合并后的头上实测、且同一次运行里有一条可解析的对照(
⇒ 发布绑定的 changeset 里那句笼统的「unresolvable visibility predicates fail open」,对它自己点名的三个表面里的两个是错的。 现在它列出七个方向,并说明为什么这两个方向升级后需要相反的检查。 ⭐ 这正是「两个来源一致不构成一次读数」的反面用法:两个来源不一致时,答案往往是它们说的不是同一个东西 —— 而唯一能证明这一点的是逐表面测量。 F2 是查 API 得到的,⛔ 不是读文本
钉子是跨包三面的,理由说得比钉子本身更重要文案在 ⭐ 并且它同时钉整段文本,理由是:一次改写式的重新引入可能绕过 消融:方向在跑之前就预测了(两个包里四个具名单元变红,其余全绿),盘上先证 blob 与 marker,结果与预测一致,再按状态还原。 Clause-②
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
CI red at
|
| ledger line | row | shifted? | verdict |
|---|---|---|---|
:928 |
react/src/hooks/useExpression.ts:163 useExpression |
+2 from line 12 | RED |
:784 |
plugin-form/src/WizardForm.tsx:361 WizardForm |
no — single hunk at 534, 1140 → 1140 | green |
The WizardForm row is a ledger row pointing into an edited file that correctly stayed green, because the edit sits below the block. That is what makes the single failure a discrimination rather than a blanket, and it is the control the patch must not move.
What is actually owed
⛔ Repairing :928 alone is a repair, not a fix. This is a coupling keyed by position, not by content — which is why this PR's own 20-member enumeration of "states app is a bound expression-scope root" could not have caught it: the ledger says nothing about app, it merely points at a line number. The enumeration axis for a PR is not only what does my change say but what does my change move.
So the dispatched round owes an enumeration of every position-keyed reference into the 38 files, in both sub-classes — machine-read keys, which go red, and prose File.ts:NNN claims in comments and changesets, which go silently stale with no gate to report them. Cheap filter already measured and handed over: 22 of the 35 non-changeset files shifted line numbers, 13 did not, with each file's first-hunk line, so a reference above the first hunk is safe and one below it is suspect.
⛔ The gate will not be skipped, loosened, or exempted, and the row will not be deleted — its own header says a debt that can be declared once and never re-examined is a mute button rather than a ledger. The key gets repaired; codes: [18004] gets re-verified.
⛔ The PR will not be widened: any pre-existing stale position reference this diff did not move lands as a 验收备注 with a named successor, not as diff.
Generated by Claude Code
…falsified
The ledger in `scripts/check-doc-example-types.mjs` is keyed by
`path:line symbol`, so it is coupled to POSITION, not to content. This PR's
docblock edit to `useExpression.ts` (+2 lines at line 12) moved the `@example`
it exempts from :163 to :165, and the row went stale -- RED, exactly as the
gate header's fifth verdict says a row naming a vanished block should.
Repaired the row's key rather than deleting the row: `codes: [18004]` is still
the truth about that block (the gate re-derives both TS18004 shorthand
diagnostics at the new position), so the declared debt still exists and only
its address moved.
Enumerated the whole class -- every position-keyed reference into the 38 files
this PR touches -- in both halves:
* machine-read keys: extracted the actual key sets of both doc gates from
their object literals rather than grepping. 90 rows in
check-doc-example-types, exactly two of which point into a touched file:
the repaired one and `WizardForm.tsx:361`, which correctly stayed green
because the WizardForm edit sits below the block it exempts.
check-doc-component-types has ZERO position-keyed rows; its `path:line`
text is all prose in `reason` strings and header comments.
* prose claims: 14 references moved, but movement is only a necessary
condition. Ten of the fourteen were ALREADY FALSE at the merge-base --
they cite code that had drifted long before this branch -- so shifting
them by the delta would not have made them true. Repaired only the ones
this diff falsified: `crud.ts`'s two `action.method` citations and
check-action-forward-parity's citation of the execute-gates docblock.
Cites the convention objectui#7853 already ruled (cite the assertion by
content, not by line address), mechanized for test names as the
`no-line-address-in-test-name` rule (objectui#8047).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Fixes #8155
Post-merge audit residue owed on #8155 / merged PR #8164, per the contract-review verdict on that PR (comment 5602158340, CHANGES REQUIRED — follow-up owed on
main, no revert) and the PM dispatch on #8155 (comment 5604933514). ⛔ This is not a revert: the batch #67 option-B ruling is implemented correctly by #8164 and is untouched here.F1 — the class, not the two coordinates
PR #8164 removed
appfrombuildExpressionScopethree-sidedly inside app-shell and never asked which other surfaces state the same fact. Enumerated first, then fixed. The class predicate: "no place in the tree still states thatappis a bound expression-scope root."The blocking member the audit named:
packages/react/src/utils/visibilityDiagnostic.ts:154-158,SCOPE_TIER_ADVICE['app-shell']— published@object-ui/reactoutput, printed at exactly the moment a staleapp.*predicate faults. It read "…plusappandfeatures", i.e. it answered "why did my predicate not resolve?" by naming the root that is the reason, and its own byte-pin (expect(msg).toContain('\app`')atadviceTier.test.ts:182`, whose comment still quoted the pre-#8164 bag) held the sentence in place.19 other members found by sweep (
git grepover every.ts/.tsx/.md/.mdx, three independent patterns: scope-bag literals with anappkey, root-list prose, andapp-as-root claims near bind/scope/root/predicate):react/src/utils/visibilityDiagnostic.ts(message + tier docblock)react/src/SchemaRenderer.tsx,react/src/hooks/useExpression.ts,core/src/actions/ActionRunner.ts(ActionParamDef.visible),core/src/evaluator/listConditional.ts(RowPredicateOptions.scope),components/src/renderers/form/form.tsx,components/src/renderers/layout/containers.tsx,plugin-detail/src/renderers/record-alert.tsx,plugin-form/src/WizardForm.tsx,app-shell/src/providers/ExpressionProvider.tsx:313,app-shell/.../metadata-admin/predicate.ts,app-shell/.../inspectors/ConditionBuilder.tsx,apps/console/src/components/FormPage.tsxplugin-form/README.mdreact,components,plugin-detail,plugin-form,app-shell,apps/console.changeset/6487-visibility-advice-per-tier.md(unreleased; it quotes the bag and says the line namesapp)The two
app-shellfixtures that hand-transcribed the bag now callbuildExpressionScopeinstead — that pair cannot drift again, which is the mechanism-over-transcription half of the fix.⛔ Deliberately NOT touched:
skills/**(governed surface, already filed as #8810 — one governed path would move this whole PR onto the human-merge route);data(#8166's);celAuthoring.ts(suppressing the diagnostic is the lenient-fallback shape AGENTS.md #0.1 bans); theappprop and theappfield onExpressionProvider's React context value (never CEL roots —DashboardViewreads it as a plain value);os(advertised on measured evidence, not reopened).The pin — three faces, modelled on #8164's
The message text itself is pinned, and the pin reddens if
appreturns on any face. Because the copy lives in@object-ui/reactand the bag lives in@object-ui/app-shell, neither package can pin the pair alone, so the coupling cell lives where both are reachable:react/.../visibilityDiagnostic.adviceTier.test.ts— census (not.toContain('\app`')over the whole message) **and** a byte-exact pin of the paragraph, so a re-worded re-introduction that slips past the census still reddens. Plus a labelled control on the node tier, which never namedapp` and must not move.app-shell/.../ExpressionProvider.visibleFaultDiagnostic.test.ts— face 1 (copy) and face 2 (buildExpressionScopehas no ownappkey) asserted against the real printed line; and face 3, the invariant: every root the advice names is a root the bag really binds. The reverse (bound but unadvertised) stays legal, becausedatais deliberately that.Ablation — from the committed tree, mutation proved on disk before running
Direction predicted before the run: four named cells red in two packages, everything else green.
Restored by state, not by exit code:
git hash-objectback to57b5bd62…=HEADblob,git diff HEADempty,git status --porcelainempty, marker counts back to 0 / 1. The mutation ran under atrap … EXIT INT TERMwith an absolute path.F2 / F3 — the release-bound changeset now says only what was measured
.changeset/7727-conditional-formatting-record-scope.mdcarried two false sentences.F2 — measured, not read from the text:
GET /repos/objectstack-ai/objectstack/issues/16420→state: closed,state_reason: not_planned,closed_at: 2026-09-07T04:16:22Z,closed_by: os-zhuang. It did not stay open as "the record to reopen". Corrected in the changeset and inExpressionProvider.tsx, which repeated it.F3 — the audit and comment 5600986967 are BOTH right, about different surfaces. ⛔ No side was picked from the text; every surface was measured on the committed tree with a resolvable control (
record.status == 'open'→true) firing in the same run:app.*nowconditionresolveConditionalFormatting→evalRowPredicate(fallback: false){}— rule stops matching)visible/disabledevalRowPredicate(fallback: false)false)visible—action-button/action-menu/action-baruseCondition(…, throwOnError)false)visible—action-icon/action-groupuseCondition(default)true)visibleWhen(form field rules)resolveFieldRuleState→evalFieldPredicate(fallbacktrue)visible: true)visibleWhen(app-shell object field), nav / areavisibleisObjectFieldVisible/evaluateVisibilitytrue)readonlyWhen/requiredWhenresolveFieldRuleState(fallbackfalse)false)So the changeset's blanket "unresolvable visibility predicates fail open" was wrong about two of the three surfaces it named. It now lists all seven directions and says why they need opposite post-upgrade checks. The measurement probe was temporary and is not in this diff.
F4 / F5 — filed as cards, not as diff
Dedup searched before filing, each with a control query that returned results in this run (
total_count16 on objectui, 3 on objectstack, 1 for thepage.zod.tsword-shape):packages/spec/src/ui/page.zod.ts:303/:326.describe()still says "the shipping renderer additionally mountsapp…", verified verbatim on objectstackorigin/main. ⛔ No cross-repo code change here.appexpression-scope root (PR #8164, batch #67) ship as a straight removal, or with a migration note / release-note line / deprecation window? #8866. The fail-open / fail-closed consequence as a decision-inbox item: straight removal vs migration note vs release-note line vs deprecation window, with the measured cost table and the cost of each option. ⛔ The card rules on nothing.needs-user-decisionlabel to reach the inbox — the filing seat does not apply labels, so that is owed to the PM.Clause-② —
no, measured three wayscheck-widening-tells.mjs --declaration no --diff→ exit 0, "37 changed file(s) read, no widening tell on any declared surface".git diff BASE -- '*.ts' '*.tsx' | grep -E '^[+-][^+-].*\bexport\b'→ no output: not one export line added or removed anywhere in the diff.index.tsand nopackage.jsonin the diff → no barrel and noexports-map movement.The only published value that changed is a string literal inside an already-exported function's lookup table — neither a new exported symbol nor a new key on a published payload.
Verification
check-changeset-presencewas red before (34 source files of 7 released packages, 0 changesets) and is green after:.changeset/8155-app-root-residue-swept.md,@object-ui/react: patch. Editing the existing finding(app-shell/metadata-admin): the conditional-formatting CEL editor lints row predicates in theflattenedscope and advertises bare fields — declared-but-unbound once #5741 (Phase 2) retires the bare-field binding #7727 changeset does not count — the gate reads added changesets.turbo run type-check --concurrency=2over the 7 affected packages: 42 successful, 42 total (each package's script istsc --noEmit && tsc -p tsconfig.test.json, so the test files are inside the program).vitest runover all 26 modified test files plus every enumerated reader of this copy: 26 files, 546 tests passed.vitest run packages/react/ packages/plugin-form/ packages/plugin-detail/: 316 files, 3172 passed, 1 skipped.vitest run packages/core/ packages/app-shell/src/providers/ packages/app-shell/src/views/metadata-admin/ packages/components/src/__tests__/ packages/components/src/renderers/{form,layout,action}/: 617 files, 7631 passed, 1 skipped.@object-ui/app-shell,@object-ui/componentsand@object-ui/console. Their share of this diff is comment-only insrc/plus test fixtures, every modified test file was run above, and all three type-check green including theirtsconfig.test.jsonprograms.check:spec-symbols✅ (this diff is mostly JSDoc, and fix(app-shell): lint conditional-formatting in the record scope, and align the predicate scope to the engine's root vocabulary #8164's last commit was a JSDoc reword to clear exactly this gate) ·check-control-bytes✅ ·check:comment-mask-corpus✅ (report-only, residue unchanged) ·check:action-ref-convention✅ ·check-doc-links.mjs✅ (a published README changed) ·check-governed-queue-guard --test→ NOT GOVERNED, 38 paths, 0 matches.pnpm lint(=turbo run lint, the whole farm) is CI's run, not this seat's.Patch round 2 — the position-keyed coupling this PR's line shifts broke
Test (shard 3/4)went red at1fab6542, and it is this PR's failure, not #8757's known main-redness (that card is aboutstripImportedDefaults' own@example, a different symbol):Reproduced red locally at
1fab6542before any edit —Tests 1 failed | 50 passed (51)— and the gate itself named the repair rather than my taking a predicted number on trust:The class
UNGATED_EXAMPLESis keyed by`${block.file}:${block.line} ${block.symbol}`. That is a coupling keyed by position, not by content — so this PR's enumeration axis was never only what does my change say (the 20app-root members above) but also what does my change move. The ledger says nothing aboutapp; it merely points at a line number. This PR's single hunk atuseExpression.tsline 12 (+2) shifted the@examplefrom:163to:165, and the row went stale — RED, the fifth verdict in the gate header's own list.Repaired the key, did not delete the row:
codes: [18004]is still the truth about that block, verified by the gate re-deriving both TS18004 shorthand diagnostics at the new position. The debt still exists; only its address moved.Both halves of the class, enumerated
Sub-class 1 — machine-read position keys. Extracted the actual key sets from both gates' object literals rather than grepping them, because a grep of a ledger reads its prose as readily as its keys:
check-doc-example-types.mjscheck-doc-component-types.mjspath:linetext is all prose inreasonstrings and header comments, not keysThat second row is the re-derivation, not an inherited reading: the sibling ledger has no machine-read position key at all, so its 5
path:lineoccurrences cannot go silently wrong the way a key can. Its one substantive claim,ActionRunner.ts:112, sits above that file's first hunk (631) and did not move.Sub-class 2 — prose position claims. A probe over every tracked file for all 35 non-changeset basenames, in four syntaxes —
name.ts:NNN,name.ts#LNNN, and "line NNN" before or after the name — returned 73 hits, of which the permalink form is one abasename:[0-9]+grep cannot see. Path-aware resolution (a written path suffix beats a bare basename) reduced that to 14 references that moved.⭐ Movement turned out to be necessary but not sufficient, and the difference is most of the finding. A reference is this PR's to repair only if it was true at the merge-base. Checked every one against the base tree, and 10 of the 14 were already false before this branch existed:
form.tsx:1652—const rules: any = { ...validation }...validationis at 2479form.tsx:1428— the renderer awaitsonSubmitawait onSubmitProp(formData)is at 2223containers.tsx:1692—page:headerpublishesactionsastype: 'array'ActionRunner.ts:816— the runner accepts either params shapeActionRunner.ts:1014—handler(action, this.context)ActionRunner.ts:1038— readsbodyto replace "no script provided"Shifting those by the hunk delta would not have made a single one true — it would have moved a wrong pointer to a differently wrong place while making the diff look diligent. They are left alone and recorded under 验收备注 below.
The four addresses this diff actually falsified, and repaired:
check-doc-example-types.mjsledger keyuseExpression.ts:163 @example:165types/src/crud.tsdocblockActionRunner.ts:1787—method = action.method:1788types/src/crud.ts, same sentence`:1793`—method = config.method || action.method:1794check-action-forward-parity.mjsmessageActionRunner.ts:773— the execute-gates docblock:774That third row is a continuation address — a bare
`:1793`with no filename, carried by the same sentence. No basename-anchored probe can see it; it was found by reading the site the probe did hit. Recorded because the same shape exists elsewhere in the tree and will bite the next enumeration.Controls — one per sub-class, in both directions
⭐ A lit control proves the probe runs, not that its shape can see the thing. All four assert on the content now standing at the cited line, not on the number:
useExpression.ts:165ledger key* @exampleWizardForm.tsx:361ledger row@examplecrud.ts→ActionRunner.ts:1788/:1794action.methodlinescheck-doc-component-types.mjs→ActionRunner.ts:112ALL CONTROLS PASS. TheWizardFormcontrol is the load-bearing one: a ledger row pointing into a file this PR edited that correctly stayed green, proving the gate discriminates by position rather than blanket-failing every touched file. Had the patch reddened it, the patch was wrong.The convention already exists
⛔ Not re-derived here: objectui#7853 already ruled this class — cite the assertion by CONTENT, not by line address — landed as
fa7d66c45, with #6548, #6998, #7289, #7913 and #8045 as the one-at-a-time repairs that followed, and objectui#8047 as its mechanical form, theno-line-address-in-test-nameESLint rule. That rule deliberately carves out comments and failure messages, on the reasoning that "a human reads them beside the code they annotate". Every stale reference found above is a cross-file citation, where that justification does not hold — the reader ofcrud.tsis not looking atActionRunner.ts. See 验收备注.Runs
All at
898f7e004unless stated.1fab6542(Tests 1 failed | 50 passed (51)) → green (Test Files 1 passed (1) · Tests 51 passed (51)).node scripts/check-doc-example-types.mjs: exit 2PREREQUISITE NOT METuntil the packages were built (read as NOT MEASURED, not as red), then exit 1 red at1fab6542, then exit 0:Examples: 125 block(s) — 35 compile, 90 fail, 90 of those declared in the ledger (90 row(s)). Was89 of thosebefore.scripts/__tests__/suite (it owns every doc gate's own tests, including both gate scripts this patch edits): 132 files passed, 2 skipped · 3833 tests passed, 2 skipped.node scripts/check-changeset-presence.mjs— measured, not assumed: exit 0.35 source file(s) of 8 released package(s) changed, and this change declares 1 changeset(s).crud.tsis publishedsrc/, so this patch does add published source, and the existing.changeset/8155-app-root-residue-swept.mdalready covers it; ascripts/-only patch would not have needed one, but this one is not scripts-only.node scripts/check-action-forward-parity.mjs(the gate whose message string moved): exit 0.node scripts/check-doc-component-types.mjs(the sibling ledger): exit 0,Every documented component type is registered.@object-ui/types:type-checkexit 0 (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json), and its suite via the root runner: 162 files, 3182 tests passed.pnpm --filter @object-ui/types testmatched zero scripts and exited 0 — that package has notestscript; that call is reported as NOT MEASURED and the suite was run through the root vitest instead.node scripts/check-control-bytes.mjs: exit 0, 7061 tracked text files. Plus a direct control-character scan over the three edited files: no hits.node scripts/check-governed-queue-guard.mjs --testover all eight candidate paths: NOT GOVERNED, 8 paths, 0 matches against 5 governed surfaces.eslint . --no-inline-config --format jsonover 4665 files (population read from eslint's own config, count read from the JSON). The three edited files carry 0 errors;crud.ts's 2 warnings are pre-existingno-explicit-anyat lines 89 and 245, nowhere near the edited line 57. Type-aware linting is not enabled (noproject/projectServiceineslint.config.js), so this diff cannot move any untouched file's verdict.Clause-②: no. Three comment- and string-literal line-number repairs; no new exported symbol, no new key on a published payload, and the ledger row count is unchanged at 90 (a key was re-addressed, not added). Measured rather than inherited from the parent verdict:
check-widening-tells.mjs --declaration no --diff→ exit 0 on the patch round (3 changed file(s) read, no widening tell) and exit 0 on the full PR diff (41 changed file(s) read, no widening tell).验收备注
containers.tsxstill copiesctx.appfrom the ambient predicate scope. Post-fix(app-shell): lint conditional-formatting in the record scope, and align the predicate scope to the engine's root vocabulary #8164 no producer supplies that key, so it is dead rather than wrong; removing it would change a key from present-and-undefinedto absent, which is a runtime fail-direction change and out of this card's class. The comment above it no longer claims the ambient scope bindsapp. Noted, not filed — the next PR to touch this header-predicate path is the one that should carry it..changeset/6293-conditionbuilder-reference-value.mdlistsappamong "roots this builder never offers". That is a statement about what the builder does not capture, not about what is bound, so it was left as-is.Added by patch round 2 — the position-citation class
plugin-form.mdx:141,plugin-form/README.md:261and:926,plugin-form/CHANGELOG.md:324:366:394,action-forward-parity.test.tsx:156,action-forward-precedence.test.tsx:139,check-action-forward-parity.mjs:295and:566,layout/src/index.ts:101. Noted, not filed.file:linecitation inside a test NAME is read by nothing — five recurrences under a standing ruling, and per-instance repair has not closed the class #8047 deliberately carves out comments and failure messages because "a human reads them beside the code they annotate" — a justification that does not hold for a cross-file citation, which is what all ten of these are. The right owner is a successor card in that line, extending the rule (or a report-only census) to cross-filepath:linecitations. Deduped before proposing: the only two matching cards, finding(scripts):js-comment-mask.mjscites two line numbers as evidence and both point at the wrong line (86 and 107 lines off) #6998 and finding(plugin-markdown): toc-anchor-parity cites packages/react/README.md by line address (:224) and the heading has moved to :253 — a citation nothing checks #7913, are both closed per-instance repairs, and no open card covers the class. Handed to the PM to file rather than filed from this seat, since filing it is not this PR's scope.plugin-form/CHANGELOG.md's three sit under the released heading## 17.6.0. A changelog entry is a dated record of what was true at that release and is regenerated from changesets; re-addressing it to today's tree would make it false as history.crud.ts:57carries`ActionRunner.ts:1787` and `:1793`— the second address has no filename. It was repaired here only because reading the site the probe hit revealed it. Other instances of the shape exist in tree (data-display.ts,chart-series-keys-7546.test.ts,check-doc-component-types.mjs's:290-:293); none point into a file this PR touches. Noted, not filed — it belongs to the same successor card.check-doc-component-types.mjs:597cites "action vocabulary declared atActionRunner.ts:112", but line 112 is the closing*/of a docblock andActionDefopens at 113. Off by a line, unmoved by this PR, same class. Noted, not filed.Part of the audit residue; the two remaining halves are objectstack#17203 and #8866. #8810 stays where it is. ⛔ Nothing about #7727's own board state was touched by this PR.
Authored by the
domain:uiagent seat in Claude Code sessionsession_01611D6ZaRaMmwTNQmSbk8MH, on branchclaude/issue-8155-post-merge-audit-followup.Generated by Claude Code
Generated by Claude Code