From bf3b90c86c19cb15d88d85ebbeb2039f2a9476de Mon Sep 17 00:00:00 2001 From: Coldwings Date: Wed, 2 Sep 2026 20:09:47 +0800 Subject: [PATCH] =?UTF-8?q?governance:=20evidence=20anchors=20pilot=20?= =?UTF-8?q?=E2=80=94=20file::symbol=20drift=20check,=20anchored=20in=20doc?= =?UTF-8?q?s/fetch.md=20(fixes=20#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Between the mechanical floor checks and uncheckable prose sits one more machine-checkable layer: contract entries that name their implementing symbol can be drift-checked, so the text cannot silently outlive the code. - check-docs.sh check 8: every backticked path::symbol anchor in docs/*.md must resolve — file exists and declares the symbol (func/method, or type/const/var). Opt-in: the check never requires writing an anchor, it only keeps written ones valid. No line numbers, by design — symbol names drift far less and existence is grep-checkable (checks 2/3 granularity). Negative-tested: missing file and missing symbol both fail. - docs/fetch.md: five pilot anchors on key contract entries (the §3.5 stale-flight and completion invariants -> acquire/finish, §3.4 geometry -> FetchBlock, §3.3 -> DefaultMaxFlight, §3.2 206 validation -> Fetch). - docs/README.md: the anchor convention, marked experimental/pilot; promotion beyond the pilot would go through an ADR (T4). --- docs/README.md | 20 ++++++++++++++++++++ docs/fetch.md | 7 +++++-- scripts/check-docs.sh | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index a00e78c..122cc4e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -83,6 +83,26 @@ heading-aliases: Concepts <=> Wire form; Determinism / Stability Contract <=> St "Testing" is not a single line saying "tests exist". It must let the reader judge **trustworthiness**: list the specific property each test guards, the coverage number, and whether `vet`/`-race` pass, plus reproduction commands. If a function is left uncovered, state why explicitly. +### Experimental: evidence anchors (pilot) + +A contract-level entry (e.g. a Public API or Stability Contract entry) may +carry an **evidence anchor** — a backticked +"path::symbol" reference (file + symbol, **never a line number**: line +numbers drift on every edit; symbol names are stable and their existence is +grep-checkable, the same granularity as the test-name and exported-symbol +checks). `scripts/check-docs.sh` check 8 keeps written anchors valid: the +file must exist and declare the symbol (function, method, or +`type`/`const`/`var`). Anchors are **opt-in** — the check never requires +writing one; it only stops contract prose from silently outliving the symbol +it describes. + +Status: **pilot**, currently only in `docs/fetch.md` (see the `Anchor:` +sentences there for live examples) — evaluating signal-to-noise before any +wider adoption. Promotion to a repository-wide convention, if it happens, +goes through an ADR. Point-in-time historical claims anchor a commit SHA +instead of a live symbol — frozen is a feature there. ADR bodies never carry +live anchors (see `docs/adr/README.md`). + ## Running the tests locally DART has no external dependencies, so the standard commands are all that is diff --git a/docs/fetch.md b/docs/fetch.md index 3f3fd45..f092043 100644 --- a/docs/fetch.md +++ b/docs/fetch.md @@ -63,6 +63,7 @@ type HTTPFetcher struct { or the wrong offset would otherwise have its bytes cached under the block's key, and the block cache is write-once per key, so that corruption would be permanent. `Content-Range` is also parsed for `Total`. + Anchor: `internal/fetch/fetch.go::Fetch`. - If the origin **ignores** the Range and returns `200`, the window is sliced out of the stream **without buffering the whole object**: bytes before `start` are discarded, at most `end-start+1` are read, and the rest of the response is @@ -95,6 +96,7 @@ Range-ignoring origins. Time complexity is O(1); no body bytes are buffered. starts a new one (the fix for issue #4's permanently-poisoned cache key: a stalled flight expires instead of blocking the key forever). Joiners of a flight wait inline, one worker goroutine per flight (#60). +Anchor: `internal/fetch/fetch.go::DefaultMaxFlight`. ### 3.4 `func FetchBlock(ctx, f Fetcher, url string, blockSize, blockIndex, size int64) (Range, error)` @@ -116,7 +118,7 @@ index whose start multiplication or end addition would overflow — is an **error before the fetcher is invoked**: a wrapped start would otherwise recycle to 0 (silently fetching block 0's bytes) or go negative, which `HTTPFetcher.Fetch` reads as "no Range header" — a one-block fetch degrading -into a whole-object GET. +into a whole-object GET. Anchor: `internal/fetch/fetch.go::FetchBlock`. ### 3.5 `type Coalescing` @@ -137,6 +139,7 @@ first) the flight becomes **stale**: a caller still waiting re-checks, evicts the stale entry, and leads a replacement; a late-finishing stale leader never deletes the replacement's entry. Without the bound, one half-dead origin connection would poison the cache key for the process lifetime. +Anchor: `internal/fetch/fetch.go::acquire`. A flight keeps **exactly one worker goroutine** — started by whoever leads it — no matter how many callers join or abandon it: joiners wait inline on the @@ -155,7 +158,7 @@ not still waiting for it (the engine's size probe relies on this: a failed probe is re-issued against origin, never served twice — issue #69). The mirror cost: a caller arriving in the window between removal and publication leads a duplicate fetch instead of sharing the just-finished result — bounded extra -traffic, by design. +traffic, by design. Anchor: `internal/fetch/fetch.go::finish`. Each caller's own `ctx` only bounds how long that caller waits (a cancelled caller returns `ctx.Err()`). diff --git a/scripts/check-docs.sh b/scripts/check-docs.sh index 892f155..0b50a55 100644 --- a/scripts/check-docs.sh +++ b/scripts/check-docs.sh @@ -15,6 +15,9 @@ # (modulo leading indentation) # 7. every non-superseded ADR is referenced by number from current law # (docs/*.md or AGENTS.md, excluding docs/adr/) +# 8. evidence anchors: backticked `path::symbol` references in docs/*.md +# resolve — the file exists and declares the symbol (opt-in anchors; +# pilot convention, see docs/README.md) # # Plus one warning (never fails): relative .md links that point nowhere. # @@ -22,6 +25,12 @@ # semantics. Known limits: check 3 covers single-line const/var declarations # and methods, not grouped const members without individual doc comments, and # a symbol only appearing in example code still counts as "documented"; +# check 8 likewise matches only single-line declarations (func/method/type/ +# const/var), not grouped const members — anchor individually declared +# symbols; check 8 sees that a declaration exists, never that the anchored +# prose describes it correctly, and its greps are not comment-aware (a +# `type Foo` inside a comment would satisfy it — same class of limit as +# check 3's example-code caveat). # check 4 matches required words at non-letter boundaries in heading text # (first-word aliases included), so "## Instability notes" does not satisfy # Stability but "## 6. Determinism / Stability Contract" does; check 7 sees @@ -251,6 +260,32 @@ for f in docs/adr/[0-9][0-9][0-9][0-9]-*.md; do done ok "check7: ADR back-references" +# ---------------------------------------------------------------- check 8 +# Evidence anchors: backticked `path::symbol` references in docs/*.md must +# resolve — the file exists and declares the symbol. Anchors are opt-in: the +# check never requires writing one, it only keeps written anchors valid, so +# contract prose cannot silently outlive the symbol it describes. No line +# numbers, by design: symbol names drift far less than lines, and existence +# is grep-checkable — the same granularity as checks 2/3. +for doc in docs/*.md; do + anchors=$(grep -oE '`[A-Za-z0-9_/.-]+::[A-Za-z_][A-Za-z0-9_]*`' "$doc" | tr -d '`' | sort -u) + while IFS= read -r a; do + [ -z "$a" ] && continue + af=${a%%::*}; as=${a##*::} + if [ ! -f "$af" ]; then + err "check8: $doc anchors \`$a\`, but $af does not exist" + continue + fi + # func/method: func Symbol( or func (recv) Symbol( + # otherwise: type|const|var Symbol + if ! grep -qE "func +(\([^)]*\) +)?$as\(" "$af" \ + && ! grep -qE "(type|const|var) +$as([^A-Za-z0-9_]|\$)" "$af"; then + err "check8: $doc anchors \`$a\`, but $as is not declared in $af" + fi + done <<<"$anchors" +done +ok "check8: evidence anchors resolve" + # ------------------------------------------------- link warning (soft) for doc in docs/*.md docs/adr/*.md AGENTS.md CONTRIBUTING.md README.md; do [ -f "$doc" ] || continue