Repository navigation
Commit 23f1de0
fix(scripts/pm): H52 stands down on a seat ANSWER a held card can actually write (#19436)
Fixes #19160
Clause-②: no
## The defect
H52 stood down on exactly three acts: close the card, hang
`needs-user-decision`, or post a newer `os-dev-report` whose
`open_questions` is empty. A card no dev is dispatched to can perform
**none of the three** — closing a defect card is forbidden (「缺陷卡 ⛔ 不藏进
hold 也不自行关闭」), the row's own remedy forbids re-hanging
`needs-user-decision` for a residual raised while executing a ruling,
and no newer report can exist because a `pm:on-hold` card is dispatched
to nobody. The one act a seat CAN perform — answering the questions on
the thread — was invisible, because `latestDevReport` reads only report
comments.
That is the 「无机制可唤醒的卡 ⛔ 不 hold」 shape the charter refuses for a CARD,
one layer up: a patrol row with no act that can ever clear it.
## The answer shape — MEASURED from the two live answers, ⛔ not invented
Both live carriers already carry it, **unedited**. The pin is one ATX
heading line that names the array and says ANSWERED:
| card | answering comment | the heading line, verbatim |
|:--|:--|:--|
| objectui#8348 | `5737496254` | `### The two open questions, answered`
|
| objectui#9868 | `5736885341` | ``## Half-state patrol H52 — the two
`open_questions` on this card are ANSWERED, and this comment is the
record that stands the row down`` |
```js
export const H52_ANSWERED_HEADING =
/^#{1,6}[ \t]+(?=[^\n]*\bopen[\s_`*]*questions?\b)(?=[^\n]*\banswered\b)/im;
```
LINE-anchored (`m`) where `OS_DEV_REPORT_MARKER` is body-anchored,
because objectui#8348 carries its heading 32 lines into a seat review,
so a body anchor would have read only one of the two. The ATX heading is
what keeps that safe: the same words in **prose** are refused, and so is
a **blockquoted** heading (a QUOTE of another card's answer). Both
refusals are pinned, and both are ablated below.
## Before / after, measured on the two REAL threads
Not a fixture — the live comment rows, fetched and handed to both
versions of the predicate:
| card | pre-fix (`488f4f5`) | post-fix (`a978f79`) |
|:--|:--|:--|
| objectui#8348 (`pm:on-hold`, 14 comments) | H52 fires | **clears** |
| objectui#9868 (`pm:on-hold`, 7 comments) | H52 fires | **clears** |
Neither card needs an edit: the answers they already carry are the
record. Population unchanged, the `needs-user-decision` refusal
unchanged, `latestDevReport` unchanged.
## The four axes, per design choice
**1. A fourth null-exit, ⛔ not a state-based exclusion.** *Business
need*: the two live instances are correctly held and correctly answered
— what is missing is a reader, exactly as the row's own filing said of
`open_questions` itself. *Long-term*: excluding `pm:on-hold` would
delete the row's whole population argument (the census measured 14
on-hold carriers and 52 of 63 carriers outside `pm:dispatched`) and
would silence a card whose question really is open. *AI-error*: an exit
keyed on a DECLARED act keeps 「declared = enforced」; a state exclusion
makes silence depend on a label nobody wrote for this purpose. *Startup
focus*: no new surface — one reader beside an existing one.
**2. The shape is measured from the two live answers, ⛔ not a fourth
prose rule.** *Business need*: both answers satisfy it with zero edits,
so the fleet pays nothing and the shape is already in use. *Long-term*:
a rule nobody has written yet would have no corpus behind it and would
drift. *AI-error*: the ATX heading is a loud, structural declaration —
the seat SAYS the questions are answered; prose that merely reads like
an answer is refused, which is the strict-contract direction rather than
lenient matching. *Startup focus*: one regex, no new protocol.
**3. Ordering by the same rule the family already uses.** *Business
need*: a seat answer OLDER than the report does not clear — the report
re-raised the questions after it. *Long-term*: `latestMarkedComment`
(newest-of) and `releaseAnswersClaim` (is this the later record) are
CALLED, not restated, so "which of two is newer" stays answered in one
place for every marker. *AI-error*: a second hand-rolled comparator is
where the next tie-break bug lives. *Startup focus*: zero new machinery.
**4. A SIBLING reader, ⛔ never a change to `latestDevReport`.** That
reader is shared by other rows and returns exactly what it returned; the
new exit is additive.
**5. ⭐ A guard I wrote, ablated, and REMOVED as the phantom it was.**
The first draft excluded a report from being the answer with an explicit
test. Ablating it left the suite green: the newest `os-dev-report` is BY
CONSTRUCTION at least as new as any report carrying the heading, so the
ordering rule already answers false and the guard could never change an
answer. *AI-error axis, decisively*: a guard that can never fire teaches
the next reader that the exclusion is enforced there, and the pin behind
it was vacuous. Removed, with the proof recorded in the docblock; the
pin now rides the ordering rule and the ordering ablation reds it.
## Ablation — every behavioural pin proved non-vacuous
Each leg via `scripts/ablation-replace.mjs` (anchor must HIT; on-disk
blob before/after; restore verified `blob == HEAD` and `git diff HEAD`
empty).
| leg | mutation | result |
|:--|:--|:--|
| 1 | delete the fourth exit from the predicate | RED — both
live-heading pins fail |
| 2 | delete the report-exclusion guard | GREEN ⇒ phantom; guard removed
from the diff |
| 3 | `releaseAnswersClaim(answer, report)` to `true` | RED — the OLDER
pin and the REPORT pin fail |
| 4 | drop the ATX anchor from the shape | RED — the PROSE pin and the
QUOTE pin fail |
Direction observed: **turned red**, on the legs that own each pin — leg
2 is the one that came back green, and that green is what removed a line
of code rather than weakening a gate.
## Verification
`node scripts/pm/check-half-states.mjs --self-test` exit 0 — **5142
cases** on the merged tree (5124 at the branch point, +9 mine, +9 from
the H59 row merged in). `SELF_TEST_BATTERY_FLOOR` untouched at 8; all
eight batteries unchanged.
The derived union — `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at `a978f79`, no paths — listed 41
commands. Every one was run with its exit captured before any pipe; the
union run and this reading are taken at that head.
## Acceptance notes
- Net `+60` lines in `scripts/pm/check-half-states.mjs` (72 added / 12
deleted vs `origin/main`), inside the dispatch's `+60` budget, self-test
cases included.
- `skip-changeset`: `scripts/pm/**` publishes nothing from any released
package — the fast-track path, no measurement owed.
- `origin/main` was merged once, after the H59 row (PR #19427) landed on
it; the two H-row regions are disjoint and the merge auto-resolved with
no conflict.
- noted, not filed: `markerMatches` undecorates backticks and asterisks
but not a leading blockquote marker, which is exactly why the QUOTE
refusal holds. That is load-bearing for this row and is now pinned here;
承接者: the next author of an undecoration spelling, who will find the pin.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b71d9e7 commit 23f1de0
1 file changed
Lines changed: 72 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11987 | 11987 | | |
11988 | 11988 | | |
11989 | 11989 | | |
11990 | | - | |
11991 | | - | |
11992 | | - | |
11993 | | - | |
11994 | | - | |
11995 | | - | |
11996 | | - | |
11997 | | - | |
11998 | | - | |
11999 | | - | |
12000 | | - | |
| 11990 | + | |
| 11991 | + | |
| 11992 | + | |
| 11993 | + | |
| 11994 | + | |
| 11995 | + | |
| 11996 | + | |
| 11997 | + | |
| 11998 | + | |
| 11999 | + | |
| 12000 | + | |
| 12001 | + | |
| 12002 | + | |
| 12003 | + | |
| 12004 | + | |
| 12005 | + | |
12001 | 12006 | | |
12002 | 12007 | | |
12003 | 12008 | | |
| |||
12229 | 12234 | | |
12230 | 12235 | | |
12231 | 12236 | | |
| 12237 | + | |
| 12238 | + | |
| 12239 | + | |
| 12240 | + | |
| 12241 | + | |
| 12242 | + | |
| 12243 | + | |
| 12244 | + | |
| 12245 | + | |
| 12246 | + | |
| 12247 | + | |
| 12248 | + | |
| 12249 | + | |
| 12250 | + | |
| 12251 | + | |
| 12252 | + | |
| 12253 | + | |
| 12254 | + | |
| 12255 | + | |
| 12256 | + | |
| 12257 | + | |
| 12258 | + | |
| 12259 | + | |
| 12260 | + | |
| 12261 | + | |
| 12262 | + | |
| 12263 | + | |
| 12264 | + | |
| 12265 | + | |
| 12266 | + | |
| 12267 | + | |
| 12268 | + | |
| 12269 | + | |
| 12270 | + | |
| 12271 | + | |
| 12272 | + | |
12232 | 12273 | | |
12233 | 12274 | | |
12234 | 12275 | | |
| |||
12266 | 12307 | | |
12267 | 12308 | | |
12268 | 12309 | | |
| 12310 | + | |
| 12311 | + | |
| 12312 | + | |
12269 | 12313 | | |
12270 | 12314 | | |
12271 | 12315 | | |
| |||
12291 | 12335 | | |
12292 | 12336 | | |
12293 | 12337 | | |
12294 | | - | |
| 12338 | + | |
| 12339 | + | |
| 12340 | + | |
12295 | 12341 | | |
12296 | 12342 | | |
12297 | 12343 | | |
| |||
33568 | 33614 | | |
33569 | 33615 | | |
33570 | 33616 | | |
| 33617 | + | |
| 33618 | + | |
| 33619 | + | |
| 33620 | + | |
| 33621 | + | |
| 33622 | + | |
| 33623 | + | |
| 33624 | + | |
| 33625 | + | |
| 33626 | + | |
| 33627 | + | |
| 33628 | + | |
| 33629 | + | |
| 33630 | + | |
33571 | 33631 | | |
33572 | 33632 | | |
33573 | 33633 | | |
| |||
0 commit comments