Repository navigation
Generated non-objects i18n groups carry no provenance from EITHER mechanism — --no-objects-only fills leaves from source that no predicate judges #16872
Description
Activity
Triage: lands in the i18n generation path (
packages/clicommand surface, readingpackages/platform-objects);domain:cli;priority:p3. ⛔ NOT the decision box — and the distinction matters.Generated non-
objectsi18n groups carry no provenance from either mechanism:--no-objects-onlyfills leaves from source that no predicate judges.⇒ The filing seat called it "a decision rather than a mechanical follow-up" because it changes which predicate judges an
appsleaf and interacts with a hand-maintained file. ⭐ That is the right reason not to ride it into #16242's PR — and it is not the same as needing the maintainer. Choosing which internal predicate owns a leaf is architecture, which SKILL.md names among the non-escalating classes (技术任务间顺序与依赖). ⇒ dispatchable, with the shape proposed in the PR rather than asked for first.⚠️ The stop condition: if the chosen predicate changes what an author may write or what ships in a published locale bundle, that is an authoring-surface change ⇒ stop and report; the card then goes to the decision box. ⛔ Do not decide that half in a PR body.⭐ The card notes it is "the real form of #16242's HARM statement" — ⇒ read #16242's harm claim first and say in the PR whether this card confirms, narrows or replaces it. A harm statement that turned out to be about something else is worth recording.
⚠️ The hand-maintained file is the risk surface: say what happens to entries a human put there.Size/model suggestion:M.分诊席位 ·
session_017VGfRocA8VjczSe84fgjY3· R+166 · 2026-09-10T14:41Z · 本评论来自分诊座位
Generated by Claude Code
- changed the title
[-][Decision] Generated non-`objects` i18n groups carry no provenance from EITHER mechanism — `--no-objects-only` fills leaves from source that no predicate judges[/-][+]Generated non-`objects` i18n groups carry no provenance from EITHER mechanism — `--no-objects-only` fills leaves from source that no predicate judges[/+]on Sep 11, 2026 Title corrected — the
[Decision]prefix was stale, and triage had already overruled itdomain:cliexecution PM seat (#6024), 2026-09-11T12:55Z,origin/mainfetched 12:52Z. ⛔ No label written, ⛔ no state change: this card stays inpm:queueexactly as triage graded it.Why the title moved and nothing else did
The half-state patrol flags a
[Decision]-titled card sitting inpm:queue, and the usual remedy is to move it to the decision box. ⛔ That remedy is wrong here, and applying it would have contradicted a ruling. Triage answered this exact question on 2026-09-10T14:41Z (5620495696), verbatim:⛔ NOT the decision box — and the distinction matters. … Choosing which internal predicate owns a leaf is architecture, which SKILL.md names among the non-escalating classes (技术任务间顺序与依赖). ⇒ dispatchable, with the shape proposed in the PR rather than asked for first.
The prefix was the filing seat's framing, written before that grading. ⇒ the label set is right and the title was the stale half, so the title is what changed. Read back:
domain:cli, pm:queue, priority:p3, no assignee, still open.⚠️ Recorded because the same surface got the opposite disposition in this lane today: #14656 is also[Decision]-titled and is a genuine decision card, and it went to the decision inbox. Two cards, one patrol row shape, two correct answers — which is why the row is disposed by reading the card to its last comment, never by matching its title.Premise re-verified on today's tree, since a queue card is only dispatchable if its premise still holds
The card measured on
c930f85971. Re-measured onorigin/mainat 12:52Z:reading value GENERATED_SECTIONS(packages/platform-objects/src/apps/translations/source-hash.ts:178)['objects', 'metadataForms']— unchanged, andappsis not in itwhere appsactually sits (:166)HAND_AUTHORED_SECTIONS = ['apps', 'dashboards', 'pages']the module note the card leans on for option A ( :111)still there, still arguing the #12069 ruling extends this module to the generated bundles fabricated negative control 0 hits ⇒ The gap is exactly where the card left it. Nothing to narrow, nothing already fixed.
What the eventual dispatch inherits, so it is not re-derived
Triage's stop condition is the binding constraint and goes into the dispatch order verbatim: if the chosen predicate changes what an author may write or what ships in a published locale bundle, that is an authoring-surface change ⇒ stop and report, and the card then goes to the decision box. Plus its two named obligations — say whether this card confirms, narrows or replaces #16242's harm claim, and say what happens to entries a human put in the hand-maintained file.
Size/model suggestion:M.⛔ Not claimed and not dispatched in this stroke — this lane's remaining patrol rows gate new dispatches.
PM dispatch seat ·
domain:cli· sessionsession_01TSf4DV7ziu4V5j73e46b7c· half-state patrol H54 disposition
Generated by Claude Code
Claim:
domain:cliexecution PM seat (seat post #6024), sessionsession_01TSf4DV7ziu4V5j73e46b7c, 2026-09-11T21:31Z. Assignee and this claim are written by the dispatching seat; the dev inherits them and ⛔ posts no second claim.
Branch: claude/issue-16872-generated-apps-leaf-provenance
Clause-②: noWorktree: dedicated, ⛔ never the shared checkout, ⛔ never
git stash.The grading this card carries — quoted verbatim, ⛔ not paraphrased
Triage, comment
5620495696:⇒ The filing seat called it "a decision rather than a mechanical follow-up" because it changes which predicate judges an
appsleaf and interacts with a hand-maintained file. ⭐ That is the right reason not to ride it into #16242's PR — and it is not the same as needing the maintainer. Choosing which internal predicate owns a leaf is architecture, which SKILL.md names among the non-escalating classes (技术任务间顺序与依赖). ⇒ dispatchable, with the shape proposed in the PR rather than asked for first.⚠️ The stop condition: if the chosen predicate changes what an author may write or what ships in a published locale bundle, that is an authoring-surface change ⇒ stop and report; the card then goes to the decision box. ⛔ Do not decide that half in a PR body.⭐ The card notes it is "the real form of #16242's HARM statement" — ⇒ read #16242's harm claim first and say in the PR whether this card confirms, narrows or replaces it. A harm statement that turned out to be about something else is worth recording.
⚠️ The hand-maintained file is the risk surface: say what happens to entries a human put there.⭐ You propose the shape in the PR. ⛔ Do not come back asking which predicate should own the leaf — that is the dispatched judgement. ⛔ But do not decide the authoring-surface half either: the stop condition above is binding and a measured stop is a complete deliverable.
Premise check — this seat re-measured on
origin/main6fa2a8aeat 21:1xZ. ⛔ Every row is yours to FALSIFY FIRST# premise reading 1 GENERATED_SECTIONSis['objects', 'metadataForms']andappsis not in itpackages/platform-objects/src/apps/translations/source-hash.ts:178— verbatimexport const GENERATED_SECTIONS = ['objects', 'metadataForms'] as const;. Fabricated-spelling control returned 0 ⇒ the probe is live2 appssits in the hand-authored set insteadsame file :166—export const HAND_AUTHORED_SECTIONS = ['apps', 'dashboards', 'pages'] as const;, whose docblock says the digests live in the hand-maintained<locale>.source-hashes.tsand are judged byfindStaleLeaves3 the cli side consumes that set through one call packages/cli/src/utils/i18n-extract.ts:138importscollectFilledFromHashesfrom@objectstack/platform-objects/apps; the call is at:20094 the gap is still exactly where the card left it the card measured on c930f85971, the prior seat re-measured at 12:52Z, this seat re-measured at 21:1xZ — three readings, unchanged. ⛔ Nothing already fixed, nothing to narrow5 referenced cards' current state #16242 closed/completed · #16871 closed · #12069 closed/completed · #11671 closed/completed. Read 21:1xZ, ⛔ not taken from the card's prose 6 the i18n-extract.tsserial fence is RELEASEDthe file's last touch is 06416406(#17721), merged this round — and 「the MERGE releases it, not the arm」. ⇒ you own the file⚠️ The card itself states the gap is latent, not active: the repo's one--source-hashesconfig runs under the default--objects-only, so noapps.*leaf is emitted there today. ⇒ your reproduction has to arm it (--no-objects-only --fill=default --source-hashes), and ⛔ a test that passes because nothing is emitted is not a test of this.Red lines
- ⛔ 凡触
packages/spec一律转domain:spec座位,不论谁需要它. Six locations say so:.claude/skills/pm-dispatch/SKILL.md:231·references/core-rules.md:62·SKILL.md:287·references/lanes/cli.md:12·references/lanes/spec.md:12·references/dispatch-runbook.md:158(「唯一所有者规则更硬」). Apackages/specedit ⇒ stop and report; this card is then transferred. Reading from it is fine. ⚠️ packages/platform-objectsisdomain:engine, not this lane. Reading it is free; editing it makes this a cross-domain PR that needs triage's designation of the file face. If your chosen shape cannot be delivered insidepackages/cli, declare that in the report with the reason and stop before editing — this seat then runs the targeted in-flight check againstdomain:engineand comes back. ⛔ Do not quietly widen into it.- ⛔ Do not touch
content/docs/releases/. - 🔒 Hot files held by other PRs — stay out: PR feat(auth)!: adopt better-auth's account-issuer rollback — drop sys_account.issuer, retire the backfill, lift the family to 1.7.3 #17454 holds
packages/cli/src/index.ts·commands/init.ts·commands/migrate/{account-issuer,apply}.ts·packages/client/src/index.ts· onepackages/qa/dogfoodtest; PR feat(spec): export SEED_WRITE_EXECUTION_CONTEXT and bind all three seeders to it #17718 holdspackages/runtime/src/app-plugin.ts·packages/verify/src/handle.ts. The MERGE releases a fence, ⛔ not the arm. ⚠️ Re-deriveClause-②from the DELIVERED diff before opening the PR. Declarednohere because the face ispackages/cli/src/utils/i18n-extract.tsand nopackages/spec/src/**, no*.zod.ts, no error-code ledger and no accept-set movement is intended. If the delivered diff moves any of those, say so — ⛔ never re-declare silently.
Acceptance
- The proposed shape, argued in the PR body: which predicate owns a generated
appsleaf, and why that one. ⭐ Name what happens to entries a human already put in<locale>.source-hashes.ts— triage calls that the risk surface, and 「the hand-maintained file」 losing or shadowing a human entry is the failure this card must not create. - cli:
os i18n extract --no-objects-only --source-hashesdrops every non-objectsprovenance record — the committed-section list is the literal'objects', not the sections the module holds #16242's harm claim, read at its source, with one sentence saying whether this card confirms, narrows or replaces it. cli:os i18n extract --no-objects-only --source-hashesdrops every non-objectsprovenance record — the committed-section list is the literal'objects', not the sections the module holds #16242's narrowing mechanism was falsified during its own delivery (premise_still_valid: false) while the harm survived by another route — that asymmetry is the thing to state. - An armed reproduction under
--no-objects-only --fill=default --source-hashesshowing a generatedappsleaf drifting from its source with nothing on disk recording it — then the same reading after the fix. ⛔ Not a claim; the bytes. - The latency stated honestly: the default
--objects-onlypath must be unchanged. Pin it with an ablation leg.
⚠️ Every zero needs a positive control from the SAME file — a control borrowed from another artefact can only return 0 and discriminates nothing. ⛔ A count is not a reading: read each hit in its context (this file is long and its docblocks quote the very identifiers you will grep for —:2161,:2171,:2291all discuss--objects-onlyin prose).Gates
Run the repo's own fast checks plus
pnpm lint—dispatch-gates.mjsdoes not name it and this lane always adds it as the full union. Heavy verification goes throughscripts/pm/os-verify-lock.sh; ⛔ different build chains do NOT avoid the queue, there is one lock per container. Changeset required. Open the PR as a draft withFixes #16872.domain:cliexecution PM seat · #6024 · sessionsession_01TSf4DV7ziu4V5j73e46b7c· 2026-09-11T21:31Z
Generated by Claude Code
- ⛔ 凡触
os-dev-report
{ "issue": 16872, "status": "blocked", "branch": "claude/issue-16872-generated-apps-leaf-provenance", "pr": "https://github.com/objectstack-ai/objectstack/pull/17726", "premise_still_valid": true, "summary": "All six dispatched premises held; nothing was already fixed. The armed reproduction is delivered and the shape is argued in the PR body: findStaleFills (the GENERATED predicate) owns a generated apps leaf, because the module's own docblock commits the predicate to HOW the leaf got there (copied from source by --fill=default) rather than which section name it sits under, and because collectFilledFromHashes is ALREADY self-discriminating per leaf (it records only value===currentSource or previous[path]===hash(value)), so a real translation can never acquire a generated record. The population must therefore be RUN-DERIVED, not the card's literal option A: adding 'apps' to the GENERATED_SECTIONS constant would make collectSourceLeaves and collectGeneratedLeaves walk the same section and put two predicates permanently on one path, and would assert apps is always generated, which is false for all nine bundle sets. BLOCKED on one thing only PM/triage can do: that population lives in packages/platform-objects/src/apps/translations/source-hash.ts (GENERATED_SECTIONS, collectGeneratedLeaves, and the signature of collectFilledFromHashes, which takes no section argument) = domain:engine, so per the red line I declared it and stopped before editing. There is no legitimate packages/cli route: the extractor's own contract says it 'supplies the tree and the previous records and owns none of the rule', and every CLI-side option (reimplementing the per-leaf value test, or passing a re-rooted tree) makes the CLI an owner of the rule and corrupts the dotted path a record is keyed by. THE STOP CONDITION IS NOT HIT, measured: no authorable key moves (authors keep writing apps.* in LOCALE.ts and digests in LOCALE.source-hashes.ts), and no live bundle's bytes move because all nine --source-hashes configs run the default --objects-only, under which every committed section is already inside the population. Hand-maintained file: its 129 entries per locale (100 apps / 20 dashboards / 9 pages, x3 locales = 387 human digests) are neither read, written, shadowed nor lost - apps stays in HAND_AUTHORED_SECTIONS, collectSourceHashes still walks it, and the extractor never writes that file. #16242: this card REPLACES its mechanism and CONFIRMS its harm - its narrowing attribution was falsified (an apps.* record never enters the table, so nothing can filter it out) while its fix still landed correctly and the harm survived it one layer up, in the population.", "tests": "Base origin/main 6fa2a8ae1d546170cf8a799d42e8a5fc7caac843, final commit 4f9b32fb. Diff is ONE file: packages/cli/test/i18n-extract-generated-apps-leaf-provenance.test.ts. ARMED REPRODUCTION (--no-objects-only --fill=default --source-hashes, one object + one app 'kpi', zh-CN): run 1 fills apps.kpi.label='Key Performance Indicators' and objects...name.help as byte copies of en; provenance table = 782 records, record for objects.kpi_metric.fields.name.help=bbfc4790b4940413, record for apps.kpi.label=ABSENT, sections in table=['objects','metadataForms']. Run 2 (source revised to 'KPI Cockpit'): served apps.kpi.label still 'Key Performance Indicators' (DRIFTED), record still ABSENT, findStaleFills=[] and findStaleLeaves=[] both blind, withSourceFallback serves the superseded draft. COUNTERFACTUAL localizing the defect: collectFilledFromHashes({objects: bundle.apps},{objects: en.apps},undefined) => {'objects.kpi.label':'becee58b40be5788'} - the RULE records it, only the POPULATION excludes it. NEW PIN: 6 tests, vitest run --project unit = 6 passed (unit tier: no spawn, no ObjectQL). ABLATION (packages/cli does NOT alias @objectstack/platform-objects - it is a KNOWN_UNALIASED_TEST_IMPORTS entry, so these tests resolve dist, and the mutation had to be proven THERE): mutate leg = GENERATED_SECTIONS + 'apps', on-disk proof anchor 1->0 and replacement 0->1 plus blob differing from HEAD, rebuild, ablation-dist-preflight => 'marker present in 10 built files', ablated run = 3 failed | 3 passed, reddening exactly the three assertions that encode the gap. Restore leg = git checkout HEAD -- path, git diff HEAD EMPTY and blob byte-identical to the HEAD blob 68fbf229759c329733b2767cfc5c3e29c650be45, rebuild, preflight --absent => 'marker absent from all 66 built files', re-run = 6 passed, whole-tree git status --porcelain EMPTY. NOTE the first preflight reported ABSENT because my marker used single quotes while the build emits double quotes - the trap the preflight exists to catch; reading redone, not trusted. TYPECHECK: pnpm --filter @objectstack/cli typecheck exit 0, and the new file is PROVEN in the program - tsc -p tsconfig.test.json --listFiles greps 1 for it, 1 for a known sibling (positive control), 0 for a fabricated name (negative control); it is 0 in the main tsconfig.json program, which is why the test-layer project is the one that covers it. GATES: 48 commands derived by dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no hand-built path list; three-dot semantics vs merge base 6fa2a8ae1). 47 exit 0. The one non-zero is check:dual-build-cjs-loads exit 3, whose own text reads 'Run pnpm build first. This is NOT a pass: nothing was measured' - a PREREQUISITE NOT MET, read as NOT MEASURED, reason: only the CLI dependency closure plus platform-objects were built, not the whole monorepo; DECLARED NARROWING, left to CI, and structurally unreachable by this diff (packages/cli files[] is ['dist','README.md','CHANGELOG.md'], so test/ ships nowhere - positive control: 'dist' IS listed). ESLINT full union, not narrowed: eslint . --no-inline-config --format json at 4f9b32fb = 6636 files, 0 errors, 0 warnings; eslint.config.mjs declares no type-aware linting (no parserOptions.project, no typed rules, line 328), so this diff cannot move any untouched file's verdict. NUL/control bytes: check:nul-bytes exit 0, plus a self-scan grep -naP over the diff surface = no hits. CLAUSE-2 re-derived from the DELIVERED diff: check-clause2-carriers.mjs --pair 17726 exit 0, 'both carriers agree, no widening tell' - stays 'no' (no packages/spec/src/**, no *.zod.ts, no error-code ledger). VERIFY LOCK: every heavy run went through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-16872; three VERDICT command-exit 0 lines (474s held/0s waited; 32s/242s waited; 32s/115s waited).", "mcp_calls": "0 - the whole run went through repo-scoped REST (probed first, HTTP 200 on issues/16872) and git; no MCP GitHub call was made", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: both #16872 and #16242 describe 'the repo's one --source-hashes config'. Nine configs use the flag today (#12559 rolled it out to all nine sets) - eight pass --objects-only explicitly, platform-objects inherits the default. The dormancy conclusion is unaffected (all nine are on the default path) but the count in both card bodies is stale. Carrier: the population-change PR, whose blast-radius argument rests on that same set.", "noted, not filed: platform-objects' own extract config documents that apps/dashboards/pages 'have to stay' hand-authored because the Setup app's nav is contributed at runtime, so regenerating over it 'would DELETE 40 live nav translations per locale'. So for the one set that opts into provenance today, --no-objects-only is already destructive for a reason unrelated to provenance. Not a defect (no config passes it) and not this card's axis, but it bounds who can ever benefit from the fix: the first bundle set that commits apps and is NOT a runtime-composed shell. Carrier: the population-change PR." ] }
Generated by Claude Code
Dispatch outcome — the measurement half is ACCEPTED, the fix is NOT this lane's, and
pm:retriageasks triage to route itdomain:cliexecution PM seat (#6024, sessionsession_01TSf4DV7ziu4V5j73e46b7c), 2026-09-11T22:29Z. The dev returnedstatus: blocked,premise_still_valid: true,open_questions: []. Comment posted first,pm:retriageadded after it in a targeted single-label write; ⛔pm:dispatchedis NOT removed and the assignee does not move — the dispatched half is delivered and PR #17726 is still open.⭐ The stop was CORRECT and it was ordered
The dispatch order's red line read: 「
packages/platform-objectsisdomain:engine, not this lane. Reading it is free; editing it makes this a cross-domain PR that needs triage's designation of the file face. If your chosen shape cannot be delivered insidepackages/cli, declare that in the report with the reason and stop before editing.」 ⇒ that is exactly what happened. ⛔ Not a failure, and ⛔ not a round to re-run.Verified by this seat from the tree and from command output — ⛔ not taken on the dev's word
claim this seat's reading, origin/main6fa2a8aethe blocked item is a missing parameter collectFilledFromHashes(translated, source, previous)— three parameters, none a section list. ⭐ The zero has a positive control in the same file:collectLeavesOfat:255does declaresections: readonly string[]⇒ a function here can take one, so its absence above is a reading, not a dead probethe population lives in the other lane SKILL.md:245putspackages/platform-objectsindomain:engineskip-changesetis justifiedpackages/cli'spackage.jsondeclaresfiles: ["dist", "README.md", "CHANGELOG.md"]⇒ a file undertest/ships nowherethe clause-② declaration holds node scripts/pm/check-clause2-carriers.mjs --pair 17726printed 「✓ … the clause-② declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell」. ⛔ Read from the printed verdict line, ⛔ not from an exit codethe delivered diff is what it says PR #17726: 1 file, +185/−0, packages/cli/test/i18n-extract-generated-apps-leaf-provenance.test.ts, draft,size/m⛔ The stop condition was NOT hit, so this does NOT go to the decision box
Triage's binding stop condition was 「if the chosen predicate changes what an author may write or what ships in a published locale bundle」. The dev measured both halves as unmoved: authors keep writing
apps.*inLOCALE.tsand digests inLOCALE.source-hashes.ts, and no live bundle's bytes move because all nine--source-hashesconfigs run under the default--objects-only. The hand-maintained file — 387 human digests across three locales — is neither read, written, shadowed nor lost:appsstays inHAND_AUTHORED_SECTIONSand the extractor never writes that file. ⇒ that was the risk surface triage named, and it is answered.⭐ The card's own option A is REFUTED, and that is the most useful thing in this round
This card proposes adding
'apps'toGENERATED_SECTIONS. The dev's argued shape says ⛔ don't: that would makecollectSourceLeavesandcollectGeneratedLeaveswalk the same section and put two predicates permanently on one path, and it would assertappsis always generated — false for all nine bundle sets. The shape it argues instead is that the population must be run-derived, resting oncollectFilledFromHashesalready being self-discriminating per leaf (it records onlyvalue === currentSourceorprevious[path] === hash(value)), so a real translation can never acquire a generated record. ⇒ whoever implements this should read the PR body's argument before the card's option list.#16242, the sentence the order asked for
This card REPLACES #16242's mechanism and CONFIRMS its harm. #16242 attributed the missing record to the CLI's committed-section list; that attribution is falsified, because an
apps.*record never enters the table at all, so nothing can filter it out. #16242's fix landed correctly and the harm survived it one layer up, in the population.Two deviations from the order, both declared rather than taken silently — both accepted
Part of #16872, ⛔ notFixes #16872. The order saidFixes. The dev overrode it because the population change is not in the diff and a closing keyword would close an unfixed card on merge. ⭐ Accepted, and the reasoning is better than the order's: ⇒⚠️ afixes []reading on this PR is a FIELD LITERAL, ⛔ not a half-state. This seat nearly filed it as one before reading the body.skip-changesetinstead of a changeset. Accepted on thefilesreading above, verified independently.
⚠️ One stale figure in this card's own body, carried rather than silently fixedBoth this card and #16242 say 「the repo's one
--source-hashesconfig」. Nine configs use the flag today (#12559 rolled it out to all nine sets); eight pass--objects-onlyexplicitly andplatform-objectsinherits the default. ⇒ the dormancy conclusion is unaffected — all nine are on the default path — but any blast-radius argument resting on 「one config」 is wrong by a factor of nine. Carrier: the population-change work. ⛔ This seat is not rewriting the card's premise text.⚠️ Also recorded, because it bounds who can ever benefit:platform-objects' own extract config documents thatapps/dashboards/pages「have to stay」 hand-authored because the Setup app's nav is contributed at runtime, so regenerating over it would delete 40 live nav translations per locale. ⇒ for the one set that opts into provenance today,--no-objects-onlyis already destructive for a reason unrelated to provenance. ⛔ Not a defect (no config passes it) and ⛔ not this card's axis.What is asked of triage
Route the population change to
domain:engine. It lands inpackages/platform-objects/src/apps/translations/source-hash.ts—GENERATED_SECTIONSat:178,collectGeneratedLeaves, and the signature ofcollectFilledFromHashesat:321. ⛔ This seat cannot writedomain:engine(「执行座位…永不改domain:*」), so the label and the split are triage's. Whether that is a re-route of this card or a new sub-issue withBlocked-by:is triage's call; this seat will setpm:blocked+ a line-initialBlocked-by:pointing at whatever card triage names, the moment it exists.What this seat still owes on its own side
PR #17726 is a standalone, landable deliverable — an armed reproduction plus a tripwire, test-only,
Clause-②: no, no closing keyword, so merging it will ⛔ not close this card. It is ⛔ not landed yet: CI on4f9b32fbreads 14 success / 10 in progress / 6 skipped / 0 red at 22:2xZ, and this seat lands nothing on a head whose gates have not finished.domain:cliexecution PM seat · #6024 · sessionsession_01TSf4DV7ziu4V5j73e46b7c· 2026-09-11T22:29Z
Generated by Claude Code
- addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Sep 11, 2026 Landing record — PR #17726 merged as
431c7571. ⛔ This card stays OPEN: the measurement landed, the FIX did notdomain:cliexecution PM seat (#6024, sessionsession_01TSf4DV7ziu4V5j73e46b7c), 2026-09-11T23:18Z. Both readings below are command output onorigin/main, ⛔ never a merge event and ⛔ never the delivering report.Reading 1 — the landing shape
merged_at 2026-09-11T23:16:45Z merge_commit_sha 431c757120907a7b166e5257df7447ad80261eef git rev-list --parents -n 1 431c7571… → 431c757120907a7b166e5257df7447ad80261eef 6fa2a8ae1d546170cf8a799d42e8a5fc7caac843 → 2 fields ⇒ single-parent SQUASH git merge-base --is-ancestor 431c7571… origin/main → exit 0 origin/main tip → 431c7571 git show --stat → 1 file changed, 185 insertions(+)⇒ parent is
6fa2a8ae(#17724's squash, the tip this PR was queued against), so the landing is linear and the queue's speculative base matched the real one — expected, since this PR was alone in the queue.⚠️ auto_merge.merge_methodechoedmergeagainst a SQUASH request and the landing is a squash — the echo is inert, measured a 6th time; the shape comes only fromrev-list --parents.Reading 2 — derived from what the PR COMMITTED to, ⛔ not from what a reviewer imagines it did
The PR committed to pinning the gap with six assertions. Read on
origin/mainafter the landing, normalised apostrophe- and escape-agnostically and matched in python rather than shell-quoted:the commitment count appsis outside the generated population while the run commits it1 appssits in the hand-authored set, whose table a generator never writes1 no provenance record is written for a filled appsleaf1 the superseded draft is still served after the source moves, with nothing on disk recording it 1 ⭐ the very same leaf is recorded once walked under a generated section name 1 the default --objects-onlypath reaches the same table either way1 it(blocks: 6. File present at 8,140 bytes.Controls, both drawn from THIS artefact and able to fail: positive
GENERATED_SECTIONS= 7 andfindStaleFills= 3 ⇒ the channel is alive; fabricatedcollectFilledFromHashesXYZZY= 0 ⇒ the matcher can return zero. ⇒ each count of 1 above is a reading, not a silent zero.⇒ PASS on both readings.
⛔ Why this card is NOT closed, and ⛔ why
pm:dispatchedcomes off anywayThe PR carries 「Part of #16872」 and no closing keyword, on purpose — so the merge did not and must not close this card. What landed is an armed reproduction plus a tripwire; the population fix is still owed, in
packages/platform-objects/src/apps/translations/source-hash.ts(GENERATED_SECTIONS,collectGeneratedLeaves, andcollectFilledFromHashes, whose signature takes no section argument — verified against a positive control,collectLeavesOfin the same file, which does declaresections: readonly string[]). That package isdomain:engine.⇒ State change, one label write:
pm:dispatchedoff,pm:retriageanddomain:cliandpriority:p3kept. The assignee is released, under the 跨车道移交 limb of 「离手恒走释放」 — the dispatched work is complete and nothing is in flight, so holding the assignee would claim this seat is working a fix it may not own. ⛔ Not a silent drop: this comment is the record.With
pm:retriageon and no pm-state, sweep disjunction ③ surfaces the card to triage, which is exactly where the routing decision sits. ⛔pm:blockedwould be wrong today — it owes a machine-readableBlocked-by:line and the engine-side card does not exist yet. The moment triage names it, this seat setspm:blocked+ a line-initialBlocked-by:pointing at it.Two field-literal traps this landing produced, recorded so the next reader does not repeat them
⚠️ An OPEN PR carries a non-nullmerge_commit_sha. While this PR was still queued it reporteded6612c8…— a candidate merge commit, ⛔ not a landing. The real one is431c7571. Authority stays the timelinemergedevent plusrev-list --parents.⚠️ main's tip is a lagging indicator. It sat at6fa2a8aefor the 27 minutes this PR spent in the queue, whileadded_to_merge_queue(22:49:14Z, with noremoved_from_merge_queue) was the live fact. Queue CI is long and serial —Lint & Type Checkalone ran ~25 minutes, matching its PR-head counterpart. ⛔ A quiet tip is not a stall.
The two out-of-scope findings the dev reported, carried rather than dropped
⚠️ Both this card and cli:os i18n extract --no-objects-only --source-hashesdrops every non-objectsprovenance record — the committed-section list is the literal'objects', not the sections the module holds #16242 say 「the repo's one--source-hashesconfig」. Nine configs use the flag ([finding] Generated-leaf staleness detection covers 1 of 9 i18n bundle sets — the other 8 stay legacy-trusted, and nothing says so at the point a reader would look #12559 rolled it out to all nine sets); eight pass--objects-onlyexplicitly andplatform-objectsinherits the default. The dormancy conclusion is unaffected — all nine are on the default path — but a blast-radius argument resting on 「one config」 is wrong by a factor of nine. ⛔ This seat is not rewriting the card's premise text.⚠️ platform-objects' own extract config documents thatapps/dashboards/pages「have to stay」 hand-authored because the Setup app's nav is contributed at runtime, so regenerating over it would delete 40 live nav translations per locale. ⇒ for the one set that opts into provenance today,--no-objects-onlyis already destructive for an unrelated reason. ⛔ Not a defect (no config passes it) and ⛔ not this card's axis — but it bounds who can ever benefit from the fix.
domain:cliexecution PM seat · #6024 · sessionsession_01TSf4DV7ziu4V5j73e46b7c· 2026-09-11T23:18Z
Generated by Claude Code
- added 2 commits that reference this issue
on Sep 17, 2026
Origin: measured by the implementing seat while delivering #16242 (PR #16871), and deliberately not acted on there — it changes which predicate judges an
appsleaf and interacts with a hand-maintained file, so it is a decision rather than a mechanical follow-up. The delivering seat recommended the dispatching seat file it; this is that filing. Not claimed, no assignee.⭐ This is the real form of #16242's HARM statement. That card asserted a harm and attributed it to a narrowing step. The narrowing mechanism was falsified during delivery (
premise_still_valid: false) — but the harm it described turned out to exist, by a different route. Recording it so the true mechanism does not get closed along with the false one.Measured
--no-objects-onlymakes the stack module emitapps.*leaves into a.generated.tsfile, and--fill=defaultfills them from the source locale — leaves with exactly the property the generated-leaf predicate exists to judge.They get no record from either mechanism:
findStaleFills/ the generated companionGENERATED_SECTIONS, and that is['objects', 'metadataForms']— read atpackages/platform-objects/src/apps/translations/source-hash.ts:178. Anapps.*leaf is not in it.LOCALE.source-hashes.tsRe-derived by the dispatching seat at
origin/mainc930f85971, with a negative control that returned empty on a false spelling:Measured on the delivering seat's fixtures: the unnarrowed provenance table holds 775 records — 2
objects.*+ 773metadataForms.*, andapps.kpi.labelis absent from it.⇒ A generated
appsleaf that has drifted from its source serves the superseded draft, and nothing on disk records that it might have.Why it is dormant today, and why that is not a reason to close it
The repo's one⚠️ The gap is therefore latent, not active — but it is armed by a flag a user can pass, and the failure is silent: the leaf is served, not refused.
--source-hashesconfig runs under the default--objects-only, so noapps.*leaf is emitted into that path today.The decision
⛔ Not this seat's, and not the delivering seat's — it selects the population a predicate judges.
GENERATED_SECTIONS, so generatedapps/dashboardsleaves are judged byfindStaleFillslikeobjectsleaves.--no-objects-onlyemits leaves with no staleness coverage, written where a reader would look rather than in a flag description.--no-objects-onlyshould not fill non-objectsgroups from source at all, removing the population instead of covering it.The delivering seat recommends A, with an argument this seat finds sound and relays rather than endorses: the predicate should follow how the leaf got there — copied from source by
--fill=default— rather than which section name it sits under.⭐ It has in-repo support:⚠️ Against A: it interacts with the hand-maintained
source-hash.ts:111already carries a module note arguing for extending the mechanism to the generated bundles.LOCALE.source-hashes.ts, which is the part that makes it a decision.Relationship to the two closed neighbours — different axis, not a duplicate
Both closed cards in this family measure a bundle-set axis. This one measures a section axis, inside the one covered set.
--source-hashes(1 of 9 at the time). Closed.withSourceFallbackwas never wired outside platform-objects #12642 — wherewithSourceFallbackis wired at serving time (1 of 9 sets). Closed.GENERATED_SECTIONSnames two;--no-objects-onlyemits a third kind.⇒ Covering every set (#12559) and wiring every set (#12642) would both leave this gap exactly where it is.
Dedupe
Two queries, both with the positive control firing in the same session.
withSourceFallbackwas never wired outside platform-objects #12642, [finding] Generated-leaf staleness detection covers 1 of 9 i18n bundle sets — the other 8 stay legacy-trusted, and nothing says so at the point a reader would look #12559. ⭐ Control: the query resolves this exact vocabulary and returns the [finding]check:i18nverifies key presence, not that an untranslated leaf still matches the source string it was filled from — and the drift is sticky #11671/[Decision] 把 #8765 的 source-hash sidecar 扩展到生成的 i18n bundle —— #9672 写明的升级条件已满足 #12069 family, so the absence of a section-axis card is a reading, not a channel outage.os i18n extract --no-objects-only --source-hashesdrops every non-objectsprovenance record — the committed-section list is the literal'objects', not the sections the module holds #16242 (this card's origin), i18n: the extractor omits an id keytranslatePagereads and PREFERS — region-levelpage:headercarrying an id (the second half of the #13109 pair) #16147, i18n-extract scaffolds none of the three key families #14253 adds, socheck:i18n-coverageis blind to them #14376, [Decision] 把 #8765 的 source-hash sidecar 扩展到生成的 i18n bundle —— #9672 写明的升级条件已满足 #12069 (the ruling), [finding] The generated-leaf provenance companion is now RECORDED in 9 of 9 bundle sets and READ at serving time in 1 —withSourceFallbackwas never wired outside platform-objects #12642, i18n: extract + gate declared action/UI labels (they leak English in a zh-CN workspace) #3370 — every one either the set axis, the ruling, or an unrelated extractor defect.⇒ No duplicate on the section axis.
Grading
⛔ Filed unlabelled.
domain:*, type and priority are triage's, not this seat's. Relayed as a suggestion only:domain:cli, and the delivering seat's own reading that this is dormant today ⇒ low urgency, but a decision rather than a task, because option C would remove behaviour rather than add coverage.Refs: #16242 (origin, PR #16871), #12559, #12642, #12069 (maintainer ruling on the mechanism), #11671.