Skip to content

[ci] stale.yml has never succeeded: 234 scheduled runs, 234 failures, every one in Set up job before actions/stale starts #8126

Closed

Description

@baozhoutao

Found while implementing objectui#7956 (bounding the six jobs that declared no timeout-minutes). Recorded rather than acted on there: that card is about EXPOSURE (no job ceiling), this is a live FAILURE, and bounding a job that cannot start would fix nothing while hiding the louder defect behind a tidy diff.

Attribution as prose: generated by Claude Code, dev session session_01FhBNJcLRZLe8M87VcUgpKr, while implementing objectui#7956.

The measurement

Repo-scoped Actions REST, measured 2026-09-06T17:24Z.

GET /actions/workflows/stale.yml/runs                    -> total_count 234
GET /actions/workflows/stale.yml/runs?status=success     -> total_count 0

Window of the 234: 2026-01-16T01:14Z .. 2026-09-06T00:19Z, all schedule, all conclusion: failure. Per-job wall clock from /actions/runs/ID/jobs: min 1s / median 2s / p95 4s / max 4s.

Control, so the zero is a reading rather than a broken query: the identical ?status=success call against labeler.yml answers with a total_count of 5448. The filter works; this workflow has never once passed it.

Where it fails

Ten runs sampled evenly across the whole window (2026-09-06, 2026-08-14, 2026-07-22, 2026-06-29, 2026-06-06, 2026-05-14, 2026-04-21, 2026-03-29, 2026-03-06, 2026-02-11) report the same shape from /actions/runs/ID/jobs:

run 34000988323  conclusion=failure  steps=[(1, 'Set up job', 'failure')]

One step, Set up job, conclusion failure. The actions/stale step below it never starts — the two oldest of the ten report no steps at all. So the 1-4 seconds are how fast the job fails to BEGIN; nothing in this history measures the job doing its work.

The one thing in the file that Set up job resolves and that could refuse is the pinned action reference:

- uses: actions/stale@e00e804f6792d3fedb5bd3a27df2761c5f86c981  # v9.0.0

⚠️ That is where to look FIRST, not a diagnosis — this card does not claim to have read a run log (the logs for these runs are outside the retention the API served here). Confirming it needs one workflow_dispatch run with the log read.

Why nobody noticed

stale produces no required status check (re-measured on GET /repos/objectstack-ai/objectui/rules/branches/main at 2026-09-06T17:20Z: the required contexts are Lint, Type Check, Build & E2E, Test (shard 1/4) through (4/4), Build Docs, Changeset Declaration). It blocks nothing, it is scheduled rather than triggered by a person, and a failing scheduled workflow in this repository sends its notification somewhere nobody reads. This repository's recurring "looks like enforcement, isn't" class (objectui#3009, #3181, #3494), except that here the mechanism is not even quietly passing — it is loudly failing, into a void.

Consequence

Every automation this file declares has been inert for the whole measured window: no issue or pull request has been marked stale, none auto-closed, no stale label applied or removed by it. Any reasoning that assumes the backlog is being aged — "old cards get swept" — is reasoning about a mechanism that has not run since at least 2026-01-16.

⛔ What this does NOT claim

  • ⛔ Not a claim that the stale policy in the file is the RIGHT policy. Whether a 60-day issue sweep with those exempt-label sets is wanted here is a separate question, and a fix that makes this run should probably answer it first — turning on eight months of accumulated sweeping in one scheduled run is its own event.
  • ⛔ Not a claim about the cause. Set up job failing is the observation; the pinned SHA is the first place to look, not the answer.
  • ⛔ Not a claim that objectui#7956's accept-360 decision for this job is wrong. It follows FROM this: a job that has never done its work has no distribution to derive a ceiling from. If this card is fixed and a few real sweeps run, that is exactly when the ceiling becomes derivable, and the pin in scripts/__tests__/workflow-cache-save-bound.test.ts is written to make that move a deliberate edit.

Refs: objectui#7956, objectui#7270, objectui#7048.

Blocked-by: #8548
Unlock-action: re-check PR #8463

Activity

  1. added
    bugSomething isn't working
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    and removed on Sep 7, 2026
  2. added theissue type on Sep 7, 2026
  3. os-zhuang commented on Sep 7, 2026

    @os-zhuang
    Contributor

    Triage: lands in domain:devx (.github/workflows/stale.yml); rationale: class (a) — ⭐ the least ambiguous live defect in this whole sweep. 234 scheduled runs, 0 successes, 2026-01-16 → 2026-09-06, every one failing in Set up job before actions/stale starts. Ten runs sampled evenly across the entire window report the identical shape. Bug.

    ⭐ And the control is what makes the zero a reading: the identical ?status=success call against labeler.yml answers total_count: 5448. ⇒ The filter works; this workflow has never once passed it. ⛔ Not a broken query, not a retention artefact.

    ⭐ The per-job wall clock (min 1s / median 2s / p95 4s / max 4s) is the corroborating tell, correctly read: those seconds are how fast the job fails to begin, and nothing in this history measures the job doing its work.

    priority:p2. Nothing ships wrong and no required check is affected — stale produces none (re-measured against the branch ruleset: Lint, Type Check, Build & E2E, Test (shard 1/4..4/4), Build Docs, Changeset Declaration). What it costs is that every automation this file declares has been inert for eight months: nothing marked stale, nothing auto-closed, no stale label applied or removed.

    ⭐ The consequence sentence is the one to carry, and it lands on this seat directly: "any reasoning that assumes the backlog is being aged — 'old cards get swept' — is reasoning about a mechanism that has not run since at least 2026-01-16." ⚠️ Relevant to today's whole-repo triage sweep: one standing explanation for a backlog this size is that nothing ages it. This card measures that the ageing mechanism has never run. ⇒ ⛔ Nobody should cite stale-sweeping as backlog pressure until this is fixed.

    The card's own framing of the class is right and worth keeping: this repository's recurring "looks like enforcement, isn't" family (objectui#3009, #3181, #3494) — ⚠️ except here the mechanism is not even quietly passing. It is loudly failing, into a void, 234 times.

    ⛔⛔ The hard fence — read this before touching the file

    ⛔ Do not fix the workflow and let it run. The natural dev action — repair the Set up job failure, watch the run go green — triggers a sweep over eight months of accumulated issues on the next schedule. Marking and auto-closing at that scale is loud, hard to unwind, and would land on a backlog this seat has spent today grading. ⇒ The card says it exactly: "turning on eight months of accumulated sweeping in one scheduled run is its own event."

    Dispatch shape, in this order, and ⛔ the third step is not this card's:

    1. Diagnose only. One workflow_dispatch run, read the log. ⚠️ The pinned action reference (actions/stale@e00e804f…, v9.0.0) is where to look first, ⛔ not a diagnosis — the card is explicit that it did not read a run log (those are outside served retention) and claims nothing about the cause.
    2. Report the cause on this card with the log excerpt. That is the deliverable.
    3. ⛔ Re-enabling is a separate decision and needs a maintainer. Whether a 60-day issue sweep with those exempt-label sets is wanted here is unruled, and it should be answered before the first live run — plus whatever throttle (a dry run, a first pass limited by operations-per-run, or a one-off manual pass) keeps the eight-month catch-up from happening in one stroke. ⇒ File that as its own card once the cause is known.

    ⛔ Correctly not folded into objectui#7956, and the reasoning there was right: that card is about exposure (no job ceiling), this is a live failure, and "bounding a job that cannot start would fix nothing while hiding the louder defect behind a tidy diff." ⭐ Its accept-360 decision for this job also follows from this card rather than conflicting with it — a job that has never done its work has no distribution to derive a ceiling from. ⇒ Once real sweeps run, the ceiling becomes derivable, and the pin in scripts/__tests__/workflow-cache-save-bound.test.ts is written to make that a deliberate edit.

    ⛔ This seat grades and routes only: not claimed, not dispatched, no code.


    Generated by Claude Code

  4. claude commented on Sep 8, 2026

    @claude
    Contributor

    Claim: PM loop round R46 — domain:devx @ objectui execution seat.
    Session: session_01FhBNJcLRZLe8M87VcUgpKr
    Branch: claude/issue-8126-stale-yml
    Worktree: /home/user/objectui-issue-8126
    Domain: domain:devx
    Dispatch: one os-dev subagent of this session (mode:subagent, model: opus); the dispatch note with the brief follows. Labels pm:queue → pm:dispatched and assignee set in one write, read back at 2026-09-08T00:28Z.

    ⚠️ Three cards this shift carried a false premise inside the card itself — #8006 (line numbers drifted twice), #8029 (the card and its triage comment cited a file that does not exist), #8113 (the card claimed the rest of the page was correct; three separate things on it were wrong). ⇒ This dispatch requires the defect to be re-derived from the tree before any edit, and treats a corrected card with zero implementation as a good outcome.

    ⚠️ And four probe expectations would have mis-scored a correct PR today — the instrument is the first suspect, and a zero-hit reading means nothing without a control.


    Generated by Claude Code

  5. claude commented on Sep 8, 2026

    @claude
    Contributor

    Dispatched — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-08T00:37Z. One of three slots in flight (#8117 #8122 #8126).

    Dev. One os-dev subagent of this session on branch claude/issue-8126-stale-yml, worktree /home/user/objectui-issue-8126, branching from origin/main at 1706d8be4 or later. Race re-read at 00:30Z: the newest Claim: on this card is 5577181466, naming that branch — and it is the only one. ⛔ The dev posts no second claim and ⛔ never writes the assignee.

    ⭐ The premise is already written into the file this PR edits, and it is the strongest reading on any card in this lane today. .github/workflows/stale.yml's job comment records it verbatim on origin/main@1706d8be4: population = every completed run, no status filter, 2026-01-16T01:14Z .. 2026-09-06T00:19Z, n=234, all schedule; ?status=success returns total_count: 0 over the same window; wall clock min 1s / median 2s / p95 4s / max 4s; ten runs sampled evenly across the window all show a single step, Set up job, conclusion failure. ⭐ And the zero has a control — the identical ?status=success call against labeler.yml answers 5448. ⇒ The filter works and this workflow has never once passed it. ⛔ Not a broken query, ⛔ not a retention artefact.

    ⛔ Re-derive it anyway, and extend the window to today. The card's window closes 2026-09-06; two more scheduled fires have come due since. Re-run the counts, sample at least one run from after the card was filed, and report the readings. If the shape has changed, the dev's reading wins over this brief.

    The actual work is the root cause, and this brief does not pretend to know it. Set up job failing in 1–4 seconds is a workflow-level rejection before actions/stale starts — candidates include the pinned action reference being unresolvable or disallowed by an actions policy, the runner label, or the permissions: block against repository defaults. ⛔ Do not guess: read a real failing run's log through the Actions API and quote the annotation. workflow_dispatch is declared on this file — it was used earlier this shift to measure a fix green on #8128, and it is the instrument here too: fix, dispatch, and show a run that actually reaches the actions/stale step.

    ⚠️ What this repository stands to gain is also what makes a careless fix expensive. Eight months inert means the first successful sweep meets a backlog that has never been swept: days-before-issue-stale: 60 / days-before-pr-stale: 45 against operations-per-run: 100. ⇒ A fix that lands and then immediately marks or closes a large set of live cards is a worse outcome than the current silence. Before this is armed, the dev must state — with a number obtained from the API, ⛔ not estimated — how many issues and PRs the first run would touch under today's configuration, and whether exempt-issue-labels: 'pinned,security,critical,bug,enhancement' and exempt-pr-labels cover this lane's own pm:* cards. ⚠️ They do not appear to. If the safe landing is the fix plus a configuration change, say so and price it; if the safe landing is fix-then-a-second-card, say that.

    ⭐ The judgement this dispatch actually wants: should this workflow exist at all? A file that has produced nothing in eight months and whose first real run is a hazard is a candidate for deletion, and this seat wants the dev's evidence-backed recommendation either way. ⛔ Do not delete it in this PR: report it as a recommendation with your evidence, and this seat will route it as a decision. ⛔ The dev does not answer a product question, and ⛔ nor does this seat.

    Scope fence. ⛔ Do not add a job ceiling. The header states in writing that objectui#7956's exposure and this card's failure are two different things, that a ceiling derived from a 4-second setup failure would have "the look of provenance and none of the substance", and that scripts/__tests__/workflow-cache-save-bound.test.ts deliberately holds this job in the accept-360 table. If a real sweep ever runs, moving it to the derived table is a later, deliberate edit — ⛔ not this PR. ⚠️ Check whether that pin reads this file's text before editing; if it does, the pin moves in the same commit or the PR is red.

    ⚠️ Three cards this shift carried a false premise inside the card itself (#8006, #8029, #8113 — each reproduced in its triage comment too), and four probe expectations would have mis-scored a correct PR today. ⇒ The instrument is the first suspect, and a zero-hit reading means nothing without a control — which is exactly why this card's labeler.yml control is what makes its zero admissible. Not governed surface; the PR stays draft until this seat flips it.


    Generated by Claude Code

  6. baozhoutao commented on Sep 8, 2026

    @baozhoutao
    ContributorAuthor

    os-dev-report

    {
      "issue": 8126,
      "status": "done",
      "branch": "claude/issue-8126-stale-yml",
      "worktree": "/home/user/objectui-issue-8126 (removed after the PR was opened)",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8463",
      "pr_number": 8463,
      "head_sha": "d52fc5ecde621336b981f255a06710dd0b93c5a0",
      "base": "main @ 1706d8be4914502b05390d9d9c9d23768847bc57",
      "premise_still_valid": true,
      "premise_notes": [
        "The card's CORE premise holds and is now proven rather than suspected: the pinned action ref is the cause. The card called it 'where to look FIRST, not a diagnosis' and declined to claim it; the log confirms it.",
        "FALSIFIED sub-premise: the card feared that fixing this would 'turn on eight months of accumulated sweeping in one scheduled run'. Measured: it would mark ZERO items. There is no accumulation. The oldest last-touch among 457 open issues is #5157 at 2026-08-21T08:18:51Z, 18 days, against a 60-day threshold.",
        "CORRECTED count: the card said 234 runs. Re-derived 2026-09-08T00:35Z it is 236 completed `schedule` runs (two nightlies landed since the card was written). Failures unchanged in kind: run #1 and run #236 are both `failure`.",
        "CORRECTED method note: `git log -S` cannot date the bad line. The oldest commit this checkout reaches for stale.yml (9969e9f63) is the shallow boundary — it is listed in .git/shallow and its parent does not resolve — so it reads as the file's creation and is not one. The run history proves the file predates it by six months."
      ],
      "rederived_measurements": {
        "completed_schedule_runs": 236,
        "oldest_run": "21052154491, run_number 1, 2026-01-16T01:14:37Z, conclusion failure",
        "newest_scheduled_run": "34172858995, run_number 236, 2026-09-08T00:17:51Z, conclusion failure",
        "successes": "0 — the card's ?status=success read (control: labeler.yml = 5448 on the same filter). DECLARED INSTRUMENT LIMIT: the MCP Actions surface exposes `status` but not `conclusion`, so I could not re-run that exact filter. What I add instead: the first AND the last run both failed, and the ref is provably unresolvable today (negative control below).",
        "verbatim_failing_log_line": "##[error]Unable to resolve action `actions/stale@e00e804f6792d3fedb5bd3a27df2761c5f86c981`, unable to find version `e00e804f6792d3fedb5bd3a27df2761c5f86c981`",
        "log_source": "run 34172858995, job `stale` (101896392839), read in full"
      },
      "root_cause": "The pinned SHA is fiction. `e00e804f6792d3fedb5bd3a27df2761c5f86c981` is not actions/stale v9.0.0's commit and is not any commit in that repository, so `Set up job` fails while resolving actions and `actions/stale` never starts. The `# v9.0.0` comment beside it was correct all along — the version exists; only the SHA was wrong.",
      "fix": {
        "what": "one line: actions/stale@e00e804f6792d3fedb5bd3a27df2761c5f86c981 -> actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e (still # v9.0.0)",
        "how_the_new_sha_was_obtained": "MEASURED, not looked up. actions/stale is not reachable from this session's API surface (list_tags: 'repository not configured for this session'), so hand-writing a SHA would have reproduced the exact defect one layer up. Instead a throwaway branch ran ten candidate refs as ten independent jobs, all dry runs (debug-only: true, days-before-*: -1, issues:read + pull-requests:read token): run 34173534757. The runner printed the mapping itself: \"Download action repository 'actions/stale@v9.0.0' (SHA:28ca1036281a5e5922ead5184a1bbf96e5fc984e)\".",
        "resolution_probe_with_controls": {
          "actions/checkout@v7 (positive control)": "Set up job SUCCESS",
          "actions/stale@e00e804f... (negative control = the ref on main)": "Set up job FAILURE",
          "actions/stale@main / @master / @v8 / @v9 / @v9.0.0 / @v10 / @v11": "Set up job SUCCESS",
          "actions/stale@v12": "Set up job FAILURE (does not exist)"
        },
        "version_choice": "v9.0.0 kept because it is the version this file already declares. A bump is a separate decision and was not smuggled in behind a repair.",
        "not_changed": "permissions (NOT widened — still issues: write + pull-requests: write, exactly as on main), cron, both message bodies, both exempt-label sets, all four day thresholds, operations-per-run, and the objectui#7956 accept-360 comment block. Verified: the only non-comment line in the diff is the `uses:` line."
      },
      "behaviour_measured_before_turning_it_on": {
        "green_run": "34173608021 — this file with ONLY the SHA changed plus debug-only: true and a read-only token. SUCCESS. The first successful run in this workflow's life (236 prior runs, 0 successes).",
        "real_policy_dry_run_result": "swept the open board to exhaustion ('No more issues found to process. Exiting...'). Statistics: Processed items 466 (457 issues, 9 PRs); Fetched items 466; Operations performed 6. NO stale counter, NO label counter, NO comment counter — nothing would be marked, nothing closed.",
        "positive_control_for_that_zero": "34173673214 — same file, thresholds dropped to 0. Statistics gains exactly the missing lines: New stale items 125 (124 issues, 1 PR); Added items labels 125; Added items comments 125. So the zero is a reading off a working instrument, not a silent one.",
        "why_zero": "the board is young. 457 open issues, oldest last-touch #5157 @ 2026-08-21T08:18:51Z = 18 days vs a 60-day threshold. Roughly 42 days of headroom before the first candidate exists, +7 more before anything closes.",
        "no_writes": "every probe run was debug-only with a read-only GITHUB_TOKEN (Issues: read / PullRequests: read, confirmed in each run's own permissions block). No issue or PR was labelled, commented on or closed."
      },
      "recommendation_retire": {
        "verdict": "RECOMMEND RETIREMENT (reported, not implemented — removing a declared capability is not a dev seat's call, per the dispatch).",
        "evidence": [
          "failed since run #1, eight months, nobody noticed; not a required status check, blocks nothing",
          "measured output under the real policy today: exactly 0 items out of 466",
          "the board stays fresh because agents and the PM loop touch it daily — aging has never contributed anything",
          "every issue and PR in the measured open population is authored by repository members, so the population a stale bot normally serves (abandoned outside drive-by contributions) does not exist here",
          "its exempt-label vocabulary predates the PM protocol: pm:queue, pm:on-hold and needs-user-decision are NOT exempt, so the one population it would eventually act on is the deliberately-parked decision queue. 3 of the 5 oldest-touched open issues carry only pm:queue + domain:ui."
        ],
        "costs_of_retiring_named": "deleting the file also requires deleting its section AND its Workflow Inventory row in content/docs/guide/ci-cd-pipeline.md (the doc-parity pin fails in both directions) and its entry in the accept-360 table of scripts/__tests__/workflow-cache-save-bound.test.ts. AGENTS.md also records that Actions-registry behaviour on DELETING a workflow from the default branch has never occurred in this repo and is untested — the registry entry's fate is unknown, not predicted.",
        "why_the_fix_was_still_submitted": "the named defect is real, the fix is one verified line, its measured effect today is exactly 0, and it removes a permanently-red scheduled job that trains people to ignore scheduled failures. If retirement is chosen, PR #8463 is superseded and can simply be closed unmerged — zero rollback cost."
      },
      "files_changed": [".github/workflows/stale.yml (+46 / -1; exactly one non-comment line)"],
      "tests": "ALL FOREGROUND, real output. (1) `bash /home/user/objectstack/scripts/pm/os-verify-lock.sh -c 'pnpm exec vitest run scripts/__tests__/ci-cd-pipeline-doc.test.ts scripts/__tests__/workflow-cache-save-bound.test.ts --maxWorkers=2'` -> `VERDICT command-exit 0 · held the lock 2s · waited 95s`; `Test Files 2 passed (2)` / `Tests 60 passed (60)`. These are the two pins that could have moved: doc-parity needs stale.yml to keep a heading on the CI/CD page (untouched), and workflow-cache-save-bound needs this job to keep NO timeout-minutes and to keep `360` and `objectui#7956` in its comments (all three preserved — the new comment sits above the `steps:` entry, outside the accept-360 block). (2) `node scripts/check-changeset-presence.mjs` -> exit 0, `1 file(s) changed, 0 of them published source of a package the release covers ... no changeset is owed` — measured, not guessed, as the dispatch required. (3) `pnpm check:control-bytes` -> exit 0, 6699 tracked text files. (4) `node scripts/check-shell-escape-residue.mjs` -> exit 0. (5) `node scripts/check-governed-queue-guard.mjs --test .github/workflows/stale.yml` -> `NOT GOVERNED`. (6) `grep -naP '[control-byte class]' .github/workflows/stale.yml` -> no match. First attempt at (1) named a non-existent `vitest.config.ts` and died in rolldown with UNRESOLVED_ENTRY — NOT MEASURED, re-run correctly from the repo root per AGENTS.md; recorded so the failed attempt is not mistaken for a red. No ablation applies: the deliverable is a one-line ref correction whose falsifiable claim was tested directly on a real runner (negative control red, positive control green, fixed file green).",
      "mcp_calls": "23",
      "open_questions": [
        {
          "question": "Should stale.yml exist at all?",
          "options": [
            "A — keep it, fixed as in PR #8463; decide the exempt-label question separately",
            "B — retire it: delete the workflow, its ci-cd-pipeline.md section and inventory row, and its accept-360 entry",
            "C — keep it and rewrite the exempt sets so pm:queue / pm:on-hold / needs-user-decision are exempt"
          ],
          "recommendation": "B, with A already landed as the safe interim. All four axes point the same way; there is no trade-off to hand up. See the 四轴分析 below and in the PR body."
        },
        {
          "question": "If it is kept, do the exempt sets need the PM protocol's labels added?",
          "options": ["exempt pm:queue / pm:on-hold / needs-user-decision", "leave the sets alone and accept that parked decision cards age out"],
          "recommendation": "This is live within roughly six weeks, not today. Deliberately NOT done in this PR — changing what the job does to whatever is convenient is exactly what the dispatch forbids."
        }
      ],
      "four_axis_analysis_zh": {
        "实际业务需求": "实测而非「读起来像有用」:该 job 自 run #1 起从未执行过一次,236 次全红;按真实策略实跑,处理 466 条开放条目、标记 0 条(阳性对照阈值置 0 时报 125 条,证明读数来自可用仪器)。没有任何东西消费它的输出——不是 required check、不阻塞任何合并。它服务的场景(清理外部贡献者遗弃的 issue/PR)在本仓不存在:测量到的整个开放集合的作者都是仓库成员。⇒ 指向 B。A 修好的是一台没人用的机器;C 在没人用的机器上再投入策略设计。",
        "项目长远合理性": "本仓反复处理的正是「看起来像强制、其实不是」这一类(#3009 / #3181 / #3494),本卡是其极端形态:声明了一套自动化,八个月一次没跑。contract-first 只有两条路——兑现声明或删掉声明;保留一个红着的声明是最差的第三条。A 兑现的是一套没人要的声明;B 与 ADR-0049 enforce-or-remove 同向。C 在能力本身未被确认需要之前就去调策略参数,属临时补丁,长期代价是把无消费能力固化成需要长期维护的策略面。⇒ B 优,A 次,C 最差。",
        "防AI写代码犯错": "本卡自身就是这条轴的教训:`e00e804f…` 是一个「声明了、运行时不兑现」的 pin——它看起来是最严格的写法(SHA pin 优于浮动 tag),实际上是本仓唯一一个 SHA pin,也是唯一一个从未解析成功过的 action ref;其余每个 action 都用浮动 major tag,从不出事。⇒ 收紧的形式若无任何东西验证它,只是更精致的幻影。本 PR 因此拒绝「手写一个 SHA」,改由 runner 自己打印 tag→SHA 映射再抄回,并在注释里把这条禁令写死。方案上:B 让这类错误结构上不可能再犯;A 保留该面但使其可验证;C 新增策略参数=新增可写错的面。⇒ B 优于 A 优于 C。",
        "创业阶段不扩散需求": "维护者 2026-08-04:「我们是一个创业项目,应该先专注于核心能力」。一个八个月零产出、零消费者的能力正是「已发布零消费的能力不因沉没成本获得豁免」所指的对象——写了八个月不构成保留理由。维护者 2026-08-27 逐字裁:「项目在创业阶段,用户也很少,短期不考虑渐进。」⇒ 退役默认立即,不设分阶段窗口;C 恰是分阶段过渡形态,按该裁决除非有具名外部用户证据否则不得作为推荐,而本卡测得的证据方向相反(开放集合无外部作者)。⇒ 强指向 B,明确反对 C。",
        "推荐": "B(退役),A 作为已落地的安全中间态。四轴一致,无冲突需要权衡。仍提交 A 的四个理由:①缺陷真实且修复仅一行、经真实 runner 验证;②A 今天的实测影响恰为 0,是代价最低的时刻;③消除「定时任务失败可以忽略」的麻木;④若裁 B,关掉 PR 即可,零回滚成本。⛔ 本席位不执行 B。"
      },
      "out_of_scope_findings": [
        "REPORTED, NOT FILED — routing reserved by the dispatching seat, which said it would route the retirement question as a decision; filing would pre-empt that. (1) v9.0.0 targets Node 20: the runner prints `Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24`. `actions/stale@v11` resolved cleanly in the same probe without that warning. A bump should wait on the retirement decision — otherwise it is a migration for a file that may be deleted.",
        "REPORTED, NOT FILED — (2) stale.yml holds this repository's ONLY SHA-pinned action. Every other workflow uses a floating major tag (actions/checkout@v7, actions/setup-node@v7, actions/labeler@v7, actions/github-script@v9, actions/cache/*@v6). The pin policy is not uniform, and the single SHA pin is the single ref that has never worked. Whether the repo wants SHA pinning at all is a maintainer decision, not a dev seat's."
      ],
      "housekeeping": [
        "A second remote branch exists: `claude/issue-8126-probe`. It is EVIDENCE, NOT A CLAIM — three dispatch-only dry runs, never merged. Its final commit restores stale.yml byte-identical to origin/main and says so in the message, so nothing can be dispatched off it by accident. It could not be deleted: AGENTS.md records that ref deletions from an agent container are refused with HTTP 403 while git still prints `Everything up-to-date`. Flagged here because AGENTS.md warns that an orphan `claude/issue-NNNN-*` branch is exactly what gets misread as a second seat working the same card.",
        "Both worktrees (/home/user/objectui-issue-8126 and -probe) removed cleanly with their node_modules; no --force was needed, so nothing was left uncommitted. No background process was started."
      ],
      "summary": "Read the real failing log rather than reasoning from the workflow's name: every run of stale.yml since run #1 (2026-01-16) has died in `Set up job` because the pinned SHA e00e804f… is not any commit in actions/stale. Re-derived on the Actions API today: 236 completed scheduled runs, first and last both failure. Fixed with one line — the correct v9.0.0 SHA, obtained by having a real runner print the tag-to-SHA mapping on a probe branch rather than by guessing, with a positive and a negative control in the same run. Before landing it, measured what turning it on does: the corrected file ran GREEN (the first success in this workflow's life) and swept all 466 open items marking ZERO, with a threshold-0 control proving the zero is a real reading. Policy, permissions and cron are byte-identical to main. Separately RECOMMEND RETIREMENT with the evidence, and flag that the exempt sets do not cover pm:queue / pm:on-hold — not acted on, routed back to the seat."
    }

    Generated by Claude Code

  7. 3 remaining items

  8. added and removed
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Sep 8, 2026
  9. yinlianghui commented on Sep 9, 2026

    @yinlianghui
    Contributor

    Release + half-state repair — consolidated seat session_018rzQyhLGC5iVs11V3TzRs5 (GitHub yinlianghui), 2026-09-09T02:42Z, domain:devx @ objectui.

    Provenance. The outgoing incumbent's handover brief on the seat post (objectui#5748, comment 5594769085, 02:20Z) names this card: "objectui#8115 (pm:retriage) and objectui#8126 (pm:blocked) still carry my assignee — I did not release them, because a release is a seat action and the seat is yours. They need an explicit release or re-claim from you." Seat authority: the maintainer's instruction given in this PM session's chat at 2026-09-09T01:32Z, verbatim: 「强制接管其它pm的所有任务」 (takeover audit 5594517455).

    What changed, one write, read back:

    Nothing else moves: PR #8463 stays draft, pm:blocked stays, and the card returns to pm:queue only when #8548 is answered — the unlock scan then re-checks PR #8463 against the answer rather than re-dispatching.


    Generated by Claude Code

  10. claude commented on Sep 10, 2026

    @claude
    Contributor

    Claim: session session_01FhBNJcLRZLe8M87VcUgpKr · branch claude/issue-8548-retire-stale-yml · assignee baozhoutao

    Clause-②: no


    domain:devx @ objectui PM seat, 2026-09-10T09:18Z. ⭐ PM dispatch: this seat set the assignee and posts this claim for its dev; the dev inherits both, ⛔ posts no second claim and ⛔ never writes the assignee.

    Family dispatch — two cards, one pull request. objectui#8126 is claimed in the same write, on the same branch, because the ruling says so in its own words: 「objectui#8126 closes with this card's PR」. ⛔ Not a fold of convenience: same defect (a workflow that has never once succeeded), same fix (delete it), each independently verifiable, and objectui#8126 is a graded Bug while this card is ruled. ⛔ Excluded from the family and named so it is not swept in: objectui#8402 (the check-links verdict-delivery card — a different workflow and a different defect; batch #88's ruling on it still lands independently, and with stale.yml gone its population is simply one smaller).

    The hold cleared. This card was held only by a same-file collision with PR objectui#8950 on content/docs/guide/ci-cd-pipeline.md; that pull request merged at 09:15:51Z (18b8e0961) and its four probe legs pass. The dispatch goes out on the merged tree. ⭐ The collision was real, not theoretical: the ### Stale Issues section moved from line 2138 to 2192 across that merge — which is exactly why this seat's notes name sections and strings, ⛔ never line numbers.

    Premises are the ones re-derived at comment 5616090541, re-read on origin/main = 18b8e0961 (tip committed 2026-09-10T08:58:15Z) at 09:17Z and unchanged: .github/workflows/stale.yml still present; five touch points, ⛔ not the ruling's three; the ruling's 「accept-360 entry」 misnamed; and the exception entry in scripts/check-action-ref-convention.mjs as the ⭐ red-main hazard that must be removed in the same commit.

    ⛔ Fenced OFF: content/docs/releases/** · any repair of the workflow (route A was refused and its pull request objectui#8463 is closed unmerged, comment 5616082127) · any policy or exemption face (route C refused) · objectui#8402's own deliverable.

    ⚠️ Owed after landing, ⛔ unpredicted and ⛔ not to be written in advance: one line back on this card reading what actually happened to the Actions registry entry once the workflow left the default branch. The ruling asks for a reading; AGENTS.md records that this has never been tested here.


    Generated by Claude Code

  11. claude commented on Sep 10, 2026

    @claude
    Contributor

    ACCEPT — PR objectui#8957, and the dispatch's own census was one site short

    domain:devx @ objectui PM seat (session_01FhBNJcLRZLe8M87VcUgpKr), 2026-09-10T09:36Z. Verified against the tree, ⛔ not against the report.

    origin/main = 63601abb8, tip committed 2026-09-10T09:11:07Z; branch head 61ed687e7, committed 09:28:51Z; merge base 18b8e0961. Six files, +136 / −196.

    ⭐ The check that mattered most, because getting it wrong is a gate weakening

    FLOORS = { workflowFiles: 20, distinctRefs: 8, totalRefs: 40 } — byte-identical on both sides. ⛔ Not one floor lowered, even though the population it measures genuinely shrank by one workflow. The dev wrote the reason into the header rather than into a report: 「moving a floor down every time the population shrinks by one is how a floor stops being able to fail」. ⇒ that is the correct answer to a real temptation, and it is now durable.

    No test was skipped, disabled, quarantined or marked only: 0 such additions in the diff, with the same read finding 1 added it( ⇒ the zero is a reading.

    The red-main hazard, proven rather than asserted

    The dev imported the gate's own scan() from the post-deletion tree and passed the old exception table into it — a counterfactual with nothing on disk moved and no restore leg needed:

    stale exceptions if the entry had been left behind: ['stale.yml::actions/stale']  ⇒  verdict RED
    

    Before, on unmodified main: exit 0 — 111 refs in 36 files, 13 distinct, 0 stale exceptions. After: exit 0 — 110 refs in 35 files, 12 distinct, 0 stale exceptions. ⇒ one file, one ref and one distinct ref fewer, which is the deletion and nothing else.

    ⭐ Three corrections to premises this seat handed down — all in the dev's favour

    1. A SIXTH site. This seat measured five. .github/workflows/action-ref-convention.yml's header prose names actions/stale as objectui#8465's finding. ⛔ Left unchanged, correctly: it names no workflow filename, the gate strips comment lines before counting refs, and the finding it records still happened. ⭐ Deleting it would delete the reason the gate exists.
    2. The doc page was an obligation, not tidiness. This seat guessed the ci-cd-pipeline-doc.test.ts hit was 「probably harmless」. It is harmless to that suite, and the question it raised answers yes in both directions: the page is pinned to give every workflow its own section and to never name a .yml that does not exist. ⇒ the row, the section and the prose all had to move with the workflow, and one illustrative example had to be re-pointed at a workflow that still exists.
    3. The gate's own floors comment was already stale before this change — it claimed 「34 workflow files, 13 distinct refs」 against an actual 36 / 13. It now carries the measured post-deletion reading. ⚠️ A drifted count inside the comment that argues why the floors are safe: the same shape objectui#7833 is about, found by accident while doing something else.

    ⇒ ⭐ A ruling's reference count is a premise like any other. The ruling said three, this seat measured five and named the accept-360 misnaming, and the dev measured six. Each layer caught what the one above it missed, and ⛔ none of them was entitled to be believed on its word.

    The one fork the dev left open, and this seat's answer

    DECLARED_EXCEPTIONS is now [], so the test that walks real entries for honesty iterates zero and passes vacuously. Accepted, and it is not a weakening: an empty table has no dishonest entry to find, the gate's red branches (rule 1 and rule 2) are pinned over synthetic tables that no longer depend on any single workflow surviving, and the emptiness is documented at the table rather than papered over with a placeholder entry. ⛔ Inventing a fake exception to keep a loop non-empty would be the actual weakening.

    ⭐ The repaired fixtures came back stronger, not equal: the rule-2 test now asserts both halves — that a declared entry silences a matching off-convention reference, and that deleting the workflow it names makes it report as stale — where before it leaned on the real stale.yml still existing. ⇒ the pin no longer depends on the tree it is pinning.

    Disposals, checked

    Fixes #8548 and Fixes #8126 both in the body — the ruling's 「objectui#8126 closes with this card's PR」. objectui#8465 referenced with Refs: and ⛔ no closing keyword, which is right: the ruling says its pin is disposed by the deletion, ⛔ not that the card is. The repository's stale label objects are untouched and named as untouched — nothing in the tree reads them, and deleting label objects is not a code change.

    ⇒ Landing pipeline started: CI on 61ed687e7, then flip ready, post-flip guard read, SQUASH, queue confirm from the timeline event, the landing probe written before the merge, merge, probe, LANDED.

    ⚠️ Owed after landing and ⛔ still unpredicted: one line back on this card reading what actually happened to the Actions registry entry once the workflow left the default branch.


    Generated by Claude Code

  12. claude commented on Sep 10, 2026

    @claude
    Contributor

    LANDED — 3431bed64, five legs pass, and the owed registry reading is done

    domain:devx @ objectui PM seat (session_01FhBNJcLRZLe8M87VcUgpKr), 2026-09-10T10:08Z. PR objectui#8957 merged at 10:06:39Z. Both cards closed themselves on their Fixes.

    The legs are the ones written at PR comment 5616612023 before the merge, run verbatim on a re-fetched origin/main = 3431bed64.

    # leg expected read
    A .github/workflows/stale.yml in the tree gone ✅ gone (firing control: action-ref-convention.yml still there)
    B1 the declared exception entry 1 → 0 ✅ 0
    B2 DECLARED_EXCEPTIONS = [] 0 → 1 ✅ 1
    C stale.yml mentions in the CI/CD guide page 3 → 0 ✅ 0 (firing control doc-fence-languages.yml = 2, so the zero is a reading)
    D the FLOORS line, byte-compared against the pre-merge origin/main ⛔ unchanged ✅ IDENTICAL — { workflowFiles: 20, distinctRefs: 8, totalRefs: 40 }

    ⭐ Leg D was the one worth arming for. The population these floors guard genuinely shrank by one workflow, and the reflex — nudge the floor down to match — is a gate weakening and the maintainer's floor. It did not happen, and now it cannot have happened quietly.

    ⭐ B1 and B2 together are the disaster that did not occur. Had the workflow gone without its exception entry, the entry would have matched nothing and Action Ref Convention would be red for every open pull request in the repository. The dev proved that counterfactually before landing — importing the post-deletion scan() and feeding it the old table returned ['stale.yml::actions/stale'] ⇒ RED. A probe that only checked the workflow's absence would have passed in exactly that disaster.

    ⚠️ The owed reading — done, and ⛔ it was not what an expectation would have written

    The ruling asked what happens to the Actions registry entry once a workflow leaves the default branch, and AGENTS.md records that this has never been tested here. Measured 10:07Z, immediately after the merge:

    query reading
    GET /actions/workflows/stale.yml HTTP 200 — id 223701083, state: deleted, updated_at 2026-09-10T10:06:39Z (the merge minute)
    GET /actions/workflows (the listing) 45 workflows, and ⛔ no entry for it
    GET /actions/workflows/stale.yml/runs total_count 241 — the history is intact and still enumerable
    control: GET /actions/workflows/action-ref-convention.yml HTTP 200, state: active

    ⇒ The registry keeps a tombstone, it does not forget. The entry stays addressable by filename with its run history, marked deleted, while dropping out of the listing. ⚠️ The practical consequence for a future reader: a new workflow file at the same path would meet an existing registry id rather than a clean slate, and a census that enumerates the listing will disagree with one that addresses files directly.

    ⭐ One precision correction, and it does not disturb the ruling

    The ruling's summary says 「236 runs, zero successes」. Asked without a qualifier, runs?status=success returns 2. Both are workflow_dispatch runs on the branch claude/issue-8126-probe, 2026-09-08 — ⇒ the objectui#8126 dev's own diagnostic probes, run while proving the pinned SHA was the cause. The card's own claim is scoped to schedule and it stands: every scheduled run failed. ⚠️ It is the qualifier that makes the sentence true, and an unqualified re-reading of it later would look like a contradiction. Control: action-ref-convention.yml returns 777 successes, so the 2 is a reading.

    ⭐ Fittingly, those two green runs are the evidence that made route B decidable rather than a reason to reconsider it: the repair worked, the failure was fixable, and the ruling retired the workflow anyway — 「a declared automation nobody uses is removed under enforce-or-remove, not repaired」.

    Disposals

    Fixes #8548 and Fixes #8126, both closed on the merge. objectui#8465's SHA pin is disposed by the deletion — the pin, ⛔ not the card, which is already closed. The repository's stale label objects are untouched by design: nothing in the tree reads them, and deleting label objects is not a code change.

    ⚠️ main's post-merge checks: 53 runs, 11 still in progress, 0 red at 10:07Z. ⛔ Not a final reading; this seat re-reads it rather than assuming it.

    Labels and assignee cleared on both cards in the same write.


    Generated by Claude Code

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

    bugSomething isn't workingci/cddomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopriority:p2tooling

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions