Skip to content

feat(lint,spec)!: refuse a holeless ${record.*} / ${result.*} action copy sentence, the last action-copy warning - #25989

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-8vtd0b
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-8vtd0b

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #25709

Clause-②: no (narrowing)
Metadata-protocol: narrowed

What changes

The console's action runner no longer fills ${…} in an action's copy: ActionRunner.confirmMessage and composeSuccessMessage in ui/packages/core/src/actions/ActionRunner.ts return a sentence with no {{ }} hole as written (objectui#13476, in the imported ui/ tree on main). So the one action-copy shape the doors still kept a warning, a holeless ${record.*} confirmText or ${result.*} successMessage, is now an error at objectstack validate, compile and the publish gate, like every other ${…} token.

  • packages/lint/src/validate-action-copy-templates.ts: isTransitionalDollar, its warning branch and the TRANSITIONAL suffix are deleted. Every closedScopeTemplateRefusal finding is now refuse(...).
  • packages/spec/src/ui/action-copy-template.ts: ACTION_COPY_DOLLAR_TRANSITION_ROOTS is retired (and its export in ui/index.ts). The module note and the confirmText / successMessage describes no longer say the shape is a warning; the stale .objectui-sha wording now names the ui/ tree.
  • packages/spec/src/automation/flow-text-slot-template.ts: dollarOnlyTokenRoots is retired; the lint branch was its only reader.
  • Stale wording fixed: packages/lint/src/authoring-rules.ts, validate-expressions.ts (two comments), the conformance ledger rows template-action-confirm-text / template-action-success-message.
  • Generated: api-surface/ui.json, api-surface/automation.json, export-origins/ui.json, export-origins/automation.json, and the reference pages the describes feed (content/docs/references/ui/action.mdx, ui/component.mdx, data/object.mdx, kernel/metadata-plugin.mdx), all through check:generated --fix.

Landing point: as the order expected, packages/spec/src/ui/action-copy-template.ts and packages/lint/src/validate-action-copy-templates.ts. No producer carries the shape (below), so nothing moves on the producer side.

The two retired exports are not in @objectstack/spec@18.1.0 (the latest published version; read from its npm tarball: 0 hits for either name in dist/, against hits for closedScopeTemplateRefusal, the control). No published version carries them.

Producer measurement (before the crossing)

Every action-copy sentence reachable from each module's exports, walked on the base 38050ced and judged through the built @objectstack/lint:

producer confirmText successMessage errorMessage with a ${…} token holeless ${…} shape lint copy findings
examples/app-crm 0 2 2 0 0 0
examples/app-todo 1 8 0 0 0 0
examples/app-showcase 6 24 0 0 0 0
examples/app-multi-package 0 0 0 0 0 0
@objectstack/platform-objects 68 256 16 0 0 n/a
@objectstack/plugin-approvals 0 9 0 0 0 n/a

Count: 0 sentences of the shape in examples/**, and 0 in the platform packages. Control: a planted Delete ${record.name}? confirmText on each example stack draws the finding (a warning before this change). A text scan of examples, packages/platform-objects, packages/plugins and packages/services for a confirmText / successMessage line carrying ${ also answers 0. (The platform-objects counts include translation-map copies the deep walk reaches, so they are an upper bound on sentences, not a declaration count.) HotCRM was not re-measured here.

The pending #25680 release note is corrected in this PR

.changeset/25680-action-copy-refusal.md is still pending (not yet released). Two of its sentences become false with this change, and they would ship in the same release: "Still a warning. …" (the holeless shape) and "@objectstack/spec adds two exports: ACTION_COPY_DOLLAR_TRANSITION_ROOTS … dollarOnlyTokenRoots". This PR rewrites exactly those two sentences (plus "all but one" in the summary sentence) and adds its own changeset, .changeset/25709-action-copy-holeless-dollar-refusal.md.

This is the DELIBERATE CORRECTION class check-empty-changeset.mjs names, so Check Changeset stays red on purpose (route 0 of its message). Confirmation requested in writing on this PR that the rewrite of the #25680 note is wanted. skip-changeset is not applied.

Pins flipped (load-bearing)

  • packages/lint/src/validate-action-copy-templates.test.ts: the holeless ${result.email} / ${record.name} case now asserts severity: 'error', its location, its {{ }} prescription and no "A warning, not a refusal"; the action:group / inline-action case asserts four errors; the publish-gate cases assert one error on the error channel and an empty advisory channel for a page write and an action write.
  • packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts (c): through the real saveMetaItem, the holeless ${record.*} page save now answers 422 INVALID_METADATA with one expression-invalid error prescribing {{ record.name }}, and nothing is stored.
  • Spec: the ACTION_COPY_DOLLAR_TRANSITION_ROOTS and dollarOnlyTokenRoots unit pins are deleted with their subjects; the export snapshot (api-surface/) pins their absence.
  • Repo-wide sweep for the same meaning ("still fills", "transitional", "A warning, not a refusal, for now", the two symbol names): every hit outside ui/ and released text is in this diff.

Evidence

Final head a3b0809d.

  • Ablation (committed fix first; scripts/ablation-replace.mjs with a trap restore): the refusal branch was mutated back to a warning for a holeless ${…} sentence. Lint unit file: 6 failed / 11 passed (the flipped pins). Rebuilt @objectstack/lint, ablation-dist-preflight confirmed the marker in 4 built files, then the protocol gate file: 1 failed / 210 passed (pin (c)). Restore: blob equals HEAD, git diff HEAD empty, rebuilt, --absent preflight clean.
  • Tests: @objectstack/lint 163 files / 7335 tests passed; @objectstack/spec 722 files / 21881 passed (1 todo); protocol.runtime-authoring-gate.test.ts 211/211; expression-conformance.test.ts 8/8 on a3b0809d. Typecheck: @objectstack/lint, @objectstack/spec, @objectstack/metadata-protocol exit 0.
  • Derived gates (dispatch-gates.mjs --commands, 113 families, --ran reconciliation: 113 derived, 113 run): green except check-empty-changeset (the declared correction above, exit 1); check:type-check-debt and check:dual-build-cjs-loads exited 3, PREREQUISITE NOT MET (no lock turn for the re-measure's closure build; unbuilt packages outside this closure): NOT MEASURED locally, CI's Lint & Repo Gates runs both. check:skill-examples, check:dts-closure and check-plugin-teardown-shape --self-test first read a dist mid-rebuild or a shallow clone and were re-run green. check:doc-authoring found a tracker id in the ledger string; fixed in a3b0809d and re-run green. check-platform-checklist --changed-from origin/main: OK, 0 anchors lost. check:generated: all 14 artifacts up to date.
  • Dogfood battery (owed by the (narrowing) arm): pnpm --filter @objectstack/dogfood test under the verify lock, launched at 5a6ed48a; the ledger string edit of a3b0809d landed on disk 43 s into the run, so expression-conformance.test.ts was re-run on a3b0809d (8/8). VERDICT command-exit 1: Test Files 1 failed / 295 passed / 3 skipped; Tests 1 failed / 2522 passed / 18 skipped. The one failure is test/authz-probe-blind-spot.test.ts "packages/runtime/src/domains/i18n.ts — every positive control is still present in THAT file" (control handleI18nRequest expected 3, read 4). This diff touches nothing under packages/runtime or that test, so the failure does not come from it (inferred from the file list; the test was not re-run on the base 38050ced).

Acceptance notes

  • Contract review at CONTRACT_REVIEW_TIER is owed (narrowing).
  • outcomeMessages and ActionSchema.errorMessage stay outside the family, unchanged.

Generated by Claude Code

…copy sentence

The console's runner no longer fills ${…} in an action's copy (objectui#13476,
in the ui/ tree), so the one shape the doors kept a warning is now an error.
Retire ACTION_COPY_DOLLAR_TRANSITION_ROOTS, dollarOnlyTokenRoots and the
isTransitionalDollar warning branch; flip the pins that held the warning.

Claude-Session: https://claude.ai/code/session_0148U1UfHrMrKAyJbiSs1ZYr
Co-authored-by: Claude <noreply@anthropic.com>
…s for the retired transition exports

Claude-Session: https://claude.ai/code/session_0148U1UfHrMrKAyJbiSs1ZYr
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/spec, touching 13 documentable anchor(s). ⚠️ 5 changed file(s) yielded no anchor (packages/spec/api-surface/automation.json, packages/spec/api-surface/ui.json, packages/spec/export-origins/automation.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/actions-as-tools.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/ai/connect-mcp.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/api/client-sdk.mdx (via errorMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/automation/flows.mdx (via errorMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
  • content/docs/getting-started/build-with-claude-code.mdx (via successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/protocol/kernel/i18n-standard.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/protocol/objectui/actions.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), errorMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/protocol/objectui/record-alert.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/ui/actions.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), errorMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/ui/translations.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))

⛔ 4 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-1.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), errorMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/releases/v17/17-5.mdx (via errorMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/releases/v17/17-7.mdx (via successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))
  • content/docs/releases/v18/18-1.mdx (via confirmText (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), errorMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS), successMessage (symbol, a field of const object ACTION_COPY_DOLLAR_TRANSITION_ROOTS))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 5 changed file(s) yielded no anchor (packages/spec/api-surface/automation.json, packages/spec/api-surface/ui.json, packages/spec/export-origins/automation.json, …) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 144 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 05f7758f054d70f3ba0f709b37423124be3e6e3d → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 7773245a2f0b1660afe4063a13db880385929b99 — the merge of head a3b0809dd24087fabd8546299106ab1b4f8643b6 into base 05f7758f054d70f3ba0f709b37423124be3e6e3d, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 7773245a2f0b1660afe4063a13db880385929b99 && git checkout 7773245a2f0b1660afe4063a13db880385929b99
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 05f7758f054d70f3ba0f709b37423124be3e6e3d a3b0809dd24087fabd8546299106ab1b4f8643b6 && git checkout -B drift-repro 05f7758f054d70f3ba0f709b37423124be3e6e3d && git merge --no-ff a3b0809dd24087fabd8546299106ab1b4f8643b6

node scripts/docs-audit/affected-docs.mjs --json 05f7758f054d70f3ba0f709b37423124be3e6e3d

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 05f7758f054d70f3ba0f709b37423124be3e6e3d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a3b0809dd24087fabd8546299106ab1b4f8643b6
Local-runs: none
Scope: packages/spec/src/automation/flow-text-slot-template.ts, packages/spec/src/ui/action-copy-template.ts, packages/spec/src/ui/index.ts + packages/lint/src/validate-action-copy-templates.ts, packages/lint/src/validate-action-copy-templates.test.ts, packages/lint/src/authoring-rules.ts, packages/lint/src/validate-expressions.ts, packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts, packages/qa/dogfood/test/expression-conformance.ledger.ts, packages/spec/api-surface/ui.json, packages/spec/api-surface/automation.json, packages/spec/export-origins/ui.json, packages/spec/export-origins/automation.json, packages/spec/src/automation/flow-text-slot-template.test.ts, packages/spec/src/ui/action-copy-template.test.ts, .changeset/25680-action-copy-refusal.md, .changeset/25709-action-copy-holeless-dollar-refusal.md, content/docs/references/ui/action.mdx, content/docs/references/ui/component.mdx, content/docs/references/data/object.mdx, content/docs/references/kernel/metadata-plugin.mdx; read on main for the claims the hunks make: ui/packages/core/src/actions/ActionRunner.ts (confirmMessage, composeSuccessMessage), packages/runtime/src/domains/i18n.ts and packages/qa/dogfood/test/authz-probe-blind-spot.census.ts (the reported dogfood red), scripts/check-empty-changeset.mjs (the DELIBERATE CORRECTION route). Narrowing face: every non-prose path of the diff is in scope; no remainder.

Inputs: card #25709 (body and all 7 comments, the os-dev-report 6411273906 included), PR #25989 (body, 21-file list, diff against its merge base 05f7758), the 47 check-runs on the head. The one local checkout read is origin/main at cdd4eea; main has since moved 3 commits past the PR base with no overlap on any touched path or on ActionRunner.ts (compare 05f7758...main).

Gate verdicts on the head

47 check-runs, all completed: 37 success, 9 skipped by workflow condition (Lint UI, Test UI, Temporal Conformance, Dogfood Verify CLI, the 3 dogfood shards, the two opt-in harnesses), 1 failure: Check Changeset. Its annotations name exactly one cause: .changeset/25680-action-copy-refusal.md exists on the merge base and was not added by this PR (the #17712 foreign-changeset refusal), with the two-class remedy text; the same run posts a notice recognising the new changeset's ADR-0087 exemption. That is the DELIBERATE CORRECTION class of scripts/check-empty-changeset.mjs (route 0, remedy do NOT restore it -- say so on the PR and get it confirmed), declared on the PR body. Red by design, judged below in section ② sentence by sentence. Test Core (3/8) was in_progress at the first read and completed success before this record was rendered. Type Check · debt ledger and Lint & Repo Gates are green, which measures the two families the dev could not run locally (check:type-check-debt, check:dual-build-cjs-loads).

① Derived judgments

Premise (the card's precondition). RIGHT. On main, ui/packages/core/src/actions/ActionRunner.ts confirmMessage returns the text unchanged when hasTemplateHoles(text) is false, and composeSuccessMessage returns successCopy as written on the same test; only a sentence with a {{ }} hole reaches fillTemplateHoles. The only substituteScopeTokens call left on copy is the outcomeMessages rung, which the card keeps out of the family. The .objectui-sha precondition of the card body is superseded by the premise correction 6409937588 (the ui/ tree on main); the diff's rewording of the pinned .objectui-sha to the ui/ tree in the spec module note and the ledger rows is true.

Accept-set, the narrowing. RIGHT, and the only one. Before: at every door (objectstack validate, compile, the publish gate on an action, object or page write, and the draft promotion) a confirmText with no {{ }} hole whose every token is ${record.*}, or a successMessage of that shape over ${result.*}, drew closedScopeTemplateRefusal's message plus the TRANSITIONAL suffix at severity warning, rode the advisory channel, and the row was stored (lint isTransitionalDollar, protocol pin (c) on main). After: the same sentence is refuse(refusal, text), severity error, the write answers 422 INVALID_METADATA with one expression-invalid issue at the key, and nothing is stored. No other finding moves: every other branch of judgeAction was already refuse(...), and closedScopeTemplateRefusal itself is untouched, so the prescription text (Write X as Y with the {{ }} hole) is unchanged and the ${{ guard still holds.

Error envelope. RIGHT. Every refusal in the family now goes through the one refuse closure (where, message, source, severity error), which is the envelope validateStackExpressions already carried for the other findings; the protocol pin (c) is rewritten in the exact shape of pin (a) on main (422, INVALID_METADATA, rule EXPRESSION_INVALID, one issue, activePageRows(rows) empty) and additionally pins the {{ record.name }} prescription in the message. The lint publish-gate pins assert one error on the error channel and an empty advisory channel for both a page write and an action write.

Stored data. RIGHT, covered by measured absence plus an unchanged parse. The parse is not touched: actionCopyTemplateSlot and its blank arm are outside the diff, and the spec test still pins ok('Delete ${record.name}?') parsing to the template envelope, so a stored row carrying the shape keeps loading until its next save (as #25680 shipped it). The producer count on the PR body (0 sentences of the shape across the four example stacks, platform-objects and plugin-approvals, with a planted control drawing the finding) is consistent with the #25680 note's own measurement, where this shape was a warning finding and all producers drew 0 findings. ADR-0087 not-required (no-migration-prescription) is the #25702 precedent the card prescribes and the Check Changeset run recognises it.

Public surface. RIGHT. @objectstack/spec/ui loses ACTION_COPY_DOLLAR_TRANSITION_ROOTS and @objectstack/spec/automation loses dollarOnlyTokenRoots; both api-surface and export-origins snapshots drop exactly those two lines and nothing else, and both were the only lines #25702 added to those snapshots (git show 0c81694). Never published: the @objectstack/spec@18.1.0 tag is at e649c2d (2026-10-24 08:02Z), #25702 merged at 0c81694 (2026-10-24 21:17Z), no tag contains 0c81694, and the #25680 changeset is still pending on main. A repo-wide sweep on main for the two names, isTransitionalDollar and A warning, not a refusal, for now (ui/ included, node_modules and dist excluded) hits only files this diff rewrites; no reader is left. In @objectstack/lint the deleted TRANSITIONAL and isTransitionalDollar were module-private, so lint's public surface does not move. @objectstack/dogfood is private; @objectstack/metadata-protocol moves only in a test.

Describes and reference pages. RIGHT. The transition(root) sentence is deleted from the two describes and the four generated pages lose exactly that sentence (eight table rows); no hand-written page under content/docs states the warning (sweep for still fill, transitional, warning, not a refusal, and ${record. / ${result. in copy context, releases excluded: no hit). The Docs Drift Check comment lists pages by the field names confirmText / successMessage only; none of them restates the retired rule.

Ledger rows. RIGHT. Both enforcement strings now say a holeless sentence is shown as written, read in the ui/ tree; true against ActionRunner on main.

One wording left behind, non-blocking. packages/lint/src/validate-action-copy-templates.test.ts keeps two unchanged context comments inside the renamed refuses a ${…} token in every other shape too case: line 72 composeSuccessMessage substitutes ${result.*} alone and line 76 The ${…} evaluator does not bind the static params. Both describe the runner reading objectui#13476 retired; the assertions under them are right (every shape is an error). Not in the dev's sweep terms, so not a sweep miss by its own definition; a one-line follow-up, not a verdict.

② Semver level

Changeset .changeset/25709-action-copy-holeless-dollar-refusal.md: @objectstack/lint minor, @objectstack/spec minor, title with !, Clause-②: no (narrowing: ...), Metadata-protocol: narrowed, exactly one ADR-0087 marker, a FROM/TO block and a one-line fix. The PR body carries the same Clause-②: no (narrowing) and Metadata-protocol: narrowed. RIGHT for what the diff publishes: a value refused that saved with a warning is narrowed, which takes at least minor on both moved packages (AGENTS.md rule 3), and the retired exports never reached a published version, so there is no removal to carry a migration for; the changeset states that fact in the body. skip-changeset is correctly not applied. The spec's packages/spec/package.json is 18.1.0 and the pending #25680 note is in the same release, so the two entries ship together, which is what the next paragraph relies on.

The rewrite of the pending .changeset/25680-action-copy-refusal.md (DELIBERATE CORRECTION, Check Changeset red by design), each sentence before and after:

  1. Summary sentence. Before: They are now errors, all but one. TRUE on main (the holeless shape is a warning on main), FALSE after this diff. After: They are now errors. TRUE after this diff (every closedScopeTemplateRefusal finding is refuse(...)), and it is the sentence the release will ship, since both notes release together.
  2. The Still a warning. paragraph. Before: TRUE on main (isTransitionalDollar, ACTION_COPY_DOLLAR_TRANSITION_ROOTS, the for now suffix all exist there), FALSE after this diff on every clause, and it names a .objectui-sha that no longer exists. After: The holeless ${…} sentence too. ${record.*} in a confirmText and ${result.*} in a successMessage, in a sentence with no {{ }} hole and no other token, is refused as well: the console's runner shows it as written. Its own entry in this release says so (#25709). TRUE after this diff (the lint branch, the protocol pin, ActionRunner on main, and the new changeset in the same release).
  3. The exports bullet. Before: No schema shape moves. @objectstack/spec adds two exports: ACTION_COPY_DOLLAR_TRANSITION_ROOTS (...) and dollarOnlyTokenRoots (...). TRUE on main, FALSE after this diff. After: No schema shape moves, and @objectstack/spec adds no export. TRUE after this diff, judged against the released 18.1.0 snapshot: feat(lint)!: refuse an action's copy-template findings (blank, unbound root, single-brace, logic, raw-shown ${…}) at validate, compile and the publish gate #25702 added exactly those two lines to the api-surface and this diff removes exactly them, so the release's net export delta from the two notes is zero.

The rewrite touches nothing else in the note (frontmatter, declaration lines, the ADR-0087 marker and the measurement bullet are unchanged). Leaving the note as it stands on main would ship three false sentences in the same CHANGELOG entry, which is the class the gate's own remedy says not to restore. This record is the written judgment the gate's remedy asks for on the PR: each rewritten sentence is true against the diff and main.

③ Boundary flags

Dev deviations (os-dev-report 6411273906) and the one out-of-scope finding; open_questions is empty.

  1. .changeset/25680 edited beyond one changeset. ANSWERED: judged true sentence by sentence in ② ; the red Check Changeset is the designed route 0 and is the only red on the head.
  2. Test files and four generated reference pages edited outside the claimed file surface. ANSWERED, not a breach: the claim comment 6410153130 names the test files of these modules and 4 generated reference pages under content/docs in its file surface, and the pages regenerate from the describes through check:generated (Lint & Repo Gates green).
  3. Dogfood battery launched at 5a6ed48, one commit before the head. ANSWERED: compare 5a6ed48...a3b0809 is one commit, one file, one line (the tracker id taken out of the ledger's prose string in expression-conformance.ledger.ts, which check:doc-authoring had refused); the ledger's own test was re-run on the head (8/8) and Lint & Repo Gates on the head carries check:doc-authoring. CI's dogfood shards are skipped on pull_request by the workflow condition (ci.yml line 2198), so the local run is the battery the (narrowing) arm owes, and it is full: 299 files, one red.
  4. HotCRM not re-measured, though the card body names it. ESCALATED to the seat, with the standing reading: the spec + lint 18.x: refuse an action's copy-template findings (blank text, an unbound root, a ${…} or single-brace token) at validate / compile / the publish gate — the refusal half of #24814 (ruling 6365736632) #25680 note measured HotCRM at a4b3560 through the built lint whose warning branch fired for exactly this shape, and all 20 copy sentences drew 0 findings, so HotCRM carried 0 of the shape at a4b3560; anything HotCRM wrote after a4b3560 is unmeasured, and the repository is not reachable from this review session (objectstack-ai/hotcrm is not enabled here and code search is blocked), so this record cannot close it. A sentence of the shape there would fail HotCRM's own build, not a stored row, so the exposure is a downstream build red, not data. The seat re-measures at HotCRM's current sha or accepts the a4b3560 reading in writing.
  5. Out-of-scope finding, class a: dogfood red test/authz-probe-blind-spot.test.ts, control handleI18nRequest expected 3, read 4 on packages/runtime/src/domains/i18n.ts. CONFIRMED main's, not this diff's: i18n.ts at the PR base 05f7758 carries 4 occurrences against the census pin of 3 (authz-probe-blind-spot.census.ts line 1084), last moved by fix(runtime): GET /i18n/translations/:locale carries an ETag and Cache-Control: private, no-cache, and a matching If-None-Match is answered 304 (#25785) #25956 (69fa23d) on main; this diff touches neither file. Reported by the dev with dedupe words; a card is owed by the seat for the census count, outside this PR.

The dev's two NOT-MEASURED local families are measured green on the head by Type Check · debt ledger and Lint & Repo Gates. The ablation on the PR body (refusal branch mutated back to a warning: 6 lint pins and protocol pin (c) red, restore verified) is the dev's own evidence and is consistent with the pins this record read; it was not re-run here.

Implemented-by: claude/project-thread-8vtd0b
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:spec seat 2 · 2026-10-26T08:41Z · head a3b0809dd24087fabd8546299106ab1b4f8643b6

  1. Shape. Draft, base main, ! title, closes lint: once the objectui pin carries objectui#13476, refuse the holeless ${record.*} / ${result.*} action copy that ACTION_COPY_DOLLAR_TRANSITION_ROOTS still keeps a warning #25709. 21 files, +115/−178. check-governed-merges --pr 25989: NOT governed.
  2. Premise and stop conditions. The premise held: the console returns holeless copy as written (ActionRunner confirmMessage / composeSuccessMessage). The card's ordered producer measurement over examples/** found 0, and so did platform-objects and plugin-approvals. The landing point is as ordered. The claim's file surface was amended for the tests, the 4 generated pages and the spec + lint 18.x: refuse an action's copy-template findings (blank text, an unbound root, a ${…} or single-brace token) at validate / compile / the publish gate — the refusal half of #24814 (ruling 6365736632) #25680 changeset correction.
  3. Gates (check-runs pinned to this head). 47 runs: 37 success, 9 skipped, 1 failure. check-expected-skips --pr 25989: OK.
    • The failure is Check Changeset, red by design on the DELIBERATE CORRECTION class (route 0: this PR corrects the pending .changeset/25680-action-copy-refusal.md). The three conditions for landing past it hold:
      • .github/workflows/pr-automation.yml names this red as by design;
      • that workflow runs on pull_request only, never merge_group;
      • this comment records the gate and its cause.
  4. Contract review. PASS at CONTRACT_REVIEW_TIER on this head, comment 6411796404 (narrowing face, scope every non-prose path). The record judges each rewritten spec + lint 18.x: refuse an action's copy-template findings (blank text, an unbound root, a ${…} or single-brace token) at validate / compile / the publish gate — the refusal half of #24814 (ruling 6365736632) #25680 sentence: each was true on main, is false after the diff, and is replaced by a true one. That is the confirmation the DELIBERATE CORRECTION class requires.
  5. Narrowing. Clause-②: no (narrowing) and Metadata-protocol: narrowed on the PR body and the changeset. The dogfood battery ran at 5a6ed48a: 2522 of 2523. The head differs from it by one ledger string, and the battery was re-run 8/8 on it. The one red is main's authz-probe-blind-spot census on packages/runtime/src/domains/i18n.ts after fix(runtime): GET /i18n/translations/:locale carries an ETag and Cache-Control: private, no-cache, and a matching If-None-Match is answered 304 (#25785) #25956, which this diff does not touch. That red is anchored on card dogfood: main's Dogfood Regression Gate (3/3) is red since #25956 — the authz blind-spot census still pins handleI18nRequest at 3 in packages/runtime/src/domains/i18n.ts; #25956 made it 4 #25988.
  6. Accepted, not fixed here.
  7. Landing. Ready plus auto-merge, past the by-design Check Changeset red.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

2 participants