Repository navigation
ci(lint): every job in lint.yml carries a timeout-minutes derived from its measured duration — Lint & Repo Gates has none, so a hang holds the merge-queue head until GitHub's 360-minute default #21050
Description
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iterateand removed
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: director seat, summon #31 — maintainer direct order, not a PM loop round
Session:session_01PGMD6TRDaZY8Ubyo7Ukm66
Account:os-litant(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-21050-lint-yml-job-timeouts
Worktree:objectstack-issue-21050
Domain:domain:spec
Seat: domain:spec#6 (the next free seat number on the spec lane, held by the DIRECTOR seat for this one card only under the maintainer's order 「Lint & Repo Gates 这个 job 没有 timeout-minutes ,这个问题你负责派发解决。」 given in the live director chat; no seat post is opened for it, the lane's seats 1–5 are not displaced, and the card never entered their queue)
File surface:.github/workflows/lint.ymlonly (stop on breach; explain in the report)
Container & model:S 级机械卡,mode:subagent,model: opus(the default judgment tier;node scripts/pm/dispatch-gates.mjs --tier .github/workflows/lint.yml --repo objectstack-ai/objectstackprinted "Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s)"; reason: the values are ruled on the card, the edit is fivetimeout-minuteslines plus their comments, and correctness is judged by the gate farm)
Clause-②: no
Thread-read: none
Serial constraints cleared: none — no open PR on this repository touches.github/workflows/lint.yml(every open PR's file list was read in this claim's own minute); same-day churn on the file:f7501191e(#20856, merged 2026-09-30) — the dev works from the merged tree; no in-flighttoolingsibling edits this file.
Ruling-ref: the card body — the maintainer's order is quoted there and thetimeout-minutesvalues are ruled there by the director seat under it (acceptance item 1).The dispatch's
Claim:is the dev's identity; the dev posts no second claim and never writes the card's assignee. Written asobjectstack-fleet[bot]via the relay; read back.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21050,
"status": "done",
"branch": "claude/issue-21050-lint-yml-job-timeouts",
"pr": "#21064",
"session": "session_01PGMD6TRDaZY8Ubyo7Ukm66 (subagent; the parent session id, matching the Claim: comment 5924227205)",
"premise_still_valid": true,
"summary": "Five of the six jobs in .github/workflows/lint.yml declared no timeout-minutes at origin/main d1f8ce8 (verified by YAML parse: onlytypecheckcarried 10). Added the ruled values: lint (Lint & Repo Gates) 60, typecheck-workspace 30, typecheck-consumers 20, typecheck-debt 15, typecheck-source-gates 10, withtypecheckleft at 10. Each new line sits directly under its job's runs-on line with a comment giving p50/p99/max, the measurement window and the rule. The lint comment also explains why a wall matters there: the queue's 60-minute check-response removal leaves no failing check, Merge Queue Triage watches onlyCIand only onfailure, and a hung job otherwise holds a runner for up to five more hours. No tracker numbers or model ids appear in the comments. The diff is 61 insertions and 0 deletions. A structural YAML fingerprint (names, step counts, hash of every other job key, top-level keys including concurrency, job order) is identical before and after apart from the five timeout values. Two of the PM's premise readings were off, with no effect on the change. A1:git grep -c run-with-stall-guard -- lint.ymlreturns 1, not 0, but the hit is comment prose and check:stall-guard-budget counts 10 guard-wrapped steps repo-wide, none in lint.yml. The card also places theStall-guard budget invariantstep in the lint job when it is in typecheck-source-gates.",
"tests": "Structure: python3 PyYAML 6.0.1 and js-yaml 5.4.2 both parse the file. Per-job fingerprint diff before (d1f8ce8) vs after (5e6d90c) differs ONLY in the five timeout values (null to 60/10/30/15/20). git diff: 61 insertions, 0 deletions, five hunks each directly under a runs-on line. actionlint is not on the PATH; nothing was installed. | Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived the change set from git against merge base d1f8ce8 at commit 5e6d90c and printed 44 commands. 43 exit 0, including pnpm check:pm-dispatch-gates (1976 cases, 680.4 s). pnpm check:type-check-debt exits 3 (PREREQUISITE NOT MET): its --re-measure half needs the built package closure, so it is NOT MEASURED locally and CI runs it in the Type Check debt ledger lane. Its --self-test half passed, and check:type-check-coverage (which reads lint.yml via observed()) exits 0. --ran reconciliation exits 0: 44 accounted, 43 run, 1 NOT-MEASURED (derived from exit 3), 0 UNRUN. 15 of the 44 are --self-test-only (10 node --self-test rows plus pm-expected-skips, pm-post-stamped, pm-widening-tells, stall-guard-headroom, select-gate-families) and judge their own script, not this diff. | Key verdict lines: check-stall-guard-budget OK (39 workflow files, 65 jobs, 730 steps, 10 guard-wrapped, none in lint.yml); check-required-contexts: 7 required context names pinned across 3 workflows; check-aggregator-roster: lint.yml/typecheck 4 members == needs; check-ci-filter-parity OK; check:doc-authoring all four lines clean; check:pm-skill-id-lint 34 files clean; check-issue-citations: no issue citations added against d1f8ce8; check:nul-bytes OK (9628 files, no raw control bytes). | Mechanism A3 is a reading on this repository, not a local test: run 36803445860 (CI, merge_group), job Test Core (1/6), stopped by its 45-minute wall, has conclusion cancelled in the jobs API, and its check-run annotation reads, at failure level: The job has exceeded the maximum execution time of 45m0s. Ruleset 12119582 read this session: merge_queue.check_response_timeout_minutes 60, and its required contexts include Lint & Repo Gates and TypeScript Type Check. | Ablation and reverse verification: not applicable. The diff changes no type, schema or code path; the structural fingerprint is the proof that nothing besides the five keys moved. | Added-line scan: 0 matches for tracker numbers and 0 for model ids.",
"gates": [
"node scripts/check-aggregator-roster.mjs :: exit 0",
"node scripts/check-aggregator-roster.mjs --self-test :: 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-merged-branch-reaper-outcome.mjs :: exit 0",
"node scripts/check-merged-branch-reaper-outcome.mjs --self-test :: exit 0",
"node scripts/check-position-name-fold-loaders.mjs :: exit 0",
"node scripts/check-position-name-fold-loaders.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-step-collectors.mjs :: exit 0",
"node scripts/check-step-collectors.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/ci/scheduled-full-run.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/ci-failure.mjs --self-test :: exit 0",
"pnpm check:agent-test-spelling :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:declared-population-live :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:node-version :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:pm-expected-skips :: exit 0",
"pnpm check:pm-post-stamped :: exit 0",
"pnpm check:pm-widening-tells :: exit 0",
"pnpm check:pnpm-acquisition :: exit 0",
"pnpm check:pnpm-filter-targets :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:required-contexts :: exit 0",
"pnpm check:select-gate-families :: exit 0",
"pnpm check:shard-attestation :: exit 0",
"pnpm check:stall-guard-budget :: exit 0",
"pnpm check:stall-guard-headroom :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:workflow-status-functions :: exit 0",
"pnpm check:workflow-step-name-quoting :: exit 0",
"pnpm check:pm-dispatch-gates :: exit 0",
"pnpm check:type-check-debt :: exit 3",
"node scripts/check-ci-filter-parity.mjs --self-test :: exit 0 (named or PM-listed extra)",
"node scripts/check-ci-filter-parity.mjs :: exit 0 (named or PM-listed extra)",
"node scripts/check-required-contexts.mjs :: exit 0 (named or PM-listed extra)",
"pnpm check:doc-authoring :: exit 0 (named or PM-listed extra)",
"node scripts/ci/select-shard-timings-run.mjs --self-test :: exit 0 (named or PM-listed extra)",
"node scripts/measure-test-shard-timings.mjs --self-test :: exit 0 (named or PM-listed extra)",
"node scripts/pr-labels.mjs --self-test :: exit 0 (named or PM-listed extra)",
"pnpm check:pm-governed-prose :: exit 0 (named or PM-listed extra)",
"pnpm check:route-ledger-census :: exit 0 (named or PM-listed extra)",
"pnpm check:select-shard-packages :: exit 0 (named or PM-listed extra)",
"pnpm check:pm-skill-id-lint :: exit 0 (named or PM-listed extra)",
"node scripts/check-issue-citations.mjs :: exit 0 (named or PM-listed extra)",
"pnpm check:stall-guard :: exit 0 (named or PM-listed extra)"
],
"line_budget": "n/a",
"files_changed": [
".github/workflows/lint.yml (+61 / -0)"
],
"deviations": [
"The derivation rule as ruled ("never below the maximum plus 10 minutes") is not met by four values: consumers 20 vs 20.1, debt 15 vs 17.5, source gates 10 vs 13.7, and the untouched aggregate 10 vs 10.2. I kept the ruled values and wrote the rule as ruled. The lint comment names the gap and notes that all six meet a flat 10-minute floor, so the comment states the rule without claiming the values satisfy it. See open_questions[0].",
"The ruling said to write that Merge Queue Triage fires on failure only. The comment says that and also states that it watches the CI workflow only (workflow_run workflows: [CI]), so a lint.yml red never reaches it anyway.",
"The ruled rationale says the job turns red at the same moment the queue drops the entry. The comment says within minutes of the queue window instead and claims no ordering (see open_questions[1]).",
"The derived gate list (44 at 5e6d90c, no paths) differs from the PM dispatch-time list. pnpm check:pm-governed-prose, check:route-ledger-census, check:select-shard-packages, check-ci-filter-parity --self-test, select-shard-timings-run --self-test, measure-test-shard-timings --self-test and pr-labels --self-test are not derived for this tree. All seven were run as extras anyway and exit 0.",
"The commit message carries the model-free trailer pair (Co-authored-by: Claude, Claude-Session). The harness reminder asked for a model-named Co-Authored-By; AGENTS.md and the dispatch both forbid a model id, so the unpushed commit was amended before the first push."
],
"mcp_calls": "0 (no MCP GitHub tool called, read or write)",
"api_writes": "3 REST writes, each delivered as ONE repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204) executed by the fleet-write relay as objectstack-fleet[bot]: (1) POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft forced; relay run 36813142863; read-back 7093 bytes sent = stored); (2) POST /repos//issues/21064/assignees [os-litant] via scripts/pm/label-write.mjs (relay run 36813200671; read-back matches); (3) POST /repos//issues/21050/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Not REST: 2 git pushes (the empty-branch probe, then 5e6d90c). No labels written; skip-changeset is left to the seat as dispatched.",
"ci": "in_progress at 2026-10-01T04:02Z (Lint & Repo Gates and all four Type Check lanes in_progress on 5e6d90c); not awaited, per contract",
"open_questions": [
{
"question": "The ruled rule and the ruled values disagree on the floor clause. "Never below the maximum plus 10 minutes" would give consumers 25 (20.1 on the grain), debt 20 (17.5), source gates 15 (13.7) and the aggregate 15 (10.2), but the ruled values are 20/15/10/10. All six ruled values do satisfy "about 2x the maximum, rounded to a 5-minute grain, never below 10 minutes, never above 60". Which half should be amended?",
"options": [
"A: Amend the rule text in the lint comment to a flat 10-minute floor and drop the gap sentence. One comment edit; values unchanged.",
"B: Keep the max-plus-10 clause and raise consumers/debt/source-gates to 25/20/15 (the aggregate to 15). Three or four value edits; more headroom for runner jitter on short lanes; a hung lane turns the required TypeScript Type Check red 5 minutes later."
],
"recommendation": "A. All six ruled values (including the pre-existing aggregate 10) fit the flat floor, while the max-plus-10 clause fits none of the three short lanes. The 2x multiplier already scales headroom with duration, and the queue critical path is bounded by the lint wall at 60 either way. Keeping a clause the values contradict invites the next author to re-derive instead of re-measure, which is what the comment exists to prevent. This is a director-ruled value set, so it is the director's call."
},
{
"question": "Should the lint wall sit below 60? A job's timeout clock starts when the job starts on a runner, which is after the queue opens its 60-minute check-response window. At equal values the queue therefore always gives up on the entry first, and the cancelled check plus annotation follows by the pickup and setup delay. The ruled rationale (red at the same moment as the drop) holds only approximately. Whether the queue clock starts at group creation or at check-run creation is unmeasured here.",
"options": [
"A: Keep 60 as ruled. The annotation lands minutes after the queue removal, a large improvement on 300 minutes after.",
"B: Set 55. In the typical case the failing required check lands before the queue window closes, so the removal is attributable to a visible failing check. Headroom over the measured max 32.7 drops from 27.3 to 22.3 minutes (1.7x)."
],
"recommendation": "B if the director wants the removal attributable to a failing check, at the cost of 5 minutes of headroom on a job whose max is 32.7. A otherwise. Kept at 60 in this PR as ruled."
}
],
"out_of_scope_findings": [
"carrier: none. Noted in the PR Acceptance notes, not filed. Merge Queue Triage subscribes to workflow_run workflows: [CI] only and fires on failure only, so neither a cancelled nor a failed Lint & Type Check job in the queue ever gets a triage comment. Its header scopes it to "every red merge_group CI run", so this is declared scope, not a contract violation, and none of classes a/b/c applies. The card already lists the triage-on-cancelled question as out of scope.",
"carrier: none. Noted, not filed. The card body says the Stall-guard budget invariant step is in the lint job; at d1f8ce8 it is in typecheck-source-gates. This is a card-text inaccuracy with no runtime surface."
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21050,
"round": "patch round 2 (ruled Q1 -> A, Q2 -> B)",
"status": "done",
"branch": "claude/issue-21050-lint-yml-job-timeouts",
"pr": "#21064",
"head": "51f3743508dac75a6b6845ca4d9318f7741e70ec (one new commit on top of 5e6d90c, fast-forward push, no force)",
"session": "session_01PGMD6TRDaZY8Ubyo7Ukm66 (subagent; the parent session id)",
"premise_still_valid": true,
"summary": "Both rulings are applied in the lint job comment of .github/workflows/lint.yml (commit 51f3743, +13/-12 on top of 5e6d90c; +62/-0 against d1f8ce8 in five hunks). Q1 -> A: "never below the maximum plus 10 minutes" now reads "never below 10 minutes", and the sentence listing the four jobs under the maximum-plus-10 clause is deleted. The four typecheck-* values are unchanged. Q2 -> B:timeout-minutes: 60is now 55. "within minutes of the queue's own window" now reads "before the queue's own window closes". "Here 2 x 32.7 = 65.4, capped at 60." became: "Here the window binds, not 2x (2 x 32.7 = 65.4), and the cap is placed BELOW it, at 55: this job's clock starts only once a runner picks it up, after the queue's has started, so the five minutes are room for that pickup, and 55 still leaves 22 minutes (1.7x) over the measured maximum." The rule keeps "never above the queue's 60-minute window", and the "Above 60 ... remedy is a faster job ... never a higher number here" and "Re-measure" sentences stand unchanged. PR body replacements for the seat to apply (the body was not PATCHed; it is still byte-identical to the create-time body). R1, in "What changed": replace "The diff is 61 insertions, 0 deletions, in five hunks, each directly under a job'sruns-on:line." with "The diff against d1f8ce8 is 62 insertions, 0 deletions, in five hunks, each directly under a job'sruns-on:line (two commits: 5e6d90c, then 51f3743 for the ruled patch round)." R2, the table row: replace "|lint|Lint & Repo Gates(required) | none, so GitHub's default 360 | 60 | 15.8 / 32.6 / 32.7, 81 |" with "|lint|Lint & Repo Gates(required) | none, so GitHub's default 360 | 55 | 15.8 / 32.6 / 32.7, 81 |". R3, the rule sentence: replace "Rule: about 2x the measured maximum, rounded to a 5-minute grain, never below the maximum plus 10 minutes, never above the merge queue's 60-minutecheck_response_timeout_minutes." with "Rule: about 2x the measured maximum, rounded to a 5-minute grain, never below 10 minutes, never above the merge queue's 60-minutecheck_response_timeout_minutes. Forlintthe window binds rather than 2x, and the wall sits below it, at 55, leaving 22 minutes (1.7x) over the measured maximum." R4: replace the heading "## Verification (head 5e6d90c, base d1f8ce8)" with "## Verification (head 51f3743, base d1f8ce8)". R5, append as the last Verification bullet: "- Patch round at 51f3743.pnpm check:doc-authoring,pnpm check:pm-skill-id-lint,node scripts/check-issue-citations.mjs,pnpm check:nul-bytes,node scripts/check-ci-filter-parity.mjs,node scripts/check-required-contexts.mjsandpnpm check:stall-guard-budgetall exit 0. The file parses under PyYAML and js-yaml. The structural fingerprint against d1f8ce8 differs only in the five addedtimeout-minutesvalues (55/10/30/15/20), and the aggregate's 10 is unchanged." R6, replace the Acceptance-notes bullet starting "The rule's floor clause and four of the values disagree." with "- Floor clause (ruled). The rule's floor is a flat 10 minutes, which all six values meet. The earlier maximum-plus-10 wording, which four values did not meet, was dropped from thelintcomment on the seat's ruling." R7, replace the Acceptance-notes bullet starting "Clock ordering at 60." with "- Clock ordering (ruled). A job's wall starts when a runner picks the job up, which is after the queue's 60-minute check-response window has opened, so a wall equal to that window would let the queue remove a hung entry before the check turnscancelled. On the seat's ruling thelintwall is 55. Thecancelledcheck and its annotation land before the queue's window closes whenever pickup takes under five minutes, so the removal is attributable to a visible failing check. 55 leaves 22 minutes (1.7x) over the measured maximum of 32.7." The rest of the body still holds: the Why, the GitHub-at-the-wall reading, the Structure, A1/A2 and derived-gate bullets (those were measured at 5e6d90c, and the derived set is unchanged for the same single path), and the Triage and card-location notes.",
"tests": "At 51f3743: python3 PyYAML 6.0.1 parse plus the structural fingerprint script against d1f8ce8 differ ONLY in five timeout values: lint null->55, source gates null->10, workspace null->30, debt null->15, consumers null->20. The sixth, the aggregate typecheck 10, is identical to base. Names, step counts (193/38/11/11/28/1), every other job key hash, top-level keys (incl. concurrency) and job order are identical. | js-yaml 5.4.2 parse gives lint 55, source gates 10, workspace 30, debt 15, consumers 20, typecheck 10. | The seven named gates all exit 0. doc-authoring: four clean lines. pm-skill-id-lint: 34 files clean. check-issue-citations: no issue citations added against d1f8ce8. nul-bytes OK (9628 files). ci-filter-parity OK. required-contexts: 7 names pinned across 3 workflows. stall-guard-budget OK (39 files, 65 jobs, 730 steps, 10 guard-wrapped, none in lint.yml). | Added-line scan vs d1f8ce8: 0 tracker numbers, 0 model ids, 0 raw control bytes. Every added line is at most 80 characters. | Not re-run this round: the other 37 derived commands (the 44 at 5e6d90c with exit codes are in the previous report). Same single path, comment text plus one value. The coordinator named this round's list.",
"gates": [
"pnpm check:doc-authoring :: exit 0",
"pnpm check:pm-skill-id-lint :: exit 0",
"node scripts/check-issue-citations.mjs :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-required-contexts.mjs :: exit 0",
"pnpm check:stall-guard-budget :: exit 0"
],
"line_budget": "n/a",
"files_changed": [
".github/workflows/lint.yml (this round +13 / -12; cumulative vs d1f8ce8 +62 / -0)"
],
"deviations": [
"The fingerprint against d1f8ce8 differs in FIVE timeout values, not six as the round message phrased it: the aggregatetypecheck10 was already declared at base and is unchanged.",
"The 55 sentence adds one clause beyond the ruled wording, explaining why the cap sits below the window: the job clock starts at runner pickup, after the queue clock, and the five minutes are room for that pickup. Without it a reader could not tell why 55 rather than 60 under a rule that says "never above 60".",
"The PR body is NOT patched, per the round message. R1 to R7 in summary are the exact replacements.",
"The 5e6d90c lint-workflow checks read cancelled/failure at 04:07Z. Their check runs went 04:01 to 04:07, about 6 minutes, ending as the 51f3743 push landed, which reads as lint.yml's concurrency cancel-in-progress and not a wall. TheTypeScript Type Checkfailure on 5e6d90c is its aggregate failing on cancelled lanes, as designed."
],
"mcp_calls": "0 this round (no MCP GitHub tool called)",
"api_writes": "1 REST write this round: POST /repos//issues/21050/comments (this os-dev-report, via scripts/pm/post-stamped.mjs, delivered as one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write relay as objectstack-fleet[bot]). Not REST: 1 git push (5e6d90c..51f3743, fast-forward). No PR body PATCH, no labels, no assignee change this round.",
"ci": "in_progress at 2026-10-01T04:07Z on 51f3743 (Lint & Repo Gates, consumer gates, source gates, workspace in_progress; debt ledger queued); not awaited, per contract",
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actions✅ ACCEPT — PR #21064 @
51f3743508(director seat, summon #31,session_01PGMD6TRDaZY8Ubyo7Ukm66, 2026-10-01T04:33Z)Implemented-by:
claude/issue-21050-lint-yml-job-timeouts
Reviewed-by:session_01PGMD6TRDaZY8Ubyo7Ukm66Checked against the diff, the file list and the head's check-runs on GitHub, not against the report's own account. Reports:
os-dev-report5924495119 (round 1,5e6d90c) and the round-2 report that follows it on this thread (51f3743).item reading PR shape draft, base main, first body lineFixes #21050and no other closing keyword beside a card number (grep over the body); assigneeos-litantmirrors the cardfile surface exactly .github/workflows/lint.yml, +62 / −0, five hunks, each directly under a job'sruns-on:line — matches the claim 5924227205the ruling lint55 (window-bound, below the queue's 60-minutecheck_response_timeout_minutes, 22 minutes over the measured max 32.7),typecheck-workspace30,typecheck-consumers20,typecheck-debt15,typecheck-source-gates10,typecheckunchanged at 10 — read in the diff; the rule text reads "never below 10 minutes" (round 2, Q1 → A) and the lint comment explains why 55 rather than 60 (Q2 → B); both PM-ruled, not escalated: execution parameters, no product semanticsstructure the six name:contexts unchanged;scripts/check-required-contexts.mjsexit 0 ("7 required context names pinned across 3 workflows"); no step,needs:,if:,permissions:orconcurrency:change (the dev's structural fingerprint; the diff shows only comment andtimeout-minuteslines)premise corrections accepted A1: git grep -c run-with-stall-guardon the file returns 1, a comment line, andcheck:stall-guard-budgetcounts 10 guard-wrapped steps repo-wide, none inlint.yml; the card's placement of theStall-guard budget invariantstep in thelintjob was wrong (it is intypecheck-source-gates) — a card-text inaccuracy with no runtime surface, noted hereCI on 51f374333 check-runs: 21 success, 12 skipped, 0 failure; Lint & Repo Gatessuccess (04:07→04:32, 25 minutes — inside the new 55-minute wall),TypeScript Type Checksuccess; both required jobs'conclusionread assuccesson this exact headgates round 1: 44 derived commands with exit codes, --ranreconciled (43 run, 1 NOT-MEASURED:check:type-check-debtexit 3, needs the built closure, CI'sType Check · debt ledgerlane is green on this head); round 2: the seven named gates exit 0 (doc-authoring,pm-skill-id-lint,check-issue-citations,nul-bytes,ci-filter-parity,required-contexts,stall-guard-budget); 15 of the 44 are--self-testonly and were counted as checker health, not clearancetext discipline added lines: 0 tracker numbers, 0 model ids, 0 raw control bytes (dev scan; check:doc-authoring,check:pm-skill-id-lint,check:nul-bytesexit 0)changeset skip-changesetapplied by this seat after its own check: the diff publishes nothing from any released package (.github/workflows/is in no package'sfiles[]); theCheck Changesetjob is green with the labelgoverned / size check-governed-merges.mjs --pr 21064: no governed path; 62 changed lines ≤ 5000Clause-②noin the claim and the PR body; no published schema, export or accept-set moves — contract review not owed (none of the five faces is touched)writes mcp_calls0 both rounds;api_writes3 + 1, all throughscripts/pm/via the relay, as declaredAcceptance notes carried from the reports (observations, not filed):
Merge Queue Triagesubscribes toworkflow_runofCIonly and fires onfailureonly, so a lint-workflow wall never produces a triage comment — declared scope; the card already lists it as out of scope.Landing: ready for review and auto-merge are armed by this seat through the relay in the next stroke; the queue lands it. The card closes on
Fixes; this seat stripspm:dispatchedafter the merge is read onorigin/mainby content.
Generated by Claude Code
- added a commit that references this issue
on Oct 7, 2026
Path: devpath · CI gates · the
Lint & Type Checkworkflow (.github/workflows/lint.yml)Unblocks: the merge queue for every PR on this repository (a hang in the queue head's
Lint & Repo Gatesholds every entry behind it)Maintainer's direct order (live director chat, 2026-10-01 ~03:3x UTC, verbatim): 「Lint & Repo Gates 这个 job 没有 timeout-minutes ,这个问题你负责派发解决。」 Filed and dispatched by the director seat (summon #31,
session_01PGMD6TRDaZY8Ubyo7Ukm66) under that order.tooling+domain:spec(.github/workflows/wiring anchors to the spec lane per the lane table).The defect
.github/workflows/lint.ymldeclares six jobs. Five carry notimeout-minutes(read atorigin/main2a4254a608):name:(the check context)timeout-minuteslintLint & Repo Gates(required, ruleset 12119582)typecheck-workspaceType Check · workspacetypecheck-consumersType Check · consumer gatestypecheck-debtType Check · debt ledgertypecheck-source-gatesType Check · source gatestypecheckTypeScript Type Check(required)Measurement: the 100 most recent completed runs of
lint.yml(eventspull_request,merge_group,push,schedule; window 2026-10-01T00:23Z → 03:24Z; 96 runs with jobs read, 525 successful job rows), jobstarted_at→completed_atfrom the jobs API.Lint & Repo Gatesby event:merge_groupn = 30 p50 15.8 max 32.6;pull_requestn = 47 p50 15.7 max 32.7;schedulen = 3 max 31.3. A docs-only PR (12 skipped steps) runs it in 7–11 minutes; a code PR in 25–33.Why it matters today, measured. On 2026-10-01 a
Test Core (1/6)shard went silent mid-build on the queue head (PR #20992, run 36803445860): it satin_progressuntil its 45-minute job timeout cancelled it at 02:51:33Z, the queue removed the entry at 02:52:05Z and rebuilt every entry behind it;mainsaw no merge between 02:09:45Z and 03:19:43Z. That shard had a wall.Lint & Repo Gatesis the longest required job in every queue group and has none: the same silence in one of its 198 sequential steps holds the queue head for GitHub's 360-minute default. The merge-queue ruleset (check_response_timeout_minutes: 60) would drop the entry after 60 minutes with no red check and noMerge Queue Triagecomment (that workflow fires onfailureonly; a silent removal is neither), while the hung job keeps burning a runner for up to six hours.Governing text
AGENTS.md→ Route & surface ownership, rule 3 「Absence must be loud」: "a verifier that silently degrades … is worse than no verifier, because it reports success. Prefer failing to falling back." A job that sitsin_progressfor six hours after it stopped producing output is the silent form..github/workflows/lint.yml, theStall-guard budget invariantstep's comment (in thelintjob): the guard's guarantee is "a stalled job says so instead of sitting in_progress until the job timeout". The lint-workflow jobs wrap no step in the stall guard (git grep -c 'run-with-stall-guard' -- .github/workflows/lint.yml→ 0), so for these jobs the job timeout is the only wall there is..github/workflows/ci.ymlTest Core (${{ matrix.shard }}/6)job:timeout-minutes: 45— the sibling workflow's precedent, and the wall that bounded today's stall.GET /repos/objectstack-ai/objectstack/rulesets/12119582):merge_queue.check_response_timeout_minutes: 60,required_status_checkscontexts includeLint & Repo GatesandTypeScript Type Check.Acceptance
.github/workflows/lint.ymldeclarestimeout-minutes. The values, derived from the table above and ruled by the director (batch 🔗 Broken links detected in documentation #259 rider, maintainer order):lint60 (max 32.7 × ~1.8, and equal to the merge queue's 60-minute check-response window so the job turns red with GitHub's "exceeded the maximum execution time" annotation at the same moment the queue would otherwise drop the entry silently);typecheck-workspace30;typecheck-consumers20;typecheck-debt15;typecheck-source-gates10;typecheckstays 10. Rule applied: about 2× the measured maximum, rounded to a 5-minute grain, never below the maximum plus 10 minutes, never above 60.timeout-minutesline carries a one-paragraph comment in the workflow stating the measured p50 / p99 / max and the window they were read over, and the rule above — so the next author re-measures instead of re-deriving; no tracker number in the comment (pnpm check:doc-authoring,pnpm check:pm-skill-id-lint-class rules), the lesson in the text.name:contexts are byte-identical before and after (scripts/check-required-contexts.mjspins them; the required-status-check registry and the ruleset are not touched). No step is added, removed or reordered; noconcurrency:block changes.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackrun from the worktree with no paths (the change set derived from git), every command run with its exit code captured and--ranreconciled;pnpm check:stall-guard-budget(reads.github/workflows/**, must stay green or REFUSE for the unchanged reason, never red because of this diff);node scripts/check-ci-filter-parity.mjs;actionlintif present in the toolchain, elsenode -eYAML parse of the file.Fixesthis card; draft;skip-changesetapplies (no package publishes from.github/workflows/; the seat applies the label);Clause-②: no.Not in scope
check_response_timeout_minutes,max_entries_to_build) — repository settings, the maintainer's hand.Merge Queue Triagefiring oncancelled— a separate finding if wanted; this card makes the lint timeout fire visibly as a red check, which is enough for a human to see.Lint & Repo Gatesjob (sharding, parallelism) — throughput work, its own card.Dedupe words: lint.yml timeout-minutes, Lint & Repo Gates job timeout, merge queue head hang 360 minutes, lint workflow job wall