Repository navigation
check-expected-skips reads scaffold-e2e.yml's by-design 'Registry canary' skip as outside the roster (exit 4) on every PR that touches create-objectstack #20756
Description
Activity
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsTriage: first grade —
tooling·priority:p3·domain:skills·pm:queue. Direction: the roster learns the schedule / dispatch-only job under its raw matrix name, derived from the workflow's ownif:. ⛔ No hand-listed nameTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T03:59Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
scripts/pm/check-expected-skips.mjs, a non-gate PM loop tool ⇒domain:skills(SKILL.md:165).Why p3. A landing seat hand-classifies one by-design skip on every
create-objectstackPR. It is loud (exit 4), not silent, and no merge is wrong because of it.Direction.
- Derive, don't list. The roster reads a job-level
if:that selects onlyschedule/workflow_dispatchas "skipped onpull_requestby design", under the check-run's raw name (the unexpanded${{ matrix.* }}form GitHub reports when the job is skipped before expansion).- ⛔ Not a literal
Registry canaryentry: the next schedule-only matrix job would repeat this. - If the roster's derivation can't read this shape in general, the report says why, and one entry with that reason is the fallback.
- ⛔ Not a literal
- Pins:
- a
--check-runs-jsonfixture with the raw-named skipped canary answers exit 0; - a genuinely unexpected skip still answers exit 4 (the control).
- a
- Derive, don't list. The roster reads a job-level
- added and removed
on Sep 30, 2026 objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsClaim: PM loop round 5
Session:session_01KTZmMfzVzjNvyaLyQ8mHvg
Account:os-warren(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-20756-expected-skips-schedule-only-jobs
Worktree:objectstack-issue-20756
Domain:domain:skills
Seat:domain:skills#1
File surface:scripts/pm/check-expected-skips.mjs(the roster derivation and its self-test batteries). ⛔ Not.github/workflows/**: the workflow's by-designif:is the input, not the defect. Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, so the default judgment tier; the surface touches none of the five contract-review surfaces, so the record is CI + the seat's read)
Clause-②: no
Thread-read: 5903772130
Serial constraints cleared:- Fleet-wide
tooling+pm:dispatchedread empty at this claim: finding(fleet-write): anissue_patchbody of about 41 KB comes back with two multi-byte characters replaced by U+FFFD, about 16 KB apart, identically on a re-send — anddispatch.mjsreports success without comparing the read-back #20571 closed 2026-09-30T09:36Z after PR fix(fleet-write): read every relayed body back — a stored body that is not the bytes sent exits 4, never 0 #20806 merged asbe47d0c6. This card takes the one slot; [finding] pm-dispatch platform reading: in this repo a draft conversion alone did NOT remove a queued PR from the merge queue (PR #20695), contrary toplatform-readings.md:42#20764 waits behind it. - No open PR touches
scripts/pm/check-expected-skips.mjsor.github/workflows/scaffold-e2e.yml(the twelve open PRs' file lists, read at this claim). - The file's last touch is
6eaa0f4a(2026-09-23); no same-day churn.
Readings on
origin/mainat 2026-09-30T09:36Z:.github/workflows/scaffold-e2e.yml:464-465still declaresname: 'Registry canary: ${{ matrix.template }}'withif: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'.
Generated by Claude Code
- Fleet-wide
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 20756,
"status": "done",
"branch": "claude/issue-20756-expected-skips-schedule-only-jobs",
"pr": "#20835",
"session": "session_01KTZmMfzVzjNvyaLyQ8mHvg",
"premise_still_valid": true,
"head_sha": "3993387d1462ebec5c833150ba756dd0676bfee7",
"files_changed": [
"scripts/pm/check-expected-skips.mjs (+386 / -11)"
],
"summary": "The roster now has a DERIVED half beside its 11 listed rows. deriveNonPrEventSkips reads every .github/workflows/.yml and admits a job whose job-level if: is a disjunction of github.event_name == 'schedule' / 'workflow_dispatch' terms ONLY, optionally wrapped whole in one ${{ }}. nonPrEventGate is an allow-list recogniser, not an evaluator: &&, !=, parentheses, inputs/needs/success()/matrix/label reads, push, merge_group, pull_request, case-folded or double-quoted literals all answer null. The row takes the raw name: name: verbatim, else the job key; only bare ${{ matrix. }} references are admitted. A candidate is refused, so its skip stays exit 4, when its name is shared with another job, already listed, untellable, or its workflow is unreadable. The judge (--pr/--head/--check-runs-json) and --roster read the joined roster, and there is no literal 'Registry canary' row. On 73155fe (39 workflow files) the predicate admits 2 jobs with 0 refused: scaffold-e2e.yml › registry-canary as 'Registry canary: ${{ matrix.template }}', and publish-smoke.yml › registry-canary as 'Registry canary (published latest)', a workflow with no PR trigger. Neither ever runs on a pull_request run. Live control on PR 20748: exit 4 before, exit 0 after.",
"tests": "At head 3993387: pnpm check:pm-expected-skips, i.e. node scripts/pm/check-expected-skips.mjs --self-test, exit 0 with '✓ check-expected-skips self-test: 154 cases pass' (was 99). New pins: (1) the measured #20748 check-runs listing, carried verbatim as MEASURED_20748 (42 runs, raw-named skipped canary in suite 99276458880), judged exit 0 through the CLI via --check-runs-json and in-process against the joined roster; the listed half alone still names the canary. (2) CONTROL: the same head plus a genuinely unexpected skip answers exit 4, naming only it: 'Lint & Repo Gates' via the CLI, 'TypeScript Type Check' in-process, and 'Scaffold with repo dist', the canary's PR-running sibling. (3) NARROWNESS: an if: of schedule || pull_request is not admitted, and its synthetic job skipped answers exit 4; push, merge_group, negation, inputs, needs, success(), matrix, label, parenthesised, case-folded and double-quoted forms each answer null; both live mixed shapes (release.yml version-pr, merged-branch-reaper.yml reap) answer null. ABLATION, committed first: scripts/ablation-replace.mjs replaced nonPrEventGate's first line with 'return null;'. On disk: anchor x1 to x0, marker x0 to x1, blob a999480b to 0dc4f77f. Self-test RED, 22 of 154 failed, including 'measured #20748: the raw-named skipped canary judges exit 0 (got 4, want 0)' and 'cli: the measured #20748 head on disk → exit 0 (got 4, want 0)'; the control and narrowness cases stayed green. Restore proven by the tool plus the trap: blob == HEAD a999480b, git diff HEAD empty, marker grep -c 0, porcelain empty. LIVE CONTROL node scripts/pm/check-expected-skips.mjs --pr 20748: before (73155fe) 'VERDICT check-expected-skips: ⛔ 1 skipped check-run(s) outside the roster (exit 4)'; after (3993387) 'VERDICT check-expected-skips: OK — 8 skipped check-run(s), every one in the roster (exit 0)'. Unrelated control --pr 20816 after: OK, 3 skipped, exit 0. Narrowed LINT on the one touched file, a measurement: eslint --print-config resolves 2 rules for the file (not ignored); --no-inline-config --format json reports 1 file, 0 errors, 0 warnings; the config has no parserOptions.project and no projectService, so it is not type-aware and the diff cannot move any untouched file.",
"gates": {
"derivation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) at 3993387 gave 30 commands, identical to the dispatch's list",
"reconciliation": "dispatch-gates --ran: '30 derived famil(ies) accounted for — 30 run, 0 NOT-MEASURED', exit 0",
"results": [
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-declaration-mirrors.mjs :: exit 0",
"node scripts/check-declaration-mirrors.mjs --self-test :: exit 0",
"node scripts/check-scripts-symbol-anchors.mjs :: exit 0",
"node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0",
"node scripts/check-self-test-wired.mjs :: exit 0",
"node scripts/check-self-test-wired.mjs --self-test :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0",
"node scripts/check-whole-set-label-write.mjs :: exit 0",
"node scripts/check-whole-set-label-write.mjs --self-test :: exit 0",
"node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0",
"pnpm check:agent-test-spelling :: exit 0",
"pnpm check:bash32-floor :: exit 0",
"pnpm check:cli-command-ids :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:entry-guard :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:parse-guard :: exit 0",
"pnpm check:pm-dispatch-gates :: exit 0 ('✓ dispatch-gates self-test: 1976 cases pass.', 923.4s)",
"pnpm check:pm-expected-skips :: exit 0 (154 cases)",
"pnpm check:pnpm-filter-targets :: exit 0",
"pnpm check:ratchet-remedy-authority :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:watch-hint-literal :: exit 0"
],
"ci": "in_progress: not awaited, per contract"
},
"deviations": [
"Zone 2 item 4 ('each expanded name if the job ever runs') is NOT implemented. An expanded name is only reported once the job ran, and a ran job is never a job-level skip, so rostering expanded names would widen the roster with names that cannot skip by design. Only the raw pre-expansion name is admitted.",
"The verbatim #20748 fixture was built from one read-only curl GET of /repos/objectstack-ai/objectstack/commits/a84b73af13/check-runs with the session token, because the tool's --pr/--json output does not carry the success-run names. The tool's own --pr reader was used for both live-control verdicts.",
"pnpm check:pm-dispatch-gates ran detached (nohup, PID recorded), as its own header prescribes for agent containers. I waited on it in the foreground with tail --pid until it exited, and its exit code was captured to a file before any pipe.",
"The header edit was held to the same line count, so the self-test-reads declaration stays at line 131, which dispatch-gates.mjs pins as 'scripts/pm/check-expected-skips.mjs:131 self-test-reads'. No file outside the declared surface was touched.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude). The harness reminder's model-bearing Co-Authored-By line was not used, because AGENTS.md takes precedence and pre-push refuses a model identifier.",
"main was not merged before the PR opened. One commit landed after BASE 73155fe: 4b45afa (runtime/metadata-protocol only), disjoint from scripts/pm and .github/workflows. CI runs the merge ref."
],
"mcp_calls": "4 — all reads: github issue_read(get #20756), issue_read(get_comments #20756), pull_request_read(get #20835), issue_read(get_comments #20756, report read-back). Zero MCP write tools.",
"api_writes": "2 REST writes, both through the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches x2. The first stroke was pr_create, executed as POST /repos/objectstack-ai/objectstack/pulls and yielding draft #20835; read-back 8906 bytes sent, 8906 stored, identical. The second stroke was comment, executed as POST /repos//issues/20756/comments (this report). Plus 3 git push (not REST). No label, no assignee, no body PATCH.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed. merged-branch-reaper.yml runs on pull_request, path-filtered to itself. Its reap job ('Delete the reapable branches') skips there by design, but its if: (success() && != 'pull_request' && inputs.dry_run …) is outside the narrow recogniser by construction, as the dispatch's Zone 2 item 3 required. So a PR editing that one file still reads exit 4 on it. Recorded in the PR's Acceptance notes.",
"carrier: none · noted, not filed. check-expected-skips --self-test keeps its verdict handshake but pins no battery floor (no SELF_TEST_BATTERIES / floor roster in the AGENTS.md sense). This predates the PR, and the new cases join the existing sections. Recorded in the PR's Acceptance notes."
],
"cleanup": "worktree ../objectstack-issue-20756 removed after rm -rf node_modules (removal succeeded without --force); private fetch ref refs/os-issue-20756/main deleted; the one detached process (dispatch-gates self-test, PID 23101) exited before cleanup."
}
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsACCEPT — PR #20835 @
3993387d(skills seat 1,session_01KTZmMfzVzjNvyaLyQ8mHvg, 2026-09-30T10:37Z)Implemented-by:
claude/issue-20756-expected-skips-schedule-only-jobs(dev report on this card). Reviewed-by:session_01KTZmMfzVzjNvyaLyQ8mHvg.Review checklist, read on GitHub (not the report):
- Shape. Draft, base
main, first body lineFixes #20756,Clause-②: noat a line start. One file,scripts/pm/check-expected-skips.mjs, +386 / −11, inside the claim surface. - Tier.
check-governed-merges --pr 20835reads NOT governed (0 of 1 path on the register). The diff touches none of the five contract-review surfaces, so the record is CI plus this seat's read. - Diff read by the seat against the triage direction (5903772130), "derive, don't list".
deriveNonPrEventSkipsreads every.github/workflows/*.ymland admits a job only whennonPrEventGateaccepts its job-levelif:.- That recogniser is an allow-list, not an evaluator. It admits only a disjunction of
github.event_name == 'schedule' | 'workflow_dispatch'terms, optionally inside one${{ }}. Any other operator, a parenthesis, or aneeds/inputs/matrix/success()read answersnull. - The row takes the raw
name:, and only a bare${{ matrix.* }}is admitted. - A candidate is refused, so its skip stays exit 4, when its name is shared, already listed, underivable, or its workflow is unreadable.
- There is no literal
Registry canaryrow.
- Admitted on
main: 2 jobs, 0 refused. They arescaffold-e2e.yml › registry-canary(Registry canary: ${{ matrix.template }}) andpublish-smoke.yml › registry-canary, a workflow with no PR trigger. Neither can run on apull_requestrun. - Deviation, accepted. Expanded matrix names are not rostered. A job-level skip always happens before expansion, and an expanded name only exists once the job has run, so rostering it would widen the roster with names that cannot skip by design.
- Pins. The measured docs(create-objectstack): re-anchor the dead tracker citations in packages/create-objectstack/src to the commits that decided them #20748 check-run listing is carried verbatim and judges exit 0. Three controls still answer exit 4: an unexpected skip, the canary's PR-running sibling, and an
if:mixingschedule || pull_request. The ablation (predicate removed) reds 22 of 154, with the restore proven. - Live control.
check-expected-skips --pr 20748: exit 4 before, exit 0 after. - Changeset.
skip-changeset: no package'sfiles[]namesscripts/pm. The seat applied the label and the assigneeos-warren(the maintainer's standing word; the dispatch gave the dev no label write), and re-ran the staleCheck Changeset, which is now green. - CI on
3993387d: 24success, 11 path-filteredskipped, no other state.mainhas not touched this file or.github/workflows/**since the head.
Out-of-scope findings (2,
carrier: none) are accepted as noted and not filed:merged-branch-reaper.yml'sreapjob still reads exit 4 on a PR that edits that one file. Its mixedif:is outside the narrow recogniser by design.- The tool's self-test pins no battery floor. That predates this PR.
Neither misleads a reader, and neither has a reader who acts now.
Landing: non-governed, so this seat lands it in this act: relay
pr_ready+automerge_enable. The card closes on merge, or the seat closes it ifFixesdoes not fire; that frees the fleet'stoolingslot for #20764.
Generated by Claude Code
- Shape. Draft, base
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsClosed
completed— skills seat 1,session_01KTZmMfzVzjNvyaLyQ8mHvg, 2026-09-30T11:09Z.Delivered by PR #20835, MERGED 2026-09-30T11:08Z through the merge queue as
5cd403e3.- Content confirmed on
origin/main:scripts/pm/check-expected-skips.mjscarriesderiveNonPrEventSkips. The roster now derives the schedule / dispatch-onlyregistry-canaryjobs from their ownif:under the raw matrix name, with no literal row. - Landing record: ACCEPT 5909518356. The PR is non-governed, so its record is CI plus the seat's read. The seat readied it and armed auto-merge.
Fixes #20756did not close this card at merge (the seventh queue merge in a row this shift), so the seat closes it here;pm:dispatchedcomes off and the assignee is cleared. This frees the fleet'stoolingslot for [finding] pm-dispatch platform reading: in this repo a draft conversion alone did NOT remove a queued PR from the merge queue (PR #20695), contrary toplatform-readings.md:42#20764.
Generated by Claude Code
- Content confirmed on
- added a commit that references this issue
on Oct 7, 2026
Filing gate: ③ a defect in a PM loop tool, measured.
reach:every PR whose diff selects.github/workflows/scaffold-e2e.yml(it is path-filtered tocreate-objectstack): the enqueue bar's skip reading answers exit 4 on a by-design skip, so the landing seat must hand-classify it each time. Filed by thedomain:cliexecution seat (#6024, sessionlocal_1d2a197c-c20e-4e90-9be8-413d4d432289) while landing PR #20748. ⛔ Filed bare: routing belongs to triage (the lane table puts non-gatescripts/pm/**withdomain:skills). ⛔ Not a claim.Measured
node scripts/pm/check-expected-skips.mjs --pr 20748at heada84b73af13:⛔ unexpected skips (1) — outside the roster: Registry canary: ${{ matrix.template }}(run36658032070, theScaffold E2Eworkflow onpull_request), classified by the tool asfilter-miss: skipped before matrix expansion (the name is the raw template): a job-level if: or needs gate did not select this head;VERDICT … 1 skipped check-run(s) outside the roster (exit 4).Why it is by design
.github/workflows/scaffold-e2e.yml:464-465declaresname: 'Registry canary: ${{ matrix.template }}'withif: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'. On everypull_requestrun the job is skipped before its matrix expands, so its check-run carries the raw, unexpanded name. The skip is expected on every PR that triggers the workflow, and the roster does not list it: likely because the roster matches expanded job names, or because it does not read this job-levelif:shape. That is for the fix to measure.Fix shape
Teach the roster this job (a schedule/dispatch-only job-level
if:, under its raw matrix name), socheck-expected-skips --pranswers exit 0 for acreate-objectstackPR whose only extra skip is this one. Pin: a--check-runs-jsonfixture carrying the raw-named skipped canary answers exit 0, and a genuinely unexpected skip still answers exit 4.Dedupe: a search for
check-expected-skips Registry canaryandexpected-skips roster matrixfinds no open card. Dedupe words:check-expected-skips Registry canary·raw matrix name skipped before expansion roster·scaffold-e2e schedule-only job skip