Skip to content

fix(lint): skill tool references stay a whole-stack verdict; the runtime door never judges them - #19898

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-19527-skill-tool-verdict-whole-stack
Sep 24, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-19527-skill-tool-verdict-whole-stack

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #19527

Clause-②: no

This carries out ruling 5791822697 on #19527 (batch 215 item 4, letter B, maintainer 「215 同意」): 「cross-item reference resolution belongs to the whole-stack rule」. ai-skill-tool-unresolved is reported by os validate / os lint / os build. The runtime publish door never emits it. RuntimeStackContext and CONTEXT_STACK_KEYS are unchanged (ruling item 3), and no rule was added (item 2).

What was measured first, on origin/main @ 1f89ba0d70 (before any edit)

The measurement ran from a scratch tsx script against packages/lint/src. I deleted the script afterwards and did not commit it.

probe reading
runtimeGatedTypes() 17 types: action app book dashboard dataset datasource email_template flow hook mapping object page permission position report seed view. skill is not one of them
runtimeAuthoringRulesFor('skill') / stackKeyForType('skill') [] / null
the wiring guard's unmapped set, runtimeGatedTypes().filter(t => stackKeyForType(t) === null) []
the real door, a skill write naming a stack-level action / a plugin tool / a tool that exists nowhere errors: [] · advisories: [] · rulesRun: [] for all three
the card's reproduction: validateAiToolReferences on the door's snapshot shape (objects permissions books datasets, with actions dropped by the builder) ai-skill-tool-unresolved for all three
the same member over the whole stack (with stack.actions) stack-level action: [] · plugin tool: ai-skill-tool-unresolved · missing tool: ai-skill-tool-unresolved
a flow write, which dispatches the suite with this member on its frozen flow default 0 ai-skill-tool-unresolved: no door snapshot carries skills (WRITTEN_STACK_KEYS has no skills)

⚠️ The door does not emit the false advisory on main today. skill has been outside the door since #19542, and the card says so itself ("Nothing is broken today"). The advisory appears only if the type is crossed. The reproduction is the rule run on the door's snapshot shape (the LIT case), and ablation 1 below produces it through the real door. So this PR changes no runtime behaviour. It turns a hold-out that was waiting on a wider snapshot into a design boundary, and it pins both halves of the ruling. I re-ran the measurement script after the edits and its output was byte-identical to the base run.

Which of the two shapes the wiring guard allows: skill stays OUTSIDE the door

The guard (authoring-rule-wiring.test.ts, 「every runtime-gated metadata type maps to a stack key」) checks runtimeGatedTypes(). That set is built from rule runtimeTypes declarations, not listed by hand. runRuntimeAuthoringRules returns empty at rules.length === 0, before it looks at TYPE_TO_STACK_KEY. So a type with an empty runtime rule set is not a gated type, and "crossed with an empty rule set" can only be written in two ways:

  • declare skill on some entry (for example the reference-integrity suite) with no member claiming it. Then rulesRun names a rule that judges nothing, which is the ruling's ⛔ on inventing a rule to give the door something to run.
  • add a skill: 'skills' row with no rule declaring it. The gate never reads such a row, which is the mapping-ahead-of-its-rules shape runtime-gate.ts refuses (the seed: 'data' note).

So both absences stay, and the guard holds with skill outside it.

The change

Comments are restated from 「held out on a measurement」 to 「by design: cross-item reference resolution belongs to the whole-stack rule」, citing the ruling. Each place also drops the old "cross it once actions / tools are carried" direction, which the ruling rejected:

  • packages/lint/src/authoring-rules.ts, the skill block on the validateReferenceIntegrity entry.
  • packages/lint/src/reference-integrity-suite.ts, the validateAiToolReferences member. This member held the measurement and still prescribed widening the snapshot. It now cites ADR-0109 Decision §3 as the governing text.
  • packages/lint/src/runtime-gate.ts, the skill note on TYPE_TO_STACK_KEY.
  • packages/lint/src/runtime-gate.inert-type-writes.test.ts: the file header, the GROUP_A docblock, the skill block and the report/skill fence comment. The two DARK pins keep their assertions, and their reason text now points at the ruling.
  • packages/lint/src/runtime-gate.datasource-writes.test.ts: two comments that call skill "held out on the measurement". This is the second place that pins both halves (runtimeAuthoringRulesFor('skill') is [] and stackKeyForType('skill') is null).

The LIT case 「one skill, one rule, two universes」 stays as the reason pin. Its snapshot-bound assertion now says what a red means under the ruling: someone widened RuntimeStackContext against item 3. It no longer says skill may now cross.

New cases in the skill block of runtime-gate.inert-type-writes.test.ts:

  • (a) it.each over three tools (a stack-level AI-exposed action, a plugin-registered tool, a tool that exists nowhere). A skill write through the real door returns errors [], advisories [] and rulesRun []. The in-process firing control: the member the door does not dispatch does return ai-skill-tool-unresolved on the door's snapshot shape for each of the three.
  • (a) No write type can carry a skill into a door snapshot. No gated type maps to skills, and skills is not a context key.
  • (b) runAuthoringRules for each of validate, build and lint over a whole stack reports exactly [{ path: 'skills[1].tools[0]', severity: 'warning' }]: the tool that exists nowhere is reported, and the stack-level action is not.
  • (c) The guard invariant holds over the real tables, and a skill write's rulesRun is [].

Firing controls: ablations on the committed tree (eb4f0cae09)

Each mutation went through node scripts/ablation-replace.mjs in wrap mode. The tool checked the anchor count and blob hash on disk after each write, and each restore was confirmed as blob == HEAD with an empty git diff HEAD. No build step was involved: every subject is a relative import inside packages/lint, so vitest reads src.

# mutation red green
1 cross skill fully: suite entry runtimeTypes + member runtimeTypes: ['flow','skill'] + skill: 'skills' row 9: all three (a) cases, the (a) no-other-write case, (c), both DARK pins, the report/skill fence, and the datasource DARK pin. The (a) failure is the exact false advisory through the real door: advisories: [{ rule: 'ai-skill-tool-unresolved', path: 'skills[0].tools[0]' }] for action_showcase_portfolio_snapshot 68, including the wiring guard (a full crossing is consistent)
2 declare skill on the suite entry only 3: the repo's own 「every runtime-gated metadata type maps to a stack key」, (c), DARK 64
3 delete the validateAiToolReferences member (b): os validate must still report the tool that exists nowhere: expected [] … none (run with -t, only (b) selected)
4 drop addActionFamily(stack.actions) from collectToolUniverse (b) gets 2 findings instead of 1, and the LIT lit-control leg fails none (run with -t, only (b) and LIT selected)

The first attempt at ablation 1 did nothing: its third anchor was a substring of its own replacement, ablation-replace refused with "anchor count moved 1 -> 1", and no test ran. All three files were confirmed restored. It was re-run with a non-overlapping multi-line anchor, and those are the readings above.

Downstream readers of ai-skill-tool-unresolved

I ran git grep over packages/, skills/, content/, examples/ and apps/. None of these expects the runtime door to emit the rule id, so none needed updating: content/docs/ai/agents.mdx:166, content/docs/ai/tools.mdx:92, packages/cli/src/commands/generate.ts:394, packages/spec/src/ai/skill.zod.ts:373, skills/objectstack-ai/SKILL.md:85, packages/mcp/src/skill-prompts.ts:17, packages/lint/src/reference-integrity-suite.test.ts:362 and packages/lint/src/validate-ai-tool-references.test.ts. Each one describes a whole-stack (os validate) verdict, which is the ruled state. The only door-facing readers were the lint tests edited above. @objectstack/metadata-protocol (runtime-authoring-gate.ts, the only source file that imports @objectstack/lint/runtime) has no skill-specific door logic.

Changeset: @objectstack/lint patch

The dispatch assumed this changes runtime behaviour. The measurement shows it does not (see above). The repo rule still asks for a changeset, because the published bytes change. @objectstack/lint ships dist, and its build keeps source comments: after the rebuild, the ruling id is in dist/index.js, dist/index.cjs, dist/runtime.js and dist/runtime.cjs (3 hits each, one per restated block), and 「held out on a measurement」 is in none of them. That phrase was in authoring-rules.ts on the base, so it shipped there. Under AGENTS.md, skip-changeset is only for a diff that publishes nothing from a released package, so this takes a patch. There is precedent for comment-only changes that ship to dist (for example .changeset/15295-serve-observability-mirror-comment.md).

Clause-② reading: no, with no arm. No authorable key or accept set moves in either direction. The door judged no skill write before this PR and judges none after it, and the whole-stack verdict is unchanged. It is neither widening nor narrowing.

Local verification, at HEAD fd206c3164

  • pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2: 108 files / 4127 tests passed.
  • pnpm --filter @objectstack/lint typecheck (tsc --noEmit and check:test-typecheck): exit 0. check:test-typecheck: OK. tsc -p tsconfig.test.json --listFilesOnly includes both edited test files.
  • pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2 (the package that wires the runtime gate): 188 files passed, 3 skipped · 2673 tests passed, 19 skipped.
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, derived again after the changeset commit: 59 commands, all run. --ran reports: 「59 derived famil(ies) accounted for — 59 run, 0 NOT-MEASURED」. The first pass got exit 3 (PREREQUISITE NOT MET) from check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt. After a full turbo run build --filter='./packages/*' --filter='./packages/*/*' (72/72 tasks), all three measured green at the same HEAD.
  • Scoping, declared: the repo-wide pnpm test and pnpm lint are left to CI (Test Core shards, Lint & Repo Gates). origin/main has moved 4 commits past the base, and none of them touches packages/lint, packages/metadata-protocol or packages/spec/src/ai.

Acceptance notes

  • validateAiToolReferences keeps its frozen flow default, so it still runs on every flow write. It judges nothing there, because a flow snapshot has no skills. I considered an explicit empty member runtimeTypes to make "never" structural, but did not make that change, since it would change which members run on flow writes and the ruling did not ask for it. The new (a) case pins that "never" holds for every write type.
  • The pair card 19477 (written without the sigil on purpose) still answers 404, as the claim comment recorded.

Generated by Claude Code

…e runtime door's

The skill hold-out at the runtime publish gate is restated from a
measurement awaiting a wider snapshot to a design boundary: cross-item
reference resolution belongs to the whole-stack rule, so
ai-skill-tool-unresolved is reported by os validate / os lint / os build
and never by the runtime door. The door snapshot carries neither
stack.tools nor stack.actions, and no snapshot can carry a
plugin-registered tool, so the member has no truthful unresolved verdict
there. RuntimeStackContext and CONTEXT_STACK_KEYS are unchanged.

skill stays outside the door (no runtimeTypes declaration, no stack-key
row): the wiring guard's invariant is over runtime-gated types, which are
derived from rule declarations, so a type with no rule to run owes no row.

Pins: the door gives no tool-reference verdict for a skill naming a
stack-level action, a plugin tool or a missing tool (with an in-process
firing control on the door's snapshot shape); no write can carry a skill
into a door snapshot; the whole-stack runner still reports the missing
tool on validate, build and lint; the guard invariant holds. The LIT
reason pin stays.

Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
Co-authored-by: Claude <noreply@anthropic.com>
Comment-only source change that ships: the reworded blocks are emitted
into @objectstack/lint dist, so the package takes a patch changeset
rather than skip-changeset. No behaviour change.

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint, touching 3 documentable anchor(s).

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

  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
What this run could not see

Coarse fallback — 4 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 dabf8d795ee9279c18b4b00bfabb322544106b2f → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 1259b0454b4d735940d3fa7d3e4afadf36dcaa8f — the merge of head fd206c3164f033f08ba9b06caa7afc6864b5b3f7 into base dabf8d795ee9279c18b4b00bfabb322544106b2f, 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 1259b0454b4d735940d3fa7d3e4afadf36dcaa8f && git checkout 1259b0454b4d735940d3fa7d3e4afadf36dcaa8f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dabf8d795ee9279c18b4b00bfabb322544106b2f fd206c3164f033f08ba9b06caa7afc6864b5b3f7 && git checkout -B drift-repro dabf8d795ee9279c18b4b00bfabb322544106b2f && git merge --no-ff fd206c3164f033f08ba9b06caa7afc6864b5b3f7

node scripts/docs-audit/affected-docs.mjs --json dabf8d795ee9279c18b4b00bfabb322544106b2f

⚠️ 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 dabf8d795ee9279c18b4b00bfabb322544106b2f → 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: 54/54 CONTRACT_REVIEW_TIER
Head-sha: fd206c3164f033f08ba9b06caa7afc6864b5b3f7

Isolated at-tier reviewer subagent, run by the domain:spec seat-4 session on the maintainer's instruction in that session (「帮我处理」, with the landing route chosen there); every one of its 54 transcript turns served at the tier the constant names. Adopted by the seat 2026-09-24T05:35Z. The record below is the reviewer's, unedited except the two header lines.

Refs read: refs/review/pr-19898 = fd206c3164f033f08ba9b06caa7afc6864b5b3f7 (2 commits over merge-base 1f89ba0d704a797225ab8cf306e4cdec89edae8e; PR base dabf8d795ee9279c18b4b00bfabb322544106b2f is an ancestor of origin/main = c1641868a3da71537c9c6c572d2bd2044103236b). Ruling B is card comment 5791822697 on #19527 (items 1–3). pulls/19898/files: 6 files, all under packages/lint/src/ plus one .changeset/ entry; no review threads; commits/fd206c3164…/check-runs: 34 runs, 31 success, 3 skipped (Console Pin Gate, Build Docs, Packed-tarball smoke opt-in), 0 failures — Check Changeset, Lint & Repo Gates, Test Core 1–6, Governed Surface Queue Guard, single-writer-path and same-issue guards all green.

① Derived judgments

Accept-set / behaviour: none moves — verified, not trusted. For each of the three source files the non-comment changed-line filter is empty: git diff origin/main refs/review/pr-19898 -- packages/lint/src/authoring-rules.ts | grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' | grep -vE '^[+-]\s*(//|\*|/\*)' → (empty); same for reference-integrity-suite.ts and runtime-gate.ts. validate-ai-tool-references.ts (the emitter, AI_SKILL_TOOL_UNRESOLVED at :39, severity: 'warning' at :226, three-limb collectToolUniverse at :144-165) is not in the PR file list. AUTHORING_COMMANDS = ['validate', 'build', 'lint'] at refs/review/pr-19898:packages/lint/src/authoring-rules.ts:153 is unchanged; the validateReferenceIntegrity entry keeps commands: ALL (:813) and runtimeTypes: ['flow', 'view', 'object', 'dataset', 'report'] (:939). Correct against ruling B: the whole-stack verdict is exactly where it was.

Regression in loudness: none, because no door judged skill before. At origin/main: git grep -nE "runtimeTypes.*skill" origin/main -- 'packages/lint/src/*.ts' ':!packages/lint/src/*.test.ts' returns only a comment line (reference-integrity-suite.ts:463), and TYPE_TO_STACK_KEY (origin/main:packages/lint/src/runtime-gate.ts:75-175) has no skill: row. Mechanically the door returns before any judgement: runtimeAuthoringRulesFor filters AUTHORING_RULES by runtimeTypes (refs/review/pr-19898:packages/lint/src/runtime-gate.ts:549-554); runRuntimeAuthoringRules has if (rules.length === 0) return empty; at :891, ahead of the snapshot builder, which itself returns null for an unmapped type at :622-623. So a genuinely unresolved tool on a skill write was silent at the door on main and is silent at head; os validate / os build / os lint warned before and warn after. The ruling asked for exactly this (item 1: NEVER emitted by the door).

Ruling item 1 (restate the hold-out and its two pins as by-design, citing the ruling; LIT stays): done, right. authoring-rules.ts:907-931 (the skill block on the validateReferenceIntegrity entry), runtime-gate.ts:161-166, reference-integrity-suite.ts:463-506 (the member's docblock, which on the base still prescribed route A: "Crossing it needs actions / tools carried in RuntimeStackContext + CONTEXT_STACK_KEYS…") now all cite 5791822697 and drop the route-A direction. The two DARK pins keep their assertions (runtimeAuthoringRulesFor('skill') → [], stackKeyForType('skill') → null, runtimeGatedTypes() not containing skill; member runtimeTypes not containing skill) with reason strings restated (runtime-gate.inert-type-writes.test.ts:513-534); the LIT case 「one skill, one rule, two universes」 is kept at :610-680 and its snapshot-bound message now reads a red as a widening of RuntimeStackContext against item 3 rather than as "cross skill now" — the right reading.

Ruling item 2 (report which shape the guard allows, land that one): reported OUTSIDE, and the reasoning holds. The guard is over runtimeGatedTypes() (refs/review/pr-19898:packages/lint/src/authoring-rule-wiring.test.ts:383-393), which is DERIVED from rule declarations (runtime-gate.ts:556-563); with no rule declaring skill, the invariant asks nothing of it. The two alternatives are both refused by the ruling or by the table's own note (runtime-gate.ts:93-99, the seed: 'data' inert-mapping shape). No rule was invented.

Ruling item 3 (no snapshot widening): honoured. CONTEXT_STACK_KEY_ORDER at runtime-gate.ts:510-515 is objects, permissions, books, datasets, unchanged; RuntimeStackContext (:281) is untouched (comment-only diff); @objectstack/metadata-protocol is not in the file list and its door still hands exactly those four collections (refs/review/pr-19898:packages/metadata-protocol/src/runtime-authoring-gate.ts:675-680, CLOSURE_CONTEXT_KEY_BY_TYPE at :397-402).

New pins: weight-bearing on the ruling's two halves. (a) it.each over three tool names through the real door asserts errors [], advisories [], rulesRun [] and, in the same process, that the undispatched member DOES fire on the builder-derived door snapshot (inert-type-writes.test.ts:571-596) — crossing skill on the entry turns rulesRun non-empty and red. (a') no gated type maps to skills and skills is not a context key (:598-609). (b) runAuthoringRules over every AUTHORING_COMMANDS value on a stack carrying actions reports exactly [{ path: 'skills[1].tools[0]', severity: 'warning' }] (:611-631) — deleting the member or the stack.actions limb reds it; the runner reads run.parsed ?? run.normalized (authoring-rules.ts runAuthoringRules body), so the { normalized } call reaches the parsed-input entry. (c) the guard invariant over the real tables plus a skill write's rulesRun → [] (:529-545).

Governing text truthfully restated. ADR-0109 Decision §3 at origin/main:docs/adr/0109-ai-tool-authoring-model.md:69-77 names validate-ai-tool-references in @objectstack/lint, the universe stack.tools ∪ platform registry ∪ the materialised action_ family from stack.actions and objects' actions, severity warning, and "a runtime plugin outside the registry remains statically invisible, and the runtime deliberately tolerates unresolved names" — matching the new comments and the changeset body.

Left advising falsely: nothing found. Every downstream reader of the rule id describes an os validate / whole-stack verdict, which is the ruled state: content/docs/ai/agents.mdx:165-166, content/docs/ai/tools.mdx:91-93, packages/cli/src/commands/generate.ts:393-395, packages/spec/src/ai/skill.zod.ts:372-373, skills/objectstack-ai/SKILL.md:85, packages/mcp/src/skill-prompts.ts:17 (all at refs/review/pr-19898). The docs-drift-flagged page content/docs/deployment/validating-metadata.mdx mentions skill only at :717 (a formula-skill link); its runtime-publish column never listed skill, so it needs no edit. git grep -n "on a measurement" refs/review/pr-19898 -- packages/lint → only the history paragraph in the test file (:485), which is deliberate.

Required and missing: nothing. Beyond the ruling: nothing functional (see ③ for the file-surface note).

② Semver level

patch on @objectstack/lint (.changeset/19527-skill-tool-verdict-whole-stack.md, package at 17.4.0, all packages in one fixed group per .changeset/config.json). Correct ceiling and floor: no export, type, authorable key, accept set or runtime string moves (comment-only source diff, verified above), so minor is unavailable; skip-changeset is reserved by origin/main:AGENTS.md:1081-1085 for a diff that publishes nothing from a released package, and this package publishes dist built by tsup (packages/lint/package.json:32, packages/lint/tsup.config.ts), where the repo's own recorded measurements say source comments ride into dist/*.js (.changeset/17676-package-registry-docblock-capability-name.md:15, .changeset/18614-conversion-registry-retryconfig-liveness-claim.md:9 — the latter a registry-array entry comment in a tsup-built package, the same position as the three blocks here; .changeset/16270-org-record-tab-strip-provenance.md:42-44). Clause-②: no in the PR body is right (nothing widens or narrows); AGENTS.md :1095-1096 requires the line inside the changeset body only for a breaking changeset. Check Changeset is green at head.

③ Boundary flags

  • non-blocking — file surface wider than the claim's literal list. Claim comment 5796885591 named authoring-rules.ts, "its two pins", runtime-gate.inert-type-writes.test.ts, the wiring file and .changeset/; the PR also edits packages/lint/src/reference-integrity-suite.ts (the member docblock that still prescribed route A) and packages/lint/src/runtime-gate.datasource-writes.test.ts (a third pin restating "held out on the measurement"). Both are comment-only restatements of the same hold-out, inside the claimed package; the claim's census declared only authoring-rules.ts and runtime-gate* CLEAR and did not census reference-integrity-suite.ts, but the No other open PR may claim the same single-writer path check-run is success at head.
  • non-blocking — one stale wording left. refs/review/pr-19898:packages/lint/src/runtime-gate.inert-type-writes.test.ts:161 still reads "skill is held out — asserted absent in its own block, not here." Test-file comment, not shipped; the ruling's restatement asked for "by design" wording.
  • non-blocking — the pending spec(lint): wire the inert runtime-create rules for action / hook / report / skill / email_template / mapping — six types, one edit (ruling #203/4 group A+C) #19474 changeset still publishes the rejected route. origin/main:.changeset/19474-six-inert-runtime-create-doors.md:25 ends "Crossing it needs actions / tools carried in RuntimeStackContext … and its own card." It is another card's unconsumed input and out of this claim; when both compile into the same CHANGELOG the later entry supersedes it, but a reader grepping the rule id will meet the route-A prescription first.
  • non-blocking — whole-stack judgement is not reached on runtime-mint paths, by ruling. skill is allowRuntimeCreate: true (origin/main:packages/spec/src/kernel/metadata-plugin.zod.ts:1199); a skill written through Studio / REST /meta / MCP passes the metadata-protocol door with no tool-reference verdict (runtime-authoring-gate.ts:667-680 → runRuntimeAuthoringRules → early return), and only the CLI callers (packages/cli/src/commands/{validate,compile,lint}.ts, utils/artifact-packages.ts, utils/scaffold-validate.ts) reach validateAiToolReferences. This is the state on origin/main and the outcome ruling B chose over route A; recorded so the maintainer sees the accepted gap named, not because the PR changed it.
  • non-blocking — the "plugin-registered tool" leg is mechanically the "exists nowhere" leg. acme_plugin_lookup is registered by nothing in the test, so the member treats it exactly as no_such_tool_anywhere; the pin still holds (door silent, member fires), the label is narrative.
  • non-blocking — dist comment survival not re-measured here. The changeset's rationale rests on the dev's stated rebuild measurement (3 hits per dist file) and on the three repo precedents cited under ②; per the review contract no build was run locally.
  • No deviation from ruling items 1–3; no pin found that fails to bear weight; no governed surface touched (GOVERNED_SURFACES at origin/main:scripts/pm/check-governed-merges.mjs:1055-1061 lists docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md, docs/NORTH-STAR.md — none in the file list).

Implemented-by: claude/issue-19527-skill-tool-verdict-whole-stack
Reviewed-by: session_019c3Hi6ZMU1p6m6aA6Bz45d

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 24, 2026 05:41
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit fd920ca Sep 24, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-19527-skill-tool-verdict-whole-stack branch September 24, 2026 06:03
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 size/m tests tooling

Projects

None yet

1 participant