Skip to content

ci: wire the issue-citation verdict (blocking) and its census (report-only), plus the merged-result probe - #19259

Merged
os-elon-musk merged 12 commits into
mainfrom
claude/issue-18224-wire-issue-citations-to-ci
Sep 20, 2026
Merged

os-elon-musk merged 12 commits into
mainfrom
claude/issue-18224-wire-issue-citations-to-ci

Conversation

@os-try-charles

@os-try-charles os-try-charles commented Sep 20, 2026

Copy link
Copy Markdown
Collaborator

Fixes #18224

Two gates were registered in the root manifest and invoked by zero workflows:
scripts/check-issue-citations.mjs (delivered by #18223 / card #17512) and
scripts/check-merged-result.mjs (delivered by #18338 / card #16287). Both cards' declared
file surfaces excluded .github/workflows/**, so both devs correctly stopped and filed the
wiring rather than widening. This PR is that wiring.

The three entry points, and their postures

entry lane posture
the DIFF-scoped citation verdict lint.yml · Lint & Repo Gates blocking
check:merged-result self-test lint.yml · Lint & Repo Gates blocking
check-issue-citations --census half-state-patrol.yml (scheduled) report-only, never blocking

The census posture is a ruling, not a preference, and the card carries the measurement that
forces it: #16783, #16786 and #16787 were measured RESOLVING on 2026-09-10 and 404 on
2026-09-14, with no change to this tree.
A tree-wide blocking verdict would therefore red a
motionless repository because a third party deleted an issue. The diff-scoped half is the part
an author owns, it is small, and it is the only thing that stops 2,785 unresolvable sites
becoming 3,000.

The manifest alias is NOT the verdict

package.json maps check:issue-citations to node scripts/check-issue-citations.mjs --self-test
and nothing else -- the shape every credential-needing gate in this manifest uses
(check:pm-half-states, check:pm-closed-card-sweep), because a live mode needs a board and a
credential. Wiring that alias alone would have run the self-test twice and scanned nothing. So
the lint step holds two commands, self-test first:

pnpm check:issue-citations && node scripts/check-issue-citations.mjs

The second spelling is lint.yml's documented gate-invocation idiom (dispatch-gates.mjs's own
header names it), and the first is what check-self-test-wired requires of every script CI runs.

check:merged-result needed no such split: the manifest key already IS
node scripts/check-merged-result.mjs --self-test, which is the whole gate.

package.json is deliberately untouched

No new manifest key was added. Both keys already exist and both are now named by lint.yml, so
the wiring needs no manifest edit, and leaving the file alone keeps this PR textually disjoint
from PR #18414, which adds a key two lines from where a new one would have gone.

The non-step changes: issues: read AND pull-requests: read

⚠️ Heading corrected by the dispatching PM at 2026-09-20T07:25Z: this section was written when the block
gained ONE scope. It now adds TWO — pull-requests: read followed from a measurement taken after
the body was written (patrol run 35495222460 censused 3628 unresolvable sites on a token without it,
run 35496169133 on the fixed head censused 2167, the exact full-scope reading; the 1460 difference is
exactly the resolves-as-pull-request tally, because GET /issues omits pull requests without that scope).
⛔ Nothing else in this body was altered.

The Lint & Repo Gates job's permissions: block gains issues: read. An explicit
permissions: block sets every unnamed scope to none; this board is public today, but a
blocking gate whose transport depends on repository visibility is a gate that goes red on a
settings change no file in this repo can assert. Read-only, one scope wide: the gate never
writes an issue, a comment, a label or an assignee.

Acceptance 2 -- both directions measured, on the CI side

PR #18223 measured the red/green pair on the gate side. This card asks for the same pair on
the CI side. Two instruments, both here.

A. The commits on this branch ARE the CI-side ablation. test(ci): ABLATION LEG 1 of 2
adds one citation naming a number beyond this board's allocation frontier, in a declared surface
(packages/**/src/**/*.ts, comment-prose projection). ABLATION LEG 2 of 2 removes it and
restores the file to the byte. The run on the first head is the red reading; the run on the final
head is the green one. Both run identifiers are recorded in the dev report on the card.

B. The exact command the new step holds, ablated locally with disk evidence. Three legs, run
from a committed state, each restored with git checkout HEAD -- path and proven by hash:

target                packages/types/src/index.ts
HEAD blob             0235d4e7d2ebe529c7101b5e1e004597b30c4554

leg 1  unresolvable   marker 0 -> 1   blob 0235d4e7d -> 2ac9ba333   exit 2   RED
       finding: [never-issued] packages/types/src/index.ts:8  #999999
                beyond the allocation frontier (19258) -- this number was never minted

leg 2  restored       marker 1 -> 0   blob back to 0235d4e7d       exit 0   GREEN
       `git diff HEAD` empty after the restore

leg 3  resolvable     cites #17512 instead                          exit 0   GREEN
       board: probed (1 citations), frontier #19258, 2 numbers resolve

Leg 3 is the control on leg 2: a green produced by a live board read, not by a run that never
asked the board anything.

Acceptance 3 -- where the census reports, how often, who pays

Written into the step itself, and repeated here:

  • Where. The patrol run's step summary and job log, plus one ::warning:: carrying the
    site count. Deliberately not the anchor issue: that body is owned end to end by
    check-half-states.mjs's generator, and a second writer is how half a generated body goes stale.
  • How often. This workflow's schedule -- four times a day, six hours apart (37 1,7,13,19) --
    plus any workflow_dispatch, plus the pull_request runs the paths filter admits. A row for
    scripts/check-issue-citations.mjs was added to that filter for the reason the file already
    gives for its two siblings: a step whose script can change without the trigger firing is a step
    whose PR-time proof is a coincidence.
  • Who pays the 159 requests/run. This repository's own secrets.GITHUB_TOKEN core quota --
    the same 5,000/hour the job already draws the live sweep from. Four runs a day is roughly 636
    requests/day, under half a percent of a single hour's allowance. No PAT and no cross-repo
    credential, per this file's own standing rule.

The step is gated on github.repository == 'objectstack-ai/objectstack', exactly as the
closed-card sweep above it is, because half-state-patrol.yml is copied verbatim into sibling
repos that do not carry this script -- a copy must skip the step, not fail on a missing file. It
is placed after the anchor write, unlike the closed-card sweep: the anchor is this patrol's
product, the job has a 15-minute timeout, and a report-only reading must never be able to starve
it. It always exits 0.

Acceptance 4 -- the 422 wall

This diff touches .github/workflows/**, which is outside the PM seat's arming channel: the
seat's auto_merge answers HTTP 422 for this PR. It merges by a human. That is the same
wall as PR #18096 and #18341, it is not a tool fault, and it must not be retried. No seat should
undraft this PR or arm auto-merge on it.

Note that .github/workflows/** is not on the GOVERNED_SURFACES register in
scripts/pm/check-governed-merges.mjs, so the governed-merge machinery is not what holds this
one -- the 422 is.

Placement, and the three in-flight PRs on these files

Read before editing: #18414 (lint.yml + package.json, green, awaiting a human merge),
#19024 (lint.yml), #19225 (half-state-patrol.yml, draft and frozen). Only additions here; no
existing step was moved, renumbered or reformatted.

Measurement this PR does not relay

The card's prose carries three disagreeing counts for the manifest census. Re-measured on this
branch's base 0f42d36ff, with a firing control and a dark control:

root manifest check:* keys                 165
not named by any workflow (name or path)     2   -> check:merged-result, check:issue-citations
...and not named by any other script         2
firing control 'check:nul-bytes' in a wf   true
dark control   'check:zznotreal' in a wf   false

Both unreached keys are the two this PR wires, so the reading after this lands is 0.

Acceptance notes

  • check-self-test-wired admits a script when a workflow names it directly or through a root
    manifest alias, repo-wide rather than per workflow, so the patrol's census step needs no second
    self-test invocation: the lint step above already runs it.
  • No changeset: nothing any package publishes moves. Workflow files ship in no package's files[].

Generated by Claude Code


Generated by Claude Code

Two gates were registered in the root manifest and called by zero workflows:
`scripts/check-issue-citations.mjs` (delivered by #18223) and
`scripts/check-merged-result.mjs` (delivered by #18338). Both cards' file
surfaces excluded `.github/workflows/**`, so both devs correctly stopped and
filed the wiring instead of widening.

Three entry points, two lanes, two deliberately opposite postures:

- `lint.yml` / `Lint & Repo Gates`: the DIFF-SCOPED citation verdict, blocking.
  Two commands in one step -- the manifest alias (which is the checker's own
  `--self-test` and nothing else) and then the live diff run. Wiring the alias
  alone would run the self-test twice and scan nothing.
- `lint.yml` / `Lint & Repo Gates`: `check:merged-result`, blocking. Offline,
  no credential, sub-second.
- `half-state-patrol.yml`: `--census`, REPORT-ONLY, four times a day. The
  census verdict moves on a motionless tree -- three numbers in its own
  control set went 404 in four days with no change here -- so a tree-wide
  blocking verdict would red a repository nobody touched.

The lint job gains `issues: read`: an explicit `permissions:` block sets every
unnamed scope to `none`, and a blocking gate must not depend on repository
visibility for its transport.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
…st go red

Temporary, and reverted by the next commit on this branch. The card's
acceptance asks for the red/green measurement to be taken ON THE CI SIDE, not
only on the gate side (#18223 already did the gate-side pair with disk
evidence). This commit is the red leg: one citation naming a number beyond
this board's allocation frontier, inside a declared surface
(`packages/**/src/**/*.ts`, comment-prose projection).

Expected: the `Issue citations this change adds resolve on the board` step in
`Lint & Repo Gates` exits 2 and the job goes red. The next commit removes the
line and the same step must go green.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
@os-try-charles os-try-charles added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 20, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/types/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/types/src/index.ts) — pages documenting those are invisible to this run
  • 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 — 3 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 0f42d36ffcf2dd094911eb1ee45f3cc0061754e3packageMentionDocs.

… real runner

The census step landed on a runner in this PR's own patrol run and reported
3,628 unresolvable citation sites. A full-scope read of the same tree reports
2,168. The difference is 1,460 — EXACTLY the `resolves-as-pull-request` tally,
so every citation naming a PR number was classified as a number the board
never had.

Cause: `GET /repos/{owner}/{repo}/issues` omits pull requests unless the token
holds `pull-requests`, and an explicit `permissions:` block sets every unnamed
scope to `none`. The same call is how the gate reads the allocation frontier
(`?per_page=1&sort=created&direction=desc`), so an understated frontier turns
later numbers into `never-issued` as well.

Both lanes take the row, read-only:

- `half-state-patrol.yml`: a report-only reading wrong by 67% is still a
  machine-readable surface telling a lie.
- `lint.yml`: on the BLOCKING step it is worse than a wrong number — it is a
  false red on a correct citation.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
…must go green

The reset half of the CI-side ablation the card asks for. `packages/types/src/
index.ts` returns to the blob it carried before leg 1
(`0235d4e7d2ebe529c7101b5e1e004597b30c4554`), marker count 1 back to 0, and
this branch's delta against `main` is again the two workflow files alone.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
@os-elon-musk
os-elon-musk marked this pull request as ready for review September 20, 2026 14:29
@os-elon-musk
os-elon-musk added this pull request to the merge queue Sep 20, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 20, 2026
… queue ahead of it

This step's first queue build ejected the PR that added it, and the reason was
neither the tree nor a crash: `exit 2` is this gate's own FINDINGS code, and the
findings were real citations written by somebody else.

MEASURED on queue entry `a7109d1f08`. A queue entry is built on the GROUP's
base, which carries the entries AHEAD of it in the queue and has not landed on
`main` yet. `merge-base origin/main HEAD` therefore lands at the PUBLISHED tip:
`231283a6e` at 14:30:14Z, while the group's base `8271c81425` reached `main`
only at 14:47:59Z. Everything between the two read as "added by this change" --
15 file(s) / 16 citations judged, 3 unresolvable, and all three written by the
two entries ahead: `#6361` twice from `ada701220` (#19364), `#18003` from
`8271c81425` (#19363). Against the group's own base the same tree judges 0
file(s). The Governed Surface Queue Guard, in the same build, read
`merge_group.base_sha` and correctly saw 1 commit and 178 changed lines.

Two halves, and the second is not cosmetic:

1. `lint.yml` declares the base -- `OS_GATE_MERGE_GROUP_BASE_SHA`, the name and
   the expression this file already uses for that fact. It renders empty on
   `pull_request` and `push`, where the ref guesses are CORRECT and are kept: a
   PR's merge ref already contains the main it was computed against.
   The step is not `if:`-skipped on `merge_group` -- this file asserts that
   every gate step here runs there.

2. The gate verifies the base resolves before handing it to `git diff`. It did
   not: an unresolvable `--base` threw `fatal: bad object` and exited 1, a
   failed read wearing a code that is neither the clean answer, the findings
   answer, nor the refusal. It now refuses with PREREQUISITE NOT MET (exit 3)
   and names every spelling tried and what to pass instead. Half 1 alone is
   inert -- the pre-change gate ignores the variable entirely -- and half 1 is
   what makes an unverified base reachable, so both are required.

Firing controls, on the real queue tree with `origin/main` pinned to the
published main of 14:30:14Z: ref guess -> exit 2 with the three findings,
reproducing the ejection; declared base -> exit 0; declared base absent from
the checkout -> exit 3; `--base` absent -> exit 3 (was: uncaught throw, exit 1).
`--self-test` covers all of it: 66 -> 73 cases, 7 batteries, and the
`diff-scope` battery floor moves 6 -> 13 so the new cases cannot stop running
unnoticed.

Also corrects the gate docblock sentence this wiring falsifies ("Neither is
installed here").

Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh
Co-authored-by: Claude <noreply@anthropic.com>
PR #19225 made the half-state patrol callable: the patrol's steps now live in
the composite action `.github/actions/half-state-patrol`, and the workflow
`uses:` it. This branch adds the report-only `--census` leg of the citation
gate to the OLD shape, so the two sides overlapped in two places.

Conflict 1 (`on.pull_request.paths` + the `permissions:` note) is additive in
both directions: the branch's `scripts/check-issue-citations.mjs` trigger row
and main's `.github/actions/half-state-patrol/**` glob name different files,
and main's rewritten `issues: write` paragraph is kept with this branch's
`pull-requests: read` paragraph appended under it.

Conflict 2 is not textual. Main emptied that region -- every step in it moved
into the composite action -- and the census step is the one thing there that
main did not relocate. It stays a step of the CALLER, and the placement is the
argument rather than an accident:

  - `scripts/check-issue-citations.mjs` is objectstack-only, so inside the
    action its "Locate the patrol sources" step would either have to name it
    and refuse to run in every sibling that adopted the action, or not name it
    and fail on a missing file there. The repo-name gate only works in the
    caller.
  - The action runs its scripts from `steps.sources.outputs.root` (the tree the
    action ships from) while the board's checkout is `github.workspace`. A
    census of the WORKSPACE tree run from inside the action would, in a
    sibling, census this repo's release pages and report the count under the
    sibling's name.
  - `pull-requests: read` is granted by this workflow's `permissions:` block,
    which a composite action cannot carry and the action's inputs do not name.

One behaviour had to be spelled rather than inherited: the step's guard is now
`${{ !cancelled() && github.repository == '...' }}`. In the old shape the
census sat above the only step that failed the job, so it ran whatever the
sweep returned; the patrol is now one `uses:` step that goes red itself, and a
default `success()` would have skipped the census on exactly the runs where the
patrol is down. This is the guard main gave the closed-card sweep one step up,
for the reason its own comment states.

Nothing else changed: the blocking diff-scoped step in `lint.yml` and the
`OS_GATE_MERGE_GROUP_BASE_SHA` declaration are untouched, and the census
command stays a bare literal path so the gate derivation keeps seeing it.

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

Copy link
Copy Markdown
Collaborator

os-dev-report

{
"issue": 18224,
"status": "done",
"branch": "claude/issue-18224-wire-issue-citations-to-ci",
"pr": "#19259",
"session": "session_019srGWGCBBCBHqcDoRZpQRh",
"premise_still_valid": true,
"summary": "Conflict-resolution round only. Merged origin/main (43d492f, PR #19225) into the branch as a MERGE commit bfe3abc and resolved the two hunks in .github/workflows/half-state-patrol.yml. HUNK 1 (on.pull_request.paths plus the permissions note) is additive in both directions: kept this branch's scripts/check-issue-citations.mjs trigger row AND main's .github/actions/half-state-patrol/** glob, and kept main's rewritten issues-write paragraph with this branch's pull-requests-read paragraph appended under it; the pull-requests: read row itself auto-merged. HUNK 2 is not textual: main EMPTIED that whole region because #19225 moved every step in it into the composite action, and the census step is the one thing there that main did not relocate. It stays a step of the CALLER, after the uses: of the action and after the closed-card sweep, with three placement reasons written into the step. Exactly one behaviour changed, deliberately and declared: the guard is now the same shape main gave the closed-card sweep, not-cancelled AND the repository name, because in the old shape the census sat above the only step that failed the job and so ran whatever the sweep returned, whereas the patrol is now a single uses: step that goes red itself and a default success() would have skipped the census on exactly the runs where the patrol is down. Diff versus origin/main: 3 files, 374 insertions, 13 deletions, and ZERO lines of main's copy of the patrol workflow removed; lint.yml and scripts/check-issue-citations.mjs are byte-identical to the pre-merge tip 7d60a60, so both halves of the accepted merge_group fix survive untouched. PR head is bfe3abc and mergeable_state moved from dirty to blocked (mergeable: true).",
"tests": "ALL exit codes captured before any pipe. (1) node scripts/check-issue-citations.mjs --self-test: EXIT=0, 'grammar narrowed, four 404 causes kept apart, both board strategies agree, diff scope red AND green, scope contract pinned (73 cases, 7 batteries)'. 73 is unchanged. (2) node scripts/check-issue-citations.mjs: EXIT=0, 'no issue citations added against 43d492f (0 file(s) read)'. Zero files because this diff touches none of the gate's declared surfaces (release pages, package source docblocks). (3) DERIVATION after the resolution, the dispatched command with both paths: EXIT=0, and the census family is still derived. Verbatim row: 'node scripts/check-issue-citations.mjs --census [half-state-patrol.yml] matched via .github/workflows/half-state-patrol.yml, CI trigger in half-state-patrol.yml; and via .github/actions/half-state-patrol/action.yml, CI trigger in half-state-patrol.yml .github/actions/half-state-patrol/**'. It sits in the value-bearing-argv-or-env block (NOT RUNNABLE LOCALLY, 1 value from the workflow: env GITHUB_TOKEN), the identical classification it carried BEFORE the merge, so the resolution lost nothing. (4) RECONCILIATION on the final commit bfe3abc: 56 derived, 56 run, 0 NOT-MEASURED, 0 UNRUN; dispatch-gates --ran EXIT=0; every one of the 56 recorded an exit code and all 56 are 0. check:pm-dispatch-gates ran for real (837.1s, 1883 self-test cases pass). One gate needed two runs: pnpm check:type-check-debt first exited 3, PREREQUISITE NOT MET, because its --re-measure leg needs the built workspace closure; I built the closure through the shared verify lock (turbo run build over the packages, 72 of 72 tasks, lock VERDICT command-exit 0, 428s held) and re-ran it: EXIT=0, '4 ledger entries re-measured in 19.7s, 53 raw tsc errors total, none above its recorded number'. The recorded exit for that row is the re-run's 0. Re-derived after origin/main moved on to 0870fb5: the 56-command set is byte-identical, so no newly-derived family is owed. (5) YAML PARSE of the resolved workflow with the yaml package 2.9.0: parses; top-level keys name, on, permissions, concurrency, jobs; permissions equals contents read, issues write, pull-requests read; pull_request.paths equals the four script rows, then the action glob, then the workflow file; job patrol has 5 steps. [0] Checkout repository. [1] Setup Node.js. [2] Sweep the board and update the anchor, uses ./.github/actions/half-state-patrol with github-token and anchor-issue. [3] Sweep the closed cards, not-cancelled AND repo name. [4] Census the repo's issue citations, not-cancelled AND repo name, env GITHUB_TOKEN, a 31-line run whose only citation command line is the BARE LITERAL 'node scripts/check-issue-citations.mjs --census'. A comment-stripped diff of the census step before versus after the resolution shows exactly one changed line, the if:. No ablation was owed: this round adds no gate and changes no checker.",
"mcp_calls": "0 - no MCP GitHub tool was called, for reading or for writing.",
"api_writes": "1 git push (7d60a60..bfe3abc on claude/issue-18224-wire-issue-citations-to-ci; pre-push check:commit-card-trailers passed, no card relation and no model identifier in the trailer pair), 1 read of pull 19259, and 1 attempted comment on pull 19259 carrying this report. No label, body, title, draft, assignee, queue or auto-merge write, and no write of any kind on card #18224.",
"open_questions": [
{
"question": "Report channel: this dispatch forbids posting on card #18224 and says the seat carries the report, while the standing os-dev contract and AGENTS.md's dispatch-protocol paragraph both make the executor's own report COMMENT its record on GitHub, GitHub-first.",
"options": [
"A. Post the report as a comment on PR #19259: keeps a GitHub-side record and touches neither the card nor the PR body, title, labels or state.",
"B. Post nothing and rely on the seat relaying the handback: obeys the dispatch literally, but leaves no GitHub record if this session dies between the push and the handback.",
"C. Post on card #18224 as the standing contract literally says: breaches an explicit dispatch prohibition on a card the seat owns."
],
"recommendation": "A, because it satisfies both instruments at the cost of one additive comment, on the surface this round's reviewer reads. Flagged rather than silently chosen."
}
],
"out_of_scope_findings": [
"noted, not filed: THE DISPATCH'S DERIVATION CLAIM IS FALSE AS STATED. 'If you put the census invocation inside the composite action, the derivation stops seeing it' is measured false: followCompositeActions (#19229) reads local composite-action steps into the CALLING workflow's invocation set. Positive control: the check-half-states invocation exists ONLY in the action file and IS derived; and the counterfactual, my census step pasted verbatim into the action's runs block and driven through extractCheckInvocations, derives the family with viaAction equal to .github/actions/half-state-patrol/action.yml. What IS real is the path-spelling half: all four spellings the dispatch names derive ZERO. So the derivation was never a reason to prefer the caller; the three structural reasons written into the step are. Successor: the dispatching seat, this round.",
"to file (3 classes; dedupe words: extractCheckInvocations, quoted literal path, DIRECT_CHECK_INVOCATION, derivation blind spot): the quote alone defeats the matcher, no variable needed. A double-quoted but otherwise literal script path derives ZERO families, exactly like the dollar-bearing spellings. That is a fully static, paste-runnable command the derivation cannot see, which is class (c), a trap that makes a writer's metadata (the derived gate family) silently disappear. Card #19395 already owns the open class, so this is one more measured spelling to add ON #19395 rather than a new card; I wrote nothing to it.",
"noted, not filed: two dispatch or harness details measured false and harmless. (i) The probe worktrees /home/user/wt-conflict and /home/user/wt-v19259 do not exist in this container - the harness also declared /home/user/wt-conflict as this session's primary working directory and as a git worktree, and it was absent - so there was nothing to avoid reusing; I created /home/user/wt-18224-conflict as instructed. (ii) dispatch-gates reports its change set with THREE-dot semantics and says so in its own stderr, 'three-dot semantics, never origin/main..HEAD', so the sister-PR attribution hazard my standing instructions warn about does not apply to this tool. Successor for both: the seat, when it next writes a dispatch."
]
}


Generated by Claude Code

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

Labels

ci/cd size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants