Pular para conteúdo

AUR-363 — Fixar parsers e grammars

Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.

Purpose

Pins, for every documentation-extraction adapter under internal/documentation/extractors/, the language it handles, the SHA-256 digest of its extractor.go implementation, the identity and version (or explicit unpinned/not-applicable disposition) of the external grammar/ tool that adapter shells out to, the SHA-256 digest of a small non-sensitive fixture corpus file, and the 4 MiB (4194304 byte) maximum file size every adapter is bound to. This card does not parse real source code and does not judge grammar quality — see Non-goals in .board/cards/done/AUR-363.md; it seals identity, digests, and bounds only.

tests/acceptance/AUR-363.sh verifies all of this two ways: (1) against the real repository tree, cross-referencing the lock against every adapter directory that actually contains an extractor.go, and (2) against ten sealed fixtures under tests/bootstrap/locks/AUR-363/_fixtures/, driven by tests/bootstrap/locks/AUR-363/cases.tsv. The fixtures tree is named _fixtures/, not fixtures/: the _ prefix is a Go module-aware package discovery convention (like the built-in testdata/ skip), so go build ./... and go vet ./... never descend into it, even though several fixtures below deliberately contain synthetic .go files — including one, implementationsymlink, where a real symlink and its target both declare the same type name in the same directory, which would otherwise redeclare across the whole-repository build.

Why the test was extended, not just read

The card's own Skeptical mutations (MUT-001 parser-unlocked, MUT-002 corpus-digest-mismatch) name disposition words that the originally sealed four-line check could never produce: that check only confirmed the lock file's own presence, its schema: line, the presence of some sha256: pattern anywhere in the file, and the absence of a latest/branch-style literal. It never associated a digest with the specific file it is supposed to bind, and never compared the declared adapter set against what actually exists in internal/documentation/extractors/. Two of the three named mutations were therefore structurally undetectable — not a probabilistic gap, an unconditional one — which the card's TDD proof requires ("aplicar MUT-001, observar o erro comportamental"). tests/acceptance/AUR-363.sh is listed in this card's own paths, so it was extended strictly additively: every original check (parser_lock_incomplete, invalid_lock_or_manifest, mutable_reference) is reproduced byte-for-byte, in the same order, before any new check runs, so no previously-passing or previously-failing case changes disposition. MUT-003's exact word, grammar-ref-mutable, is new vocabulary added for the same reason: the pinned Go grammar's only real version pin in the repository (Dockerfile:26) is a semver tag, not a latest/branch literal, so swapping it for a bare, unversioned reference would not have tripped the original mutable_reference check at all.

Artifacts and schema

.board/bootstrap/locks/parsers.yml

Flat key: value document, LC_ALL=C, one pair per line, 0 < size <= 16384 bytes (the real file is ~5 KiB; the bound leaves headroom for future adapters without changing itself). A 4-line header:

Key Value
schema bootstrap-lock-v1
card AUR-363
adapter_count 8
max_file_bytes 4194304

Then, for each adapter <lang> in {go, python, javascript, csharp, cpp, rust, bash, powershell}, nine flat keys prefixed adapter_<lang>_:

Suffix Meaning
language The Language value from internal/documentation/extractors/types.go; always equal to <lang>.
implementation_path Repository-relative path to that adapter's extractor.go.
implementation_sha256 sha256:<64 hex> — recomputed SHA-256 of implementation_path's content, never trusted from the file itself.
grammar_identity The external tool/grammar the adapter's Extract/Validate shells out to (or native-go-comment-scanner when there is none — see below).
grammar_version One of a pinned v<major>.<minor>.<patch>, the literal unpinned (an external tool exists but no version is asserted anywhere in the repository), or the literal not-applicable (no external grammar; the adapter's own Go source is the grammar).
grammar_pin_source path:line of the exact repository evidence for grammar_version — a go install ...@vX.Y.Z pin or a bare --version probe with no assertion.
fixture_corpus_path Repository-relative path to a small, non-sensitive, single-file corpus for that language.
fixture_corpus_sha256 sha256:<64 hex> — recomputed SHA-256 of fixture_corpus_path's content.
max_file_bytes Always 4194304 (4 MiB) — the per-adapter ceiling this card's Outcome commits to.

Per-adapter grammar pins (real, repository-derived)

Language grammar_identity grammar_version Evidence
go gomarkdoc v1.1.0 Dockerfile:26 — go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@v1.1.0, chosen "rather than @latest so the image is reproducible" (Dockerfile:21).
python pydoc-markdown unpinned internal/documentation/extractors/python/extractor.go:111 — p.runner.Run(ctx, "pydoc-markdown", []string{"--version"}, ...); no version is asserted.
javascript typedoc unpinned internal/documentation/extractors/javascript/extractor.go:110 — same --version-probe-only pattern.
csharp xmldocmd unpinned internal/documentation/extractors/csharp/extractor.go:179 — same pattern; dotnet itself is also probed at line 170 but xmldocmd is the doc-generating grammar per the type doc comment at line 41.
cpp doxygen unpinned internal/documentation/extractors/cpp/extractor.go:114.
rust cargo-doc unpinned internal/documentation/extractors/rust/extractor.go:125 — cargo --version probed, no assertion; cargo doc itself invoked at line 79.
bash native-go-comment-scanner not-applicable internal/documentation/extractors/bash/extractor.go:82 — the only external call is bash --version (an availability check, not a parser); extraction is native Go comment scanning.
powershell native-go-comment-scanner not-applicable internal/documentation/extractors/powershell/extractor.go:82 — identical pattern, pwsh -Version only.

No network lookup was performed or needed: every value above is read directly from a file already in the repository at the cited line, per this card's read_paths (go.mod, go.sum, internal/documentation/extractors) plus Dockerfile, which documents its own pin rationale inline.

tests/bootstrap/locks/AUR-363/cases.tsv

Same shape as the sibling card's fixture table: #name, fixture, input_sha256 (- for deliberate absence), expected. Ten rows, <= 256, <= 4 MiB. Each row's fixture names a subdirectory under tests/bootstrap/locks/AUR-363/_fixtures/, a self-contained two-adapter (alpha, beta) micro-repository — never the real 8-language lock — that exercises verify_parsers_lock in isolation:

Row Fixture shape Expected
nominal Complete, consistent 2-adapter lock, corpus, and implementations. pass
absence No .board/bootstrap/locks/parsers.yml. parser_lock_incomplete
tamper parsers.yml is a symlink to real.yml (also digest-wrong). parser_lock_incomplete (this card's file-existence check treats a symlink the same as absence — see "Codes" below)
invalid adapter_alpha_max_file_bytes is 4194303, not 4194304. invalid_lock_or_manifest
overlimit Lock padded past 16384 bytes. input_limit_exceeded
unlocked internal/documentation/extractors/gamma/extractor.go exists with no adapter_gamma_language line. parser-unlocked
corpusmismatch adapter_alpha_fixture_corpus_sha256 does not match the recomputed digest of corpus/alpha.txt. corpus-digest-mismatch
grammarmutable adapter_alpha_grammar_version is current (name-based, not a pin). grammar-ref-mutable
implementationtamper adapter_alpha_implementation_sha256 does not match the recomputed digest of extractor.go's actual content. source_integrity_error
implementationsymlink internal/documentation/extractors/alpha/extractor.go is a symlink to a sibling real_impl.go, not a regular file. source_integrity_error

Bounds

Bound Value
.board/bootstrap/locks/parsers.yml size 16384 bytes
Any implementation_path or fixture_corpus_path file read 4194304 bytes (4 MiB) — checked by size before any read; a file over this bound is never opened for hashing
tests/bootstrap/locks/AUR-363/cases.tsv size 4 MiB
cases.tsv rows 256
Acceptance container wall-clock (informational) 15 s

Codes

Typed dispositions on stderr as AUR-363/AC-001/<code> (exit 1, behavioral) or AUR-363/AC-001/inconclusive/<reason> (exit 79, environment):

Code Trigger
parser_lock_incomplete .board/bootstrap/locks/parsers.yml is absent, or exists but is a symlink or not a regular file (this card's file-level check does not distinguish absence from symlink/non-regular — both are "the lock this card owns does not exist as declared").
invalid_lock_or_manifest The lock is empty, missing schema: bootstrap-lock-v1, missing any sha256:[0-9a-f]{64} pattern, missing a required per-adapter key, or a per-adapter max_file_bytes value is not exactly 4194304.
input_limit_exceeded The lock exceeds 16384 bytes, or an implementation_path/fixture_corpus_path file exceeds 4194304 bytes.
mutable_reference The lock contains a latest or @main/@master/@head literal anywhere.
parser-unlocked An adapter directory containing extractor.go exists under internal/documentation/extractors/ with no matching adapter_<name>_language: <name> line in the lock (MUT-001).
corpus-digest-mismatch A declared fixture_corpus_sha256 diverges from the recomputed SHA-256 of the file at fixture_corpus_path (MUT-002).
grammar-ref-mutable A declared grammar_version is not unpinned, not not-applicable, and not v<major>.<minor>.<patch> — i.e. it is a name-based or otherwise unverifiable reference (MUT-003).
source_integrity_error A declared implementation_path is absent, a symlink, or its recomputed SHA-256 diverges from implementation_sha256.
fixture_sensitivity_error This program's own fixture self-test observed a disposition other than what cases.tsv seals — the verifier failed to discriminate a known fixture.

Diagnostic example (synthetic)

$ bash tests/acceptance/AUR-363.sh
{"card":"AUR-363","scenario":"AC-001","selector":"AC-001","fixture_cases":10,"result":"pass"}

An absent lock instead produces, on stderr, exit 1:

AUR-363/AC-001/parser_lock_incomplete

Offline OCI command

This is exactly the card's accept: line, run through the pinned bootstrap profile (bash@sha256:ae4668c2560999e65e89532cd2ad1b6688bb23298189f0bd229ef80fa4bd0831, network=none, non-root, read-only rootfs — see .board/bootstrap/locks/trust-root.yml and .board/oci/profiles/ bootstrap-readonly-v1.json), reproducible offline once that image is present in the local image cache:

./.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-363