@nurix/etna 0.20.2 → 0.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/catalog/rules/testing.md +13 -9
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/CLAUDE.md +5 -1
- package/data/harnesses/code/agents/bookkeeper.md +1 -1
- package/data/harnesses/code/agents/issue-bridge.md +16 -0
- package/data/harnesses/code/agents/maintenance.md +36 -3
- package/data/harnesses/code/docs/README.md +1 -1
- package/data/harnesses/code/docs/history/backlog.md +1 -1
- package/data/harnesses/code/docs/structure.md +2 -2
- package/data/harnesses/code/harness.json +12 -2
- package/data/harnesses/code/hooks/awareness.mjs +75 -14
- package/data/harnesses/code/hooks/compliance.mjs +25 -17
- package/data/harnesses/code/hooks/lib/exempt.mjs +7 -5
- package/data/harnesses/code/hooks/lib/frozen.mjs +9 -6
- package/data/harnesses/code/hooks/lib/ledger.mjs +26 -4
- package/data/harnesses/code/hooks/lib/ownership.mjs +73 -2
- package/data/harnesses/code/hooks/lib/plans.mjs +2 -2
- package/data/harnesses/code/hooks/lib/repo.mjs +22 -0
- package/data/harnesses/code/hooks/lib/state.mjs +38 -2
- package/data/harnesses/code/hooks/lib/sweep.mjs +37 -15
- package/data/harnesses/code/rules/testing.md +13 -9
- package/data/harnesses/code/skills/codegraph/SKILL.md +57 -0
- package/data/harnesses/code/skills/documentation/SKILL.md +12 -5
- package/data/harnesses/code/skills/documentation/references/frontmatter.md +29 -4
- package/data/harnesses/code/skills/documentation/templates/behavioral-checklist.md +18 -1
- package/data/harnesses/code/skills/documentation/templates/feature.md +1 -0
- package/data/harnesses/code/skills/documentation/templates/plan.md +2 -0
- package/data/harnesses/code/skills/documentation/templates/ux-behavior.md +1 -1
- package/data/harnesses/code/skills/issue-bridge/SKILL.md +55 -0
- package/data/harnesses/code/skills/issue-bridge/references/trackers/github.md +37 -0
- package/data/harnesses/code/skills/issue-bridge/references/trackers/jira.md +40 -0
- package/data/harnesses/code/skills/issue-bridge/references/trackers/linear.md +37 -0
- package/data/harnesses/code/skills/testing/SKILL.md +20 -4
- package/data/harnesses/delivery/agents/maintenance.md +36 -3
- package/data/harnesses/delivery/harness.json +4 -2
- package/data/harnesses/delivery/skills/documentation/SKILL.md +12 -5
- package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +29 -4
- package/data/harnesses/delivery/skills/documentation/templates/behavioral-checklist.md +18 -1
- package/data/harnesses/delivery/skills/documentation/templates/feature.md +1 -0
- package/data/harnesses/delivery/skills/documentation/templates/plan.md +2 -0
- package/data/harnesses/delivery/skills/documentation/templates/ux-behavior.md +1 -1
- package/data/harnesses/discover/harness.json +2 -0
- package/data/harnesses/discussion/harness.json +2 -0
- package/data/harnesses/etna/agents/maintenance.md +36 -3
- package/data/harnesses/etna/harness.json +4 -2
- package/data/harnesses/etna/rules/testing.md +13 -9
- package/data/harnesses/etna/skills/documentation/SKILL.md +12 -5
- package/data/harnesses/etna/skills/documentation/references/frontmatter.md +29 -4
- package/data/harnesses/etna/skills/documentation/templates/behavioral-checklist.md +18 -1
- package/data/harnesses/etna/skills/documentation/templates/feature.md +1 -0
- package/data/harnesses/etna/skills/documentation/templates/plan.md +2 -0
- package/data/harnesses/etna/skills/documentation/templates/ux-behavior.md +1 -1
- package/data/harnesses/hr/harness.json +2 -0
- package/data/harnesses/kb-agi/harness.json +2 -0
- package/lib/install/plan.mjs +22 -1
- package/package.json +2 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codegraph
|
|
3
|
+
requires-tool: codegraph
|
|
4
|
+
description: Query the codegraph index before reading source. An LLM-first codebase index (symbols, call references, git history, co-change coupling, ownership) answering whole questions in one call. Use it to localize, scope, and assess blast radius on this repository; use grep/read for string literals, mechanism, and data flow — the skill states which is which. Applies to any task that starts with "where is", "who calls", "what changed", "who owns", "what breaks", or "is this tested".
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codegraph — the index-first method
|
|
8
|
+
|
|
9
|
+
**If the tool is absent** (`codegraph --version` fails): do not stall. Read `.claude/state/decisions.tsv` for `tool.codegraph`.
|
|
10
|
+
|
|
11
|
+
- **`declined`** — fall back to the cold method (grep, targeted reads, `git log`) **silently**. Do not mention the tool, do not re-offer. The user has answered.
|
|
12
|
+
- **undecided** (no file, no key, or anything unrecognised) — fall back to the cold method *and* make the offer **once**: this repository would answer "where is / who calls / what changed / who owns" from an index instead of a grep sweep; install with `npm install -g @nurix/codegraph`, then `codegraph index`. **Ask, never act** — a global npm install modifies the machine, and indexing writes to `~/.codegraph`. Record the answer with `recordDecision` either way, so the question is asked at most once per repo per machine.
|
|
13
|
+
- **`accepted` but the binary is gone** — treat as undecided; a failed or removed install is not a standing yes.
|
|
14
|
+
|
|
15
|
+
**A failed install is `absent`, not an error to retry.** Global installs fail on machines without permission; say so once, fall back, and move on.
|
|
16
|
+
|
|
17
|
+
**An explicit invocation overrides a stored decline.** If the user asks for this skill by name, honour it and re-offer — "stop asking" must never become "never usable".
|
|
18
|
+
|
|
19
|
+
**If the repository is not indexed** (a query answers `UNAVAILABLE: this repository has not been indexed yet`), that is the tool degrading correctly, not a failure: offer `codegraph index` under the same ask-first rule. The SessionStart probe deliberately does **not** claim index freshness — it can only see the binary — so this is where index state is actually established.
|
|
20
|
+
|
|
21
|
+
Everything below assumes the tool is present and the repository indexed.
|
|
22
|
+
|
|
23
|
+
The `codegraph` CLI is on PATH and defaults to the git root of the working directory — inside this repository, no flags are needed. The index is prebuilt; `codegraph projects` shows freshness (a `[STALE]` marker means HEAD moved since indexing — refresh with `codegraph index --commits 30000`, ~40s). Every answer states its own confidence per section (`CERTAIN` / `INFERRED` / `UNRESOLVED`), announces anything the token budget dropped, and says what an empty section means. Trust those labels.
|
|
24
|
+
|
|
25
|
+
## The method order
|
|
26
|
+
|
|
27
|
+
Localize through the index **before** opening any file, in this order; then read only the files it points at.
|
|
28
|
+
|
|
29
|
+
1. **`codegraph find <fragment>`** — symbols and file paths by name fragment. The entry point when you only have issue vocabulary. Symbol names only — it will never match string literals, dict keys, or SQL fragments (grep for those, without shame).
|
|
30
|
+
2. **`codegraph where-defined <symbol>`** / **`codegraph outline <file>`** — pin the definition; map a big file. `outline` turns a 100+ declaration file into a navigable, line-numbered list — use it instead of reading god files blind. On a large file add `--budget 0` (see traps).
|
|
31
|
+
3. **`codegraph who-calls <symbol>`** / **`codegraph impact <target>`** — callers, and blast radius (static reach + historical co-change reach, reported separately on purpose: they disagree, and the disagreement is information).
|
|
32
|
+
4. **`codegraph churn --days N --path <dir>`** · **`coupling --path <file>`** · **`owners <path>`** · **`tests-covering <file>`** — the history layer: what's hot, what changes together (including *hidden coupling* — co-change with no static reference, the one fact reading code cannot reveal), who knows this area and how recently, which test file historically guards this file.
|
|
33
|
+
5. **`codegraph god-objects --minSymbols 30`** / **`hotspots`** / **`overview`** — repo-scale orientation and risk.
|
|
34
|
+
|
|
35
|
+
Then read, with `sed -n` ranges at the lines the index gave you. The index does discovery; reading does diagnosis. Add `--json` when a wrapper needs structure.
|
|
36
|
+
|
|
37
|
+
There is also **`codegraph blame <file> [--line N]`** — the commits behind a file or one specific line, answered by git at query time; **`codegraph refs <name>`** — the data-reference layer: where a name is used as a dict/object key, attribute/field, import, or type annotation, with read/write direction, and a cross-language flag when the same name spans Python and TypeScript files (this answers "who reads `x_axis_sort`" / "who writes `chart.query_context`" in one call — the questions that used to need grep); and **`codegraph drift`** — near-duplicate function bodies crossed with co-change history: similar code whose files never change together is an unfinished refactor waiting to disagree. `who-calls` additionally tabulates which keyword arguments each call site passes, so "which call sites omit this option" is one query.
|
|
38
|
+
|
|
39
|
+
## The boundary — what the index cannot answer (fall back immediately, don't retry)
|
|
40
|
+
|
|
41
|
+
- **Free-text string literals** (SQL templates, prose, format strings) — grep. Identifier-shaped keys and fields ARE indexed (`refs <name>`); anything longer or with spaces is not.
|
|
42
|
+
- **Framework reflection** (Marshmallow `@post_load`, signal handlers, `appbuilder.add_api(Cls)` registration) — invisible to call edges. Bare decorator *application* (`@has_access_api`) IS indexed as a reference; what the decorator does and when it runs is not.
|
|
43
|
+
- **Data flow and mechanism** ("does this value reach that function", "what order do these run in") — always reading work.
|
|
44
|
+
|
|
45
|
+
Indexed symbol forms include statement declarations, `const X = () => {}`, named function expressions inside wrapper calls (`typedMemo(function DataTable() {})`), TS/JS class fields, and Python class-body fields (`is_dttm = Column(...)`); nested members render as `Parent › member` in `outline`.
|
|
46
|
+
|
|
47
|
+
## Traps (each cost a prior agent real time)
|
|
48
|
+
|
|
49
|
+
- **Bare-name collision.** Call references match by name, not resolved binding. When a name has several definitions, `who-calls` lists every candidate and says so in its subject line — read that section before trusting the call sites, and pass `--scope <path-fragment>` (also on `find`, `where-defined`, `impact`) to narrow to a package.
|
|
50
|
+
- **`codegraph functions` lists the CLI's own functions**, not the repository's. For repo functions use `find`. An explicit `--limit` lifts the default token budget, so a full `outline` needs no `--budget 0` incantation (an explicit `--budget` still wins).
|
|
51
|
+
- **Churn windows.** `churn --days 180` returning nothing does not mean quiet history — widen the window. Directory-scope churn ranks by raw commit count and buries small-but-critical files; for specific files use `owners`/`coupling` per file.
|
|
52
|
+
- **`tests-covering` is co-change evidence, not executed coverage** — it names the right test file to open; it cannot say the specific scenario is tested.
|
|
53
|
+
- **Coupling on a busy file has a noise tail** — partners at <15% shared commits are usually PR-cohabitation, not real coupling. Verify a partner with a read before adding it to a touch-list.
|
|
54
|
+
|
|
55
|
+
## Division of truth in this repository
|
|
56
|
+
|
|
57
|
+
`docs/` is the source of truth for **intent** — what a feature is for, its invariants, decisions, vocabulary. codegraph is the source of truth for **facts** — where things are, who calls what, what changed together, who owns what. Never copy live facts (caller counts, churn numbers) into docs as if permanent; never ask the index for intent.
|
|
@@ -122,11 +122,18 @@ The two guarantees are complementary and both break on mixing: a reader holding
|
|
|
122
122
|
|
|
123
123
|
**Frontmatter is compulsory on every document this skill authors** — `type:` (the genre) on every
|
|
124
124
|
doc, plus the genre's own keys beside it; a document without its frontmatter fails the skill's
|
|
125
|
-
contract and is not done.
|
|
126
|
-
`module:` (optional), `keywords:` (required), `status:` (required),
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
contract and is not done. **There is no frontmatter-less genre.** A feature or UX doc carries the
|
|
126
|
+
full set — `owns:` (required), `module:` (optional), `keywords:` (required), `status:` (required),
|
|
127
|
+
`applies:` (the architecture docs it obeys; omitted when none), optional `last-reviewed:`. A
|
|
128
|
+
behavioral checklist (`docs/tests/`) carries `type: checklist` and `verifies:` — the doc whose
|
|
129
|
+
promises it proves. An acceptance-criteria doc (`docs/acceptance/`) carries `type:` and `status:`.
|
|
130
|
+
|
|
131
|
+
**The frontmatter is where the docs tree's edges live**, and a doc that declares none is a node
|
|
132
|
+
nothing can reach: no change implicates it, so it rots without the rot being detectable. Deriving
|
|
133
|
+
every edge — `owns:` from code, `verifies:` and `applies:` from the docs they answer to — is
|
|
134
|
+
[`references/frontmatter.md`](references/frontmatter.md), and it carries the rule the whole graph
|
|
135
|
+
rests on: **declare once at the consuming end, generate every inverse.** Every doc create or
|
|
136
|
+
update, and the `maintenance` agent's baseline pass, route there.
|
|
130
137
|
|
|
131
138
|
---
|
|
132
139
|
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
# Frontmatter Derivation —
|
|
1
|
+
# Frontmatter Derivation — the doc graph
|
|
2
2
|
|
|
3
|
-
Load when: creating a feature doc, updating one whose
|
|
3
|
+
Load when: creating a doc that declares an edge (a feature doc, a UX doc, a behavioral checklist), updating one whose scope moved, or running a baseline/reconcile pass over many docs.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**A docs tree is a graph, and every edge in it is declared in frontmatter.** Two kinds of edge exist: **doc → code** (`owns:`), and **doc → doc** (`verifies:`, `applies:`). The SessionStart hook reads them all and injects the generated indexes; there is no hand-maintained map of either kind.
|
|
6
|
+
|
|
7
|
+
## The one rule that keeps the graph honest
|
|
8
|
+
|
|
9
|
+
**An edge is declared once, at the consuming end. Every inverse is generated, never authored.**
|
|
10
|
+
|
|
11
|
+
A parent that hand-lists its children is a second thing to forget, and the moment the two directions can disagree nothing says which is right — you have doubled the staleness surface rather than halving it. So a checklist names the feature it verifies; the feature never lists its checklists. A feature names the architecture doc it obeys; the architecture doc never lists its consumers. Both inverses are derived on demand and injected, exactly as the ownership index already is.
|
|
12
|
+
|
|
13
|
+
The consuming end is also the end that **knows**. A checklist author knows what they are verifying; an architecture doc has no idea who will obey it next year.
|
|
6
14
|
|
|
7
15
|
## When to run
|
|
8
16
|
|
|
@@ -34,7 +42,21 @@ The single bounded-context label this feature belongs to, or omit it for a cross
|
|
|
34
42
|
|
|
35
43
|
`keywords:` are the discovery terms topic→doc lookup rides. `status:` is `active`, `draft`, or `deprecated` — it steers the model toward live docs and away from draft/deprecated ones.
|
|
36
44
|
|
|
37
|
-
|
|
45
|
+
## The doc → doc edges
|
|
46
|
+
|
|
47
|
+
A prose link is not an edge. It states that one doc mentions another; it never states **why**, so nothing can be derived from it — not who consumes a doctrine, not what to re-read before an edit, not what falls stale when a surface moves. A tree can carry hundreds of prose links and still have no graph. These two keys carry the relationships that matter; everything else stays prose.
|
|
48
|
+
|
|
49
|
+
**`verifies:`** — on a behavioral checklist (`docs/tests/{feature}.md`), the feature or UX doc(s) whose promises it proves. One value is the norm; several when a checklist spans a capability that two docs decide. A checklist carrying no `verifies:` is an orphan by construction: nothing implicates it when its subject changes, so it rots silently and the rot is undetectable.
|
|
50
|
+
|
|
51
|
+
**`applies:`** — on a feature or UX doc, the architecture docs it obeys. Not "docs I read" and not "docs I mention" — **obligation**: a doctrine this surface is bound by, such that a change to the doctrine implicates this doc. The narrow reading is the load-bearing one; a key that collects everything consulted degrades into a bibliography and stops meaning anything. A feature bound by nothing cross-cutting omits the key.
|
|
52
|
+
|
|
53
|
+
Cross-cutting *contracts* remain diagnosed by the `boundary` skill from source, never persisted — `applies:` records which doctrine a surface answers to, not which code it touches.
|
|
54
|
+
|
|
55
|
+
## What is not frontmatter, deliberately
|
|
56
|
+
|
|
57
|
+
**`Pinned:` stays a section-level line inside a checklist**, naming the suite tests that guard that section. It is already typed and already resolved mechanically by the `maintenance` agent. Hoisting it into frontmatter would flatten per-section detail into a doc-level list and duplicate what the sections already say — the exact double-declaration the one rule above forbids.
|
|
58
|
+
|
|
59
|
+
That edge has a property the others lack, and it is worth naming: **a `Pinned:` reference terminates.** A test file exists or it does not, so the edge is falsifiable by a resolver sweep with no judgement. An `owns:` glob is only falsifiable at the dangling end — a glob that resolves says nothing about whether the doc still describes what the code does. Terminal edges get resolver sweeps; non-terminal edges need a human or an agent to read both ends. Do not build a checker that pretends otherwise.
|
|
38
60
|
|
|
39
61
|
## Rules
|
|
40
62
|
|
|
@@ -42,3 +64,6 @@ A doc's dependencies on other docs are expressed inline in its prose (links); cr
|
|
|
42
64
|
- Use glob patterns for directories, explicit paths for individual files. At `active`, never list paths that don't exist in the codebase; a `draft`'s projected paths are confirmed at `active`.
|
|
43
65
|
- When a doc covers a deep feature with sub-specs (e.g., `editor/`), each sub-spec owns its own globs — don't duplicate a path across siblings.
|
|
44
66
|
- For baseline/reconcile with many docs, use parallel sub-agents to read source and propose `owns:` globs.
|
|
67
|
+
- **Never author an inverse.** No doc lists its verifiers, its consumers, or its dependents — those are generated. A hand-written "consumed by" table is a defect to delete, not a convention to follow.
|
|
68
|
+
- **Every edge value is a repo-relative doc path** (`docs/architecture/common/security.md`), never a bare filename and never a display title — the resolver is a path lookup, and a title changes without the file moving.
|
|
69
|
+
- **A dangling edge is a block, not a warning.** `verifies:` or `applies:` naming a doc that does not exist is a typo or a deletion; either way the graph is lying. Resolve at authoring time.
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
# Behavioral Checklist (docs/tests/{feature}.md)
|
|
2
2
|
|
|
3
|
-
Truth document. Use for: **how to verify one feature's promised behavior by driving the real product** — the browser, the CLI binary, HTTP against the running service. One file per feature, named for the feature doc that owns the code (`docs/features/auth.md` → `docs/tests/auth.md`).
|
|
3
|
+
Truth document. Use for: **how to verify one feature's promised behavior by driving the real product** — the browser, the CLI binary, HTTP against the running service. One file per feature, named for the feature doc that owns the code (`docs/features/auth.md` → `docs/tests/auth.md`).
|
|
4
|
+
|
|
5
|
+
**Frontmatter is two keys, and `verifies:` is the load-bearing one:**
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
---
|
|
9
|
+
type: checklist # required tooling key — genre stamp
|
|
10
|
+
verifies: [docs/features/auth.md] # required — the doc(s) whose promises this proves
|
|
11
|
+
---
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
A checklist owns no code, so it carries no `owns:`. What it carries instead is the edge back to
|
|
15
|
+
its subject. **The filename convention is not the edge** — a same-named file is a coincidence a
|
|
16
|
+
resolver cannot trust, it breaks the moment a checklist spans two docs or a feature is renamed, and
|
|
17
|
+
it leaves nothing to implicate this file when its subject changes. Without `verifies:` a checklist
|
|
18
|
+
is unreachable: no change reaches it, so it goes stale and the staleness is undetectable. Several
|
|
19
|
+
values are correct where one checklist proves promises two docs decide; the inverse — which
|
|
20
|
+
checklists cover a feature — is generated, never listed in the feature doc.
|
|
4
21
|
|
|
5
22
|
**This is the driving script, and it is the source the suite is drawn from.** At the verification gate it does double duty: rung 1 drives the real surface *against these steps*, and rung 2 pins the durable subset *of these assertions* as suite tests. Writing it is therefore not a report written afterwards — it is authored or updated **before** the driving, so the gate has something to execute and the tests have something to be derived from. Which assertions earn a permanent test is decided by the `testing` skill (gate mode) § What earns a suite test, and each section records the answer on its `Pinned:` line.
|
|
6
23
|
|
|
@@ -12,6 +12,7 @@ owns: ["src/feature/**", "src/other/file.ts"] # required — code-path globs t
|
|
|
12
12
|
module: billing # optional — single bounded-context label; omit if cross-cutting
|
|
13
13
|
keywords: [invoice, pdf] # required — discovery terms
|
|
14
14
|
status: active # required — active | draft | deprecated
|
|
15
|
+
applies: [docs/architecture/common/security.md] # optional — architecture docs this surface is BOUND by (obligation, not a bibliography); omit when none
|
|
15
16
|
last-reviewed: YYYY-MM-DD # optional tooling key — stamped on substantive review/update; the maintenance agent ranks audit priority by it (metadata, never narrated in the body)
|
|
16
17
|
type: feature # required tooling key — genre stamp (generator-written or authored; enum = the template names), read by tooling (UI grouping, protected-file mapping); preserve on edits, never narrate
|
|
17
18
|
---
|
|
@@ -4,6 +4,8 @@ Record. Use for: every implementation plan. Plans are created here directly with
|
|
|
4
4
|
|
|
5
5
|
The `boundary` skill parses the **Proposed Changes** sections to check boundary impact before execution, and the `testing` skill (plan-gate mode) writes a **Test Obligations** section into the plan at the same gate. Keep that structure stable — section headings and bullet shape are load-bearing for the parsers, and the Test Obligations table is the ledger the verification gate discharges before the plan may reach `Status: Complete`.
|
|
6
6
|
|
|
7
|
+
**Both ledgers are read by a wall, so their cells obey a table's grammar.** A literal pipe inside a cell is written `\|` — the parser splits on unescaped pipes and unescapes what survives, so an escaped one is safe, and an unescaped one silently adds a column and shifts every later cell left. Prefer rephrasing (`` `install` or `update` ``) over escaping where the sentence allows it: the cell then reads the same to a person and to the wall.
|
|
8
|
+
|
|
7
9
|
**Doc Obligations is the one ledger the plan's author writes.** Deriving tests from behavior is judgment, which is why `testing` owns that section and it is marked never-hand-author; resolving Proposed Changes' file paths to the feature docs that own them is a **lookup** against the ownership index already injected at session start. Different work, different rule — fill it yourself, while the touch list is fresh.
|
|
8
10
|
|
|
9
11
|
The **`Gates:`** line carries one machine-read segment. The compliance hook greps **`Boundary:`** — implementation edits inside the plan's scope are blocked while it reads `Boundary: pending`; the `boundary` skill rewrites it to `Boundary: passed YYYY-MM-DD` or `Boundary: blocked YYYY-MM-DD`. The other two gates are enforced by **section presence**: a plan with no `## Test Obligations` or no `## Doc Obligations` section blocks the same edits — the sections are the artifacts, and the gate derives from them, not from a claim about them. Never hand-edit the line to skip a gate.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Truth document.** States what a person sees and can do on a surface, and what the surface promises never to do. Present tense, like every truth document.
|
|
4
4
|
|
|
5
|
-
**`type: ux
|
|
5
|
+
**`type: ux`**, plus `applies:` — the architecture docs this surface is bound by (the state vocabulary, the routing doctrine, the information architecture it answers to). Same key and same narrow reading as a feature doc's: obligation, not a reading list.
|
|
6
6
|
|
|
7
7
|
**Why this genre exists.** A feature doc (LLD) states what a feature is, how its data flows, and which contracts it honours — all of which stay plausibly true through a rewrite of the surface. Nothing else in the repo states what a person *observes*. So a change to the wire lands, the interface absorbs it, and the absorption reads as maintenance: no test fails, no lint fires, no document is contradicted. This document is the thing a silent change contradicts.
|
|
8
8
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: issue-bridge
|
|
3
|
+
description: The bridge between a traditional issue tracker (GitHub Issues, Jira, Linear, …) and the AI-first SDLC. Two modes — **triage** (inbound — a tracker item is routed through the ownership index to its owning feature docs, localized with codegraph, and lands as a confidence-graded triage dossier ready for a fix plan) and **file** (outbound — a verified harness finding becomes a tracker-native issue with checkable evidence, and the created ID is written back into the originating row). Tracker access goes through a connector (see the per-tracker packs under references/trackers/); tracker writes are always approval-gated per run. Use on "triage issue #N / PROJ-123 / ENG-123", "triage the new issues", "file this finding as a ticket", "/issue-bridge".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# issue-bridge — the tracker bridge method
|
|
7
|
+
|
|
8
|
+
**If no connector is reachable** (no MCP server, no CLI, no API credentials — the pack for your tracker lists the probes): do not stall — accept pasted issue text, name the fields you are missing (labels, comments, linked items), and run triage on what you have. Degraded output is acceptable; silent degradation is not.
|
|
9
|
+
|
|
10
|
+
You bridge two systems of record. Neither is asked to change:
|
|
11
|
+
|
|
12
|
+
- **The tracker owns** intake, priority, assignment, status, discussion. You never relocate those.
|
|
13
|
+
- **The AI layer owns** intent (feature docs say *why* — invariants, vocabulary, scope), location (the ownership index and codegraph say *what and where*), and history (git-derived).
|
|
14
|
+
- **The bridge rule: inbound enrich, outbound evidence.** An inbound item gains location, owning docs, and blast radius. An outbound item carries citations a human can verify without trusting you.
|
|
15
|
+
|
|
16
|
+
## Connectors
|
|
17
|
+
|
|
18
|
+
Tracker access is **this skill's own knowledge**, not a harness-declared tool. It was briefly modelled as a "connector" sub-kind of the tools class; that was wrong, and the probes are the proof — the tools class means one deterministic, versioned executable, while a tracker is an MCP entry *or* one of several unrelated CLIs *or* an env var, varying per repo and per team. So the access method lives in `references/trackers/<tracker>.md`, probed in preference order at the moment of use. It is **credentialed and side-effectful**, which is why every tracker write is approval-gated. Each tracker's pack under [`references/trackers/`](references/trackers/) lists its access options in preference order (MCP server → CLI → raw API), the probe for each, auth setup, and the exact calls for read / search / create / comment. Read the pack for your tracker before the first tracker call. More than one tracker in play → one pack each; the method below is identical across all of them.
|
|
19
|
+
|
|
20
|
+
## Tool tier
|
|
21
|
+
|
|
22
|
+
Read the codegraph status from the session context (`Tool:` line). **Present-fresh** → localize every question through the index (`find`, `where-defined`, `who-calls`, `refs`, `impact`, `blame`, `tests-covering`) before reading source. **Present-stale** → use it, note the gap. **Absent** → Glob/Grep/read, announced. Never run `codegraph index` unless you verify HEAD moved. One carve-out: **syntax-level detail — decorator lists and their order, full function bodies, statement sequence — is not in the index's query surface**; go straight to source for that class, whatever the tier. (The 0.2.0 decorator mis-attribution defect is fixed in 0.2.1 — `who-calls` and `impact` now attribute decorator references to the decorated declaration; the catalog pins `min-version: 0.2.1` accordingly.)
|
|
23
|
+
|
|
24
|
+
## Mode selection
|
|
25
|
+
|
|
26
|
+
**Triage** when given issue references, a tracker query, or pasted issue text. **File** when given a harness finding to externalize — a backlog row, a drift or bookkeeping finding, an audit or critic result, a changelog discovery. Ambiguous → ask one question, don't guess.
|
|
27
|
+
|
|
28
|
+
## Triage — tracker item in, dossier out
|
|
29
|
+
|
|
30
|
+
1. **Fetch** through the connector (per the pack). No access → pasted-text mode, missing fields named.
|
|
31
|
+
2. **Classify.** Bug / feature request / question / docs-gap. Questions get owning-doc pointers and a direct answer, not a dossier.
|
|
32
|
+
3. **Route.** Match the issue's vocabulary against the ownership index and feature docs' `keywords:`. Read the owning doc(s) **fully** — intent and invariants first, code second. No owning doc → say so; the absence is itself a finding.
|
|
33
|
+
4. **Localize.** Names from the issue go through codegraph; every location claim carries `file:line`.
|
|
34
|
+
5. **Diagnose.** Root-cause hypothesis, confidence-graded — `certain` / `inferred` / `unresolved` — and never present inferred as certain. Blast radius via `impact`/`coupling`; test surface via `tests-covering`.
|
|
35
|
+
6. **Dedup.** Search the tracker for siblings and priors (per the pack); check codegraph `drift` for the same-bug-in-a-clone class.
|
|
36
|
+
7. **Write the dossier** to `docs/operations/triage/YYYY-MM-DD-<tracker>-<id>-<slug>.md` and return the verdict summary.
|
|
37
|
+
8. **Posting back** (comment, label, assign, transition) is **approval-gated per run**: show exactly what would be posted, post only on an explicit yes in this session. A standing repo ruling (e.g. "never post to this upstream") overrides any approval.
|
|
38
|
+
|
|
39
|
+
## The dossier
|
|
40
|
+
|
|
41
|
+
Frontmatter: `tracker`, `issue` (id + URL), `date`, `confidence`, `type: triage`. Sections, in order: **Verdict** (1–3 sentences) · Classification · Owning docs (paths) · Root cause (cited, graded) · Blast radius · Suggested fix plan (feeds the plan → PR loop; you never implement) · Test surface · Duplicates & related · **Prior-thread corrections** (claims already on the tracker thread — bot answers, maintainer comments — that your evidence shows to be wrong, each with the citation that refutes it) · Open questions. A dossier is a dated working artifact — it never claims `owns:` globs and never masquerades as a feature doc.
|
|
42
|
+
|
|
43
|
+
## File — harness finding in, tracker issue out
|
|
44
|
+
|
|
45
|
+
1. **Verify first.** Reproduce the finding against source before externalizing it. A claim that doesn't reproduce is reported back, never filed.
|
|
46
|
+
2. **Compose tracker-idiomatic** (per the pack): imperative title; body: one plain-language context paragraph (no harness jargon), evidence (`file:line`, commit hashes), expected vs actual, acceptance criteria; link the owning feature doc by path.
|
|
47
|
+
3. **Show the draft(s).** Create only on explicit approval, through the connector. Batch discipline: many findings → one draft list, one approval, then individual creation; report every created ID.
|
|
48
|
+
4. **Close the loop.** Write the created issue's ID/URL back into the originating row — a backlog row gains the link, a bookkeeping row resolves.
|
|
49
|
+
|
|
50
|
+
## Boundaries
|
|
51
|
+
|
|
52
|
+
- Never edit source code. The dossier ends at the fix *plan*; implementation belongs to the plan → verification → PR loop.
|
|
53
|
+
- Nothing is created, edited, or commented in the tracker without explicit approval in the current run.
|
|
54
|
+
- Repo writes are `docs/operations/triage/**` plus the loop-closing edits to `docs/history/backlog.md` / `docs/operations/bookkeeping.md`. Nothing else.
|
|
55
|
+
- Announce degraded modes; never silently downgrade evidence quality.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# GitHub — connector pack
|
|
2
|
+
|
|
3
|
+
Access options in preference order. Probe each in order; use the first that answers. Research of record: `discussions/code-analysis-tools/research-tracker-connectors/01-github.md` (2026-08-19).
|
|
4
|
+
|
|
5
|
+
## Option 1 — the `gh` CLI (default)
|
|
6
|
+
|
|
7
|
+
Probe: `gh auth status` (exit 0 = authenticated; `gh --version` alone only proves the binary).
|
|
8
|
+
Setup if absent: `gh auth login` (browser flow) or `gh auth login --with-token` with a PAT (scopes: repo, read:org). CI: `GH_TOKEN` env.
|
|
9
|
+
|
|
10
|
+
- Read: `gh issue view <n|url> --comments --json title,body,labels,comments,state,url,author,assignees,milestone`
|
|
11
|
+
- Sweep: `gh issue list --search '<query>' --state all --json number,title,labels`
|
|
12
|
+
- Dedup: `gh search issues '<key phrases>' --repo OWNER/REPO`
|
|
13
|
+
- Create (approval-gated): `gh issue create --title <t> --body-file <f> --label <l>`
|
|
14
|
+
- Comment (approval-gated): `gh issue comment <n> --body-file <f>`
|
|
15
|
+
- Another repo: add `-R OWNER/REPO` to any of these.
|
|
16
|
+
|
|
17
|
+
## Option 2 — the official GitHub MCP server
|
|
18
|
+
|
|
19
|
+
`github/github-mcp-server` (GitHub-official, 32k★). Probe: a `github` entry in `.mcp.json` / MCP tools visible in-session.
|
|
20
|
+
Setup: `claude mcp add --transport http github https://api.githubcopilot.com/mcp/ --header "Authorization: Bearer <PAT>"` (hosted), or local Docker `ghcr.io/github/github-mcp-server` with OAuth.
|
|
21
|
+
Tool names (issues toolset, loaded by default): `issue_read` (get/get_comments/get_sub_issues/get_labels) · `list_issues` · `search_issues` · `issue_write` (create/update — the write gate applies) · `add_issue_comment` · labels via the `labels` toolset (`GITHUB_TOOLSETS` env to add).
|
|
22
|
+
|
|
23
|
+
## Option 3 — raw API fallback
|
|
24
|
+
|
|
25
|
+
Headers: `Authorization: Bearer <PAT>` · `Accept: application/vnd.github+json`.
|
|
26
|
+
- Read: `GET https://api.github.com/repos/OWNER/REPO/issues/N`
|
|
27
|
+
- Search: `GET https://api.github.com/search/issues?q=<query>` (q ≤256 chars, ≤5 boolean ops, ~10 req/min)
|
|
28
|
+
- Create: `POST /repos/OWNER/REPO/issues` `{"title","body","labels":[]}` · Comment: `POST .../issues/N/comments` `{"body"}`
|
|
29
|
+
- Labels: POST appends, PUT replaces — never PUT unless replacement is the stated intent.
|
|
30
|
+
|
|
31
|
+
## Composing for GitHub
|
|
32
|
+
|
|
33
|
+
Markdown body, native. Reference related issues/PRs by bare `#number` so GitHub auto-links. Labels from the owning doc's `module:` where the repo's label taxonomy allows — **read `gh label list` first; never invent labels** (real taxonomies differ: apache/superset uses `#bug:*`, not `bug`).
|
|
34
|
+
|
|
35
|
+
## Ecosystem notes (2026-08-19)
|
|
36
|
+
|
|
37
|
+
No incumbent GitHub-issues skill/plugin exists in anthropics/skills, the official plugin marketplace, or awesome-claude-code — this pack has no upstream to defer to. The reverse direction exists as `anthropics/claude-code-action` (@claude mentions inside issues trigger CI sessions); do not confuse the two.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Jira — connector pack
|
|
2
|
+
|
|
3
|
+
Access options in preference order. Probe each in order; use the first that answers. Research of record: `discussions/code-analysis-tools/research-tracker-connectors/02-jira.md` (2026-08-19).
|
|
4
|
+
|
|
5
|
+
## Option 1 — the official Atlassian Remote MCP Server (Cloud only)
|
|
6
|
+
|
|
7
|
+
Probe: an `atlassian` entry in `.mcp.json` / Atlassian MCP tools visible in-session.
|
|
8
|
+
Setup: `claude mcp add --transport http atlassian https://mcp.atlassian.com/v1/mcp/authv2`, then `/mcp` in-session for the OAuth 2.1 flow. **Cloud only — no Data Center.** Rate limits: 500–1,000 calls/hr by plan.
|
|
9
|
+
**Tool names are unpublished by Atlassian** — introspect the live tool list from your session (ToolSearch / the MCP tool listing) before planning calls; expect find/create/update/comment/transition capabilities per Atlassian's own examples.
|
|
10
|
+
|
|
11
|
+
## Option 2 — community MCP: `sooperset/mcp-atlassian` (Cloud + Server/DC)
|
|
12
|
+
|
|
13
|
+
The maintained community standard (5.8k★, MIT). Probe: an `mcp-atlassian` entry in `.mcp.json`.
|
|
14
|
+
Setup: `uvx mcp-atlassian` with env `JIRA_URL`, `JIRA_USERNAME`, `JIRA_API_TOKEN` (Cloud; token from id.atlassian.com) or `JIRA_PERSONAL_TOKEN` (Server/DC v8.14+).
|
|
15
|
+
Confirmed tool names: `jira_get_issue` · `jira_search` (JQL) · `jira_create_issue` · `jira_add_comment` · `jira_transition_issue` · `jira_update_issue` · `jira_get_transitions` · `jira_batch_create_issues`.
|
|
16
|
+
|
|
17
|
+
## Option 3 — CLIs
|
|
18
|
+
|
|
19
|
+
**acli** (Atlassian-official). Probe: `acli --version` + an authenticated site. **Namespace is `workitem`, not `issue`.**
|
|
20
|
+
`acli jira workitem view KEY-1 --json` · `workitem search --jql "..." --json [--paginate]` · `workitem create --summary --project --type` · `workitem comment create --key KEY-1 --body "..."`.
|
|
21
|
+
|
|
22
|
+
**jira-cli** (ankitpokhrel, 5.9k★, MIT). Probe: `jira me`. Setup: `jira init` (Cloud: email + `JIRA_API_TOKEN`; DC: PAT + `JIRA_AUTH_TYPE=bearer`).
|
|
23
|
+
`jira issue view ISSUE-1 --comments 5` · `jira issue list -q "<JQL>"` · `jira issue create -tBug -s"..." -b"..." --no-input` · `jira issue comment add ISSUE-1 "..."`.
|
|
24
|
+
|
|
25
|
+
## Option 4 — REST fallback (Cloud v3, v2 for plain text)
|
|
26
|
+
|
|
27
|
+
Basic auth `email:API_token`. Base `https://<domain>.atlassian.net/rest/api/3/`.
|
|
28
|
+
- Read: `GET /rest/api/3/issue/{KEY}`
|
|
29
|
+
- **JQL search: `GET /rest/api/3/search/jql`** — the old `/rest/api/3/search` is deprecated and being removed; do not use it.
|
|
30
|
+
- Create: `POST /rest/api/3/issue` — **`description` must be ADF** (`{"type":"doc","version":1,"content":[…]}`).
|
|
31
|
+
- Comment: `POST /rest/api/3/issue/{KEY}/comment` — body must be ADF.
|
|
32
|
+
- **ADF inconvenient → v2**: `/rest/api/2/issue` + `/rest/api/2/issue/{KEY}/comment` take plain-string bodies; v2 JQL endpoint is `/rest/api/2/search`.
|
|
33
|
+
|
|
34
|
+
## Composing for Jira
|
|
35
|
+
|
|
36
|
+
Map to the project's real issue types (`Bug`/`Story`/`Task` — list them, don't assume). Set `components` from the owning doc's `module:` where components exist. Atlassian renamed "issue" → "work item" in UI/acli — expect both vocabularies. Leave estimates, sprints, and epic links to humans unless instructed.
|
|
37
|
+
|
|
38
|
+
## Ecosystem notes (2026-08-19)
|
|
39
|
+
|
|
40
|
+
No official Anthropic Jira skill. Community skills exist at small adoption (JIRA-Assistant-Skills 42★, two atlassian-skills repos, an acli wrapper) — nothing to defer to; this pack is self-sufficient.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Linear — connector pack
|
|
2
|
+
|
|
3
|
+
Access options in preference order. Probe each in order; use the first that answers. Research of record: `discussions/code-analysis-tools/research-tracker-connectors/03-linear.md` (2026-08-19).
|
|
4
|
+
|
|
5
|
+
## Option 1 — the official Linear MCP server
|
|
6
|
+
|
|
7
|
+
Probe: a `linear`/`linear-server` entry in `.mcp.json` / Linear MCP tools visible in-session.
|
|
8
|
+
Setup: `claude mcp add --transport http linear-server https://mcp.linear.app/mcp`, then `/mcp` for OAuth 2.1. A **read-only endpoint exists**: `https://mcp.linear.app/mcp/readonly` — prefer it for triage-only sessions. Direct bearer also works (`Authorization: Bearer <token>` — OAuth token or a Linear personal API key; a Read-only API key gives enforced read-only).
|
|
9
|
+
**Tool names are unpublished by Linear** — introspect the live tool list before planning calls; expect find/create/update tools over issues, projects, comments.
|
|
10
|
+
|
|
11
|
+
## Option 2 — community MCP servers
|
|
12
|
+
|
|
13
|
+
`tacticlaunch/mcp-linear` (146★, MIT — the exhaustive option, 150+ `linear_*` tools, TOOLS.md published) or `prime-radiant-inc/streamlinear` (89★, MIT — the token-efficient option: one dispatch tool `action: search|get|update|comment|create|graphql|help`, ~500 schema tokens; auth `LINEAR_API_TOKEN`).
|
|
14
|
+
|
|
15
|
+
## Option 3 — CLI
|
|
16
|
+
|
|
17
|
+
`@schpet/linear-cli` (binary `linear`, 911★, actively maintained; Linear's own npm CLIs are abandoned — do not use `@linear/cli`). Probe: `linear --version`. Install: `brew install schpet/tap/linear`. Branch-aware issue detection, list/start/create, PR text generation.
|
|
18
|
+
|
|
19
|
+
## Option 4 — GraphQL fallback
|
|
20
|
+
|
|
21
|
+
Endpoint `https://api.linear.app/graphql`. **Auth header for a personal API key is `Authorization: <API_KEY>` — no `Bearer` prefix** (Bearer is only for OAuth tokens). Introspection-verified surface:
|
|
22
|
+
- Read: `{ issue(id: "ENG-123") { id title description state { name } labels { nodes { name } } comments { nodes { body } } } }`
|
|
23
|
+
- Search: `{ issues(first: 25, filter: {title: {contains: "…"}}) { edges { node { id identifier title } } pageInfo { hasNextPage endCursor } } }` (Relay cursors; also `issueSearch`/`searchIssues`)
|
|
24
|
+
- Create (approval-gated): `mutation { issueCreate(input: {title: "…", teamId: "<uuid>", description: "…", labelIds: […]}) { success issue { id identifier url } } }`
|
|
25
|
+
- Comment (approval-gated): `mutation { commentCreate(input: {issueId: "<uuid>", body: "…"}) { success comment { id } } }`
|
|
26
|
+
Rate limits: API key 2,500 req/hr + complexity budget (single request cap 10,000 points — keep `first:` small on nested connections); watch `X-RateLimit-*`/`X-Complexity` headers.
|
|
27
|
+
|
|
28
|
+
## Composing for Linear — including Linear's own agent rules
|
|
29
|
+
|
|
30
|
+
Linear publishes **Agent Interaction Guidelines** (linear.app/developers/aig); this pack adopts the two that bind writers even outside formal Agent Sessions:
|
|
31
|
+
- **You are a `delegate`, never an `assignee`** — humans keep ownership of issues you touch.
|
|
32
|
+
- **Never move work to a terminal state** — done/canceled transitions belong to humans (the same rule the linear-use community plugin hard-codes). Starting work maps to the team's lowest-position "started" state, and only when instructed.
|
|
33
|
+
Body is native markdown. Pick team and project explicitly — never guess a team. Priority conservatively (default: none). Cycles and estimates stay human.
|
|
34
|
+
|
|
35
|
+
## Ecosystem notes (2026-08-19)
|
|
36
|
+
|
|
37
|
+
The strongest community skill (`wrsmith108/linear-claude-skill`, 121★) itself recommends the official MCP server — aligned with this pack's ordering. No official Anthropic Linear skill exists.
|
|
@@ -66,6 +66,8 @@ You decide **where a test lives and what it may assert**. **The litmus is "where
|
|
|
66
66
|
|
|
67
67
|
The maximum form, for a full-stack application. Each layer names what only it can break, the oracle that proves it, and the technology **class** — the concrete framework comes from `testing.md`'s stack table, never from here.
|
|
68
68
|
|
|
69
|
+
**7b is lettered, not numbered, because it is layer 7's other half rather than a rung above it** — same subject, same tests-shaped artifacts, a different oracle. It exists because a simulated DOM computes no layout: `getBoundingClientRect()` returns zeros, so an assertion about what a person can actually see has nowhere legal to live and comes out as a class-name proxy (`classList.contains("w-24")`) that fails on a benign restyle and passes on `w-[10000px]`. A stack without a rendered surface has no 7b.
|
|
70
|
+
|
|
69
71
|
| # | Layer | Owns (defects only it can originate) | Oracle | Technology class |
|
|
70
72
|
| --- | --- | --- | --- | --- |
|
|
71
73
|
| 1 | **Persistence schema** | Invariants the storage engine enforces against any code: uniqueness, checks, cascades, append-only triggers | The engine's refusal of a forbidden write | The real engine, in-memory — never a mock of storage you own |
|
|
@@ -75,6 +77,7 @@ The maximum form, for a full-stack application. Each layer names what only it ca
|
|
|
75
77
|
| 5 | **Client data** | The consumer's half of the wire: error taxonomy, parsing, byte fidelity, stream reassembly, retry/abort | Intercepted network serving layer-4 artifacts; no DOM | `msw`-class interception |
|
|
76
78
|
| 6 | **View logic** | Pure presentation derivations: grouping, ordering, status vocabularies, formatting | Table-driven equality | Plain runner |
|
|
77
79
|
| 7 | **Component / surface** | Interaction orchestration — what a user action changes on screen: buffer lifecycle, optimistic states, error surfaces | Rendered output a user can observe; network answered by layer 4 | DOM testing-library class + interception |
|
|
80
|
+
| 7b | **Rendered composition** | Defects only a real rendering engine produces: geometry (overlap, clipping, a container that grows when a panel opens), the theme cascade where your tokens meet a component library's, focus order and accessible names through a third-party primitive | Measurement in a real browser engine — `getBoundingClientRect`, computed style, the accessibility tree | Real-renderer component runner (the unit runner's browser mode), **never** a simulated DOM |
|
|
78
81
|
| 8 | **System smoke** | Composition only — the layers are actually stacked: the build serves, auth flows end to end, one write round-trips | A real browser or real binary against the really-running app | Browser/process driver class; 3–5 journeys, zero variants |
|
|
79
82
|
|
|
80
83
|
Layer 4 is the hinge. Every layer ≥5 runs on the assumption that layers 1–3 are green — and the artifact makes that assumption *enforceable*: when the provider's response type changes, the fixture stops compiling, and every consumer test fails at typecheck before it runs. A consumer-side test that hand-writes a wire response has smuggled unverified layer-3 knowledge upward; route it through the artifact.
|
|
@@ -95,9 +98,12 @@ Derive the stack first, then place tests: a placement argument that starts "this
|
|
|
95
98
|
|
|
96
99
|
### Placement discipline
|
|
97
100
|
|
|
101
|
+
- **Two conditions put a test below a real browser; both must hold.** *(a)* Its assertion names a **product promise**, not a construction detail — litmus: would it survive a total visual redesign that changes no behavior? `not.toHaveProperty("taskMode")` survives; `classList.contains("w-24")` does not. *(b)* The defect it catches can **originate below the layout engine** — a wrong wire path, a dropped field, a state-machine bug, a wrong string. Fail either and the honest home is 7b or 8.
|
|
102
|
+
- **Geometry may never be asserted in a runner without a layout engine.** There is no valid proxy: a class token is not a width, and a test that pretends otherwise is simultaneously brittle and blind. When the only available runner simulates the DOM, the behavior is driven and pinned at 7b — never approximated.
|
|
98
103
|
- **An assertion provable at two layers is misplaced — it belongs at the lower one.** The duplication check is mechanical, not judgment.
|
|
99
104
|
- **Behavior variants sink to the cheapest layer that owns them** — almost always 2 or 6. A variant at layer 3 pays wire tax on a domain test; a variant at layer 8 is a placement failure below, by definition.
|
|
100
105
|
- **One representative path upward.** Layer 3 keeps one happy path per route plus exhaustive coverage of *contract edges only*; layer 8 keeps 3–5 journeys total.
|
|
106
|
+
- **7b stays small by construction.** It holds geometry regressions that actually shipped and the handful of composition assertions a component library's contract cannot make alone (the theme cascade, the accessible name through its primitive). It is not a second component suite — a behavior that a simulated DOM can prove has no business paying a browser's price.
|
|
101
107
|
- **External systems enter as modeled fakes, not spies.** A fake implements the port's semantics so tests assert resulting state; a spy only proves methods were called, which passes just as happily with the bytes swapped.
|
|
102
108
|
- **Failure localization is the payoff to protect.** One defect turns exactly one layer red, at its owner. When a single change reddens several layers, at most one of those tests was correctly placed — treat the rest as findings.
|
|
103
109
|
|
|
@@ -114,6 +120,7 @@ tests/
|
|
|
114
120
|
client/ L5 — plain unit runner
|
|
115
121
|
view/ L6 — plain unit runner
|
|
116
122
|
component/ L7 — unit runner + DOM environment (per-file opt-in)
|
|
123
|
+
rendered/ L7b — the same runner in browser mode; a real engine, kept small
|
|
117
124
|
helpers/ shared seeds, fakes, and builders — owned by no stratum
|
|
118
125
|
e2e/ L8 — its own config, never mixed into the unit runner's glob
|
|
119
126
|
```
|
|
@@ -131,7 +138,7 @@ The ledger's altitudes are coarse names for where a test *runs*; the strata say
|
|
|
131
138
|
| --- | --- |
|
|
132
139
|
| unit | 2 (pure domain), 5, 6 |
|
|
133
140
|
| integration | 1, 2 (stateful), 3 |
|
|
134
|
-
| component | 7 |
|
|
141
|
+
| component | 7, 7b |
|
|
135
142
|
| e2e | 8 |
|
|
136
143
|
|
|
137
144
|
When writing or discharging a ledger row, name the stratum, not just the altitude — "integration" alone leaves open whether the row asserts a schema invariant, a business rule, or an envelope: three owners, three oracles.
|
|
@@ -152,13 +159,22 @@ Runs only when the user triggers the verification gate (constraints — precondi
|
|
|
152
159
|
1. **Write the checklist** — derive or update `docs/tests/{feature}.md` from the ledger and the LLD's promises (the `documentation` skill's `templates/behavioral-checklist.md`; one `**Assert**` per observable). Derived before the driving it is the script; derived after, it is merely a report with nothing to trace tests back to.
|
|
153
160
|
|
|
154
161
|
**The gate owns these files, not just their first draft.** Whoever drives a checklist maintains it in the same pass — nothing else in the repo is watching, because a checklist owns no code and no `owns:` glob implicates it, so a surface can be rewritten under a checklist and every hook stays silent. Before driving, reconcile the file against the surface you are about to drive: **retire** sections whose behavior no longer exists (delete them — a checklist is a truth document, and the history lives in git), **add** sections for behavior shipped since the last run, and **repair every `Pinned:` trace**, because a trace naming a test that no longer exists is worse than no trace — it reports coverage that cannot fail. A section you cannot pin says `none yet` and names the test it owes. **Never soften an assertion to make the run green**: the checklist states what the feature promises, the run records what happened, and the backlog carries the difference.
|
|
155
|
-
2. **Drive the real surface
|
|
156
|
-
|
|
162
|
+
2. **Drive the real surface, and on a rendered surface drive it visually FIRST.** The old order ran every cheap oracle before earning the right to look at the product; it inverted here because the defects that reach users on a rendered surface are the ones no simulated DOM can see. A slider thumb sitting on its own labels, a settings panel that grows the box someone types into: a suite of two hundred component tests is blind to both by construction, and the only oracle that caught them was judgement applied to real pixels.
|
|
163
|
+
|
|
164
|
+
**The browser is reached Chrome-CDP-first, Playwright second.** CDP drives the user's own browser interactively — it is the cheapest way to *look*, it needs no script written in advance, and looking is what discovery requires. Playwright is the fallback where CDP is unavailable (headless, CI, a runtime without it) and the tool of record for anything that must run unattended afterwards. Where the runtime offers neither, emit the browser-verification checklist for the user to run; their report is the rung's verdict and `Status: Complete` waits for it.
|
|
165
|
+
|
|
166
|
+
**Judged and scripted are two different rungs and the difference is the point.** A *judged* drive — an agent or a person reading the actual rendering against the design's intent — is what **finds** geometry and taste defects; a *scripted* assertion only **guards** a contract someone already discovered. A generated script asserting "the slider sets the value" passes happily over a thumb covering its labels. So judgement leads and scripts follow it, never the reverse.
|
|
167
|
+
|
|
168
|
+
Cheap oracles have not stopped mattering — they stopped being a gate you pass before looking. Suites, the CLI binary, and HTTP against the running worker run in the same pass and are what the drive's findings get **pinned into** (rung 3). On a surface with no rendering — a CLI, a service, a library — this rung is its own real binary or real wire, and the visual clause simply does not apply.
|
|
169
|
+
|
|
170
|
+
**A failing checklist assertion is never softened to make a run green** — record the defect, send it to the backlog.
|
|
171
|
+
3. **Pin the durable behaviors** as suite tests per `testing.md`, naming each test's stratum (§ Bridge). **Pinning is dispatched, not written in your own loop** — batch the rows (per stratum or per target file) to sub-agents on the cheapest capable tier, each briefed with the ledger row, the checklist section, the target file, and the stratum; run the returned tests yourself — the cold-shell run below is the verdict, never the sub-agent's report. No dispatch mechanism ⇒ write them inline. **A pinned test must pass from a cold shell** — `pnpm test` with no dev server hand-started, no seeded state assumed beyond what the suite builds for itself. A test whose oracle is "fetch the server I happen to have running" is a driven check wearing a test's name: it goes red the moment the session ends (on Workers, `@cloudflare/vitest-pool-workers` is the self-contained harness; an Express app is driven via `supertest`, never a port). Before discharging, run the suite once from a clean shell — a suite that needs the session's environment discharges nothing. A test is a permanent maintenance cost, bought deliberately:
|
|
157
172
|
|
|
158
173
|
| Earns a suite test | Verified by driving alone |
|
|
159
174
|
| --- | --- |
|
|
160
|
-
| A promise an LLD makes about the feature's contract |
|
|
175
|
+
| A promise an LLD makes about the feature's contract | Copy and spacing that no defect has ever turned on — restated by a test, not protected by one |
|
|
161
176
|
| A bug that occurred once — its regression test | A one-off migration or backfill's output |
|
|
177
|
+
| **A layout defect that shipped** — its geometry pin, at 7b, measured in a real engine | Layout a judged drive has looked at and found correct — looking is the proof, and a pin buys nothing until something breaks |
|
|
162
178
|
| An error path, race, or idempotency guarantee | Behavior a type or the compiler already makes unshippable |
|
|
163
179
|
|
|
164
180
|
A driven check is evidence, not an artifact — it proves the change works today and leaves nothing behind; that is why this rung exists.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: maintenance
|
|
3
|
-
description: The single docs steward. Default behavior is the docs-vs-code coherence audit — detects and fixes docs drifting from code (stale docs, broken ownership, unowned source paths, architecture/backlog drift). An additive **baseline mode** layers
|
|
3
|
+
description: The single docs steward. Default behavior is the docs-vs-code coherence audit — detects and fixes docs drifting from code (stale docs, broken ownership, unowned source paths, architecture/backlog drift). An additive **baseline mode** layers reconcile on top: when the repo's `harness-version` marker is missing or behind — **or its documents are behind their genre's current schema even though the folder tree is not** — it converges `docs/` to the installed harness (scaffold the standard tree, fold dissolved-shape artifacts forward, converge every truth document to its template's current schema while leaving frozen records alone, stamp the version, propose modules) before running the audit. Mode is auto-selected from the marker. Run weekly, after major changes, or to baseline/upgrade a repo. Cross-cutting contract analysis is the `boundary` skill (`check` for plans, `scan` for diffs) — it derives contracts dynamically from source; there are no persisted boundary docs to audit. Triggers on "run maintenance", "docs health", "baseline this repo", "reconcile the harness", "upgrade the harness", "is this repo on the current harness", "/maintenance".
|
|
4
4
|
tools: Read, Glob, Grep, Edit, Write, Bash
|
|
5
5
|
model: opus
|
|
6
6
|
---
|
|
@@ -28,7 +28,9 @@ Decide the mode once, up front, from the version marker — then run.
|
|
|
28
28
|
| behind on minor/patch | **probe first**, below |
|
|
29
29
|
| equal to installed | **audit only** (the default spine) |
|
|
30
30
|
|
|
31
|
-
**The probe.** Run the Step B2 detection
|
|
31
|
+
**The probe.** Run the Step B2 **and B2b** detection passes read-only: if any `*.bkp.*` file sits under `docs/` (a fold awaiting its pass), **or** any **scaffolded file** in the target tree is missing (`docs/README.md`, `docs/structure.md`, `docs/history/CHANGELOG.md`, `docs/history/backlog.md`, `docs/memory/main.md`), **or B2b finds truth documents behind their genre's current schema**, the shape *did* change ⇒ **baseline mode**. Only when the probe finds nothing ⇒ re-stamp the version (no reshape), then audit.
|
|
32
|
+
|
|
33
|
+
**The third clause is not optional, and it is the one that used to be missing.** A repo can hold the current folder tree, every scaffold present, and still have every document in it written to a schema two minors old — because a genre's shape lives in its template, not in the directory layout. Probing only for absent scaffolds meant such a repo re-stamped its version and declared itself current, which is how a stamp comes to certify a tree nobody converged. **The version marker asserts the documents match the installed harness, not merely that the folders do.**
|
|
32
34
|
|
|
33
35
|
> **Never probe on a missing folder.** Git does not track empty directories, so an unused folder is absent on every clone — reading that as drift puts the repo in baseline mode *forever*. Only the five scaffolded files above are shape signals; every folder in the target tree is created on first use.
|
|
34
36
|
3. **Precondition for baseline mode:** a root `CLAUDE.md` must exist. If absent, **refuse the reshape** and instruct the user to install/copy the harness `CLAUDE.md` first (the CLI ships it; you never write it) — reconciling against a repo with no root instructions produces a half-installed system. Detect-and-instruct, not a hard gate on the audit.
|
|
@@ -60,6 +62,14 @@ For each `owns:` glob across the feature docs:
|
|
|
60
62
|
- Flag **equal-specificity overlaps** — two feature docs whose `owns:` globs claim the same paths at the same specificity (an ambiguous owner). `node .claude/hooks/check-ownership.mjs` reports these mechanically alongside dead globs (`overlaps()` in `lib/ownership.mjs`); a glob at strictly higher specificity carving out of a broader `**` is the designed layering and is never flagged. Surface a real overlap as a question — narrowing someone's glob is an author's call, not remediation.
|
|
61
63
|
- **Stamp `type:` where missing.** Every doc carries a `type:` genre key (enum = the `documentation` skill's template names); docs predating the key are stamped mechanically — the genre is readable from placement and shape (a `docs/features/` doc is `type: feature`, an AD is `type: architecture-decision`). Stamping is remediation, not a question; an ambiguous genre is backlogged instead.
|
|
62
64
|
|
|
65
|
+
**Then validate the doc→doc edges — `owns:` is only half the graph.** `node .claude/hooks/check-ownership.mjs` and the session-injected **doc edge index** carry both directions; read the edge index and check three things:
|
|
66
|
+
|
|
67
|
+
- **Dangling edges.** A `verifies:` or `applies:` naming a doc that does not exist is remediation, not a question — fix the path, or drop the edge when its target was deleted deliberately. The generated index marks these `← TARGET MISSING`.
|
|
68
|
+
- **Checklists with no `verifies:`.** Every `docs/tests/` file names the doc whose promises it proves. A checklist without one is unreachable — nothing implicates it when its subject moves — so it is the single highest-value backfill in the tree, and it is mechanical wherever a same-named feature doc exists (`docs/tests/auth.md` → `docs/features/auth.md`). Where the name does not match, ask rather than guess.
|
|
69
|
+
- **Architecture docs nothing declares.** An architecture doc named by no `applies:` is either genuinely unbound or a doctrine everyone obeys and nobody declares. Both are worth reporting; neither is auto-fixable, because `applies:` states an obligation only the consuming doc's author can assert. Report the list; backlog the judgement.
|
|
70
|
+
|
|
71
|
+
**Never author an inverse to fix a gap.** A doc listing its own verifiers or consumers is a defect — the inverse is generated. If the index looks wrong, the frontmatter at the consuming end is what is wrong.
|
|
72
|
+
|
|
63
73
|
For each code symbol or path a doc explicitly **names** (function/export/route/type):
|
|
64
74
|
|
|
65
75
|
- Verify it resolves in the code, independent of git-churn — Step 3's churn gate is blind to churn-free false claims.
|
|
@@ -167,14 +177,33 @@ Detecting and folding old-shape artifacts is what makes a baseline more than a f
|
|
|
167
177
|
| `*.bkp.*` files under `docs/` (the installer's first-contact backups of the project's pre-existing files) | **fold the content, then delete the backup.** Classify each backup's content by nature and land it in the current shape — a backed-up changelog's rows into `docs/history/CHANGELOG.md` and per-entry files, a backed-up structure/readme's real claims into the scaffolded truth doc, a backed-up feature doc's content merged into its sibling (or adopted whole if the sibling is a pristine seed). Content is folded, never discarded silently; what cannot be classified goes to the backlog with the backup kept. When the fold lands, delete the `.bkp` file |
|
|
168
178
|
| `*.bkp.*` files at the repo root or in config (`CLAUDE.bkp.md`, `.prettierrc.bkp.json`, …) | **never merge these.** Top-level config is harness-owned by design; list each one to the user as their call, and leave the file in place |
|
|
169
179
|
| no `harness-version` in `structure.md` | stamp it (Step B4) |
|
|
180
|
+
| `docs/tests/` checklists carrying **no frontmatter** (the shape before the graph — the genre was exempt) | **backfill `type: checklist` + `verifies:`.** Mechanical where a same-named feature or UX doc exists; where the name does not match, list the checklist to the user with your best candidate and let them confirm. This is the row that upgrades a repo from a tree of files to a graph — without it every checklist in an older repo stays unreachable, and nothing detects that |
|
|
181
|
+
| feature / UX docs with no `applies:`, in a tree whose `docs/architecture/` is non-empty | **report, never invent.** `applies:` is an obligation the doc's author asserts, not a similarity a scan can infer; a guessed edge is worse than a missing one because it reads as declared. List the candidates (architecture docs the feature's prose already links) and backlog the confirmation |
|
|
170
182
|
|
|
171
183
|
**The support window: three past minor versions.** The fold table covers shapes at most three minors behind the installed harness — which today means the rows above, because every older dissolved construct (`docs/routing.md`, `docs/modules/`, pillars, the Refinement Ledger, the ADR genre, legacy brainstorm folders, …) predates the window. An older shape is **reported, never improvised**: name what the probe found, point the user at etna's dated migration runbooks (`docs/history/migrations/` in the forge), and recommend a fresh baseline over an archaeological fold — a repo more than three minors behind re-baselines rather than migrates.
|
|
172
184
|
|
|
185
|
+
### Step B2b — Converge documents to their genre's current schema
|
|
186
|
+
|
|
187
|
+
B2 folds artifacts whose **shape dissolved** — a file in the wrong place or the wrong genre. This step handles the other axis, and it is the one that silently rots a repo that otherwise looks current: a document in the **right folder with the right name** whose *internal* schema is behind. A changelog entry missing three of its seven required fields, a checklist with no `verifies:` edge, a feature doc predating `applies:`, an AD on the old key vocabulary — none of these move a folder, so nothing structural detects them, and a repo can pass every scaffold check while no document in it matches the installed harness.
|
|
188
|
+
|
|
189
|
+
**Read the schema from the `documentation` skill's templates, never from memory.** The template is the contract; your recollection of it is a version of unknown age. For each genre, take the required frontmatter keys and required sections from the template file itself, then check the repo's documents of that genre against them.
|
|
190
|
+
|
|
191
|
+
**The two natures split the treatment, and the split is absolute.**
|
|
192
|
+
|
|
193
|
+
| Nature | Genres | Treatment |
|
|
194
|
+
| --- | --- | --- |
|
|
195
|
+
| **Truth document** | features, ux, architecture, operations, `docs/tests/` checklists, `structure.md`, READMEs | **Converge in place.** These state what is true now, so rewriting them to the current schema is not revision, it is the genre working as designed. Add missing frontmatter keys, add missing required sections, rename keys the template renamed |
|
|
196
|
+
| **Record** | plans, changelog entries, migration runbooks, brainstorm records | **Never rewritten.** A completed plan and a committed entry are frozen — immutability begins at commit, and a record edited to match a newer schema stops being evidence of what happened. Report non-conformance; repair only the **living index** that points at the record (an absent `docs/history/CHANGELOG.md` row is an index defect, not a record defect, and is backfilled with a note saying so) |
|
|
197
|
+
|
|
198
|
+
**Never invent content to satisfy a field.** A frozen changelog entry missing `Skills:` is missing the *judgement* that field carries, and no later session holds it — writing one is fabrication wearing a schema's clothes. Report it, backlog the pattern if it recurs, and leave the entry alone. The same restraint applies to a truth document: a genre's new section that needs knowledge the repo does not contain is raised as a question, not filled with plausible prose.
|
|
199
|
+
|
|
200
|
+
**Scale gate — confirm once, not per file.** Count the convergences first and fold them into B3's single reconcile table as `Fold` rows. Where the count is large (roughly **20+ files, or any change spanning more than one genre**), state the counts per genre in that table and take B3's one approval for the whole set — a per-file prompt turns a mechanical upgrade into an interrogation and gets abandoned halfway, which leaves a tree half-converged and a version marker that lies about it. Where a genre's convergence is judgement-heavy rather than mechanical, split it out as its own row so approval is informed rather than bundled.
|
|
201
|
+
|
|
173
202
|
### Step B3 — Present the reconcile plan, then execute
|
|
174
203
|
|
|
175
204
|
Table: `| File | Action | From | To | Reason |` (actions: `Move`, `Merge into {target}`, `Delete`, `Keep`, `Create`, `Fold`). For a fresh baseline this is mostly `Create`; for an upgrade it is mostly `Fold`/`Move`. Ask once: **"Approve this reconcile?"** — you own this interaction (the `documentation` skill is a library and has none).
|
|
176
205
|
|
|
177
|
-
On approval, execute: create the folder structure; move/fold/merge/delete per the plan; add `Status:` to plan files; for each feature doc, derive and write its `owns:` globs (+ `module:` label + `keywords:`) from the source it describes by calling the `documentation` skill's **frontmatter derivation**; merge scattered backlogs into `docs/history/backlog.md`; fix links broken by moves.
|
|
206
|
+
On approval, execute: create the folder structure; move/fold/merge/delete per the plan; apply B2b's schema convergences to the truth documents (records are reported, never rewritten); add `Status:` to plan files; for each feature doc, derive and write its `owns:` globs (+ `module:` label + `keywords:`) from the source it describes, and each checklist's `verifies:` edge, by calling the `documentation` skill's **frontmatter derivation**; merge scattered backlogs into `docs/history/backlog.md`; fix links broken by moves.
|
|
178
207
|
|
|
179
208
|
Scaffold missing mandated files **by calling the `documentation` skill for each template** — you orchestrate, the skill supplies templates: `docs/structure.md` (with `harness-version`), `docs/history/CHANGELOG.md`, `docs/history/backlog.md`, `docs/memory/main.md`, and the repo-root `README.md` (filled from the real lockfile/scripts/layout — **never overwrite an existing root README**).
|
|
180
209
|
|
|
@@ -182,6 +211,8 @@ Scaffold missing mandated files **by calling the `documentation` skill for each
|
|
|
182
211
|
|
|
183
212
|
Write `harness-version: <installed version>` (the semver from `.claude/harness.json`) into `docs/structure.md` frontmatter. This single field is the repo's baseline-and-version sentinel — there is no second marker (no `epoch`, no `docs/memory/index.md` "is-baselined" check).
|
|
184
213
|
|
|
214
|
+
**Stamp only what converged.** The marker asserts the repo's *documents* match the installed harness, not merely its folders — so it is written after B2b has run and its truth-document convergences have landed. Where B2b's set was too large to finish in one pass, stamp nothing and say what remains: a version marker ahead of the tree it describes is worse than an absent one, because every later probe trusts it and skips the work.
|
|
215
|
+
|
|
185
216
|
### Step B5 — Module proposal
|
|
186
217
|
|
|
187
218
|
Modules are **labels, declared, never inferred from physical layout**. Derive the repo's candidate bounded contexts directly from the code and docs: distinct wire surfaces (who owns routes/handlers), schema/data-access ownership, entry points and process units, plus any contexts `docs/architecture/` already names.
|
|
@@ -206,6 +237,8 @@ Apply the **§ Strategy memory** retirement procedure to `docs/memory/maintenanc
|
|
|
206
237
|
- **Enforce the memory cap on every run** (§ Strategy memory) — you are its only writer, so no one else can do it.
|
|
207
238
|
- **A clean report is valid** — do not invent issues.
|
|
208
239
|
- **Declarative, idempotent.** Re-running baseline mode on a current repo reshapes nothing — a second pass is just the audit plus the module proposal. Converge to the current shape; never replay a version ladder; never reconstruct a past shape.
|
|
240
|
+
- **Brownfield extraction (operator-ruled 2026-08-19).** When baseline mode meets a repo with a real source tree and few or no feature docs, the module *proposal* is not enough — run the extraction pass: produce feature docs for the recognized modules (docs/features/, full frontmatter: `owns:` · `module:` · `keywords:` · `status:` · `last-reviewed:` · `type: feature` · `derivation: "extracted (baseline YYYY-MM-DD, codegraph + source read)"`), plus the architecture, tests, and operations layers. Use the `codegraph` skill where installed — localize through the index before reading; the docs record intent, the index answers facts. **Every extracted feature doc carries a `## History & Ownership` section** — top contributors with recency, churn posture, and bus-factor notes, dated "as of YYYY-MM-DD" (operator ruling: this section is mandatory in extraction output even though doc-lint flags dated narrative in truth docs; the findings are accepted for now, and a future harness version may retire the section in favor of live index pointers). `status:` is evidence-graded: `deprecated` only when history shows the area dead, never by guess. Scale by fan-out (one extraction lane per module, a completeness critic over the result) rather than one long cold read.
|
|
241
|
+
- **Tool tiers.** The SessionStart context names each manifest-declared tool with its tier; calibrate the run to it, never stall on it. **Present-fresh** (`Tool: codegraph 0.2.0`): use it — check `codegraph projects` for staleness, re-run `codegraph index` if HEAD has moved, then localize every question through the index before reading source. **Present-stale** (below the catalog minimum): still use it, but note the version gap and the upgrade command in the run report — a stale contract may lack answer shapes the skill assumes. **Absent** (`Tool: codegraph MISSING`): fall back to the cold method (Glob/Grep/read), state in the run report that the tool was absent and give the install command — degraded output is acceptable, silent degradation is not.
|
|
209
242
|
- **One sentinel.** `docs/structure.md` `harness-version` is the only baseline/version marker.
|
|
210
243
|
- **Modules are labels, layout-blind.** Seed from the code's bounded contexts; never from `pnpm-workspace.yaml`/workspace walls. Propose, don't force; acting is a `module:` tag, never a folder.
|
|
211
244
|
- **You orchestrate docs; the `documentation` skill supplies templates.** It is a library — never expect it to baseline.
|