Repository navigation
Commit de9c319
fix(pm): H44's timestamp leg accepts an ISO instant whose hour follows a T — the ruled minute spelling no longer reads as untimestamped (#18326)
Fixes #18323
`H44_READING_TIMESTAMP` is the leg that decides a paragraph already
carries its 取数时刻. It anchored the hour with a bare word boundary, which
needs a NON-word character in front of it — and the `T` of an ISO
instant is a word character. So the leg read **false** on the very
spelling the 2026-09-02 ruling gives, and H44 reported readings that DO
carry their time as untimestamped: a false positive on the row's own
subject. A seconds stamp passed only by accident, the boundary holding
before its `mm:ssZ` tail, so the match began at the MINUTES.
```js
// before
/\b\d{2}:\d{2}(?::\d{2})?Z\b/
// after — the left anchor is a word boundary OR a lookbehind for the ISO T
/(?:\b|(?<=T))\d{2}:\d{2}(?::\d{2})?Z\b/
```
## Reverse verification — probes on the module's own export
A `file://` import of the script (a pure export; the module runs nothing
on import and makes no request), against the unpatched file at
`a46cd8c4` and against this branch:
| probe | before | after |
|---|---|---|
| `2026-09-15T14:51Z` — the live R+236 marker's fire stamp | false |
**true** |
| `2026-03T00:21Z` — the spelling the ruling itself gives | false |
**true** |
| `2026-09-15T14:51:42Z` | true | true |
| `14:51Z` — a bare clock | true | true |
| `19:38` — a bare clock with no zone | false | false |
| `12314:51Z` — a clock inside a longer digit run | false | false |
The seconds probe is the one that shows the difference between the fix
and the accident it replaces: `'2026-09-15T14:51:42Z'.match(...)[0]` was
`51:42Z` before and is `14:51:42Z` now — the match starts at the HOUR,
and that is pinned as a case rather than the bare `true` that held
either way.
## The one design choice, on the four axes
The choice is between **naming the left anchor** (start / whitespace /
punctuation, which the word boundary already spells, plus the ISO `T`)
and **dropping the left boundary altogether**. This PR names it, and
looks behind rather than consuming, so a match is still exactly the
stamp.
- **实际业务需求** — measured, not supposed: the only predecessor that occurs
on this board is the ISO `T`, in the R+236 round-open marker (`fire
2026-09-15T14:51Z`) and in the ruling's own `2026-03T00:21Z`. No
artefact was measured writing a stamp behind any other letter. Dropping
the boundary would serve a case nobody has: `12314:51Z` is a digit run
that happens to contain a clock, never a reading's time.
- **项目长远合理性** — the boundary is doing real work and the defect was its
SCOPE, not its presence; keeping it and naming the one exception is the
contract-shaped repair, where dropping it would be a workaround that
trades one class of wrong answer for another. The row's banner already
claimed that a paragraph carrying `HH:MM[:SS]Z` is clean — this makes
the regex true to a statement the file has been making all along, rather
than rewriting the statement down to the implementation.
- **防 AI 写代码犯错** — a report-only patrol that fires on a well-formed
artefact teaches the seat writing it that the ruled spelling is wrong,
and the cheapest way to silence a false row is to write a MORE precise
stamp than the ruling asks for (which is exactly what this file's own
marker case had been reduced to doing). Removing the false positive
removes that pressure. Looking behind rather than consuming keeps the
match equal to the stamp, so a future reader that echoes it gets no
stray designator. Admitting any letter — the lenient direction — would
make the leg quieter in ways nobody declared; the three negatives are
pinned as cases so the widening cannot creep.
- **创业阶段不扩散** — exactly one predecessor is admitted, and only in the
uppercase the ruling spells. RFC 3339's lowercase `t` is legal and
absent from this corpus; it is pinned as NOT admitted, so taking it
later is a measurement rather than a guess.
## Self-test
`node scripts/pm/check-half-states.mjs --self-test`: **4235 → 4243 cases
pass**, exit 0. Eight cases added, one rewritten:
- the two ISO spellings that read false (`2026-09-15T14:51Z`, and the
ruling's own `2026-03T00:21Z`);
- where the seconds stamp's match STARTS;
- three declared negatives — the digit run, any other letter, and the
lowercase `t`;
- an ISO instant with no zone (`2026-09-15T14:51`) still reads false,
exactly as a bare clock does;
- the H44 marker case re-pinned to MINUTES, with the SECONDS spelling of
the same reading kept beside it so neither can regress alone. The
comment above it said this file spelled that stamp to seconds because a
minute stamp could not clean a paragraph — a statement that is now
false, so it is rewritten (not deleted) to say the limit is gone.
**Firing control (ablation):** with the one regex line reverted and
every case kept, the self-test goes red on exactly four cases by name —
the two ISO spellings, the match-start pin, and the marker case, which
reports `{shape: 'a round-open marker', kind: 'tree', fragment:
'827cacbf'}` where `null` is wanted. That row IS the false positive this
PR removes. Restoring reproduced the committed blob hash exactly, and
`git diff HEAD` and `git status --porcelain` are both empty.
## Scope
`scripts/pm/check-half-states.mjs` only, and inside it only the constant
at the H44 grammar, its doc comment, and its self-test cases. H44's
population, its seat gate, its marker shapes (`h44RoundOpenShapes` /
`h44IsRoundOpenMarker`), the fragment grammar and every other H row are
untouched. `:9791` is the constant's only consumer (`grep` count: one
declaration, one consumer, the self-test cases) and it calls `.test`, so
nothing reads the match text today.
The two sibling cards dispatched on this same file — #18324 and #18325 —
work at other regions (`h44NeedsSeatComments`, the `seatMarkers`
capture, `latestSeatMarker`) and also add self-test cases at the tail;
whichever lands second merges `main` and re-runs the self-test. PR
#18322 is the predecessor that landed the marker spelling this card's
measurement came from.
## Acceptance notes
- `PROTOCOL_STAMP_RE` (H56's stamp shape) carries the same `\b` opener
and is NOT affected: its boundary sits in front of the four-digit YEAR,
where a non-word character really does precede it. Checked, not assumed.
- Noted, not filed: the leg validates the SHAPE of a clock and not its
ranges, so `1234-56:78Z` reads as a stamp. It read that way before this
change and still does; the direction is toward MORE paragraphs clean, so
it can only make the row quieter, and no artefact on the board writes
it.
`skip-changeset`: nothing published moves. The diff is one file under
`scripts/pm/**`, which no package's `files[]` ships.
_Generated by [Claude
Code](https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr)_
---
_Generated by [Claude
Code](https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent a46cd8c commit de9c319
1 file changed
Lines changed: 50 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9636 | 9636 | | |
9637 | 9637 | | |
9638 | 9638 | | |
| 9639 | + | |
| 9640 | + | |
| 9641 | + | |
| 9642 | + | |
| 9643 | + | |
| 9644 | + | |
| 9645 | + | |
| 9646 | + | |
| 9647 | + | |
| 9648 | + | |
| 9649 | + | |
| 9650 | + | |
| 9651 | + | |
| 9652 | + | |
| 9653 | + | |
| 9654 | + | |
| 9655 | + | |
| 9656 | + | |
| 9657 | + | |
| 9658 | + | |
| 9659 | + | |
| 9660 | + | |
| 9661 | + | |
| 9662 | + | |
| 9663 | + | |
| 9664 | + | |
9639 | 9665 | | |
9640 | | - | |
| 9666 | + | |
9641 | 9667 | | |
9642 | 9668 | | |
9643 | 9669 | | |
| |||
28900 | 28926 | | |
28901 | 28927 | | |
28902 | 28928 | | |
| 28929 | + | |
| 28930 | + | |
| 28931 | + | |
| 28932 | + | |
| 28933 | + | |
| 28934 | + | |
| 28935 | + | |
| 28936 | + | |
| 28937 | + | |
| 28938 | + | |
| 28939 | + | |
| 28940 | + | |
| 28941 | + | |
| 28942 | + | |
| 28943 | + | |
28903 | 28944 | | |
28904 | 28945 | | |
28905 | 28946 | | |
| |||
28989 | 29030 | | |
28990 | 29031 | | |
28991 | 29032 | | |
28992 | | - | |
28993 | | - | |
28994 | | - | |
28995 | | - | |
28996 | | - | |
| 29033 | + | |
| 29034 | + | |
| 29035 | + | |
| 29036 | + | |
| 29037 | + | |
| 29038 | + | |
| 29039 | + | |
| 29040 | + | |
28997 | 29041 | | |
28998 | 29042 | | |
28999 | 29043 | | |
| |||
0 commit comments