Skip to content

pm(labels): declare repo:objectstack retired from circulation - #14156

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13991-repo-label-retire
Sep 1, 2026
Merged

pm(labels): declare repo:objectstack retired from circulation#14156
baozhoutao merged 1 commit into
mainfrom
claude/issue-13991-repo-label-retire

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #13991

Comment-only change to scripts/pm/ensure-pm-labels.sh. 50 insertions, 0 deletions, one file. No gh label create row is added or removed, and no label API call is made from this diff.

What the card asked for, and what actually needed writing

The card is right on both limbs, verified against origin/main at 2bff79a before any edit: the ledger creates exactly three repo:* labels (lines 293-295) and creates repo:objectstack nowhere. A whole-repo grep finds the name in only one other place — three prose/pin lines in scripts/pm/check-half-states.mjs, which is out of scope here.

But the retirement here has a different shape from every retirement the file already records, and that difference turned out to be the deliverable. The retired lanes in the header (domain:ui, domain:spec-surface, domain:engine-core, …) were each created by this file once and had their rows deleted; "deliberately ABSENT" is a statement about a row that used to exist. repo:objectstack never had a row — its label object was auto-created by a hand-application, the same grey / empty-description drift the header describes for priority:p0 and needs:contract-review.

So its absence was already true, and undeclared. A reader of the three seam rows could not tell an oversight from a decision, and the honest default reading was oversight — which is exactly what invites a future author to "fix" it by supplying the fourth row. This PR converts the accidental absence into a declared retirement. There is nothing to delete.

The convention followed

PM's dispatch flagged this as an assumption to test: the file does have a textual convention for deliberately-absent labels, in its header (lines 39-53). Two terms, and the record here carries both:

  • deliberately ABSENT — no create row, so --reconcile never names it and can neither recreate it nor align its (empty) description;
  • object deliberately KEPT — retirement means stop circulating, never delete. Deleting the object strips the label from closed cards too and destroys the record of what was routed that way. That is a separate, deliberate PM action, exactly as the header already says for the retired lanes.

Placement: beside the three seam rows, not in the header paragraph, with a one-sentence pointer added to the header so its retired-inventory does not silently read as complete. The reason is in the file: the header paragraph answers "what is retired?", while the question that actually recreates a label is "should there be a fourth repo:* row?" — and that one is only ever asked where those rows are.

The record carries the ruling date and the operative ruling verbatim, attributed to the triage seat (comment 5487742846, R+82). See "One judgment call" below.

What this record deliberately does NOT do — all three are written into the file

One judgment call, flagged for review

Every other retirement in this file quotes a maintainer ruling. This one's authority is a triage seat ruling. I wrote it as "triage ruling on #13991, 2026-09-01" rather than borrowing the maintainer spelling — writing 「maintainer ruling」 over a triage ruling would fabricate authority, and the file's retirement records are read later as the reason a label may not come back. If the maintainer would rather this retirement carry a maintainer ruling before it is treated as settled, that is a one-line change to the attribution.

The BY-PR sync question, measured rather than assumed

The domain-lane block requires the ledger and SKILL.md to be kept in sync BY PR whenever a lane is added or retired, and the triage ruling cited that rule. Measured 2026-09-01: SKILL.md and the whole of .claude/ name repo:objectstack nowhere, so the prose sync has nothing to carry here and this PR stays single-file as dispatched. That measurement is recorded in the file so the next author does not have to redo it.

Gates — derived from the actual diff, run on the final commit 98960fe

Derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no hand-written path list; the script takes its own changeset from the merge base). 13 families, run under the shared verify lock, exit codes captured before any pipe:

Gate Exit
check:agent-test-spelling 0
check:bash32-floor 0
check:cli-command-ids 0
check:cross-package-test-inputs 0
check:entry-guard 0
check:parse-guard 0
check:pm-label-desc-cap 0
check:pnpm-filter-targets 0
check:watch-hint-literal 0
scripts/check-ci-filter-parity.mjs 0
scripts/check-cross-package-test-inputs.mjs 0
scripts/check-shard-attestation.mjs 0
scripts/check-test-completeness.mjs 3 — NOT MEASURED
check:ratchet-remedy-authority (PM-requested sweep) 0

check-test-completeness.mjs exit 3 is its documented PREREQUISITE-NOT-MET branch, in its own words: "this gate grades a saved turbo run test log, and no log was named… running the family locally, record this gate as NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix." CI passes it a teed log, so CI behaviour is unchanged.

check:pm-label-desc-cap is the gate that actually parses this file. It stays green because the parser strips whole-line comments before looking for -d text, so the new block adds no measured description, and the diff removes no create row (its MIN_DESCRIPTIONS floor is untouched).

check:ratchet-remedy-authority is green but, stated honestly, is not evidence about this diff: its corpus is scripts/*.{mjs,mts} by construction (its own header), so a scripts/pm/*.sh file is outside it. Run because PM asked; reported for what it measured.

ESLint — a measured narrowing, not a skip

pnpm lint (eslint . --no-inline-config) runs unconditionally on every PR and is CI's. Locally the check was narrowed to the changed file, and here is why that narrowing excludes nothing:

  1. Population read from ESLint's own config, not from my judgement: every files block in eslint.config.mjs declares a JS/TS extension set (**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} and narrower). .sh matches none of them.
  2. Count read from --format json: the changed file yields one entry, errorCount: 0, whose only message is "File ignored because no matching configuration was supplied." — zero files in this diff are linted at all.
  3. Invariance for untouched files: eslint.config.mjs:325-332 records, with its own planted positive control, that this repo "never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file". With no cross-file type program, a diff cannot move the verdict of a file it does not contain.

Changeset

skip-changeset, applied by me at PR open. This diff publishes nothing from any package — it is comment-only prose inside scripts/pm/** — and Check Changeset in pr-automation.yml offers no path-based exemption, so the label is the mechanism. ⚠️ Noted for PM: the dispatch prompt's scope fence reads "do not perform any GitHub label API operations", whose stated purpose is the retirement's board acts (the label object, and the 4 carrier cards). I read it as scoped to those and applied skip-changeset on this PR under the standing clause, rather than silently choosing between the two. Trivially reversible if PM meant the fence literally.


Generated by Claude Code

The label ledger creates three `repo:*` seam labels and never created a
fourth, but the board carried `repo:objectstack` on 4 open cards. The
absence was therefore true but UNDECLARED: a reader of the three seam rows
could not tell an oversight from a decision.

Record the retirement the way the header records the retired lanes —
deliberately absent so `--reconcile` never names it, label OBJECT kept so
closed cards keep their history — placed beside the seam rows, where a
future author would otherwise supply the missing fourth row.

Comment-only: no create row is added or removed, and no label API call is
made from this diff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@github-actions github-actions Bot added the size/s label Sep 1, 2026
@baozhoutao baozhoutao added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026 — with Claude
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 09:21
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 1, 2026
Merged via the queue into main with commit 8ebda70 Sep 1, 2026
34 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13991-repo-label-retire branch September 1, 2026 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] The repo:* label family has a fourth member the ledger does not create — repo:objectstack means the opposite of the three seam labels

2 participants