Skip to content

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

@objectstack-fleet

Path: devpath · CI gates · the Lint & Type Check workflow (.github/workflows/lint.yml)
Unblocks: the merge queue for every PR on this repository (a hang in the queue head's Lint & Repo Gates holds 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.yml declares six jobs. Five carry no timeout-minutes (read at origin/main 2a4254a608):

job id name: (the check context) timeout-minutes measured p50 / p99 / max (minutes)
lint Lint & Repo Gates (required, ruleset 12119582) none → GitHub default 360 15.8 / 32.6 / 32.7 (n = 81)
typecheck-workspace Type Check · workspace none 13.0 / 16.0 / 16.0 (n = 86)
typecheck-consumers Type Check · consumer gates none 7.7 / 10.1 / 10.1 (n = 87)
typecheck-debt Type Check · debt ledger none 5.8 / 7.4 / 7.5 (n = 92)
typecheck-source-gates Type Check · source gates none 3.2 / 3.5 / 3.7 (n = 94)
typecheck TypeScript Type Check (required) 10 0.1 / 0.1 / 0.2 (n = 85)

Measurement: the 100 most recent completed runs of lint.yml (events pull_request, merge_group, push, schedule; window 2026-10-01T00:23Z → 03:24Z; 96 runs with jobs read, 525 successful job rows), job started_at → completed_at from the jobs API. Lint & Repo Gates by event: merge_group n = 30 p50 15.8 max 32.6; pull_request n = 47 p50 15.7 max 32.7; schedule n = 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 sat in_progress until 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; main saw no merge between 02:09:45Z and 03:19:43Z. That shard had a wall. Lint & Repo Gates is 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 no Merge Queue Triage comment (that workflow fires on failure only; 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 sits in_progress for six hours after it stopped producing output is the silent form.
  • .github/workflows/lint.yml, the Stall-guard budget invariant step's comment (in the lint job): 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.yml Test Core (${{ matrix.shard }}/6) job: timeout-minutes: 45 — the sibling workflow's precedent, and the wall that bounded today's stall.
  • Ruleset 12119582 (GET /repos/objectstack-ai/objectstack/rulesets/12119582): merge_queue.check_response_timeout_minutes: 60, required_status_checks contexts include Lint & Repo Gates and TypeScript Type Check.

Acceptance

  1. Every job in .github/workflows/lint.yml declares timeout-minutes. The values, derived from the table above and ruled by the director (batch 🔗 Broken links detected in documentation #259 rider, maintainer order): lint 60 (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-workspace 30; typecheck-consumers 20; typecheck-debt 15; typecheck-source-gates 10; typecheck stays 10. Rule applied: about 2× the measured maximum, rounded to a 5-minute grain, never below the maximum plus 10 minutes, never above 60.
  2. Each new timeout-minutes line 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.
  3. The six name: contexts are byte-identical before and after (scripts/check-required-contexts.mjs pins them; the required-status-check registry and the ruleset are not touched). No step is added, removed or reordered; no concurrency: block changes.
  4. Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack run from the worktree with no paths (the change set derived from git), every command run with its exit code captured and --ran reconciled; 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; actionlint if present in the toolchain, else node -e YAML parse of the file.
  5. PR: Fixes this card; draft; skip-changeset applies (no package publishes from .github/workflows/; the seat applies the label); Clause-②: no.

Not in scope

  • The merge-queue ruleset values (check_response_timeout_minutes, max_entries_to_build) — repository settings, the maintainer's hand.
  • Merge Queue Triage firing on cancelled — 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.
  • Shortening the 198-step Lint & Repo Gates job (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

Activity

  1. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: director seat, summon #31 — maintainer direct order, not a PM loop round
    Session: session_01PGMD6TRDaZY8Ubyo7Ukm66
    Account: os-litant (the seat's linked user as GET /user answers 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.yml only (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/objectstack printed "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 five timeout-minutes lines 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-flight tooling sibling edits this file.
    Ruling-ref: the card body — the maintainer's order is quoted there and the timeout-minutes values 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 as objectstack-fleet[bot] via the relay; read back.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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: only typecheck carried 10). Added the ruled values: lint (Lint & Repo Gates) 60, typecheck-workspace 30, typecheck-consumers 20, typecheck-debt 15, typecheck-source-gates 10, with typecheck left 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 only CI and only on failure, 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.yml returns 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 the Stall-guard budget invariant step 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

  3. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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: 60 is 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's runs-on: line." with "The diff against d1f8ce8 is 62 insertions, 0 deletions, in five hunks, each directly under a job's runs-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-minute check_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-minute check_response_timeout_minutes. For lint the 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.mjs and pnpm check:stall-guard-budget all exit 0. The file parses under PyYAML and js-yaml. The structural fingerprint against d1f8ce8 differs only in the five added timeout-minutes values (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 the lint comment 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 turns cancelled. On the seat's ruling the lint wall is 55. The cancelled check 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 aggregate typecheck 10 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. The TypeScript Type Check failure 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

  4. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ 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_01PGMD6TRDaZY8Ubyo7Ukm66

    Checked against the diff, the file list and the head's check-runs on GitHub, not against the report's own account. Reports: os-dev-report 5924495119 (round 1, 5e6d90c) and the round-2 report that follows it on this thread (51f3743).

    item reading
    PR shape draft, base main, first body line Fixes #21050 and no other closing keyword beside a card number (grep over the body); assignee os-litant mirrors the card
    file surface exactly .github/workflows/lint.yml, +62 / −0, five hunks, each directly under a job's runs-on: line — matches the claim 5924227205
    the ruling lint 55 (window-bound, below the queue's 60-minute check_response_timeout_minutes, 22 minutes over the measured max 32.7), typecheck-workspace 30, typecheck-consumers 20, typecheck-debt 15, typecheck-source-gates 10, typecheck unchanged 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 semantics
    structure the six name: contexts unchanged; scripts/check-required-contexts.mjs exit 0 ("7 required context names pinned across 3 workflows"); no step, needs:, if:, permissions: or concurrency: change (the dev's structural fingerprint; the diff shows only comment and timeout-minutes lines)
    premise corrections accepted A1: git grep -c run-with-stall-guard on the file returns 1, a comment line, and check:stall-guard-budget counts 10 guard-wrapped steps repo-wide, none in lint.yml; the card's placement of the Stall-guard budget invariant step in the lint job was wrong (it is in typecheck-source-gates) — a card-text inaccuracy with no runtime surface, noted here
    CI on 51f3743 33 check-runs: 21 success, 12 skipped, 0 failure; Lint & Repo Gates success (04:07→04:32, 25 minutes — inside the new 55-minute wall), TypeScript Type Check success; both required jobs' conclusion read as success on this exact head
    gates round 1: 44 derived commands with exit codes, --ran reconciled (43 run, 1 NOT-MEASURED: check:type-check-debt exit 3, needs the built closure, CI's Type Check · debt ledger lane 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-test only and were counted as checker health, not clearance
    text 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-bytes exit 0)
    changeset skip-changeset applied by this seat after its own check: the diff publishes nothing from any released package (.github/workflows/ is in no package's files[]); the Check Changeset job is green with the label
    governed / size check-governed-merges.mjs --pr 21064: no governed path; 62 changed lines ≤ 5000
    Clause-② no in 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_calls 0 both rounds; api_writes 3 + 1, all through scripts/pm/ via the relay, as declared

    Acceptance notes carried from the reports (observations, not filed): Merge Queue Triage subscribes to workflow_run of CI only and fires on failure only, 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 strips pm:dispatched after the merge is read on origin/main by content.


    Generated by Claude Code

  5. added a commit that references this issue on Oct 7, 2026
    dff98c1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:specpriority:p2Medium: important, M3tooling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions