Skip to content

docs: additional alignment - #167

Open
Spelkington wants to merge 15 commits into
mainfrom
docs/align-docs
Open

Spelkington wants to merge 15 commits into
mainfrom
docs/align-docs

Conversation

@Spelkington

Copy link
Copy Markdown
Contributor

No description provided.

Five gates that make epic #154's conventions enforceable instead of
aspirational. Each fails with a message naming the offending file and the
corrective action.

- _test:context-file-guard    no CONTEXT.md / CONTEXT-MAP.md anywhere. A test
  rather than a prose rule because four VENDORED skills under .agents/skills/
  actively instruct agents to create one and will keep saying so through every
  upstream update; docs/agents/domain.md only helps if the agent read it, and
  domain-modeling's instruction fires mid-task.
- _test:wikilink-terms        every [[Term]] resolves to a glossary entry in
  some context's CONTRIBUTING.md, and a bare wikilink fails where the term has
  role-parenthetical variants. Closes a real hole: skill-links.mjs parses
  standard Markdown links only, so nothing resolved wikilinks before this.
- _test:adr-citations         every ADR-NNNN names a real ADR, and every link
  into an ADR directory resolves on disk. This is what makes #157's renumbering
  safe: the 61 bare-text citations in src/**/*.cs are comments, not Markdown
  links, so the link linter cannot see them.
- _test:context-map-freshness the context map in root CONTRIBUTING.md matches a
  fresh derivation. Generated by scripts/generate-context-map.mjs, same
  generate-then-gate pattern as skill-index-freshness (ADR-0025).
- _test:adr-frontmatter       an ADR declaring the contexts/exemplars/status
  contract honors it. Migration-tolerant: an ADR counts as migrated once it
  declares contexts or exemplars, so this lands before #157 without failing the
  27 unmigrated ADRs and tightens automatically as each is migrated.

scripts/_test/_domain.mjs holds the shared definitions of context, glossary
term and ADR, so a convention change is one edit rather than four.

Two gates are RED against the current tree, by design (#158: "tests 2 and 3 are
expected to fail; the fixes live in #160 and #157"):
  wikilink-terms  3 dangling links (the 2 known, plus [[Tool]] in
                  src/tools/flowthru-vscode/README.md)
  adr-citations   19 links into .claude/docs/adr/, a directory that has never
                  existed

Refs #158
Closes the four actionable defects from the context/ADR audit. Turns the two
meta-test gates that #158 landed red back to green.

1. Skill shard glossary entry named src/extensions/<Ext>/SKILL.md; every actual
   shard is at <Ext>/skill/SKILL.md. Corrected, and the rationale for the
   subdirectory recorded: `npx skills add` copies the whole directory containing
   a SKILL.md, so a shard in the package root would drag .cs/.csproj into a
   user's skills directory.

2. The Anchor entry cited [ADR-0009] with a path naming 0011, into a directory
   that never existed. Now a directory pointer.

3. Three dangling wikilinks, not two: [[TestCapabilities.Docker|…]] and
   [[TestCapabilities.AwsS3|…]] pointed at code identifiers rather than glossary
   terms, and [[Tool]] in flowthru-vscode/README.md named a term that does not
   exist (the glossary defines "Tool Developer"). The first is now a plain code
   reference, the second routes through [[Test capability gate]], the third is
   prose.

4. Every reference into .claude/docs/adr/ — 19 link instances across 8 files,
   matching the inventory table in #160 (the "23" in its prose overcounts; the
   table itself sums to 19). Not a path rewrite: per #154, reader-facing docs
   and package READMEs LOSE their specific ADR references, CONTRIBUTING.md files
   keep only a pointer to the ADR directory, and only code comments may cite an
   ADR by number. Bare-text citations outside code comments are cleared on the
   same rule; generated docfx output keeps its citations, which #154 permits
   because it derives from code comments.

Defect 5 (Wide vs narrow transform defined twice) is left to #156 as specified.
ADR-to-ADR links under docs/adr/ are left to #157.

Refs #160
…ontmatter

Relocates, renumbers and backfills frontmatter across the ADR set, and makes
every citation carry a resolvable path.

## Layout

  docs/adr/                                    12  process, glossary, multi-context
  src/core/docs/adr/                           10  renumbered 0001-0010
  src/tools/docs/adr/                           2
  .../Flowthru.Extensions.Python/docs/adr/      2
  .../Flowthru.Extensions.Google.Sheets/adr/    1

Python and Google.Sheets are minted as contexts to hold their own ADRs — the
forcing function working as intended ("each package is its own context, created
lazily"). Both carry a real glossary, not a stub.

## Two classification corrections

ADR-0020 STAYS at root. #157 asked to verify its single-context (AWS.S3) label
before moving: verification fails it. src/core/.../SecretText.cs enforces 0020's
"a secret never enters the catalog or the DAG" invariant in Core's own type, so
Core applies it directly rather than conforming. Two contexts ⇒ root. This also
spares its 5 inbound links, the highest in the set.

ADR-0017 goes to root, not an AWS context: the Flowthru.Harness.AWS.Lambda
package it describes does not exist. It is a scope decision ("a harness, not an
orchestrator"), which is repo-wide.

## `proposed` added to the status vocabulary

#154 specifies accepted/superseded/rejected. The exemplars rule immediately
surfaced three ADRs that decide something nothing implements — the diagnostic
anchor contract (no analyzer carries anchor metadata), the Inspector RPC surface
(src/tools contains no .NET project at all), and the Lambda harness (no package).
That is exactly the "asserted but not practised" case the rule exists to expose,
so it is recorded rather than papered over. `proposed` is exempt from the
non-empty-exemplars requirement, which is what lets `accepted` keep its teeth.

## Citations

87 bare ADR-NNNN citations — not 61; the original count missed tests/** — are now
markdown links carrying the full root-anchored path. Per-directory renumbering
makes a bare number ambiguous across five ADR directories, and a docstring is
built into reference markdown, so a citation must resolve as a link. Verified
end-to-end: docfx passes the markdown through verbatim, and docs/reference/src/
is regenerated here.

Also fixes a latent bug this exposed: ingest-docs.mjs treated /docs/adr/ as
site-internal, but ADRs are deliberately never ingested, so every such link
minted a page that cannot exist. They now resolve as repo source (GitHub).

All ADR-to-ADR links are root-anchored; no relative or upward forms remain.

Refs #157
Fixes the uphill vocabulary flow. src/extensions/CONTRIBUTING.md had become the
repo's de-facto shared glossary: 11 terms of which only 3 were extensions-owned.
The sharpest symptom was src/core citing API Surface (3x) and Error Surface (2x)
— its own two stated primary responsibilities — from one tier down.

Root gains a ## Glossary with 7 terms:
  Context (new), Shippable package, API Surface, Error Surface,
  Design-time error, Pre-flight error, Runtime error (phase)

Closed sum moves to src/core, where it belongs — it is a Core implementation
pattern, and Core's own audience-scope line had been pointing downward at it.

src/extensions keeps exactly what it owns: Extension Developer, Extension
surface, Skill shard (plus its half of the split transform entry).

## Role parentheticals

Two terms genuinely mean different things to different readers, so per #154 they
are disambiguated in the entry NAME rather than by reciprocal prose:

  Runtime error (phase)             the third error phase — when a failure is caught
  Runtime error (Core Developer)    the RuntimeError closed sum — a value, never thrown

  Wide vs narrow transform (Flow Developer)       classified by what the author must see
  Wide vs narrow transform (Extension Developer)  classified by streamability

The second pair resolves a real defect: the two entries gave conflicting defining
axes and asymmetric _Avoid_ lists with no cross-reference, so reading one file
gave you a different rule than reading the other. They now state the same split
from each audience's side and say so explicitly.

This also puts #158's ambiguity check to work for the first time — until now no
role-parenthetical term existed, so the branch was logically sound but unexercised.
A bare [[Runtime error]] or [[Wide vs narrow transform]] is now a test failure
naming both full forms.

Context is defined against Shippable package as #156 asks, but honestly: contexts
are minted lazily, so a shippable package *becomes* one when it accrues its own
words, and many contexts (examples, tests/*, docs) ship nothing at all.

Refs #156
src/website had no CONTRIBUTING.md despite clearly clearing the bar ADR-0007 set
when it minted src/tools — structurally different artifacts: Node/Astro/
TypeScript, an application rather than a .NET library, "private": true, not in
Flowthru.slnx, deployed by its own workflow.

It carried ~15 load-bearing terms that survived only as comments inside
project.json and two .mjs files, where nothing could reference them and no
contributor would find them. They are now a glossary with _Avoid_ lines:

  Ingest / Passthrough vs. synthesize   why synthesis is confined to reference/
  Link interceptor                      inside docs/ vs escapes docs/, and the
                                        docs/adr/ exception
  Frontmatter contract                  enforced twice, deliberately
  Source-path mapping                   errors name the file you would edit
  Source-inputs-not-ingested-outputs    nx hashes inputs before ingest writes,
                                        so listing the copy memoizes stale builds
  Build honesty                         Starlight's link validator is a build
                                        hook, so an incremental build skips
                                        pages AND their link checks
  Scope-primary IA                      decided, not yet implemented
  Review provenance                     absent reads as draft, never reviewed

Also fixes the dead pointer the audit found: README sent readers to
INTEGRATION.md for "the full handoff guide" — nx wiring, the C#-to-Markdown
generator contract, and Pages deployment — and that file has never existed. Those
three things are now documented where they belong.

Root goes from five roles to six. The generated context map picks up website,
Python and Google.Sheets automatically: 8 contexts at the start of this epic,
11 now, growing toward ~22 as extension packages mint themselves.

Refs #159
…matter

Records the decisions the rest of this epic implements. Supersedes ADR-0004 and
extends ADR-0007; ADR-0004 gains a superseded-by pointer and drops to superseded.

Dogfoods its own contract: contexts, exemplars, status frontmatter, with the
seven files that demonstrate it in practice as exemplars.

Two things the implementation forced into the record, neither in #154's original
statement:

- `proposed` joins the status vocabulary. Backfilling frontmatter surfaced three
  ADRs deciding something nothing implements — the diagnostic anchor contract,
  the Inspector RPC surface, the Lambda harness. Forcing them to `accepted` meant
  fabricating exemplars; leaving them unmigrated exempted them from the contract.
  `proposed` is exempt from non-empty exemplars, which is what lets `accepted`
  keep its teeth.

- Per-directory numbering makes a bare ADR-NNNN meaningless (five directories now
  each start at 0001), so every citation carries the full root-anchored path. The
  ADR states this as the transitive requirement it is: markdown must not have
  dead links, C# docstrings become markdown reference docs, therefore docstrings
  must not have dead links.

Filed as 0026 would have reused a number the old global ADR-0026 held until this
epic moved it, so this takes 0028 to keep git history unambiguous.

Refs #155
Code review caught a double-application bug in the migration script. It rewrote
ADR references in three passes: relative links, then bare ADR-NNNN into links,
then existing [ADR-NNNN](...) links onto their new numbers. The third pass ran
over the second pass's output and re-mapped the NEW number as if it were an old
one, so a citation that had been bare in the source silently acquired a valid
link to an unrelated ADR.

Five citations landed wrong, all internally consistent and all resolving, which
is why nothing caught them:

  src/core/.../0002  2x [ADR-0001] -> glossary-split      should be step-logging
  src/core/.../0009  2x [ADR-0008] -> documentation-honesty  should be streaming-reads
  src/core/.../0009  1x [ADR-0007] -> tools-as-context    should be hermetic-preflight

Found by re-deriving every citation from the pre-migration tree and diffing
against what shipped, not by inspection — the label and the path agreed in every
case, so a consistency check finds nothing.

The gate was complicit. It asked "does some ADR have this number?", which every
bare citation passes by accident now that five directories each start at 0001.
That is false confidence in exactly the check that exists to make renumbering
safe. A bare ADR-NNNN is now a failure telling the author to carry the full path,
which is the rule the epic settled on anyway and the reason docstrings carry
paths at all.

The hardened gate immediately found 6 more bare citations in files added earlier
in this branch — 5 in the new ADR, 1 in src/website/CONTRIBUTING.md, where the
fix is a directory pointer rather than a link, since CONTRIBUTING files may
reference only that an ADR directory exists.
…ecision

Spec review finding. When #160 converted specific ADR citations in CONTRIBUTING
files into "an ADR directory exists" pointers, every one was hardcoded to root
/docs/adr — correct at the time, wrong the moment #157 relocated those decisions.

Eight pointers named a directory that does not contain what they cite:

  src/core/CONTRIBUTING.md   x5  logging, ObservationOnly, conflict, hermetic
                                 pre-flight, Anchor  -> /src/core/docs/adr
  src/tools/CONTRIBUTING.md  x3  Inspector RPC, trust boundary, snapshot
                                 lifecycle           -> /src/tools/docs/adr
  examples/CONTRIBUTING.md   x1  step logging        -> /src/core/docs/adr

The four that legitimately stay at root are left alone: src/tools' "why Tools
are a context" and src/website's context-minting bar (both ADR-0007), and
src/extensions' documentation-honesty reference (ADR-0008).

Invisible to the citation validator because a directory pointer carries no
ADR-NNNN for it to scan — the same blind spot that let the mis-targeted links in
the previous commit through.

Also: adr-citations now masks code spans and fenced blocks before scanning, the
rule lint-doc-links.mjs and the ingest interceptor already follow. Documentation
about the citation convention has to be able to show an example citation without
the gate treating the example as a broken link.
Winds back the `proposed` status. `main` is the canonical state of the repository,
so an ADR merged into it should describe something the repository actually does.

`proposed` was the wrong answer to a real problem. Backfilling frontmatter
surfaced three ADRs deciding things nothing implements, and adding a status for
them meant adding an exemption from the non-empty-exemplars rule. Every exemption
is a way to assert a decision without practising it — exactly what exemplars
exists to prevent. One unimplemented ADR on main under a soft status teaches the
next reader that the standard is optional.

Moved off the mainline, each to its own adr/<slug> branch so it can merge with
its implementation:

  adr/diagnostic-anchor-contract   no analyzer carries anchor metadata
  adr/inspector-rpc                src/tools contains no .NET project at all
  adr/aws-lambda-harness           Flowthru.Harness.AWS.Lambda does not exist

main now has 25 ADRs: 23 accepted with resolving exemplars, 2 superseded. The
gate has no escape hatch left — `accepted` requires a non-empty exemplar list,
and only `superseded` / `rejected` are exempt, both describing exemplars that are
gone rather than absent.

Numbering gaps left behind (src/core 0004, src/tools 0001, root 0017) are kept,
not closed. Renumbering the survivors would invalidate citations that currently
resolve — the exact failure the last review caught — and a gap costs nothing.

One forward-looking link in the concurrency ADR pointed at the Lambda harness;
it now names the concept without citing an ADR that main does not carry.
A bare "'proposed' is not one of accepted / superseded / rejected" tells an
author the status is wrong but not what to do. The ADR is usually fine — it is
on the wrong branch. Say that instead.
An invalid status already tells the author what to fix; following it with
"status 'proposed' requires at least one exemplar" reads as a second, conflicting
demand. Check exemplars only once the status is valid.
Two findings from the review, both stale state rather than new defects.

## docs/reference/src/ was gitignored yet 340 files stayed tracked

The ignore rule landed without an untrack, so git kept carrying files it was
told to ignore — and they drift: regenerating during the ADR migration produced
6 changed files against the committed copy.

Nothing needs the committed copy. The chain is complete on its own:
site:build -> site:_lint-docs -> site:_ingest-docs -> docs:build ->
docs:_build-reference -> docfx. The reference is always regenerated before it is
consumed, so a tracked copy can only ever be a stale duplicate of a build
artifact.

## _test:capability-matrix-freshness was quarantined on a claim that expired

Its note said capability-matrix.cs referenced legacy namespaces that did not
survive the FP rewrite. It does not: the generator builds and runs clean, and
its output already matches the committed matrix byte for byte — so the target
was excluded from the barrel for a reason that had quietly stopped being true.

Back in dependsOn, and the note now records what is actually the case. Worth
noting docs:_build-capability-matrix already ran this same generator on every
docs build, so the quarantine only ever disabled the freshness CHECK, never the
generation — the matrix could have drifted with nothing watching.

Barrel is 15 subtargets, all green.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant