This is the record of an abcd-managed repository

abcdBetav0.16.2 · 2026-11-06

Development record

2026-07-30 — itd-74 (round 6), increment 1: the private guard layer end to end plus the maintenance verbs on both layers (AC1–AC4, AC6). Private entry format is `KEY<whitespace>PATTERN` with the key charset `[A-Za-z0-9][A-Za-z0-9._/-]*` — deliberately free of every regex metacharacter, which is what makes legacy compatibility safe rather than best-effort: a line whose first field is really the head of a regex cannot pass for a key, so it falls back to the whole-line reading under the synthetic key `entry-<line-number>`, and even where a split does apply to an old two-word pattern the resulting match is a superset of the old one (over-blocking, never under-blocking). Refusal names the key alone; the matched text and the pattern never reach any output, and a malformed line is a refusal naming its line number, because a banlist that cannot be read must not look like a banlist that found nothing. Two hook-internal fixes fell out of writing the proof: the candidate text moves from a pipe into `grep` to a temp file (under `pipefail`, a matching `grep -q` exits early and the writer left holding a closed pipe reports 141, which the pipeline surfaces instead of grep's verdict — a staged diff larger than the pipe buffer could silently defeat the guard), and grep's own stderr is discarded so an engine error message can never echo a pattern. Public layer: entries are managed IN the existing `banned_tokens` family of `.abcd/docs-lint.json` under a `names/` id prefix, and the prefix is the ownership boundary — `list` renders the whole family (hand-curated harness/present_tense entries included, marked as such), `remove` refuses anything outside the namespace. Config edits are byte surgery on the array located through the standard decoder's input offsets, never a re-marshal: an add is one inserted line plus a separating comma, a remove is one deleted line, and add-then-remove is byte-identical to the original — asserted against this repo's own docs-lint.json rather than a synthetic fixture, because a surgical editor proven only on a two-entry toy is not proven. Redaction is structural, not conventional: the exported private entry type has no pattern field at all, so no future rendering can leak the value, and pattern-validation errors discard the engine's message because Go's regexp errors quote the expression. One format, two readers, one fixture: `testdata/parse-corpus.txt` and one shared probe table drive both the Go parser and the committed shell hook, so their agreement is checked rather than assumed. Residual: the two engines are RE2 and the platform's POSIX ERE, so a pattern valid in one and not the other is possible — the shared corpus is restricted to constructs both accept, and the malformed-line path fails safe on either side. Deferred to later increments: `ahoy` scaffolding of the guard artefacts and the seeded stub (AC5), the honest-reach line on the status/report surfaces (AC7; `abcd banlist` itself states it), and the intent/spec lifecycle moves.

decisiondcn-2607300000000003

2026-07-30 — itd-74 (round 6), increment 1: the private guard layer end to end plus the maintenance verbs on both layers (AC1–AC4, AC6). Private entry format is KEY<whitespace>PATTERN with the key charset [A-Za-z0-9][A-Za-z0-9._/-]* — deliberately free of every regex metacharacter, which is what makes legacy compatibility safe rather than best-effort: a line whose first field is really the head of a regex cannot pass for a key, so it falls back to the whole-line reading under the synthetic key entry-<line-number>, and even where a split does apply to an old two-word pattern the resulting match is a superset of the old one (over-blocking, never under-blocking). Refusal names the key alone; the matched text and the pattern never reach any output, and a malformed line is a refusal naming its line number, because a banlist that cannot be read must not look like a banlist that found nothing. Two hook-internal fixes fell out of writing the proof: the candidate text moves from a pipe into grep to a temp file (under pipefail, a matching grep -q exits early and the writer left holding a closed pipe reports 141, which the pipeline surfaces instead of grep's verdict — a staged diff larger than the pipe buffer could silently defeat the guard), and grep's own stderr is discarded so an engine error message can never echo a pattern. Public layer: entries are managed IN the existing banned_tokens family of .abcd/docs-lint.json under a names/ id prefix, and the prefix is the ownership boundary — list renders the whole family (hand-curated harness/present_tense entries included, marked as such), remove refuses anything outside the namespace. Config edits are byte surgery on the array located through the standard decoder's input offsets, never a re-marshal: an add is one inserted line plus a separating comma, a remove is one deleted line, and add-then-remove is byte-identical to the original — asserted against this repo's own docs-lint.json rather than a synthetic fixture, because a surgical editor proven only on a two-entry toy is not proven. Redaction is structural, not conventional: the exported private entry type has no pattern field at all, so no future rendering can leak the value, and pattern-validation errors discard the engine's message because Go's regexp errors quote the expression. One format, two readers, one fixture: testdata/parse-corpus.txt and one shared probe table drive both the Go parser and the committed shell hook, so their agreement is checked rather than assumed. Residual: the two engines are RE2 and the platform's POSIX ERE, so a pattern valid in one and not the other is possible — the shared corpus is restricted to constructs both accept, and the malformed-line path fails safe on either side. Deferred to later increments: ahoy scaffolding of the guard artefacts and the seeded stub (AC5), the honest-reach line on the status/report surfaces (AC7; abcd banlist itself states it), and the intent/spec lifecycle moves.

Frontmatter

iddcn-2607300000000003
typedecision
date2026-07-30

mentions1