Repository navigation
Commit 66ac73d
feat(scripts): report-only present-tense phrase reading on the spec docblock corpus (#20057)
Fixes #19017
Clause-②: no
Executes ruling `5805901449` on #19017 (batch #219 item 2, letter 甲,
「只报告」; maintainer 「同意」): the spec docblock gate family gains a narrow
present-tense phrase reading, report-only.
## What changed
One file: `scripts/check-spec-docblock-symbol-anchors.mjs` (+403 / -3).
- `PRESENT_TENSE_PHRASES` is exactly the ruled list: `this phase`, `is a
follow-up`, `not yet implemented`, `currently no`. Matching is
case-insensitive and whole-word (`currently no` does not match
`currently none`). Comment delimiters and the JSDoc gutter are blanked
and whitespace is folded across lines, so a phrase wrapped over two
lines is one hit, reported at the line where it starts.
- **Population and projection are the anchor corpus's own**: the files
`sweepCorpus` walked (its `byDoc`), read through `commentProse`. So
"docblock" means what it means for this gate: every comment, line or
block, and never code or a string literal. Test sources are skipped.
- **Report-only by construction.** `reportPresentTense` has no exit path
and sets no exit code. It turns any throw into a `NOT MEASURED` line. It
runs before the anchor verdict, so it also prints on a red run and
cannot move the verdict. Each hit prints as a `📝 [present-tense]
FILE:LINE "phrase"` line, followed by one summary line.
- A new `--present-tense` arm prints the reading alone and always exits
0. It is also what the exit-0 self-test spawns.
- The header records the list and its growth rule: a phrase joins only
with its own measured reading in `PRESENT_TENSE_CENSUS`, and the
self-test goes red on a listed phrase that has no reading. The header
also records today's count per phrase. `PRESENT_TENSE_REFUSED_WIDE`
records why `there is no` stays out.
- ⛔ No workflow edit, no `package.json` edit, no required check, and no
docblock edited.
## Extend vs sibling: extended, and why
I extended the existing script instead of adding a sibling script. It is
the smaller change:
1. **The reading is taken where it will be read.** `lint.yml` already
runs this script's default arm and `--self-test` on every PR, so the
report lands in that step's log with no new wiring. A sibling would need
either a workflow edit (out of scope for this claim) or nothing to run
it at all. An instrument no one runs takes no reading.
2. **One population, not two.** The report reuses the sweep the gate
already makes (`byDoc`), so the corpus definition is the family's and
cannot drift from it. A sibling would need its own walker, or would have
to import this one's internals.
3. **The existing verdict is left alone.** It stays byte-identical apart
from the added report lines (proof below). The new self-test cases are a
separate battery, so the existing battery still registers exactly 124
cases.
## Today's reading, and the judgement on each hit
`origin/main` at `5581d3000f27daa19991cf9d13d5ad1ed8cf8913`: 1,046
non-test sources read and 504 test sources skipped.
| phrase | hits | file, line | judgement |
|---|---|---|---|
| `this phase` | 0 | | |
| `is a follow-up` | 0 | | |
| `not yet implemented` | 1 | `packages/spec/src/api/errors.zod.ts` line
114 | **still true.** A trailing line comment that glosses what the
`NOT_IMPLEMENTED` error code means. It defines the code and makes no
claim about the repository's state. |
| `currently no` | 0 | | |
**Positive control.** I also ran the same instrument on the card's own
tree, `43f4766889e39d7a4590c5787d38e5956d0b4cb6`. It reads 2, 1, 1 and 0
for the four phrases. The first three are the
`packages/spec/src/data/api-derivation.ts` sites that #18991 repaired
(lines 129, 130 and 196, all **rotted**). I checked on that tree that
`plugin-hono-server`'s `current-user-endpoints.ts` already passed a real
`allowExport`-derived `userExportAllowed`. The fourth is the same
`errors.zod.ts` gloss. The card's probe also counted one `currently no`.
That one is in a string literal
(`packages/spec/src/data/driver/common.zod.ts`), outside this
projection.
**The wide phrase, measured with this instrument.** `there is no` gets
320 comment-prose hits in 149 files today, and 286 on the card's tree.
It stays out.
## Proof the existing gate is unchanged apart from the report lines
I ran the default arm on `5581d3000f` before any edit and again on
`27cd45335e` after the edit:
- The exit code is 0 before and 0 after, and stderr is empty both times.
- Stdout gains exactly three lines, inserted after the last day-one
residual row and before the unchanged final line: one `📝
[present-tense]` hit line, its excerpt line, and the summary line. Every
other line is byte-identical: `diff` reports a single `34a35,37` hunk.
- `--self-test` still exits 0 and gains one line (the new battery's
success line). The existing verdict line is unchanged. The existing
battery was probed at exactly 124 cases, and the new battery registers
38.
**The exit-0-with-hits case lives in the self-test.** It spawns
`--present-tense` over a fixture tree with one planted hit per phrase,
then asserts exit 0 and each printed `FILE:LINE "phrase"` line. It also
asserts the following:
- The wide phrase, the near misses (`currently none`, `this phased`,
`follow-upper`) and a string literal print nothing.
- A `.test.ts` that carries all four phrases is in the population but
skipped.
- The instrument does find `there is no` when asked directly, so it is
kept out by the list and not by a blind matcher.
- In-process calls with hits, an unreadable source and a population with
only test files each leave `process.exitCode` untouched and print `NOT
MEASURED` instead of throwing.
**Ablations** (one-time, run through `scripts/ablation-replace.mjs`,
which checks the mutation landed on disk and proves the restore: blob
equals HEAD and `git diff HEAD` is empty; no permanent test file):
- A1: `process.exit(1)` when the report has hits. Red at `the report arm
must exit 0 WITH hits, got 1`. My first A1 attempt was a no-op, because
the replacement still contained the anchor and the tool refused (anchor
count 1 to 1, restored). I discarded it and re-ran with a different
anchor.
- A2: gutter blanking removed. Red at `the planted "this phase" must be
ONE hit at packages/spec/src/pt/rotted.ts line 3, got []`.
- A3: `there is no` added to the list. Red at `the fixture must plant
exactly one hit per listed phrase`.
## Gates (on `27cd45335e`)
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 31 commands. Results:
- **30 of 31 exit 0**, including `check-spec-docblock-symbol-anchors`
(both arms), `check-scripts-symbol-anchors` (both),
`check-self-test-wired` (both), `check-self-test-workflow-commands`
(both), `check-comment-mask-corpus`, `check:nul-bytes`,
`check:entry-guard`, `check:parse-guard` and `check:watch-hint-literal`.
- `pnpm check:pm-dispatch-gates` is longer than the foreground cap on
this shared box, so I ran its two halves separately. The `--self-test`
half exits 0. The scan half also exits 0 (`dispatch-gates self-test:
1925 cases pass`; it took 876 s).
- `--ran` reconciliation: `31 derived famil(ies) accounted for — 31 run,
0 NOT-MEASURED`, with every exit code recorded before any pipe.
- **Roster families under `scripts/`** that the derivation flagged:
`check-published-list-mirrors` (both), `check:console-injection`,
`check:engine-double-contract`, `check:i18n-stale-fill` and
`check-dts-references --self-test` exit 0. `check:dts-closure` and
`check:published-readme-exports` exit 3 (their own `PREREQUISITE NOT
MET`: no built package `dist/` in this worktree). These are NOT
MEASURED, and this diff touches no package.
- **eslint**, narrowed to the one changed file and measured: ① it is in
the population of eslint's own config object `files:
['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']`, and `--print-config` resolves
2 rules for it; ② `--format json` reports 1 file, 0 errors and 0
warnings; ③ the config never enables type-aware linting (the resolved
`parserOptions` carry only `ecmaVersion` and `sourceType`), so this diff
cannot move any other file's verdict.
- **Changeset: none.** This is a root `scripts/**` change and the root
package is `"private": true`, so nothing publishes. The repo's
documented route is the `skip-changeset` label. This dispatch forbids me
any label write, so the owning seat applies it, and `Check Changeset`
stays red until it does.
## Acceptance notes
- **Mechanism check: this family had no test-file exclusion.** The
anchor corpus sweeps `.test.ts` files too; one of its day-one residual
rows is in `filter-array-declaration.test.ts`. The report's exclusion is
borrowed from the package's own test runner: the `local` project in
`packages/spec/vitest.config.ts` includes every `.test.ts` under `src/`.
I did not invent a new definition.
- **"Docblock" follows the family, which means all comment prose.** If
it were read as `/** */` blocks only, today's count would be 0, because
the one hit is a trailing `//` comment.
- `for now` gets 4 comment-prose hits today and is not on the ruled
list. Adding it would need its own measured reading, so I recorded it
here and did not add it.
- #17487 and #16208 (the card's other two instances) are not addressed
here. They remain open under their own cards.
---
_Generated by [Claude
Code](https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 6696056 commit 66ac73d
1 file changed
Lines changed: 403 additions & 3 deletions
0 commit comments