Repository navigation
Commit 3270826
fix(pm): check-prior-rulings parses the
Fixes #19132
Clause-②: no
Run from an **objectui** checkout, `scripts/pm/check-prior-rulings.mjs`
refused — `docs/adr` parsed to zero decision units — so every objectui
decision card was entitled to exactly one paste line, `Prior rulings
read: unresolved`, and could never cite a prior ruling. Both halves of
the lane direction (comment 5738994087) land here.
## Measured first — the structural difference between the two corpora
The direction required the measurement **before** any widening. Taken
this act at `objectui@0c2eb5e` and `objectstack@13d5294`.
### What counted as a decision unit before this PR
Two shapes, both keyed on a **D-number**: a level-3/4 heading (`### D8 —
One predicate, computed once`) and a decision bullet (`- **D4** — A
tenancy kernel service…`) with the separator rule that rejects prose
about a decision. Nothing else was a unit.
### Which marker objectui's ten files use
| file | `**Status**:` line | D-number units | `## Decision` anchor |
units (before → after) |
|:--|:--|--:|:--|--:|
| `0001-master-detail-subform.md` | yes | 0 | `## Decision` | 0 → 5 |
| `0034-unified-runtime-metadata-persistence.md` | yes | 0 | — none — |
0 → 0 |
| `0036-field-conditional-rules.md` | yes | 0 | — none — | 0 → 0 |
| `0053-list-view-navigation-modes.md` | yes | 0 | `## Decision detail`
| 0 → 7 |
| `0054-ui-testability-contract.md` | yes | 0 | `## Decision detail` | 0
→ 7 |
| `0055-parameterized-bare-data-surface.md` | yes | 0 | — none — | 0 → 0
|
| `0056-permission-editing-studio.md` | yes | 0 | `## Decision` | 0 → 6
|
| `0057-console-ai-chat-one-conversation-docked.md` | yes | 0 | `##
Decision` | 0 → 6 |
| `0058-cascading-select-options.md` | yes | 0 | `## Decision:
per-option …` | 0 → 5 |
| `0059-action-params-shared-field-widgets.md` | yes | 0 | `## Decision`
| 0 → 5 |
| **total (10 files)** | **10 of 10** | **0** | **7 of 10** | **0 → 41**
|
**The dispatch's mechanism assumption 2 is REFUTED on its status half.**
It read: no numbered D-units and 「as far as the seat can see」 no
`**Status**:` line. The D-number half holds — zero across all ten. The
status half does not: **all ten carry a `**Status**:` line**, in
spellings this file already supports (`**Status**: Accepted —
implementing (2026-06-05)`, `**Status**: Proposed (2026-06-22)`, …). So
`extractStatusLine` and the positional `classifyStatus` needed **no
change at all**, and objectui units arrive correctly classified. One
measured consequence: `0034`'s `**Status**: Implemented (2026-06-06)`
carries no word in `STATUS_WORDS`, so it classifies `unknown` — the
intended loud bucket, not a regression.
The whole structural difference is therefore **the decision unit, and
nothing else**. objectui's decisions live under a level-2 `## Decision`
/ `## Decision detail` / `## Decisions` / `## Decision: TITLE` section
whose parts are level-3 headings (`### C1 — Idempotent, direct
triggers`) or numbered bold items (`1. **Two orthogonal fields, no mode
discriminator.**`).
### The same blind spot is 57% of THIS repo
| corpus | ADR files | files with zero D-number units | of those,
carrying a `## Decision` anchor |
|:--|--:|--:|--:|
| objectstack `docs/adr` | 139 | **79** | **59** |
| objectui `docs/adr` | 10 | **10** | **7** |
This is not a foreign dialect the reader is being taught to tolerate —
it is a shape the reader was already blind to in the corpus it was
written for.
## The fix
### Half ① — the line names the repository whose `docs/adr` was read
Derived from **this checkout's** `origin`, never `PM_SWEEP_REPO`: the
corpus is a property of the working directory, and the two diverge in
exactly the case the card names. Named in **both** arms, so the
wrong-corpus case is visible in the line itself:
```text
Prior rulings read: gate,required,spec,shape → 11 hits; ADR-0001 Decision §1; thread: not read (no --card); repo: objectstack-ai/objectui
Prior rulings read: unresolved (repo: objectstack-ai/fakerepo)
```
The refusal headline names it too (`docs/adr` in objectstack-ai/fakerepo
parsed to zero decision units), and with `--card` the report says
**WRONG CORPUS** outright when the board and the corpus disagree — the
card's second reading, the one six objectui cards were corrected in
place for.
⛔ Still no `--repo` flag: the corpus is *reported*, never redirected. An
unresolvable `origin` prints `repo: unknown` rather than dropping the
clause, because a line that omits the repo reads like one that agrees
with the board.
The paste line stays **additive**: `references/decision-analysis.md`
pins the terms / hits / named / thread field order, and the repo clause
is appended **after** it, so the documented order and the literal
`^Prior rulings read: ` grep are both unchanged.
### Half ② — a THIRD shape, scoped as a FALLBACK
It runs **only on a file the two D-shapes parsed to zero units**. That
is what keeps it from being a widening: the **53** D-carrying
objectstack ADRs that *also* head a `## Decision` section are untouched,
so a heading that merely *contains* their D-units can never double-count
them.
Prose is refused by three guards, each pinned:
1. the **structural** level-2 anchor — a heading whose title *opens*
with `Decision`/`Decisions`, never a sentence that mentions one;
2. the **section-lead** unit needs a non-empty first paragraph, so a
bare `## Decision` with nothing under it yields **0** — and an empty
`docs/adr` still exits 3, the control kept;
3. the file must carry an ADR number, and a file that already speaks
D-units is never re-read.
A structural *part* carries its own line instead of needing a following
paragraph, because a one-line item says everything on it (`ADR-0050`'s
`1. **FormView.type = layout only**: …` would otherwise be dropped).
Unit ids are positional (`Decision §1`, `Decision §2`, …): this shape
has no D-number to *be* an identity, so the author's own label (`C1 —
…`) rides in the heading beside it, and a bare `## Decision` takes the
ADR's H1 so its lead unit is never nameless.
## Before / after, both checkouts, with exit codes
| run — `--terms gate,required,spec,shape` | before | after |
|:--|:--|:--|
| `cwd` = `/home/user/objectui` (`0c2eb5e`) | **exit 3** — `docs/adr`
parsed to zero decision units | **exit 0** — 41 units, 11 hits, line
resolved |
| `cwd` = objectstack | **exit 0** — 465 units, 117 hits | **exit 0** —
758 units, 180 hits |
| empty `docs/adr` fixture (control) | **exit 3** | **exit 3** — and now
names the repo |
**The no-double-count invariant, measured rather than argued.** Every
`docs/adr/*.md` in both repos parsed with the `origin/main` parser and
this one, comparing unit id, line and text:
| corpus | files | units before → after | files changed | of those,
changed **from a non-zero count** |
|:--|--:|:--|--:|--:|
| objectstack | 139 | 465 → 758 | 59 | **0** |
| objectui | 10 | 0 → 41 | 7 | **0** |
Every changed file went `0 → N`. No file that already had units moved by
a single unit, so no existing rank moved under them.
## Pins added — self-test 143 → 155 cases (`pnpm
check:pm-prior-rulings`)
Pins from both sides, as the direction required:
- objectui's real structure (a `## Decision detail` of numbered bold
items) → 2 units, ADR id from the filename, `accepted` from the status
line objectui does carry, the item body present in the unit text so a
term can match it, and the following `## Consequences` contributing
nothing;
- `## Decision` with an **empty body** → **0 units**;
- a sentence *mentioning* a decision under `## Context` → **0 units**;
- a file that **has** a `D1` heading *and* a `## Decision` section →
`D1` only, ⛔ no double count;
- a path with no ADR number → 0 units, anchor or not;
- the repo clause in the resolved line, in the refusal, and its
`unknown` fallback.
The pre-existing fixture ranks are **unmoved**: the #16934 fixture still
surfaces `ADR-0131 D8` at rank 1, the superseded ADR is still
found-but-unnamed, the substring-noise control still holds, and the
fixture corpus still parses to exactly 6 units — it carries no `##
Decision` section, so the fallback never fires on it. Seven paste-line
pins were updated for the appended repo clause; each is the same
assertion with `; repo: unknown` added.
## Gates
30 of 30 derived families run at head `8ba9049`, each exit code captured
**before any pipe**; `dispatch-gates --ran` reconciles **30 derived / 30
run / 0 NOT-MEASURED — a DERIVED zero** (every family recorded a code
and none is 3). Derivation asserted with `--repo
objectstack-ai/objectstack`.
`origin/main` moved to `2277d1f` while the battery ran
(`check:pm-dispatch-gates` alone took 1047 s on a box running three
copies of it for three parallel agents), so the family list is current
as of merge-base `13d5294`; CI and the merge queue are the authority
past that point.
## Acceptance notes
- **noted, not filed:** the `owner/name` parse from an `origin` URL now
exists twice — the new local helper here, and the regex inside
`localCheckoutServes` in `scripts/pm/check-half-states.mjs` (landed in
#19288 while this branch was open). They answer different questions (a
display name vs a boolean "does this checkout serve that board"), so
neither can call the other today. The clean fix is one exported parser
in `check-half-states.mjs`, which needs a file outside this card's
surface **and** a new export against a `Clause-②: no` claim. Carrier:
the next card in the #19191 / #19288 checkout-serving lane.
- **noted, not filed:** `0034`'s `**Status**: Implemented (…)`
classifies `unknown` because `Implemented` is not in `STATUS_WORDS`. The
raw line prints beside it and `unknown` is the designed loud bucket, so
this is not a defect. Carrier: whoever next widens `STATUS_WORDS`.
- **noted, not filed:** 20 objectstack ADR files still parse to zero
units — they carry neither a D-unit nor a `## Decision` anchor.
Shrinking that further means a fourth shape, which this card's direction
does not authorize and no measurement here justifies. Carrier: none
today.
- `scripts/pm/**` is not on the governed register (`docs/adr/**`,
`.claude/**`, `skills/**`, `AGENTS.md`, `CLAUDE.md`,
`docs/NORTH-STAR.md`), so no landing-tier record is owed.
- Not published → `skip-changeset`, no changeset file. Net **+119**
lines in the one file (141 added / 22 removed) against the PM's ≤ +120
budget.
- The objectui copy is **not** edited here; it follows through the
upstream port pin.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_
Co-authored-by: Claude <noreply@anthropic.com>## Decision shape and names the corpus repo (#19353)1 parent 231283a commit 3270826
1 file changed
Lines changed: 141 additions & 22 deletions
0 commit comments