@nurix/etna 0.20.9 → 0.20.11
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/cloudflare.md +8 -0
- package/data/catalog/rules/database.md +10 -0
- package/data/catalog/rules/frontend.md +10 -0
- package/data/catalog/rules/pnpm.md +9 -0
- package/data/catalog/rules/react-native.md +10 -0
- package/data/catalog/rules/rust-desktop.md +8 -0
- package/data/catalog/rules/temporal.md +9 -0
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/CLAUDE.md +6 -4
- package/data/harnesses/code/agents/polygraph-applier.md +24 -0
- package/data/harnesses/code/agents/polygraph-finder.md +31 -0
- package/data/harnesses/code/agents/polygraph-verifier.md +20 -0
- package/data/harnesses/code/codex/agents/polygraph-applier.toml +21 -0
- package/data/harnesses/code/codex/agents/polygraph-finder.toml +28 -0
- package/data/harnesses/code/codex/agents/polygraph-verifier.toml +17 -0
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/hooks/awareness.mjs +23 -6
- package/data/harnesses/code/hooks/lib/ownership.mjs +51 -19
- package/data/harnesses/code/hooks/lib/plans.mjs +11 -1
- package/data/harnesses/code/rules/cloudflare.md +8 -0
- package/data/harnesses/code/rules/database.md +10 -0
- package/data/harnesses/code/rules/frontend.md +10 -0
- package/data/harnesses/code/rules/pnpm.md +9 -0
- package/data/harnesses/code/skills/documentation/SKILL.md +10 -3
- package/data/harnesses/code/skills/documentation/references/frontmatter.md +21 -7
- package/data/harnesses/code/skills/documentation/references/instruction-style.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/Readme.md +3 -1
- package/data/harnesses/code/skills/documentation/templates/package.md +131 -0
- package/data/harnesses/code/skills/documentation/templates/plan.md +2 -2
- package/data/harnesses/code/skills/documentation/templates/structure.md +9 -3
- package/data/harnesses/code/skills/documentation/templates/system.md +143 -0
- package/data/harnesses/code/skills/polygraph/SKILL.md +71 -0
- package/data/harnesses/code/skills/polygraph/references/dispatch.md +80 -0
- package/data/harnesses/code/skills/polygraph/references/rows.md +57 -0
- package/data/harnesses/code/skills/polygraph/scripts/audit.mjs +113 -0
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/skills/documentation/SKILL.md +10 -3
- package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +21 -7
- package/data/harnesses/delivery/skills/documentation/references/instruction-style.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/Readme.md +3 -1
- package/data/harnesses/delivery/skills/documentation/templates/package.md +131 -0
- package/data/harnesses/delivery/skills/documentation/templates/plan.md +2 -2
- package/data/harnesses/delivery/skills/documentation/templates/structure.md +9 -3
- package/data/harnesses/delivery/skills/documentation/templates/system.md +143 -0
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/documentation/SKILL.md +10 -3
- package/data/harnesses/etna/skills/documentation/references/frontmatter.md +21 -7
- package/data/harnesses/etna/skills/documentation/references/instruction-style.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +3 -1
- package/data/harnesses/etna/skills/documentation/templates/package.md +131 -0
- package/data/harnesses/etna/skills/documentation/templates/plan.md +2 -2
- package/data/harnesses/etna/skills/documentation/templates/structure.md +9 -3
- package/data/harnesses/etna/skills/documentation/templates/system.md +143 -0
- package/package.json +2 -2
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/wrangler.*"
|
|
4
|
+
- "**/.dev.vars*"
|
|
5
|
+
- "**/worker/**"
|
|
6
|
+
- "**/workers/**"
|
|
7
|
+
- "**/src/**/*.ts"
|
|
8
|
+
---
|
|
1
9
|
# Cloudflare Worker
|
|
2
10
|
|
|
3
11
|
Conventions for Cloudflare Workers — `wrangler.jsonc` setup, vars and secrets, and code shape (dispatch, errors, modules, WebSockets). Schema design doctrine is the `schema-design` skill.
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.sql"
|
|
4
|
+
- "**/migrations/**"
|
|
5
|
+
- "**/drizzle/**"
|
|
6
|
+
- "**/drizzle.config.*"
|
|
7
|
+
- "**/schema.ts"
|
|
8
|
+
- "**/schema/**"
|
|
9
|
+
- "**/db/**"
|
|
10
|
+
---
|
|
1
11
|
# Database
|
|
2
12
|
|
|
3
13
|
Naming and the fixed column vocabulary for relational schemas in **every** Nurix project — one concept, one name, everywhere. Written for SQLite / Cloudflare D1 + Drizzle; a Postgres project keeps the same column names and semantics with native types (`uuid`, `timestamptz`, `boolean`). **Design doctrine — what earns a table, state vs. logs, junctions, FK policy, uniqueness traps — is the `schema-design` skill; consult it before any schema change.**
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.tsx"
|
|
4
|
+
- "**/*.jsx"
|
|
5
|
+
- "**/*.ts"
|
|
6
|
+
- "**/*.css"
|
|
7
|
+
- "**/*.html"
|
|
8
|
+
- "**/tailwind.config.*"
|
|
9
|
+
- "**/vite.config.*"
|
|
10
|
+
---
|
|
1
11
|
# Frontend
|
|
2
12
|
|
|
3
13
|
Tailwind v4 + shadcn-based UI conventions for **web** apps — styling, the `@nurix/components` library, the `design.md` token artifact, and design-skill routing — plus the TypeScript conventions every package compiles under.
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/package.json"
|
|
4
|
+
- "**/pnpm-workspace.yaml"
|
|
5
|
+
- "**/pnpm-lock.yaml"
|
|
6
|
+
- "**/.npmrc"
|
|
7
|
+
- "**/tsconfig*.json"
|
|
8
|
+
- "**/turbo.json"
|
|
9
|
+
---
|
|
1
10
|
# pnpm — `@nurix/*` Workspaces: naming, boundaries, and consuming
|
|
2
11
|
|
|
3
12
|
How a `@nurix/*` repo is **named**, **structured**, and how its packages are **consumed** — every repo takes the workspace shape, single-package frontends included. Publishing and CLI design live in the `nustack` skill, not here — no `nustack` skill in the runtime ⇒ ask before crossing the publish boundary.
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/app.json"
|
|
4
|
+
- "**/app.config.*"
|
|
5
|
+
- "**/eas.json"
|
|
6
|
+
- "**/metro.config.*"
|
|
7
|
+
- "**/babel.config.*"
|
|
8
|
+
- "**/app/**"
|
|
9
|
+
- "**/*.tsx"
|
|
10
|
+
---
|
|
1
11
|
# React Native + Expo
|
|
2
12
|
|
|
3
13
|
How mobile apps are built — distilled from a hand-crafted reference app, the gold standard for *style*. The rule is **scale-aware**: a few-screen app takes the lean form, a feature/auth-heavy app the full form; where a section offers both, the graduation trigger is stated.
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.rs"
|
|
4
|
+
- "**/Cargo.toml"
|
|
5
|
+
- "**/Cargo.lock"
|
|
6
|
+
- "**/tauri.conf.json"
|
|
7
|
+
- "**/src-tauri/**"
|
|
8
|
+
---
|
|
1
9
|
# Rust Desktop Apps (Tauri 2)
|
|
2
10
|
|
|
3
11
|
How native desktop apps are built: a **Rust core + web-frontend** shell — the cargo/JS dual workspace, the Rust↔TS contract seam, native-capability handling, and signed distribution. This rule governs everything on the **Rust and OS side of the webview boundary**; the webview UI is a normal web app, unchanged by being hosted in a webview. Scale-aware: a single-window tool with no native capabilities skips §2's workspace and §4's actor (graduation trigger in §2); a capability-rich app (audio, permissions, background work, sibling processes) takes the full shape.
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/workflows/**"
|
|
4
|
+
- "**/activities/**"
|
|
5
|
+
- "**/*.workflow.ts"
|
|
6
|
+
- "**/*.activity.ts"
|
|
7
|
+
- "**/worker.ts"
|
|
8
|
+
- "**/temporal/**"
|
|
9
|
+
---
|
|
1
10
|
# Temporal
|
|
2
11
|
|
|
3
12
|
Workflow and activity conventions for Temporal-backed services — the determinism boundary, file layout, registration, retry defaults, and starting workflows.
|
package/data/harness-index.json
CHANGED
|
@@ -33,14 +33,15 @@ Three peer modes, then a verification gate and a Completion step — enter where
|
|
|
33
33
|
|
|
34
34
|
> Brainstorm entry needs a **basis** — a reference document or a one-shot brain dump; drip-sized asks (a bug fix, one button) are too small and get redirected. A **pillar** is a recorded strategic principle from a past brainstorm: in-progress sessions live under `docs/research/`, harvested ones under `docs/history/brainstorms/`, harvested doctrine under `docs/architecture/`. The LLD is the feature doc itself — `docs/features/{feature}.md`, design intent first, extended in place at Completion.
|
|
35
35
|
|
|
36
|
-
> **Two stops, and only two.** The work between them runs without asking. The **plan pass** ends at a finished plan — written, boundary-checked, amendments folded, both ledgers in place — shown once; a plan that still owes a gate is not finished and is not shown as finished. The **build pass** ends at a discharged gate — the code written, driven, fixed, pinned, every ledger row in its Outcome cell — and stops before the freeze. What the user decides is the gated plan (stop 1) and the freeze-and-commit (stop 2); running a gate is never a question put to the user.
|
|
36
|
+
> **Two stops, and only two.** The work between them runs without asking. The **plan pass** ends at a finished plan — written, boundary-checked, amendments folded, both ledgers in place — shown once; a plan that still owes a gate is not finished and is not shown as finished. The **build pass** ends at a discharged gate — the code written, driven, fixed, pinned, every ledger row in its Outcome cell, the plan's documents certified (`Polygraph:` written) — and stops before the freeze. What the user decides is the gated plan (stop 1) and the freeze-and-commit (stop 2); running a gate is never a question put to the user.
|
|
37
37
|
|
|
38
38
|
> **The lightweight lane:** a change touching one module, **at most ~10 files, none on a contract-heavy surface** (db schema, wire envelopes, auth, events, env/config — the `boundary` skill's list), and under about **three days'** work skips the modes above entirely — the ad-hoc regime of `.claude/rules/testing.md` applies: make the change, drive it, then pin it in the same unit of work if it alters promised behavior, cover it with a changelog row. The predicate is mechanical; when in doubt, the `boundary` diff scan is the tiebreak, not self-assessment.
|
|
39
39
|
|
|
40
40
|
**On Plan Completion:**
|
|
41
41
|
|
|
42
|
-
1. **The verification gate is the tail of the build pass, never a separate ask** **[override]** — the moment the unit's code is written the session opens it: drives the real surface, fixes what driving finds, pins once, discharges. What is the user's call is the freeze and the commit — "commit" / "ship it" triggers step
|
|
43
|
-
2. **
|
|
42
|
+
1. **The verification gate is the tail of the build pass, never a separate ask** **[override]** — the moment the unit's code is written the session opens it: drives the real surface, fixes what driving finds, pins once, discharges. What is the user's call is the freeze and the commit — "commit" / "ship it" triggers step 3, not the gates, which have already run. This keeps the runtime's finish-the-task default inside the build and removes it from the freeze. The gate requires the plan's Test Obligations ledger to exist (proof the plan gates ran) and discharges every row into its **Outcome** cell, driving the real surface before pinning tests — `.claude/rules/testing.md`. A plan with outstanding obligations stays open, whatever the code does.
|
|
43
|
+
2. **Certify the documents — the polygraph gate, still inside the build pass.** Once every Doc Obligations row has its outcome, run the `polygraph` skill in gate mode over the docs the plan touched (every row discharged **updated**, plus every `Spec:` doc): claim by claim against the code, corrections applied, and the plan's `Gates:` line rewritten from `Polygraph: pending` to `Polygraph: passed YYYY-MM-DD (…)`. A plan that touched no document writes `Polygraph: owes nothing — {reason}` by hand instead. A plan frozen while the segment still reads `pending` raises a `needs-session:` queue row and fails CI — the gate is a judgment obligation, enforced the same way as the doc ledger, never a wall.
|
|
44
|
+
3. **Discharge, then freeze — in one edit.** Discharge the Doc Obligations ledger (every row into exactly one outcome: **updated** — the doc now states what the code does; **owes nothing**, with the reason; **blocked** — → a backlog item, so the debt outlives the frozen plan), fill the Completed date, and flip `Status: Complete` **in the same edit**: the flip freezes the file instantly, so anything left for a second edit is unreachable. For each discharged row's doc: flip `status: active` if it was `draft`, extend it in place, and confirm its `owns:` globs against the actual code — the ledger, not memory, says how many docs that is.
|
|
44
45
|
3. Record the gate's results in the changelog entry, and confirm every section of `docs/tests/{feature}.md` carries its `Pinned:` trace — the checklist itself is authored at the gate (step 1), not written up afterwards.
|
|
45
46
|
|
|
46
47
|
**On Brainstorm Close** (when a session ends, not when a plan completes): close + harvest is the skill's `stop` procedure — converge, a user-approved harvest manifest, apply, then a uselessness test; conclusions disperse by scope (architecture docs, decision records, backlog), the record moves from `docs/research/{topic}-brainstorm/` to `docs/history/brainstorms/{topic}/`, and the user decides whether the moved folder is deleted or committed.
|
|
@@ -78,6 +79,7 @@ Boundaries are **diagnosed, not documented** — the `boundary` skill derives th
|
|
|
78
79
|
|
|
79
80
|
### Documentation — close the loop
|
|
80
81
|
|
|
82
|
+
- **When a document's truth is in question — "is this doc still right?", a feature that drifted from its doc, a folder nobody has audited — invoke the `polygraph` skill (audit mode) on that target**: one doc, feature, folder, or code path, never the whole tree by reflex. Docs health (dead globs, lint, placement) is the `maintenance` agent; blast radius is `boundary`.
|
|
81
83
|
- **Invoke the `documentation` skill when creating a doc, or when an edit changes a doc's structure or claims** — apply its templates and `references/style-guide.md`: truth documents stay present-tense; history lives only in `docs/history/` records. Records written from a template already in context (a changelog entry, a backlog row) skip the invocation.
|
|
82
84
|
- **[override]** Keep `docs/` updated when code changes affect documented behavior. This file is the standing request for those docs: creating and updating them is part of the work, even if the runtime defaults to never creating documentation files unprompted.
|
|
83
85
|
- After plan completion **[override]**: write the `docs/history/changelog/` entry as a **draft** immediately, while the context is fresh — no confirmation needed — and append its `docs/history/CHANGELOG.md` row; the commit fills what the draft left open and freezes it. Non-plan work needs no separate prompt: every non-exempt commit carries its own entry (the `changelog` rule).
|
|
@@ -130,7 +132,7 @@ The single policy for sub-agent dispatch — when to fan out, at what tier, and
|
|
|
130
132
|
- **A dispatch is scoped to minutes and read before anything else starts.** One file, one sweep — never a build. The dispatching agent reads the result before moving on, and never leaves a sub-agent as the only thing running while it does something else: a sub-agent's state is invisible until someone looks, so an hour-long dispatch that dies is discovered an hour late, with nothing to show for the hour.
|
|
131
133
|
- **Small or judgment work stays inline.** A task finishable in a handful of tool calls is faster done than briefed. **Never delegate judgment by reflex** — a second opinion is for a high-stakes call or an explicit request.
|
|
132
134
|
- **A delegated result is a report, not verifiable ground truth** — verify load-bearing results directly.
|
|
133
|
-
- **Tiers**: mechanical fan-out → the cheapest capable tier; judgment-heavy delegation → the session's standard tier; the strongest tier **only** on explicit user request. The session itself defaults to the standard tier, entering the strongest only by explicit user switch. Model names live in Runtime Notes.
|
|
135
|
+
- **Tiers**: mechanical fan-out → the cheapest capable tier; judgment-heavy delegation → the session's standard tier; the strongest tier **only** on explicit user request. The session itself defaults to the standard tier, entering the strongest only by explicit user switch. Model names live in Runtime Notes. **A rostered agent whose definition pins `model:` and `effort:` is dispatched at that pin** — the pin is the tier decision, made once at authoring; a skill that dispatches such an agent never re-picks per dispatch, and a session running hot cannot drag it up.
|
|
134
136
|
- **An explicit user directive about tier, cost, or delegation overrides this section.**
|
|
135
137
|
|
|
136
138
|
---
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: polygraph-applier
|
|
3
|
+
description: Applies one packet of verified polygraph rows to exactly the documents the packet names, re-checking every citation first. Dispatched by the polygraph skill only; never on its own.
|
|
4
|
+
tools: Read, Edit, Write, Grep, Glob, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
effort: medium
|
|
7
|
+
maxTurns: 15
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Polygraph applier
|
|
11
|
+
|
|
12
|
+
You are an APPLIER in a truth audit. You receive the path of a **packet** and the path of its **context pack**. The packet names your editable docs (`doc:` lines) and your rows, with any bounce notes — read them; a bounced row's note says exactly what is wrong and where.
|
|
13
|
+
|
|
14
|
+
1. Read the pack, then the packet. Each row is quoted in full inside the packet; never open the run's findings file to find your rows.
|
|
15
|
+
2. **Re-check each row's citations against today's tree before editing.** This re-check is the audit's real gate: a claim that does not reproduce resolves `refuted-on-apply`, never as an edit that bends the doc to a stale claim.
|
|
16
|
+
3. Edit **only** the docs your packet names. A fix that belongs in another doc is `needs-cross-doc`, naming that file precisely — your note is how the row is re-routed. Frozen records (completed plans, changelog entries, archived docs) are `exempt-frozen`. A subject awaiting an owner ruling is `blocked-on-question`, no edit.
|
|
17
|
+
4. Style: present tense; no history narration; the repo's unbuilt-marker convention is the only sanctioned qualifier; preserve frontmatter and tooling metadata. Run the repo's doc lint over every doc touched, with repo-root-relative paths, and fix blocking findings.
|
|
18
|
+
5. Write `apply-{doc}.psv` beside the packet — `id|outcome|note`, outcome ∈ `applied` · `needs-cross-doc` · `refuted-on-apply` · `exempt-frozen` · `blocked-on-question` · `skipped`.
|
|
19
|
+
|
|
20
|
+
**The audit never edits code.** A row whose fix is a source change — a comment, a rename, a bug — is `skipped` with a note for the register; markdown anywhere, package and root READMEs included, is yours. Rulings in the pack outrank every recorded decision.
|
|
21
|
+
|
|
22
|
+
Tree state: re-check every citation against today's tree before acting. READ-ONLY outside the packet's docs and your outcome file. Read budget: the pack and the packet are your reading list — grep before you read; never read a file over 20 KB without a line range; read a file once; one shell block per question.
|
|
23
|
+
|
|
24
|
+
Return **at most 120 words**: counts per outcome, docs touched, the lint result, and anything for the register.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: polygraph-finder
|
|
3
|
+
description: Sweeps one document against the code it describes for the polygraph skill — one row per claim, cited, into a partial file. Dispatched by the skill only; never on its own.
|
|
4
|
+
tools: Read, Grep, Glob, Bash, Write
|
|
5
|
+
model: sonnet
|
|
6
|
+
effort: medium
|
|
7
|
+
maxTurns: 25
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Polygraph finder
|
|
11
|
+
|
|
12
|
+
You are a FINDER in a truth audit. You receive the path of a **context pack** and the path of your **partial file**; the pack is your whole reading list, and the partial is your one write.
|
|
13
|
+
|
|
14
|
+
Docs state the target design; code is presumed correct unless a standing decision **names** it for retirement — the pack's precedent section decides, and a match means the precedent names your subject (quote the naming words into evidence; a resembling thing is not a match; unsure → QUESTION).
|
|
15
|
+
|
|
16
|
+
1. Read the pack in full. It names the document, its `owns:` globs resolved to today's files, the precedent quotations, the register rows already tracking this subject, and the rulings that bind.
|
|
17
|
+
2. Read the document once, whole — it is the one file you always read in full. Then check each claim it makes against the code: grep for the mechanism, open the passage with a line range, cite `file:line`.
|
|
18
|
+
3. Write one row per claim to the partial file, **no header**, exactly ten pipe-separated fields: `id|subject|claim|evidence|counter_evidence|verdict|confidence|depends_on|status|resolution`. A literal `|` becomes `¦`, a newline `; `. Verdicts: `COHERENT` (emit at the unit you audited, so coverage is countable) · `MISDESCRIBED` · `UNDOCUMENTED` · `UNBUILT` · `RESIDUE` · `CONTESTED` · `QUESTION`. `id` is `{doc-stem}-NNN`; `status` is `open`; `resolution` is empty; `confidence` is an honest 0–1.
|
|
19
|
+
4. Sweep the other direction once: code inside the `owns:` globs that no claim in the document covers is an `UNDOCUMENTED` row — after the precedent test.
|
|
20
|
+
|
|
21
|
+
Rules that bind every row:
|
|
22
|
+
|
|
23
|
+
- **No citation, no row** — `file:line`, or the exact command and its result. Prove or refute; never confirm by plausibility.
|
|
24
|
+
- **A negative claim about a long document quotes the passage you read** — the sentence you believe wrong, or the section where the missing statement should live. Unsupported negative claims are the dominant false-positive class.
|
|
25
|
+
- **An already-tracked finding is skipped** unless your evidence adds something new; cite the register row as `already-registered: path:line` in `counter_evidence`.
|
|
26
|
+
- **A document that should not exist** — its subject gone, its authority absorbed by a sibling — is one `QUESTION` row proposing deletion or merge, with the inbound-reference count.
|
|
27
|
+
- **Rulings in the pack outrank every recorded decision.**
|
|
28
|
+
|
|
29
|
+
Tree state: re-check every citation against today's tree before writing it. **READ-ONLY everywhere except your partial file.** Read budget: the pack is your reading list — read nothing outside it without naming in your return why the pack was insufficient. Grep before you read; never read a file over 20 KB without a line range; read a file once. Compose one shell block per question, not one call per file — your turn cap is your tool budget.
|
|
30
|
+
|
|
31
|
+
Return **at most 120 words**: the partial's path, the row count, up to three findings, and limitations. Findings go in the file, never in the return.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: polygraph-verifier
|
|
3
|
+
description: Blind re-derivation of polygraph claims the primary doubts — claim and subject only, no prior evidence — into a verify file. Dispatched by the polygraph skill only; never on its own.
|
|
4
|
+
tools: Read, Grep, Glob, Bash, Write
|
|
5
|
+
model: sonnet
|
|
6
|
+
effort: low
|
|
7
|
+
maxTurns: 12
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Polygraph verifier
|
|
11
|
+
|
|
12
|
+
You are a VERIFIER in a truth audit. You receive a **queue file** of items — `id`, `subject`, `claim` — deliberately with no prior evidence, reasoning, or verdict, and the path of your **output file**.
|
|
13
|
+
|
|
14
|
+
1. Read the queue. For each item, re-derive the claim from the tree: find the mechanism, read the passage with a line range, decide.
|
|
15
|
+
2. Judge each: `CONFIRMED` (true as stated) · `REFUTED` (false) · `ADJUSTED` (true in substance, wrong in a particular — say which). Cite exactly, `file:line`.
|
|
16
|
+
3. Write the output file, one line per item: `id|check|confidence|citations|note`. A literal `|` becomes `¦`.
|
|
17
|
+
|
|
18
|
+
**A wrong confirmation is worse than an honest REFUTED.** Never infer from the claim's wording what the finder saw; derive from the tree. Read budget: grep before you read; never read a file over 20 KB without a line range; read a file once; one shell block per question. READ-ONLY everywhere except your output file.
|
|
19
|
+
|
|
20
|
+
Return **at most 120 words**: the file path and counts per check.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# dropped Claude tools: Read, Edit, Write, Grep, Glob, Bash
|
|
2
|
+
name = "polygraph-applier"
|
|
3
|
+
description = "Applies one packet of verified polygraph rows to exactly the documents the packet names, re-checking every citation first. Dispatched by the polygraph skill only; never on its own."
|
|
4
|
+
developer_instructions = '''
|
|
5
|
+
|
|
6
|
+
# Polygraph applier
|
|
7
|
+
|
|
8
|
+
You are an APPLIER in a truth audit. You receive the path of a **packet** and the path of its **context pack**. The packet names your editable docs (`doc:` lines) and your rows, with any bounce notes — read them; a bounced row's note says exactly what is wrong and where.
|
|
9
|
+
|
|
10
|
+
1. Read the pack, then the packet. Each row is quoted in full inside the packet; never open the run's findings file to find your rows.
|
|
11
|
+
2. **Re-check each row's citations against today's tree before editing.** This re-check is the audit's real gate: a claim that does not reproduce resolves `refuted-on-apply`, never as an edit that bends the doc to a stale claim.
|
|
12
|
+
3. Edit **only** the docs your packet names. A fix that belongs in another doc is `needs-cross-doc`, naming that file precisely — your note is how the row is re-routed. Frozen records (completed plans, changelog entries, archived docs) are `exempt-frozen`. A subject awaiting an owner ruling is `blocked-on-question`, no edit.
|
|
13
|
+
4. Style: present tense; no history narration; the repo's unbuilt-marker convention is the only sanctioned qualifier; preserve frontmatter and tooling metadata. Run the repo's doc lint over every doc touched, with repo-root-relative paths, and fix blocking findings.
|
|
14
|
+
5. Write `apply-{doc}.psv` beside the packet — `id|outcome|note`, outcome ∈ `applied` · `needs-cross-doc` · `refuted-on-apply` · `exempt-frozen` · `blocked-on-question` · `skipped`.
|
|
15
|
+
|
|
16
|
+
**The audit never edits code.** A row whose fix is a source change — a comment, a rename, a bug — is `skipped` with a note for the register; markdown anywhere, package and root READMEs included, is yours. Rulings in the pack outrank every recorded decision.
|
|
17
|
+
|
|
18
|
+
Tree state: re-check every citation against today's tree before acting. READ-ONLY outside the packet's docs and your outcome file. Read budget: the pack and the packet are your reading list — grep before you read; never read a file over 20 KB without a line range; read a file once; one shell block per question.
|
|
19
|
+
|
|
20
|
+
Return **at most 120 words**: counts per outcome, docs touched, the lint result, and anything for the register.
|
|
21
|
+
'''
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# dropped Claude tools: Read, Grep, Glob, Bash, Write
|
|
2
|
+
name = "polygraph-finder"
|
|
3
|
+
description = "Sweeps one document against the code it describes for the polygraph skill — one row per claim, cited, into a partial file. Dispatched by the skill only; never on its own."
|
|
4
|
+
developer_instructions = '''
|
|
5
|
+
|
|
6
|
+
# Polygraph finder
|
|
7
|
+
|
|
8
|
+
You are a FINDER in a truth audit. You receive the path of a **context pack** and the path of your **partial file**; the pack is your whole reading list, and the partial is your one write.
|
|
9
|
+
|
|
10
|
+
Docs state the target design; code is presumed correct unless a standing decision **names** it for retirement — the pack's precedent section decides, and a match means the precedent names your subject (quote the naming words into evidence; a resembling thing is not a match; unsure → QUESTION).
|
|
11
|
+
|
|
12
|
+
1. Read the pack in full. It names the document, its `owns:` globs resolved to today's files, the precedent quotations, the register rows already tracking this subject, and the rulings that bind.
|
|
13
|
+
2. Read the document once, whole — it is the one file you always read in full. Then check each claim it makes against the code: grep for the mechanism, open the passage with a line range, cite `file:line`.
|
|
14
|
+
3. Write one row per claim to the partial file, **no header**, exactly ten pipe-separated fields: `id|subject|claim|evidence|counter_evidence|verdict|confidence|depends_on|status|resolution`. A literal `|` becomes `¦`, a newline `; `. Verdicts: `COHERENT` (emit at the unit you audited, so coverage is countable) · `MISDESCRIBED` · `UNDOCUMENTED` · `UNBUILT` · `RESIDUE` · `CONTESTED` · `QUESTION`. `id` is `{doc-stem}-NNN`; `status` is `open`; `resolution` is empty; `confidence` is an honest 0–1.
|
|
15
|
+
4. Sweep the other direction once: code inside the `owns:` globs that no claim in the document covers is an `UNDOCUMENTED` row — after the precedent test.
|
|
16
|
+
|
|
17
|
+
Rules that bind every row:
|
|
18
|
+
|
|
19
|
+
- **No citation, no row** — `file:line`, or the exact command and its result. Prove or refute; never confirm by plausibility.
|
|
20
|
+
- **A negative claim about a long document quotes the passage you read** — the sentence you believe wrong, or the section where the missing statement should live. Unsupported negative claims are the dominant false-positive class.
|
|
21
|
+
- **An already-tracked finding is skipped** unless your evidence adds something new; cite the register row as `already-registered: path:line` in `counter_evidence`.
|
|
22
|
+
- **A document that should not exist** — its subject gone, its authority absorbed by a sibling — is one `QUESTION` row proposing deletion or merge, with the inbound-reference count.
|
|
23
|
+
- **Rulings in the pack outrank every recorded decision.**
|
|
24
|
+
|
|
25
|
+
Tree state: re-check every citation against today's tree before writing it. **READ-ONLY everywhere except your partial file.** Read budget: the pack is your reading list — read nothing outside it without naming in your return why the pack was insufficient. Grep before you read; never read a file over 20 KB without a line range; read a file once. Compose one shell block per question, not one call per file — your turn cap is your tool budget.
|
|
26
|
+
|
|
27
|
+
Return **at most 120 words**: the partial's path, the row count, up to three findings, and limitations. Findings go in the file, never in the return.
|
|
28
|
+
'''
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# dropped Claude tools: Read, Grep, Glob, Bash, Write
|
|
2
|
+
name = "polygraph-verifier"
|
|
3
|
+
description = "Blind re-derivation of polygraph claims the primary doubts — claim and subject only, no prior evidence — into a verify file. Dispatched by the polygraph skill only; never on its own."
|
|
4
|
+
developer_instructions = '''
|
|
5
|
+
|
|
6
|
+
# Polygraph verifier
|
|
7
|
+
|
|
8
|
+
You are a VERIFIER in a truth audit. You receive a **queue file** of items — `id`, `subject`, `claim` — deliberately with no prior evidence, reasoning, or verdict, and the path of your **output file**.
|
|
9
|
+
|
|
10
|
+
1. Read the queue. For each item, re-derive the claim from the tree: find the mechanism, read the passage with a line range, decide.
|
|
11
|
+
2. Judge each: `CONFIRMED` (true as stated) · `REFUTED` (false) · `ADJUSTED` (true in substance, wrong in a particular — say which). Cite exactly, `file:line`.
|
|
12
|
+
3. Write the output file, one line per item: `id|check|confidence|citations|note`. A literal `|` becomes `¦`.
|
|
13
|
+
|
|
14
|
+
**A wrong confirmation is worse than an honest REFUTED.** Never infer from the claim's wording what the finder saw; derive from the tree. Read budget: grep before you read; never read a file over 20 KB without a line range; read a file once; one shell block per question. READ-ONLY everywhere except your output file.
|
|
15
|
+
|
|
16
|
+
Return **at most 120 words**: the file path and counts per check.
|
|
17
|
+
'''
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "code",
|
|
3
3
|
"name": "code",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.15.1",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
}
|
|
26
26
|
],
|
|
27
27
|
"docsRoot": "docs",
|
|
28
|
-
"digest": "
|
|
28
|
+
"digest": "b00642c1a47d"
|
|
29
29
|
}
|
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
resolveProjectDir,
|
|
14
14
|
buildIndex,
|
|
15
15
|
buildEdgeIndex,
|
|
16
|
-
|
|
16
|
+
scanOwningDocs,
|
|
17
|
+
isOwningDocPath,
|
|
17
18
|
owningDocs,
|
|
18
19
|
owningDoc,
|
|
19
20
|
listRepoFiles,
|
|
@@ -35,7 +36,7 @@ import {
|
|
|
35
36
|
} from "./lib/state.mjs";
|
|
36
37
|
import { readSafe, relIfInside, today, SOURCE_EXT, commandRoot, docsRoot } from "./lib/repo.mjs";
|
|
37
38
|
import { sweepResolvedRows, docLint, hasHeaderComment } from "./lib/sweep.mjs";
|
|
38
|
-
import { listPlans } from "./lib/plans.mjs";
|
|
39
|
+
import { listPlans, polygraphVerdict } from "./lib/plans.mjs";
|
|
39
40
|
import { frozenStatusAtHead } from "./lib/frozen.mjs";
|
|
40
41
|
import { undischargedDocRows } from "./lib/ledger.mjs";
|
|
41
42
|
import { approvedPackages, rejectedPackages } from "./lib/approved.mjs";
|
|
@@ -164,7 +165,7 @@ function driftBanner(projectDir) {
|
|
|
164
165
|
// Re-verify before counting: the walls must act on conditions that are live NOW, not on rows
|
|
165
166
|
// that happen to still be written down.
|
|
166
167
|
const swept = sweepResolvedRows(projectDir, repoFiles);
|
|
167
|
-
const docs =
|
|
168
|
+
const docs = scanOwningDocs(projectDir);
|
|
168
169
|
const dead = deadGlobs(projectDir, repoFiles, docs);
|
|
169
170
|
const queueRows = readQueue(projectDir);
|
|
170
171
|
const counts = countRows(queueRows);
|
|
@@ -311,7 +312,8 @@ function detectorForFile(projectDir, rel, meta, rows) {
|
|
|
311
312
|
if (rel.startsWith(`${root}/history/plans/`)) {
|
|
312
313
|
const planText = readSafe(path.join(projectDir, rel));
|
|
313
314
|
resolveRows(projectDir, (row) => row.kind === "needs-session" && row.body.includes(rel));
|
|
314
|
-
|
|
315
|
+
const complete = /^\*\*Status:\*\*\s*Complete\b/m.test(planText);
|
|
316
|
+
if (complete && undischargedDocRows(planText).length > 0) {
|
|
315
317
|
rows.push(
|
|
316
318
|
formatNeedsSessionRow({
|
|
317
319
|
date: meta.date,
|
|
@@ -320,6 +322,19 @@ function detectorForFile(projectDir, rel, meta, rows) {
|
|
|
320
322
|
})
|
|
321
323
|
);
|
|
322
324
|
}
|
|
325
|
+
// Whether the plan's documents are TRUE is the polygraph skill's judgment, not a hook's — so
|
|
326
|
+
// the gate's trace (the `Polygraph:` segment of the Gates line) is raised the same way the doc
|
|
327
|
+
// ledger is: a `needs-session:` row while it reads `pending` on a Complete plan. A plan with no
|
|
328
|
+
// segment predates the gate or runs under a harness that ships no polygraph — out of contract.
|
|
329
|
+
if (complete && polygraphVerdict(planText) === "pending") {
|
|
330
|
+
rows.push(
|
|
331
|
+
formatNeedsSessionRow({
|
|
332
|
+
date: meta.date,
|
|
333
|
+
description: `${rel} reached Status: Complete with its documents uncertified — run the polygraph skill (gate mode) and write its Polygraph: segment`,
|
|
334
|
+
refs: rel,
|
|
335
|
+
})
|
|
336
|
+
);
|
|
337
|
+
}
|
|
323
338
|
}
|
|
324
339
|
if (rel.startsWith(`${root}/`)) {
|
|
325
340
|
const findings = docLint(projectDir, rel);
|
|
@@ -534,8 +549,10 @@ function postToolUse(projectDir, payload) {
|
|
|
534
549
|
const rel = relIfInside(projectDir, input.file_path || input.notebook_path);
|
|
535
550
|
if (rel) detectorForFile(projectDir, rel, meta, rows);
|
|
536
551
|
const root = docsRoot(projectDir);
|
|
537
|
-
// Editing
|
|
538
|
-
|
|
552
|
+
// Editing an owning doc's frontmatter can orphan globs the same way a rename can. The test is
|
|
553
|
+
// the scan's own predicate, not a `features/` prefix — a package or system doc owns code too,
|
|
554
|
+
// and a folder guess here silently stops checking the moment a genre lands somewhere else.
|
|
555
|
+
if (rel && isOwningDocPath(projectDir, rel)) detectRenames(projectDir, "git mv", meta, rows);
|
|
539
556
|
// A manifest or history file written by a non-Bash tool is the same evasion surface.
|
|
540
557
|
if (rel && (path.posix.basename(rel) === "package.json" || rel.startsWith(`${root}/history/`))) {
|
|
541
558
|
detectOutcomes(projectDir, meta, rows);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// harness/code/hooks/lib/ownership.mjs — frontmatter scan + owns:-glob resolution, shared by the
|
|
2
2
|
// hook entrypoints (awareness, compliance) and the CI checks so gates and pipeline cannot disagree.
|
|
3
|
-
// Everything here is read-only over docs
|
|
3
|
+
// Everything here is read-only over the docs tree and the source tree; no network, no deps.
|
|
4
4
|
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
@@ -81,7 +81,7 @@ export function field(block, key) {
|
|
|
81
81
|
// A bare key may continue as an indented inline array on the next line, or a `- item` block.
|
|
82
82
|
// The array may span several lines, so join through the closing bracket before parsing —
|
|
83
83
|
// reading only the first line yields a bare "[", which parses to [] and makes the whole doc
|
|
84
|
-
// look like it owns nothing. `
|
|
84
|
+
// look like it owns nothing. `scanOwningDocs` skips such docs, so the failure is invisible:
|
|
85
85
|
// the doc drops out of the ownership index and out of every dead-glob count.
|
|
86
86
|
const next = (lines[i + 1] || "").trim();
|
|
87
87
|
if (next.startsWith("[")) {
|
|
@@ -114,13 +114,38 @@ export function asList(v) {
|
|
|
114
114
|
return (Array.isArray(v) ? v : [v]).map((s) => String(s).trim()).filter(Boolean);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
//
|
|
117
|
+
// Folders under the docs root that never hold an owning document, whatever their frontmatter says.
|
|
118
|
+
// `history/` is frozen records — a completed plan's frontmatter is history, not a live claim on code;
|
|
119
|
+
// `research/` is working material that has not been folded into the truth tree yet.
|
|
120
|
+
const NON_OWNING_DIRS = new Set(["history", "research"]);
|
|
121
|
+
|
|
122
|
+
// True when a repo-relative docs path is one the ownership scan reads — the same predicate
|
|
123
|
+
// `scanOwningDocs` filters by, exported so callers cannot drift from it. A hook asking "could
|
|
124
|
+
// editing this file have orphaned a glob?" must ask exactly this question, not a folder guess.
|
|
125
|
+
export function isOwningDocPath(projectDir, relPath) {
|
|
126
|
+
const rel = String(relPath).split(path.sep).join("/");
|
|
127
|
+
const root = docsRoot(projectDir);
|
|
128
|
+
if (!rel.startsWith(`${root}/`) || !rel.endsWith(".md") || rel.includes(".bkp.")) return false;
|
|
129
|
+
return !NON_OWNING_DIRS.has(rel.slice(root.length + 1).split("/")[0]);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Scan the docs tree and return one entry per document that declares owns:.
|
|
118
133
|
// Shape: { doc (repo-relative, /-separated), owns[], module, keywords[], status }.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
//
|
|
135
|
+
// OWNERSHIP IS A FRONTMATTER FACT, NOT A FOLDER FACT. This scanned `docs/features/` alone, which
|
|
136
|
+
// silently un-owned every path claimed by a genre living elsewhere — a `type: package` doc under
|
|
137
|
+
// `docs/architecture/packages/`, a `type: system` doc under `docs/architecture/systems/`. Their
|
|
138
|
+
// globs parsed fine and matched real files; nothing failed, the paths simply read as unowned in
|
|
139
|
+
// the generated index and in the drift report, which is the one failure mode neither surfaces.
|
|
140
|
+
export function scanOwningDocs(projectDir) {
|
|
141
|
+
const root = path.join(projectDir, docsRoot(projectDir));
|
|
142
|
+
// *.bkp.* files are install-time backups awaiting the maintenance fold — a backed-up doc
|
|
143
|
+
// still carries owns: frontmatter, and scanning it would double-own every path it names.
|
|
144
|
+
const files = listMarkdown(root).filter((f) => {
|
|
145
|
+
if (f.includes(".bkp.")) return false;
|
|
146
|
+
const rel = path.relative(root, f).split(path.sep);
|
|
147
|
+
return !NON_OWNING_DIRS.has(rel[0]);
|
|
148
|
+
});
|
|
124
149
|
const docs = [];
|
|
125
150
|
for (const file of files) {
|
|
126
151
|
let text;
|
|
@@ -146,10 +171,17 @@ export function scanFeatureDocs(projectDir) {
|
|
|
146
171
|
|
|
147
172
|
// Build the ownership index text from every feature doc's frontmatter.
|
|
148
173
|
export function buildIndex(projectDir) {
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
174
|
+
// Every document eligible to own, counted independently of scanOwningDocs — which drops a doc
|
|
175
|
+
// carrying no owns:, making a parse failure and a doc that owns nothing look alike. Genres that
|
|
176
|
+
// never own (checklists, operations notes) sit in this total, so `skipped` is a ceiling on the
|
|
177
|
+
// parse failures rather than a count of them; it is a smell to look at, not a gate.
|
|
178
|
+
const root = path.join(projectDir, docsRoot(projectDir));
|
|
179
|
+
const total = listMarkdown(root).filter((f) => {
|
|
180
|
+
if (f.includes(".bkp.")) return false;
|
|
181
|
+
const rel = path.relative(root, f).split(path.sep);
|
|
182
|
+
return !NON_OWNING_DIRS.has(rel[0]);
|
|
183
|
+
}).length;
|
|
184
|
+
const docs = scanOwningDocs(projectDir);
|
|
153
185
|
const lines = [];
|
|
154
186
|
let deprecated = 0;
|
|
155
187
|
for (const entry of docs) {
|
|
@@ -171,7 +203,7 @@ export function buildIndex(projectDir) {
|
|
|
171
203
|
if (lines.length === 0) return "";
|
|
172
204
|
lines.sort();
|
|
173
205
|
const header =
|
|
174
|
-
"# Ownership index (generated) — owning
|
|
206
|
+
"# Ownership index (generated) — owning doc · module · status, then the code regions it governs.\n" +
|
|
175
207
|
"# Match the file or topic you're about to touch, then read the owning doc before changing it.\n";
|
|
176
208
|
const indexed = docs.length - deprecated;
|
|
177
209
|
const skipped = total - docs.length;
|
|
@@ -179,7 +211,7 @@ export function buildIndex(projectDir) {
|
|
|
179
211
|
if (deprecated > 0) notes.push(`${deprecated} deprecated`);
|
|
180
212
|
if (skipped > 0) notes.push(`${skipped} with no owns:`);
|
|
181
213
|
const footer = notes.length
|
|
182
|
-
? `\n# (${indexed}/${total}
|
|
214
|
+
? `\n# (${indexed}/${total} doc(s) indexed - ${notes.join(", ")})`
|
|
183
215
|
: "";
|
|
184
216
|
return header + lines.join("\n") + footer;
|
|
185
217
|
}
|
|
@@ -270,11 +302,11 @@ export function globToRegExp(glob) {
|
|
|
270
302
|
return new RegExp(`^${re}$`);
|
|
271
303
|
}
|
|
272
304
|
|
|
273
|
-
//
|
|
305
|
+
// Documents owning the given repo-relative path (deprecated docs excluded).
|
|
274
306
|
export function owningDocs(projectDir, relPath, docs) {
|
|
275
307
|
const rel = relPath.split(path.sep).join("/");
|
|
276
308
|
const out = [];
|
|
277
|
-
for (const entry of docs ||
|
|
309
|
+
for (const entry of docs || scanOwningDocs(projectDir)) {
|
|
278
310
|
if (entry.status === "deprecated") continue;
|
|
279
311
|
if (entry.owns.some((g) => globToRegExp(g).test(rel))) out.push(entry);
|
|
280
312
|
}
|
|
@@ -305,12 +337,12 @@ export function listRepoFiles(projectDir) {
|
|
|
305
337
|
return out;
|
|
306
338
|
}
|
|
307
339
|
|
|
308
|
-
// Globs across all
|
|
340
|
+
// Globs across all owning docs that match zero files on disk.
|
|
309
341
|
// Returns [{ glob, doc }]; pass precomputed file/doc lists to amortize the walks.
|
|
310
342
|
export function deadGlobs(projectDir, repoFiles, docs) {
|
|
311
343
|
const files = repoFiles || listRepoFiles(projectDir);
|
|
312
344
|
const dead = [];
|
|
313
|
-
for (const entry of docs ||
|
|
345
|
+
for (const entry of docs || scanOwningDocs(projectDir)) {
|
|
314
346
|
if (entry.status === "deprecated") continue;
|
|
315
347
|
for (const glob of entry.owns) {
|
|
316
348
|
const re = globToRegExp(glob);
|
|
@@ -376,7 +408,7 @@ export function owningDoc(projectDir, relPath, docs) {
|
|
|
376
408
|
export function overlaps(projectDir, repoFiles, docs) {
|
|
377
409
|
const files = repoFiles || listRepoFiles(projectDir);
|
|
378
410
|
const owners = [];
|
|
379
|
-
for (const entry of docs ||
|
|
411
|
+
for (const entry of docs || scanOwningDocs(projectDir)) {
|
|
380
412
|
if (entry.status === "deprecated") continue;
|
|
381
413
|
for (const glob of entry.owns) {
|
|
382
414
|
const s = specificity(glob);
|
|
@@ -28,7 +28,16 @@ export function inPlanScope(rel, scope) {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// The plan's Polygraph gate, read from its Gates line: 'pending' · 'passed' · 'owes nothing' ·
|
|
32
|
+
// null when the line carries no segment (a plan that predates the gate, or a repo whose harness
|
|
33
|
+
// ships no polygraph — both out of contract, both pass).
|
|
34
|
+
export function polygraphVerdict(text) {
|
|
35
|
+
const gates = (/^\*\*Gates:\*\*\s*(.+)$/m.exec(text || "") || [])[1] || "";
|
|
36
|
+
const m = /Polygraph:\s*(pending|passed|owes nothing)\b/i.exec(gates);
|
|
37
|
+
return m ? m[1].toLowerCase() : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Every plan in the folder, parsed once: { file, status, gates, polygraph, scope, hasLedger,
|
|
32
41
|
// hasDocLedger, ledgerRows }. `gates` is null on legacy plans (pre-Gates format).
|
|
33
42
|
export function listPlans(projectDir) {
|
|
34
43
|
const dir = path.join(projectDir, docsRoot(projectDir), "history", "plans");
|
|
@@ -47,6 +56,7 @@ export function listPlans(projectDir) {
|
|
|
47
56
|
file,
|
|
48
57
|
status,
|
|
49
58
|
gates: (/^\*\*Gates:\*\*\s*(.+)$/m.exec(text) || [])[1]?.trim() || null,
|
|
59
|
+
polygraph: polygraphVerdict(text),
|
|
50
60
|
scope: planScopePaths(text),
|
|
51
61
|
hasLedger: /^##\s+Test Obligations\b/m.test(text),
|
|
52
62
|
hasDocLedger: /^##\s+Doc Obligations\b/m.test(text),
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/wrangler.*"
|
|
4
|
+
- "**/.dev.vars*"
|
|
5
|
+
- "**/worker/**"
|
|
6
|
+
- "**/workers/**"
|
|
7
|
+
- "**/src/**/*.ts"
|
|
8
|
+
---
|
|
1
9
|
# Cloudflare Worker
|
|
2
10
|
|
|
3
11
|
Conventions for Cloudflare Workers — `wrangler.jsonc` setup, vars and secrets, and code shape (dispatch, errors, modules, WebSockets). Schema design doctrine is the `schema-design` skill.
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.sql"
|
|
4
|
+
- "**/migrations/**"
|
|
5
|
+
- "**/drizzle/**"
|
|
6
|
+
- "**/drizzle.config.*"
|
|
7
|
+
- "**/schema.ts"
|
|
8
|
+
- "**/schema/**"
|
|
9
|
+
- "**/db/**"
|
|
10
|
+
---
|
|
1
11
|
# Database
|
|
2
12
|
|
|
3
13
|
Naming and the fixed column vocabulary for relational schemas in **every** Nurix project — one concept, one name, everywhere. Written for SQLite / Cloudflare D1 + Drizzle; a Postgres project keeps the same column names and semantics with native types (`uuid`, `timestamptz`, `boolean`). **Design doctrine — what earns a table, state vs. logs, junctions, FK policy, uniqueness traps — is the `schema-design` skill; consult it before any schema change.**
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.tsx"
|
|
4
|
+
- "**/*.jsx"
|
|
5
|
+
- "**/*.ts"
|
|
6
|
+
- "**/*.css"
|
|
7
|
+
- "**/*.html"
|
|
8
|
+
- "**/tailwind.config.*"
|
|
9
|
+
- "**/vite.config.*"
|
|
10
|
+
---
|
|
1
11
|
# Frontend
|
|
2
12
|
|
|
3
13
|
Tailwind v4 + shadcn-based UI conventions for **web** apps — styling, the `@nurix/components` library, the `design.md` token artifact, and design-skill routing — plus the TypeScript conventions every package compiles under.
|