51 lines
7.3 KiB
Markdown
51 lines
7.3 KiB
Markdown
---
|
|
id: T01.a
|
|
title: A provenance annotation for a target announced but not yet written
|
|
parent: T01
|
|
depends_on: []
|
|
created: 2026-09-16
|
|
claimed_by: null
|
|
claimed_at: null
|
|
blocked: false
|
|
---
|
|
|
|
## Description
|
|
|
|
Extend the `@provenance` mechanism so that a symbol aligned on a target **its developers have announced but not yet written** can be annotated honestly, and make the reference requirement enforced.
|
|
|
|
**Why this runs first.** Every later child in this batch annotates at least one symbol that has no upstream `path:symbol` to cite — `useShapeQuery` has **zero** occurrences in `nextgraph-rs`. The weakness is recorded, deliberately unfixed, in `.project/rules/nextgraph/surface-mirrors-the-sdk.md` § "The gap that same example exposes": an author holding the strongest possible justification has only two bad moves left — cite an approximate symbol, which is precisely the `readUnion` failure, or declare `invention`, which understates the alignment and tells a later reader the shape is ours to change. Fix the mechanism before anyone has to use it.
|
|
|
|
**Files in scope:** `packages/polyfill/test/provenance.test.ts` (the format and its checks), the `@provenance` header block in `packages/polyfill/src/index.ts`, the `kind` table in `.project/rules/nextgraph/no-divergence-from-nextgraph.md` (the taxonomy lives there and must gain the same entry), and any existing annotation whose kind changes under the new taxonomy.
|
|
|
|
**This child does NOT change the published surface.** No symbol is added, removed or re-signed, so there is **no contract edit and no version bump** here. Do not touch `.project/concepts/app-contract/polyfill-surface/contract_polyfill-surface.md` — the children that change the surface each own their own edit to it.
|
|
|
|
**Open questions — record the decision beside the code, do not guess a design:**
|
|
|
|
1. *"A mandatory reference on every function, which must resolve"* collides with a reason already written down. `test/provenance.test.ts` states, at length, that it deliberately does **not** check the `ref` path exists on disk: `nextgraph-rs` is a sibling checkout that may be absent on another machine, and a test that goes red for that reason teaches nothing — it just trains people to skip it. Decide what "must resolve" means here (a well-formed citation into a named checkout; resolve when the checkout is present and skip otherwise; resolve against a pinned commit) and record the reasoning next to the test. If it cannot be settled from the doctrine leaves, **stop and ask** rather than inventing a checker that will be skipped.
|
|
2. What the new kind is **called**, and what it takes as evidence. There is no `file:symbol`, so the citation has to be the statement itself — whose statement, and where it was made. Neither existing kind fits: `declared-not-wired` needs a declaration in the tree, `reserved-name` needs a name in the tree. Name it so it can never be read as `invention`, because the whole point is that it is the *strongest* justification, not the weakest.
|
|
|
|
**Pass condition.** The hard gate below, plus: every published symbol still carries a well-formed annotation; the new kind is exercised by a test case (its first real annotation belongs to T01.c, which reads this child's `## Result` before annotating); and the taxonomy in the rule file and the one in the test agree.
|
|
|
|
**Hard gate:** `bun test` → 0 fail; `bunx tsc --noEmit -p packages/polyfill/tsconfig.json` → exit 0; `bunx tsc --noEmit -p packages/polyfill/e2e/tsconfig.json` → exit 0; `python3 ~/.claude/skills/concept/lint.py` → 0 ERROR. Anti-cheat: no `any`, no `@ts-ignore`, no deleting or skipping a test, no widening an assertion.
|
|
|
|
**Resolved since planning (2026-09-16).** The "the `ref` must resolve" proposal FALLS: `provenance.test.ts` documents why it deliberately does not check disk — `nextgraph-rs` is a sibling checkout that may be absent, and a test red for that reason trains people to skip it. That reason holds. The third way to implement instead: check resolution ONLY when the sibling checkout is present, skip otherwise — the argument is preserved and a dead citation is still caught where it can be. The new kind for "announced by the developers, not yet written" is still needed.
|
|
|
|
## Progress
|
|
|
|
- 2026-09-16 — Baseline before any edit: `bun test` 394 pass / 0 fail, `lint.py` 0 ERROR. Proof set is `provenance.test.ts` plus every source file carrying an annotation.
|
|
- 2026-09-16 — Measured the "must resolve" question instead of guessing: all 92 existing `ref`s resolve in `../nextgraph-rs` today, path AND symbol. So the third way costs nothing to adopt and catches rot the reader cannot (the citation is the thing that moved). Implemented it: upstream refs checked only when the checkout is present (`../nextgraph-rs` or `$NEXTGRAPH_RS`), announced records checked ALWAYS because they point into this working copy.
|
|
- 2026-09-16 — Named the kind `announced-not-written`, parallel to `declared-not-wired`: it says what is there (a statement) and what is not (anything in the tree). Evidence is `ref` = the `.md` record here + the announced name, plus `announced-by=<who>@<YYYY-MM-DD>`. The record must be prose, never one of our own source files — otherwise our code stands as the evidence for its own shape, wearing the strongest label in the set.
|
|
- 2026-09-16 — Found a pre-existing disagreement the pass condition forces to fix: the test's closed set had five kinds, the rule file's table six (`reserved-name` missing from the test), and the rule's prose said "five kinds" over a six-row table. Test, `src/index.ts` header and rule table now carry the same seven; the prose says "a closed set" instead of a number that rots.
|
|
- 2026-09-16 — Mutation-checked both new checks rather than trusting green: a renamed upstream symbol goes red; a real `announced-not-written` line in a `.ts` file parses end to end and goes red when `announced-by` is dropped. Both probes reverted, `git diff` clean on the probed files.
|
|
- 2026-09-16 — Gate: `bun test` 397 pass / 0 fail; both `tsc` exit 0; `lint.py` 0 ERROR.
|
|
|
|
## Result
|
|
|
|
**Done.** The provenance taxonomy gains `announced-not-written`: upstream's developers have stated the direction, and the tree carries nothing — not a definition, not even a name. Being the strongest kind available there, it is the hardest to assert: its `ref` is the `.md` in this working copy where the statement is on record (never a source file), and it carries `announced-by=<who>@<YYYY-MM-DD>`, refused on every other kind. The third way on resolution is implemented: upstream citations are checked only when `../nextgraph-rs` (or `$NEXTGRAPH_RS`) is present; announced records are checked always: they point here. The kind is exercised by a fixture test over a pure `malformations()`, and verified through the parser.
|
|
|
|
**Files.** `packages/polyfill/test/provenance.test.ts`, `packages/polyfill/src/index.ts` (header block), `.project/rules/nextgraph/no-divergence-from-nextgraph.md`, `.project/rules/nextgraph/surface-mirrors-the-sdk.md`.
|
|
|
|
**Surprises.** The test's closed set was missing `reserved-name`, which the rule table already had, and the rule said "five kinds" over six rows — both fixed. All 92 existing refs already resolve, so the new check cost no fix-ups.
|
|
|
|
**Follow-ups.** T01.c's annotation is written out verbatim in `surface-mirrors-the-sdk.md` § "The gap that same example exposed" — copy it. `announced-by` is `nextgraph-developers`; a named individual would be stronger evidence, and the record names none.
|