@nurix/etna 0.15.7 → 0.16.0
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/bin/etna.js +64 -13
- package/data/harness-index.json +7 -5
- package/data/harnesses/code/CLAUDE.md +3 -7
- package/data/harnesses/code/agents/harness-manager.md +4 -4
- package/data/harnesses/code/agents/maintenance.md +1 -1
- package/data/harnesses/code/harness.json +4 -3
- package/data/harnesses/code/hooks/hooks.json +3 -17
- package/data/harnesses/code/hooks/prompt-recall.js +157 -0
- package/data/harnesses/code/hooks/session-reflect.js +283 -0
- package/data/harnesses/code/skills/distiller/SKILL.md +62 -60
- package/data/harnesses/code/skills/distiller/references/docs-mode.md +21 -21
- package/data/harnesses/code/skills/distiller/references/method.md +41 -41
- package/data/harnesses/code/skills/documentation/references/templates.md +20 -27
- package/data/harnesses/code-fable/CLAUDE.md +138 -0
- package/data/harnesses/code-fable/agents/brainstorm.md +35 -0
- package/data/harnesses/code-fable/agents/change-review.md +249 -0
- package/data/harnesses/code-fable/agents/harness-manager.md +193 -0
- package/data/harnesses/code-fable/agents/maintenance.md +123 -0
- package/data/harnesses/code-fable/agents/performance-review.md +218 -0
- package/data/harnesses/code-fable/agents/spec-mode.md +56 -0
- package/data/harnesses/code-fable/harness.json +7 -0
- package/data/harnesses/code-fable/hooks/hooks.json +27 -0
- package/data/harnesses/code-fable/hooks/lib/redact.js +37 -0
- package/data/harnesses/code-fable/hooks/prompt-recall.js +157 -0
- package/data/harnesses/code-fable/hooks/session-reflect.js +283 -0
- package/data/harnesses/code-fable/rules/general.md +96 -0
- package/data/harnesses/code-fable/rules/nomenclature.md +34 -0
- package/data/harnesses/code-fable/skills/boundary/SKILL.md +262 -0
- package/data/harnesses/code-fable/skills/boundary/references/boundary-taxonomy.md +734 -0
- package/data/harnesses/code-fable/skills/brainstorm/README.md +136 -0
- package/data/harnesses/code-fable/skills/brainstorm/SKILL.md +135 -0
- package/data/harnesses/code-fable/skills/brainstorm/harvest.md +26 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/backlog.md +39 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/build.md +37 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/decisions.md +29 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/doctrine.md +31 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/domain-model.md +25 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/glossary.md +19 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/milestones.md +43 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/non-goals.md +21 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/plan-doc.md +61 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/questions.md +35 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/references.md +28 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/rejected.md +29 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/state.md +8 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/tech.md +23 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/thesis.md +21 -0
- package/data/harnesses/code-fable/skills/distiller/SKILL.md +219 -0
- package/data/harnesses/code-fable/skills/distiller/references/docs-mode.md +526 -0
- package/data/harnesses/code-fable/skills/distiller/references/method.md +166 -0
- package/data/harnesses/code-fable/skills/documentation/SKILL.md +127 -0
- package/data/harnesses/code-fable/skills/documentation/references/examples.md +193 -0
- package/data/harnesses/code-fable/skills/documentation/references/style-guide.md +288 -0
- package/data/harnesses/code-fable/skills/documentation/references/templates.md +942 -0
- package/data/harnesses/code-fable/skills/workflow-mapper/SKILL.md +430 -0
- package/data/harnesses/code-fable/skills/workflow-mapper/references/patterns.md +214 -0
- package/data/harnesses/{re → discover}/CLAUDE.md +48 -33
- package/data/harnesses/{re → discover}/agents/api.md +3 -3
- package/data/harnesses/{re → discover}/agents/codebase.md +3 -3
- package/data/harnesses/{re → discover}/agents/community.md +3 -3
- package/data/harnesses/{re → discover}/agents/deployed-client-bundle.md +5 -5
- package/data/harnesses/{re → discover}/agents/distribution-artifacts.md +4 -4
- package/data/harnesses/{re → discover}/agents/docs.md +3 -3
- package/data/harnesses/{re → discover}/agents/infra-backend-fingerprint.md +2 -2
- package/data/harnesses/{re → discover}/agents/packages.md +3 -3
- package/data/harnesses/{re → discover}/agents/session.md +19 -11
- package/data/harnesses/{re → discover}/agents/website.md +3 -3
- package/data/harnesses/{re → discover}/agents/wire-capture.md +9 -7
- package/data/harnesses/discover/harness.json +7 -0
- package/data/harnesses/{re/rules/re-discovery.md → discover/rules/discovery.md} +54 -17
- package/data/harnesses/{re/rules/re-evaluation.md → discover/rules/evaluation.md} +12 -1
- package/data/harnesses/{re/rules/re-ingestion.md → discover/rules/ingestion.md} +192 -23
- package/data/harnesses/discover/rules/self-correction.md +177 -0
- package/data/harnesses/discover/rules/tradecraft.md +532 -0
- package/data/harnesses/hr/CLAUDE.md +91 -0
- package/data/harnesses/hr/harness.json +7 -0
- package/data/harnesses/hr/skills/interview-eval/SKILL.md +118 -0
- package/data/harnesses/hr/skills/interview-eval/TEMPLATE.md +124 -0
- package/data/harnesses/hr/skills/interview-eval/extract_session.py +418 -0
- package/data/harnesses/hr/skills/interview-eval/rubric.md +219 -0
- package/data/harnesses/kb-agi/harness.json +1 -0
- package/package.json +1 -1
- package/data/harnesses/code/hooks/post-commit.js +0 -136
- package/data/harnesses/code/hooks/post-tool-failure.js +0 -136
- package/data/harnesses/code/hooks/session-stop.js +0 -207
- package/data/harnesses/re/harness.json +0 -6
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Open Questions
|
|
2
|
+
|
|
3
|
+
This file is the **single convergence currency** of the session. Every unresolved design call lives here and only here — whether the user raised it, the sync agent found it, or a probe surfaced it. Plan docs and pillars never host their own questions; they link back here.
|
|
4
|
+
|
|
5
|
+
The session is **coherent** exactly when this file is empty after a fresh sync. That makes hygiene load-bearing:
|
|
6
|
+
|
|
7
|
+
- Every entry must be **closeable** — the `Closes when.` field says what resolution looks like. Vague musings are not questions; distill them until they're closeable or leave them in the conversation.
|
|
8
|
+
- Only **design-calls** belong here — items whose resolution changes the idea. Editorial bookkeeping is the sync agent's job, applied directly and noted in its report.
|
|
9
|
+
- A resolved question is **deleted** (its decision lives in `decisions.md`). A parked question is deleted too, with an explicit entry in `backlog.md` or `non-goals.md`. No strikethrough, no "(resolved)" markers.
|
|
10
|
+
|
|
11
|
+
Each question is **self-contained**: full text in the user's framing, readable without opening another doc. Group under `## Category` headers when the list grows past ~10.
|
|
12
|
+
|
|
13
|
+
IDs are `QN`, stable while open, never reused after deletion.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Q1 — [Title in the user's words]
|
|
18
|
+
|
|
19
|
+
**Question.** [Full prose — paraphrase what the user said, or state the contradiction/gap the sync found. Repeat context; a future reader should need nothing else.]
|
|
20
|
+
|
|
21
|
+
**Raised by.** user | sync | probe
|
|
22
|
+
|
|
23
|
+
**Closes when.** [What resolution looks like — a pick between options, a confirmed fact, an accepted risk.]
|
|
24
|
+
|
|
25
|
+
**Options.** *(optional — only when named by the user or entering converge)*
|
|
26
|
+
- A. [one line]
|
|
27
|
+
- B. [one line]
|
|
28
|
+
|
|
29
|
+
**Lean.** *(optional — the user's lean, never the skill's)*
|
|
30
|
+
|
|
31
|
+
**Claude's take.** *(opt-in only — write only when the user asks for a recommendation on this question; delete if it goes stale)*
|
|
32
|
+
|
|
33
|
+
**Resolution lands in.** *(optional)* [pillar or doc + section]
|
|
34
|
+
|
|
35
|
+
**Blocks.** *(optional)* [milestone or other Q-numbers]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# References
|
|
2
|
+
|
|
3
|
+
This file collects **external prior art** — repos, docs, papers, talks — with one-line pointers to what we use each for. Reference implementations beat invented solutions: if someone has already solved the problem you're about to design, the right move is to link it here and use it, not re-derive.
|
|
4
|
+
|
|
5
|
+
References are not bookmarks. Every entry should answer the question "what specific pattern, idea, or implementation do we lift from this?" A reference without that grounding decays into noise. If you can't write the one-line "what we use it for", you probably don't need the entry.
|
|
6
|
+
|
|
7
|
+
References to **source materials** copied into `./plan/sources/` during the session also live here — point at the local copy, not the original location. This keeps the brainstorm self-contained.
|
|
8
|
+
|
|
9
|
+
When a referenced thing changes (renames, moves, deprecation), **update or remove the entry**. A stale reference that no longer points at what the description claims is worse than no reference — it teaches the reader to distrust the file.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Internal repos / paths
|
|
14
|
+
|
|
15
|
+
- `~/path/to/repo` — [what pattern we borrow]
|
|
16
|
+
- `~/path/to/another` — [what pattern we borrow]
|
|
17
|
+
|
|
18
|
+
## Source materials (this session)
|
|
19
|
+
|
|
20
|
+
- `./plan/sources/[file]` — [original location, why it's relevant]
|
|
21
|
+
|
|
22
|
+
## External docs
|
|
23
|
+
|
|
24
|
+
- `https://example.com/foo` — [what we use it for]
|
|
25
|
+
|
|
26
|
+
## Papers / talks
|
|
27
|
+
|
|
28
|
+
- [Title] — [link or citation] — [the specific idea we're applying]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Rejected alternatives
|
|
2
|
+
|
|
3
|
+
This file captures **paths that were considered and rejected**, with the reason for rejection. Its purpose is to prevent re-litigation: when someone proposes the same alternative six weeks later, this file answers "we already looked at that; here's why we didn't go that way."
|
|
4
|
+
|
|
5
|
+
Each entry needs three load-bearing parts: **Considered** (what was proposed, in concrete terms), **Rejected because** (the specific reason — "didn't fit" is useless to future-you; name the constraint, the cost, the risk), and **Re-evaluate if** (what would need to change for the rejected path to become viable again). The re-evaluate field stops the rejection from looking permanent when it isn't — most rejections are scope-conditional, not absolute.
|
|
6
|
+
|
|
7
|
+
This is one of the four places inside the brainstorm folder where past tense and "we considered X" framing is permitted (the others: `decisions.md`, `references.md`, git history). The whole point of this file is to capture the path-not-taken so future architects don't have to re-discover it.
|
|
8
|
+
|
|
9
|
+
If circumstances change and a rejected path becomes attractive again, **do not silently revive it**. Add a new question to `questions.md` that references the rejection here, and resolve it explicitly. The new decision can override the rejection — but it should override, not pretend the rejection never happened.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## R1 — [Rejected option]
|
|
14
|
+
|
|
15
|
+
**Considered.** [What was proposed.]
|
|
16
|
+
|
|
17
|
+
**Rejected because.** [The reason. Be specific.]
|
|
18
|
+
|
|
19
|
+
**Re-evaluate if.** [What would change to make this viable.]
|
|
20
|
+
|
|
21
|
+
**Came up during.** [Q-number or D-number for context.]
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## R2 — [Rejected option]
|
|
26
|
+
|
|
27
|
+
(same format)
|
|
28
|
+
|
|
29
|
+
---
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Tech
|
|
2
|
+
|
|
3
|
+
This file holds the **technology choices** for the idea. It is seeded at bootstrap from the harness's opinionated stack (e.g. `.claude/rules/general.md` recommended-packages table) when one exists — those rows arrive as defaults, not decisions.
|
|
4
|
+
|
|
5
|
+
Its job afterward is to be a **lens**: when a conversation move conflicts with a row here, the sync (or the skill, in the moment) surfaces it — *"you just proposed local component stores; the stack says Redux Toolkit — deviating or steering back?"* A confirmed deviation gets recorded below with its why; an unconfirmed conflict becomes an open question.
|
|
6
|
+
|
|
7
|
+
## Stack
|
|
8
|
+
|
|
9
|
+
| Layer | Choice | Source |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| [e.g. UI] | [react ^19] | harness default |
|
|
12
|
+
| [e.g. state] | [@reduxjs/toolkit] | harness default |
|
|
13
|
+
| [layer] | [choice] | decided this session — D[n] |
|
|
14
|
+
|
|
15
|
+
## Deviations
|
|
16
|
+
|
|
17
|
+
Each deviation from a harness default carries a why:
|
|
18
|
+
|
|
19
|
+
### [Choice] instead of [default]
|
|
20
|
+
|
|
21
|
+
**Why.** [Constraint, requirement, or incident that justifies the deviation.]
|
|
22
|
+
|
|
23
|
+
**Scope.** [Whole project, or only a named module/surface.]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Thesis
|
|
2
|
+
|
|
3
|
+
This file is the **running narrative of what we are designing** — the single place a cold reader (or the next session) goes to understand the idea as it currently stands. It is rewritten freely as the idea evolves; it always describes the *current* shape, never the journey (writing posture per `build.md`'s mode).
|
|
4
|
+
|
|
5
|
+
Keep it prose-first. If a section outgrows this file at high complexity, it may move to a numbered deep-dive doc (`01-foo.md`) at the session root with a one-line pointer left here.
|
|
6
|
+
|
|
7
|
+
## What this is
|
|
8
|
+
|
|
9
|
+
[2–5 paragraphs. What the thing is, who it's for, what it does. Distilled from the basis at `start` and kept current by the loop.]
|
|
10
|
+
|
|
11
|
+
## Aspect map
|
|
12
|
+
|
|
13
|
+
The thinking dimensions for *this* idea — proposed by the skill from the basis, edited by the user at `start`. The sync agent audits coverage against this list: an aspect with no content anywhere becomes an open question.
|
|
14
|
+
|
|
15
|
+
- [ ] [aspect — e.g. users & jobs]
|
|
16
|
+
- [ ] [aspect — e.g. domain model]
|
|
17
|
+
- [ ] [aspect — e.g. architecture]
|
|
18
|
+
- [ ] [aspect — e.g. tech]
|
|
19
|
+
- [ ] [aspect — e.g. risks]
|
|
20
|
+
|
|
21
|
+
Check an aspect only when it has real content in the pillars, not when it has merely been mentioned.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: distiller
|
|
3
|
+
description: The one place captured session lessons are distilled into colder, higher-signal knowledge. One skill, two modes — `facts` (memory inbox → atomic provenance-linked fact-cards in `docs/memory/facts/`) and `docs` (fact-cards → proposed, human-approved doc deltas). Both modes read one shared algorithm spec, `references/method.md` (surprisal filter, EDU fact-cards, reconciliation, bi-temporal supersession — all stolen, all cited), so they can never drift apart. Consumes the inbox entries it digests (the inbox is a queue, not an audit log — git history and commit/session provenance are the audit trail); NEVER auto-runs (invoked by the user, or by accepting the maintenance agent's threshold proposal). Triggers on "distill the inbox", "distill facts", "run the distiller", "distill into fact-cards", "facts from the inbox", "distill docs", "draft doc deltas", "distiller docs", "/distiller".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Distiller Skill
|
|
7
|
+
|
|
8
|
+
The one place a repo's **captured session lessons** are distilled into colder,
|
|
9
|
+
higher-signal knowledge. The memory inbox (`docs/memory/inbox/{actor}.md`) is a
|
|
10
|
+
per-actor queue of lessons the `session-reflect` hook extracted at session end;
|
|
11
|
+
the distiller reconciles the *surprising* part of it into atomic, self-contained,
|
|
12
|
+
provenance-linked **fact-cards** (`docs/memory/facts/`) and consumes the entries
|
|
13
|
+
it digests. Volume falls, signal rises — one hop down the distillation gradient.
|
|
14
|
+
|
|
15
|
+
The methods this skill runs are **stolen, not invented** — surprisal-based
|
|
16
|
+
retention (Nemori), non-lossy EDU fact-cards (EMem), per-fact ADD/UPDATE/
|
|
17
|
+
SUPERSEDE/NOOP reconciliation (Mem0), and bi-temporal supersession (Zep/
|
|
18
|
+
Graphiti). They live in exactly one place:
|
|
19
|
+
|
|
20
|
+
> **Read [`references/method.md`](references/method.md) first, in every mode.**
|
|
21
|
+
> It carries the four mechanisms verbatim-grade (with their arXiv ids — provenance
|
|
22
|
+
> matters here of all places), the fact-card format, and the inbox-consumption
|
|
23
|
+
> protocol. This SKILL.md only orchestrates those mechanisms over the inbox.
|
|
24
|
+
|
|
25
|
+
The reason the two modes are one skill: **both read one algorithm root.** Two
|
|
26
|
+
components would mean two copies of the surprisal/reconciliation/supersession
|
|
27
|
+
model that drift apart. One skill, one spec, zero drift.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Picking a mode
|
|
32
|
+
|
|
33
|
+
| You want to… | Mode | Ships |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| Distill new inbox entries into fact-cards | **`facts`** | now |
|
|
36
|
+
| Distill fact-cards into proposed doc deltas | **`docs`** | now — procedure in [`references/docs-mode.md`](references/docs-mode.md) |
|
|
37
|
+
|
|
38
|
+
Both modes read [`references/method.md`](references/method.md); each mode's
|
|
39
|
+
procedure that sequences it lives in its own section below.
|
|
40
|
+
|
|
41
|
+
## Execution model — pure skill, never autonomous
|
|
42
|
+
|
|
43
|
+
Runs **in the main session** — no standing sub-agents, no daemon, no cron, no
|
|
44
|
+
watch. The harness **proposes**, the user **disposes**: this skill runs only when
|
|
45
|
+
the user asks, when the **Completion flow** invokes it (the kit `CLAUDE.md`
|
|
46
|
+
Completion step distils the just-finished plan's trail — `facts` if the inbox
|
|
47
|
+
holds entries, then `docs` which still drafts for approval), or when the user
|
|
48
|
+
accepts the `maintenance` agent's threshold proposal (≥5 undigested inbox
|
|
49
|
+
entries). It never schedules itself, never runs on a hook, never gates anything.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Mode: `facts` (inbox → fact-cards)
|
|
54
|
+
|
|
55
|
+
Distill the inbox's undigested entries into fact-cards. The algorithm is in
|
|
56
|
+
[`references/method.md`](references/method.md) — follow it; this section is the
|
|
57
|
+
procedure that sequences it.
|
|
58
|
+
|
|
59
|
+
1. **Collect the inbox.** Read every entry block
|
|
60
|
+
(`## <timestamp> · session <id> · <repo>@<branch>`) across
|
|
61
|
+
`docs/memory/inbox/*.md`. The inbox is a consume-on-digest queue — what is
|
|
62
|
+
present is exactly what is undigested, so there is no separate high-water
|
|
63
|
+
mark. An immediate second run finds an empty inbox and is a clean NOOP.
|
|
64
|
+
|
|
65
|
+
**Thin or empty inbox ⇒ not a NOOP — distill the decision record.** A
|
|
66
|
+
project whose sessions produced no reflected lessons is **not** a project
|
|
67
|
+
with nothing to distill. Architecture decisions and reversals are
|
|
68
|
+
high-surprisal by nature — they exist precisely because they were *not*
|
|
69
|
+
predictable. So when the inbox is thin/empty, also ingest, as surprisal
|
|
70
|
+
sources for steps 2–5:
|
|
71
|
+
- `docs/architecture/adr/**` — each accepted ADR is a surprising decision
|
|
72
|
+
(its `Decision` + **Options Considered** — the rejected-alternatives section
|
|
73
|
+
in the `documentation` §10 ADR template — carry the prediction error).
|
|
74
|
+
- `docs/memory/brainstorm.md` — owner pronouncements, reversals, and
|
|
75
|
+
discovered constraints recorded outside the inbox.
|
|
76
|
+
Provenance points at the ADR file / brainstorm section, not a session id.
|
|
77
|
+
These sources are **never consumed** — reconciliation (step 4) is what keeps
|
|
78
|
+
a re-run over them idempotent.
|
|
79
|
+
|
|
80
|
+
2. **Surprisal filter → survivors** (method §1). Present the LLM with (a) a
|
|
81
|
+
summary of the *current* fact-set, (b) the undigested entries. Keep only
|
|
82
|
+
what is **surprising** relative to (a). Predictable ⇒ dropped — no importance
|
|
83
|
+
scores, no templates, prediction error is the only retention signal. Inbox
|
|
84
|
+
entries arrive semi-distilled (the reflection hook already dropped routine
|
|
85
|
+
work), so the filter's job here is narrower: drop what the existing fact-set
|
|
86
|
+
already predicts. (First run, empty fact-set: keep decisions, reversals,
|
|
87
|
+
discovered constraints, and failures with a cause worth remembering.)
|
|
88
|
+
|
|
89
|
+
3. **Decompose survivors → EDU candidates** (method §2). Each survivor becomes
|
|
90
|
+
**one** atomic, self-contained statement — entity names normalized, minimal
|
|
91
|
+
context baked into the sentence — with `provenance` pointing at the session
|
|
92
|
+
ids and commits its inbox entry names. Decomposition, **not** summarization:
|
|
93
|
+
one survivor may yield several cards; no information is destroyed; everything
|
|
94
|
+
stays auditable back to its session and commits.
|
|
95
|
+
|
|
96
|
+
4. **Reconcile each candidate against existing cards** (method §3). Query the
|
|
97
|
+
existing facts for semantic neighbors, then decide
|
|
98
|
+
**ADD / UPDATE / SUPERSEDE / NOOP** for each. Gate on the **facts collection
|
|
99
|
+
holding cards**, not on the binary and not on the collection merely being
|
|
100
|
+
registered — a present qmd whose `{repo}-facts` collection is *registered but
|
|
101
|
+
empty* (the common first run: harness-manager's baseline registers + embeds
|
|
102
|
+
`{repo}-facts` over a freshly-scaffolded `docs/memory/facts/` that has zero
|
|
103
|
+
cards) returns nothing useful, exactly like an unregistered collection.
|
|
104
|
+
- **Cards present** — `command -v qmd` succeeds **and** `docs/memory/facts/`
|
|
105
|
+
holds at least one card (`docs/memory/facts/*.md` is non-empty): query
|
|
106
|
+
scoped to the collection —
|
|
107
|
+
`qmd query --format json -c {repo}-facts "<the candidate statement>"`.
|
|
108
|
+
`{repo}` = the repo's directory name. Scope with `-c` always. (If the query
|
|
109
|
+
returns nothing on a non-empty card set, the collection is registered-but-
|
|
110
|
+
unbuilt — treat that as the fallback trigger below, not as "no neighbors.")
|
|
111
|
+
- **No cards to query** (qmd absent, **or** `docs/memory/facts/*.md` empty —
|
|
112
|
+
the common first-run case where the facts dir was scaffolded but never
|
|
113
|
+
distilled into): fall back to filename/grep similarity over
|
|
114
|
+
`docs/memory/facts/*.md` (slug overlap + entity-name grep), and **note in
|
|
115
|
+
the report** that reconciliation ran without semantic retrieval. Step 7
|
|
116
|
+
builds/refreshes the collection at the end of the run, so the *next* run —
|
|
117
|
+
with cards now present — reconciles semantically.
|
|
118
|
+
|
|
119
|
+
5. **Write the results** (method §4 — bi-temporal, never delete).
|
|
120
|
+
- **ADD** → write a new card `docs/memory/facts/{kebab-slug}.md` in the format.
|
|
121
|
+
- **UPDATE** → refine the existing card's statement in place; extend its
|
|
122
|
+
`provenance`.
|
|
123
|
+
- **SUPERSEDE** → on the **old** card set `invalid_from:` (today) and
|
|
124
|
+
`superseded_by:` (the new fact-id); write the **new** card with `valid_from:`
|
|
125
|
+
today. The old file stays in place — history stays queryable, nothing
|
|
126
|
+
vanishes.
|
|
127
|
+
- **Self-contained reversal** (one input states X-then-reversed-to-Y, no old
|
|
128
|
+
card exists yet) → emit **two** cards in one pass: a born-invalid historical
|
|
129
|
+
card for X (`invalid_from` + `superseded_by` → Y) and a live card for Y.
|
|
130
|
+
See method.md §4 "Self-contained reversal."
|
|
131
|
+
- **NOOP** → the fact is already known; write nothing.
|
|
132
|
+
|
|
133
|
+
6. **Consume the digested entries LAST.** Only after every card is written,
|
|
134
|
+
delete exactly the entry blocks this run digested from
|
|
135
|
+
`docs/memory/inbox/*.md` (keep each file's header; never touch undigested
|
|
136
|
+
blocks). Last, so a crash mid-run leaves the entries in place and the re-run
|
|
137
|
+
re-digests them — reconciliation NOOPs the already-written cards. This
|
|
138
|
+
consumption is the only bookkeeping; there is no `.digest-state.md`.
|
|
139
|
+
|
|
140
|
+
7. **If qmd present, refresh the lens** (skip silently if `command -v qmd`
|
|
141
|
+
finds nothing):
|
|
142
|
+
- `qmd collection add docs/memory/facts --name {repo}-facts` — register the
|
|
143
|
+
collection. Re-adding an existing name exits non-zero with "already exists";
|
|
144
|
+
**treat that as success.** Never register `docs/memory` as a whole — that
|
|
145
|
+
would sweep the raw inbox queue into the index.
|
|
146
|
+
- `qmd context add qmd://{repo}-facts/ "Distilled recent decisions/observations, provenance-linked"`
|
|
147
|
+
— upserts, safe to re-run.
|
|
148
|
+
- `qmd update` — **re-scan the collection for the cards you just wrote.** In
|
|
149
|
+
qmd 2.5.3 `collection add` on an already-registered name does NOT re-index
|
|
150
|
+
new files; `qmd update` is what picks them up. Skipping this leaves the new
|
|
151
|
+
cards invisible to retrieval (to the `prompt-recall` hook, and to distill②).
|
|
152
|
+
- `qmd embed -c {repo}-facts` — regenerate vectors for the new/changed cards.
|
|
153
|
+
Always scope with `-c`; a bare `qmd embed` re-embeds every repo on the
|
|
154
|
+
machine. The `prompt-recall` hook's vector fallback only sees embedded
|
|
155
|
+
cards, so skipping this hides new cards from prompt-time recall.
|
|
156
|
+
|
|
157
|
+
8. **Report** (in chat). Cards added / updated / superseded / NOOP'd, the
|
|
158
|
+
entries consumed per actor, and — if reconciliation fell back to
|
|
159
|
+
filename/grep (qmd absent, **or** `{repo}-facts` registered but holding no
|
|
160
|
+
cards yet — the first-run-on-a-scaffolded-facts-dir case) — the one advisory
|
|
161
|
+
line saying so. Fire this advisory whenever semantic retrieval was effectively
|
|
162
|
+
unavailable, even if the collection appeared in `qmd collection list`: a
|
|
163
|
+
registered-but-empty collection is not semantic retrieval. A run that finds
|
|
164
|
+
nothing surprising is **valid**: say
|
|
165
|
+
"no surprising entries in the inbox," don't pad.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Mode: `docs` (fact-cards → proposed doc deltas)
|
|
170
|
+
|
|
171
|
+
Distill fact-cards into proposed edits to `docs/` (the cold, human-owned SSoT) —
|
|
172
|
+
the next hop down the gradient from `facts`. The full procedure (input selection,
|
|
173
|
+
delta drafting, digestion marking, the pillar escape valve, trigger wiring) is in
|
|
174
|
+
[`references/docs-mode.md`](references/docs-mode.md) — follow it; reuses
|
|
175
|
+
`method.md`'s surprisal / EDU / reconciliation / bi-temporal vocabulary unchanged.
|
|
176
|
+
It NEVER writes a doc on its own — it **drafts and presents**; the human disposes.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Rules
|
|
181
|
+
|
|
182
|
+
- **One algorithm root.** Every mode reads `references/method.md`. Extend the
|
|
183
|
+
spec there — never inline a divergent copy of the four methods in a mode.
|
|
184
|
+
- **Consume, never rewrite.** `docs/memory/inbox/` is a queue owned by the
|
|
185
|
+
`session-reflect` capture hook. The distiller deletes exactly the entry blocks
|
|
186
|
+
it digested this run — no edits, no reordering, no touching undigested blocks.
|
|
187
|
+
Beyond that consumption it writes only under `docs/memory/facts/`.
|
|
188
|
+
- **Idempotent via consumption.** What is in the inbox is what is undigested.
|
|
189
|
+
Consume last; a second run finds an empty inbox and is a NOOP.
|
|
190
|
+
- **Cards are atomic and provenance-linked.** One self-contained statement per
|
|
191
|
+
card; every card resolves back to the session ids and commits it came from.
|
|
192
|
+
Decompose — never lossily summarize.
|
|
193
|
+
- **Supersede ≠ delete.** Bi-temporal only: set `invalid_from:` +
|
|
194
|
+
`superseded_by:` on the old card and leave the file in place. Nothing is ever
|
|
195
|
+
removed from `docs/memory/facts/`.
|
|
196
|
+
- **Findings are questions, not verdicts.** When a candidate appears to
|
|
197
|
+
contradict an existing card, phrase it as a question (is X now superseded by
|
|
198
|
+
Y?) — never assert staleness or falsity.
|
|
199
|
+
- **Never auto-runs.** Invoked by the user, by the Completion flow (kit
|
|
200
|
+
`CLAUDE.md`), or by accepting the maintenance proposal. No cron, no daemon, no
|
|
201
|
+
hook self-trigger — a workflow step the agent runs is not a self-trigger.
|
|
202
|
+
- **qmd is strictly optional.** Guard every qmd call on `command -v qmd`; absent
|
|
203
|
+
⇒ silent skip. Reconciliation additionally needs the `{repo}-facts` collection
|
|
204
|
+
to hold **cards** — not merely to be registered. "Unbuilt" here means **no
|
|
205
|
+
cards** (`docs/memory/facts/*.md` empty), not "unregistered": harness-manager's
|
|
206
|
+
baseline registers + embeds `{repo}-facts` over an empty scaffolded facts dir,
|
|
207
|
+
so on a qmd-equipped, freshly-baselined repo the collection *appears in*
|
|
208
|
+
`qmd collection list` yet indexes zero cards. Binary present but collection
|
|
209
|
+
cardless (first run) falls back to filename/grep too, same as qmd being absent.
|
|
210
|
+
|
|
211
|
+
## Output locations
|
|
212
|
+
|
|
213
|
+
- `facts` reads `docs/memory/inbox/`, writes fact-cards to
|
|
214
|
+
`docs/memory/facts/{kebab-slug}.md`, and deletes the consumed entry blocks
|
|
215
|
+
from the inbox; the report goes to chat.
|
|
216
|
+
- `docs` presents doc deltas in chat and, **only on approval**, writes the accepted
|
|
217
|
+
prose into the target doc under `docs/` (+ a new `docs/history/changelog/` entry if
|
|
218
|
+
drafted) and appends `digested_into:` to each consumed card in `docs/memory/facts/`.
|
|
219
|
+
See [`references/docs-mode.md`](references/docs-mode.md).
|