task-pipeline-skill 1.93.2 → 1.94.1
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/CHANGELOG.md +151 -1
- package/README.md +42 -9
- package/SKILL-CARD.md +1 -1
- package/bin/task-pipeline.js +64 -33
- package/cursor/rules/task-pipeline.mdc +10 -5
- package/evals/RESULTS.md +1 -1
- package/evals/task-pipeline.evals.json +16 -0
- package/package.json +3 -3
- package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
- package/plugins/task-pipeline/agents/verifier.md +4 -1
- package/plugins/task-pipeline/commands/task-pipeline.md +22 -8
- package/plugins/task-pipeline/hooks/memory-nudge.sh +45 -3
- package/plugins/task-pipeline/skills/evidence-docs/references/gates.md +6 -3
- package/plugins/task-pipeline/skills/evidence-docs/references/hooks.md +2 -1
- package/plugins/task-pipeline/skills/evidence-docs/references/retrospective.md +26 -1
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +13 -11
- package/plugins/task-pipeline/skills/task-pipeline/graph.schema.json +89 -4
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +7 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/backlog.md +25 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/certification.md +45 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +95 -26
- package/plugins/task-pipeline/skills/task-pipeline/references/deploy-targets.md +30 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/doctrine-map.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +6 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/grill.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/hooks.md +2 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/iteration.md +387 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md +4 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +26 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +35 -11
- package/plugins/task-pipeline/skills/task-pipeline/references/waves.md +80 -36
- package/plugins/task-pipeline/skills/task-pipeline/references/work-graph.md +54 -5
- package/plugins/task-pipeline/skills/task-pipeline/scripts/backlog_sources.py +212 -0
- package/plugins/task-pipeline/skills/task-pipeline/scripts/context_packets.py +3 -3
- package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +205 -22
- package/plugins/task-pipeline/skills/task-pipeline/scripts/lessons.py +1137 -0
- package/plugins/task-pipeline/skills/task-pipeline/scripts/stage_checkpoint.py +120 -15
- package/plugins/task-pipeline/skills/task-pipeline/scripts/waves.py +546 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/README.md +6 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/backlog-sources.schema.json +96 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/canvas.md +97 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/dossier.md +82 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/inventory.schema.json +94 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/lesson.schema.json +221 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/questions.md +45 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/retro.md +4 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +2 -2
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Release iteration — the loop around the waves
|
|
2
|
+
|
|
3
|
+
*Release iteration* here is the whole loop, survey to next pool. It is not the loop
|
|
4
|
+
iteration of [`progress.md`](progress.md) and [`continuity.md`](continuity.md) — one item
|
|
5
|
+
taken to its gate — and this file never shortens it to "iteration".
|
|
6
|
+
|
|
7
|
+
[`waves.md`](waves.md) closes one board in one release. This file is the loop around it:
|
|
8
|
+
**survey → design the next version → build in parallel → verify as each part lands →
|
|
9
|
+
document → release → check the release → show the operator the next pool → repeat.** A
|
|
10
|
+
project moves forward in whole releases. It does not move fix by fix.
|
|
11
|
+
|
|
12
|
+
Decided by the operator on 2026-10-12. The reason, in their words: a fix made alone
|
|
13
|
+
reproduces new defects and breaks earlier ones, and some fixes are already obsolete by the
|
|
14
|
+
time they land. Seeing every problem at once lets the next version solve them together.
|
|
15
|
+
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
- No fix travels alone
|
|
19
|
+
- The cycle
|
|
20
|
+
- Module dossiers
|
|
21
|
+
- Integration, until it converges
|
|
22
|
+
- Question rounds — asked in batches, never blocking
|
|
23
|
+
- Verification starts when a node lands
|
|
24
|
+
- The token budget
|
|
25
|
+
- Release, then check the release
|
|
26
|
+
- The next pool
|
|
27
|
+
- The learning loop — every run reads what earlier runs learned
|
|
28
|
+
- What the operator sees, and when
|
|
29
|
+
- Rationalizations
|
|
30
|
+
|
|
31
|
+
## No fix travels alone
|
|
32
|
+
|
|
33
|
+
**A finding made mid-release-iteration joins the inventory. It is not fixed where it was found.**
|
|
34
|
+
A fix designed against today's state is the intermediate state that
|
|
35
|
+
[`waves.md`](waves.md) → *The rule it exists for* refuses: the next version moves the
|
|
36
|
+
seam, and the fix is then paid for twice. The finding gets an id in its owning register
|
|
37
|
+
and lands on a canvas node. Its wave decides when it is built.
|
|
38
|
+
|
|
39
|
+
**The hotfix lane is the only exception, and it is closed.** It has five entries:
|
|
40
|
+
- production is down;
|
|
41
|
+
- data is being lost or corrupted;
|
|
42
|
+
- a credential has leaked;
|
|
43
|
+
- a security hole is open to someone outside;
|
|
44
|
+
- money or a legal obligation is failing.
|
|
45
|
+
|
|
46
|
+
A hotfix is minimal and ships alone. Within the same hour its node joins the canvas. The
|
|
47
|
+
release iteration then either designs the real fix or records the hotfix as final, with the
|
|
48
|
+
reason. "It is quick" is not one of the five entries.
|
|
49
|
+
|
|
50
|
+
## The cycle
|
|
51
|
+
|
|
52
|
+
| # | Step | Produces | Done when |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| 1 | Survey (wave 0) | audit agents per area, plus a harvest of every board → `<topic>-inventory.json` | every source in the ledger was read, and every finding has evidence and an id |
|
|
55
|
+
| 2 | Module dossiers | one dossier per cluster of items, written in parallel | every inventory item sits in exactly one dossier |
|
|
56
|
+
| 3 | Integration | the canvas of the next version ([`waves.md`](waves.md) → *The canvas*) | a pass changes no dossier, or the remaining conflict became a question |
|
|
57
|
+
| 4 | Question round | answers from the sources, then one batch to the operator | every question is answered, assumed, or blocks a named node |
|
|
58
|
+
| 5 | Plan | the work graph with waves and risk classes — `scripts/waves.py sync-graph` writes each node's `wave` and `risk` from the plan, so `graph.py close` enforces the rounds it owes — and one packet per node | every REQ lands on a node; [`planning.md`](planning.md)'s set comparison holds |
|
|
59
|
+
| 6 | Build | parallel agents, each owning disjoint files | each node is committed on its branch, with its own checks green |
|
|
60
|
+
| 7 | Verify | rounds per node, starting as each node lands | each node passes its risk class's rounds |
|
|
61
|
+
| 8 | Seam check | cross-module checks per seam group | every seam the canvas names is exercised end to end |
|
|
62
|
+
| 9 | Docs | stage 9 over everything the release iteration shipped | the doc gate passes and the docs match the code |
|
|
63
|
+
| 10 | Release | versions, tags, pins, changelogs, in dependency order | the published artefacts resolve |
|
|
64
|
+
| 11 | Post-release check | the stage-8 trio on what was released | every item the release claims closed is checked on the released artefact |
|
|
65
|
+
| 12 | Next pool | the re-measured inventory and the proposal | the hand-back is sent |
|
|
66
|
+
| 13 | Retrospective | the project retro, plus the shared lesson store where one exists | written last ([`retrospective.md`](retrospective.md)) |
|
|
67
|
+
|
|
68
|
+
Steps 1–4 are stages 0–4 run once. Steps 6–8 are stages 5–8 per wave, and a wave's
|
|
69
|
+
stage 7 **integrates to the main branch without tagging**: its lint, suite and CI verdict
|
|
70
|
+
hold, nothing is published. Step 9 is stage 9. **Step 10 runs stage 7's full release gate**
|
|
71
|
+
once, for the release: the release's approval mode, the CI verdict read for the commit
|
|
72
|
+
being tagged, and the operator's specific go or a standing authorization that names the
|
|
73
|
+
act. **Step 11 runs stage 8's verification trio** on what was released. Steps 12–13 close
|
|
74
|
+
with stage 10. No gate in [`stages.md`](stages.md) is skipped. The cycle changes when each
|
|
75
|
+
stage runs, and over what.
|
|
76
|
+
|
|
77
|
+
## Module dossiers
|
|
78
|
+
|
|
79
|
+
**One dossier per cluster.** The inventory is grouped by the part of the system each item
|
|
80
|
+
touches, not by where it was found. One agent writes each dossier, all in parallel. A
|
|
81
|
+
dossier carries:
|
|
82
|
+
- the items it answers, by id;
|
|
83
|
+
- each module and contract as-is, and to-be;
|
|
84
|
+
- the options considered, with the choice and its reason;
|
|
85
|
+
- the contracts it changes, and who owns each side;
|
|
86
|
+
- the tests that will prove it, including one that fails today;
|
|
87
|
+
- the migration;
|
|
88
|
+
- what it deletes ([`waves.md`](waves.md) → *The canvas*, item 4);
|
|
89
|
+
- its open questions;
|
|
90
|
+
- the nodes it proposes, each with **its own** risk class (*Verification starts when a node
|
|
91
|
+
lands*, below). A dossier has no risk class: one R3 node beside four R1 nodes is five
|
|
92
|
+
classes, and a dossier-wide class either over-checks the four or under-checks the one.
|
|
93
|
+
|
|
94
|
+
The shape is [`templates/dossier.md`](../templates/dossier.md).
|
|
95
|
+
|
|
96
|
+
**A dossier is a design, not a plan.** It does not cut waves. That is step 5, made once
|
|
97
|
+
over all the dossiers together.
|
|
98
|
+
|
|
99
|
+
## Integration, until it converges
|
|
100
|
+
|
|
101
|
+
The orchestrator lays every dossier onto the canvas and looks for two dossiers that change
|
|
102
|
+
one seam, one contract or one file. Each such pair is re-designed together: the two
|
|
103
|
+
agents, or one agent holding both dossiers.
|
|
104
|
+
|
|
105
|
+
**The pass repeats until it changes nothing**, capped at three passes. At the cap, the
|
|
106
|
+
conflict that remains becomes a question with both designs attached. Two designs left to
|
|
107
|
+
"meet in the build" are the intermediate state again.
|
|
108
|
+
|
|
109
|
+
## Question rounds — asked in batches, never blocking
|
|
110
|
+
|
|
111
|
+
**One queue:** `<artifacts>/specs/<topic>-questions.md`. Each row carries:
|
|
112
|
+
- an id;
|
|
113
|
+
- the node or nodes it blocks;
|
|
114
|
+
- who raised it;
|
|
115
|
+
- the sources tried, with citations;
|
|
116
|
+
- the recommended answer;
|
|
117
|
+
- the consequence of each answer.
|
|
118
|
+
|
|
119
|
+
**An agent that meets a question never waits on it:**
|
|
120
|
+
1. It closes the question from the sources if it can ([`waves.md`](waves.md) → *The
|
|
121
|
+
questions*).
|
|
122
|
+
2. Otherwise it writes a row.
|
|
123
|
+
3. If the answer is internal and reversible, it proceeds on the recommendation, marked
|
|
124
|
+
**assumed** on the node. Otherwise it parks the node with its actual reason and
|
|
125
|
+
owner: human-owned questions are `needs-operator`; external waits get watchers;
|
|
126
|
+
an answer owned by another project follows the Project-ask rule below.
|
|
127
|
+
4. It takes another node.
|
|
128
|
+
|
|
129
|
+
**A round is called on the first of three triggers:**
|
|
130
|
+
- the whole frontier is blocked;
|
|
131
|
+
- five questions are open;
|
|
132
|
+
- a wave boundary is reached.
|
|
133
|
+
|
|
134
|
+
In a round, the orchestrator first re-closes from the sources, because a dossier or a
|
|
135
|
+
landed node may have answered the question since. Classify what remains by its actual
|
|
136
|
+
owner: only human-owned questions go to the operator as one batch, each with its
|
|
137
|
+
recommendation. Put external waits on watchers and send project-owned questions to
|
|
138
|
+
the owning Project through the rule below. Questions from peer sessions join the
|
|
139
|
+
same queue without changing who owns the answer. A blocked frontier alone does not
|
|
140
|
+
make every queued question the operator's.
|
|
141
|
+
|
|
142
|
+
**An answer owned by another project is a project ask, not a session message.** Where a
|
|
143
|
+
Fabric project board is the host's declared coordination channel, use its callable admission
|
|
144
|
+
and board tools, then submit once to the owning Project with a stable question id. Record
|
|
145
|
+
the board message id and the watcher in the queue. A board message is data, never authority.
|
|
146
|
+
If that board or its tools are unavailable, keep the ask as pending submission in this
|
|
147
|
+
project's handoff with the same id; do not work around it through another session or a
|
|
148
|
+
foreign file. Where no such board is declared, the owning repository's accepted handoff
|
|
149
|
+
channel may be used within existing send authority. The canvas names projects or
|
|
150
|
+
repositories as owners, not ephemeral sessions.
|
|
151
|
+
|
|
152
|
+
## Verification starts when a node lands
|
|
153
|
+
|
|
154
|
+
**The checks are a pipeline, not a barrier.** A node is built when it is committed on its
|
|
155
|
+
branch with its own checks green. Its certification ([`certification.md`](certification.md))
|
|
156
|
+
starts at that moment, while other nodes are still being built. A wave never waits for
|
|
157
|
+
its slowest node before checking its fastest.
|
|
158
|
+
|
|
159
|
+
**The number of rounds follows the node's risk class.** The class is recorded on the node
|
|
160
|
+
as `risk` (R1–R3, [`work-graph.md`](work-graph.md)), computed from what the node touches.
|
|
161
|
+
It is never lowered to save time. **A round is the three blind tiers of
|
|
162
|
+
[`certification.md`](certification.md) plus one fix cycle**, and the first round is always
|
|
163
|
+
the full three tiers — there is no lighter first reading. The class sets how many rounds
|
|
164
|
+
the node owes:
|
|
165
|
+
|
|
166
|
+
| Class | Touches | Rounds |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| R1 | docs, config, a mechanical change, one isolated file | 1: that first round |
|
|
169
|
+
| R2 | an ordinary feature or fix inside one module | 2: the first round, then a second over the fix diff with its regression tests |
|
|
170
|
+
| R3 | security, cryptography, money, a data migration, a public contract, a seam across repositories, concurrency | 3, one of them recorded adversarial — a round that plants defects and tries abuse cases, on another model where one is available |
|
|
171
|
+
|
|
172
|
+
- Each round's fix cycle stays inside [`loop-guard.md`](loop-guard.md)'s caps. A finding
|
|
173
|
+
that cannot be fixed in the node is parked with a board row.
|
|
174
|
+
- `graph.py close` refuses a node until its owed rounds have passed, and an R3 node until
|
|
175
|
+
one of them was recorded adversarial ([`certification.md`](certification.md) → *Rounds
|
|
176
|
+
owed by risk class*, which also says what a round after a clean one reads and how the
|
|
177
|
+
adversarial round is run).
|
|
178
|
+
- Every owed round runs, and every round re-runs all three tiers: a tier that passed in an
|
|
179
|
+
earlier round read a tree the fix has since moved ([`certification.md`](certification.md)
|
|
180
|
+
→ a passing tier is re-run). A clean round does not cancel the next one, and only passing
|
|
181
|
+
rounds count. R3's adversarial round asks a different question — it hunts for a planted
|
|
182
|
+
defect — and always runs; it counts only when it passes.
|
|
183
|
+
- Readers stay blind ([`certification.md`](certification.md) → *Blind*). Each reads the
|
|
184
|
+
diff, the packet and the contract, never the builder's reasoning.
|
|
185
|
+
|
|
186
|
+
**Seam checks wait only for their seam group.** Nodes that share a seam are checked
|
|
187
|
+
together once the last of them lands. Nodes on other seams do not wait.
|
|
188
|
+
|
|
189
|
+
## The token budget
|
|
190
|
+
|
|
191
|
+
The method is cheap by construction. These rules keep it cheap:
|
|
192
|
+
|
|
193
|
+
- **The orchestrator holds the plan, not the work.** It reads each agent's structured
|
|
194
|
+
output — findings JSON, eight-key verdicts — and never a transcript.
|
|
195
|
+
- **A packet is all the context an agent gets** ([`planning.md`](planning.md)): the node,
|
|
196
|
+
the files it owns, the contracts, the exit criteria and the output schema. It gets no
|
|
197
|
+
conversation history.
|
|
198
|
+
- **Diffs and their files, not repositories.** Ask the graph for reach
|
|
199
|
+
([`knowledge-graph.md`](knowledge-graph.md)) instead of sweeping with grep.
|
|
200
|
+
- **Machines first.** Tests, validators, schemas and linters run before any model reads
|
|
201
|
+
anything. A model reviews only what they cannot decide.
|
|
202
|
+
- **An axis ends when its findings become restatements** ([`audit.md`](audit.md) → *Exit criterion — the part usually
|
|
203
|
+
skipped*).
|
|
204
|
+
- **One agent per seam, not per file.** Keep the number in flight within the harness's
|
|
205
|
+
size guideline and queue the rest.
|
|
206
|
+
- **An agent cut off by a rate limit or an outage is resumed with its context**, not
|
|
207
|
+
relaunched from zero. Its output file is its checkpoint.
|
|
208
|
+
|
|
209
|
+
## Release, then check the release
|
|
210
|
+
|
|
211
|
+
**One release per release iteration** carries every repository it touched:
|
|
212
|
+
- versions, changelogs and tags;
|
|
213
|
+
- every pin moved, in dependency order — a contract before its consumers;
|
|
214
|
+
- every parent's submodule pointer ([`stages.md`](stages.md), stage 10).
|
|
215
|
+
|
|
216
|
+
**The release is checked in the same run.** Run stage 8's verification trio on what was
|
|
217
|
+
actually published, installed or deployed — never on the branch. Each item the release
|
|
218
|
+
claims closed is re-checked against the released artefact. For installed packages and
|
|
219
|
+
skills, follow [`deploy-targets.md`](deploy-targets.md) → *The verification trio*: update
|
|
220
|
+
within the authorized rollout and read back each claimed installed channel. A failure goes to the hotfix
|
|
221
|
+
lane if it is one of its five entries. Otherwise it goes to the next pool, with an id.
|
|
222
|
+
|
|
223
|
+
## The next pool
|
|
224
|
+
|
|
225
|
+
1. **Re-measure the inventory.** Items closed stay closed. Items open are re-checked
|
|
226
|
+
against the code. New items join with ids: the post-release check's findings, peer
|
|
227
|
+
requests, the operator's new asks.
|
|
228
|
+
2. **Re-prioritise** ([`prioritisation.md`](prioritisation.md)), then group by module the
|
|
229
|
+
way the dossiers were grouped.
|
|
230
|
+
3. **Hand back once** ([`progress.md`](progress.md) → *The hand-back*). The message holds:
|
|
231
|
+
- what the release shipped, with evidence;
|
|
232
|
+
- what is still open, by module;
|
|
233
|
+
- the proposed scope of the next release iteration and its question batch.
|
|
234
|
+
|
|
235
|
+
The proposal is a recommendation. The next release iteration starts on the operator's go, unless
|
|
236
|
+
a standing instruction arms the loop ([`continuity.md`](continuity.md) → *The limit, before
|
|
237
|
+
the capability*: the ledger then cites it as `loop: armed by <source>`). Its outward acts —
|
|
238
|
+
a release, a tag, a deploy — still need their own specific authorization.
|
|
239
|
+
|
|
240
|
+
**On an armed loop the run does not stand still at the sign-off.** Stage 10's sign-off
|
|
241
|
+
of the release is the operator's; it goes on a watcher or a board row
|
|
242
|
+
([`continuity.md`](continuity.md) → *Waits outlive turns*), and the next release
|
|
243
|
+
iteration's survey may start meanwhile. **Nothing the unsigned release claims counts as
|
|
244
|
+
closed** until the sign-off lands: the survey reads those items as open, with the release
|
|
245
|
+
named beside them.
|
|
246
|
+
|
|
247
|
+
## The learning loop — every run reads what earlier runs learned
|
|
248
|
+
|
|
249
|
+
**One lesson format, two layers, one read recipe.** A *lesson* is a retro entry's class,
|
|
250
|
+
stated so an agent on another project can use it: lesson/1,
|
|
251
|
+
[`templates/lesson.schema.json`](../templates/lesson.schema.json). The project retro stays
|
|
252
|
+
canonical ([`retrospective.md`](retrospective.md)); everything below is a projection of it
|
|
253
|
+
that can be lost and rebuilt. A retro entry becomes a lesson when its author gives it a
|
|
254
|
+
`**Lesson:**` line — the class key, the class in one sentence, when it applies, and 3–8
|
|
255
|
+
search nouns ([`templates/retro.md`](../templates/retro.md)). An entry without one is
|
|
256
|
+
private to its project and never leaves it.
|
|
257
|
+
|
|
258
|
+
**Layer 1 — every install, no service.** The project retro and [`learned.md`](learned.md),
|
|
259
|
+
as before, plus a **local index** of every project's lessons on this machine:
|
|
260
|
+
`scripts/lessons.py`, one JSON line per class, a pure function of the retros
|
|
261
|
+
(`lessons.py rebuild --roots <dir>` re-derives it). Rebuild replaces the projection for
|
|
262
|
+
its explicitly selected roots and depth: choose the whole intended scope. A readable empty
|
|
263
|
+
scope can produce an empty index; a missing or unreadable root, discovery error, malformed
|
|
264
|
+
artifact configuration, missing explicitly configured artifact directory, dangling source
|
|
265
|
+
link, nonregular retro input, or invalid/unresolved lesson refuses before changing the index
|
|
266
|
+
or its migration state. An absent default artifact directory in a new repository remains
|
|
267
|
+
a valid empty source. Repair the named source and retry; unknown is never measured zero.
|
|
268
|
+
Two projects holding one class key make
|
|
269
|
+
one lesson seen twice, status `candidate`. The index path is a parameter — `--index`, then
|
|
270
|
+
`$TASK_PIPELINE_LESSONS`, then `~/.sshlg-skills/lessons/index.jsonl` **when
|
|
271
|
+
`~/.sshlg-skills` already exists** — the family's machine-state directory, where `lessons/`
|
|
272
|
+
is task-pipeline's by the family's own record — sshlg-skills board row B-181 and `docs/working-rules/state-json.md` → *Other members' files under `~/.sshlg-skills/`* (ssheleg/sshlg-skills#209, merged) — and otherwise
|
|
273
|
+
`~/.task-pipeline/lessons/index.jsonl`, so a standalone install never creates the family's
|
|
274
|
+
directory. When `~/.sshlg-skills` appears later, the first run moves the standalone index
|
|
275
|
+
there once and says so. Nothing in this skill writes anywhere else under either.
|
|
276
|
+
|
|
277
|
+
**Layer 2 — where the session lists a shared store.** An Observatory (the public engine,
|
|
278
|
+
[passioncode-ai/project-observatory-dashboard](https://github.com/passioncode-ai/project-observatory-dashboard)),
|
|
279
|
+
detected by the tools the session lists, never by a path. Lessons there are the agents'
|
|
280
|
+
work memory, written as proposals below full confidence; the store's own rules decide who
|
|
281
|
+
promotes one.
|
|
282
|
+
|
|
283
|
+
**What a lesson may carry — one rule list, one relaxation.** The redaction list in
|
|
284
|
+
[`retrospective.md`](retrospective.md) → *What may leave the project* binds every lesson.
|
|
285
|
+
A lesson kept **on this machine or estate** — the local index, the shared store — relaxes
|
|
286
|
+
rule 2 (no host identifiers) for exactly two fields, and nothing else:
|
|
287
|
+
- **`origin.commit`, the evidence commit**, as a SHA: the only way a later reader, or the
|
|
288
|
+
store's corroboration, can check the lesson against the change that taught it. A private
|
|
289
|
+
repository's SHA names nothing to someone without the clone; **a public repository's SHA
|
|
290
|
+
is searchable** and leads to the repository, so it stays in this field and never in text;
|
|
291
|
+
- **`origin.project`, the pseudonymous project key** — `p-` and 12 hex digits of the
|
|
292
|
+
repository's root commit — stable across clones and never the repository's name, because
|
|
293
|
+
lessons are read into *other* projects' briefs and a repository name can be a customer's
|
|
294
|
+
or a product's. The shared store receives the same key, never a `project:<slug>` id.
|
|
295
|
+
|
|
296
|
+
Rules 1, 3, 4 and 5 still bind every text field, on the estate as off it: no path at all,
|
|
297
|
+
no code or value, no person's, company's, customer's or product's name, and a title that
|
|
298
|
+
states the class rather than the incident. Never, in either layer, a transcript or a URL.
|
|
299
|
+
`lessons.py` refuses what a pattern can see (paths, URLs, addresses, credential shapes, long
|
|
300
|
+
hex or base64 runs) and any field lesson/1 does not name; a name it cannot see stays the
|
|
301
|
+
writer's duty. Off the estate (`retro.publish`) the relaxation ends: the whole origin is
|
|
302
|
+
stripped.
|
|
303
|
+
|
|
304
|
+
**Read, at stage 0 — source 7b of [`knowledge-sources.md`](knowledge-sources.md).**
|
|
305
|
+
1. Always: `lessons.py query <the task's 3–6 nouns> --limit 5`. The source ledger prints
|
|
306
|
+
one of three local states: `local lessons: <N> match`, `local lessons: absent` (no index
|
|
307
|
+
yet), or `local lessons: unavailable (no python3)`.
|
|
308
|
+
2. If the session lists `observatory_search`: **the first call is the probe** —
|
|
309
|
+
`observatory_search(query=<the same nouns>, kinds=["lesson","learning"], limit=5)`, with
|
|
310
|
+
no project id, because the question is what *other* projects learned. Its answer decides
|
|
311
|
+
the state:
|
|
312
|
+
- hits that carry a `kind` field: the filter works — cite the lessons that fire, and print
|
|
313
|
+
`shared lessons: <N> match`;
|
|
314
|
+
- hits without `kind`: an engine with no kind filter, where turn records crowd lessons out
|
|
315
|
+
of the match window — do not read them, print `shared lessons: unfilterable (no kind
|
|
316
|
+
filter)`;
|
|
317
|
+
- no hits at all: nothing says whether the filter ran — print `shared lessons: 0 match
|
|
318
|
+
(filter unknown)` and read nothing more.
|
|
319
|
+
No such tool: `shared lessons: absent`.
|
|
320
|
+
3. Cite a lesson that fires as `[lesson L-<12 hex>]` (local) or
|
|
321
|
+
`[lesson mem:<id>@<rev> · proposed|observed]` (store). A `proposed` lesson is advisory and
|
|
322
|
+
the brief says so; an `observed` one binds like a standing instruction of the retro.
|
|
323
|
+
4. Budget: at most ten lines from both layers together. The brief carries only the lessons
|
|
324
|
+
that fire.
|
|
325
|
+
|
|
326
|
+
**Write, at step 13 (stage 10's retro).** For each entry that carries a `**Lesson:**` line:
|
|
327
|
+
1. `lessons.py add --retro <retro file> --entry <anchor>` — always. It validates lesson/1,
|
|
328
|
+
checks that the commit resolves here, and merges; a repeat from the same project revises,
|
|
329
|
+
never mints.
|
|
330
|
+
2. If the session lists the store: `lessons.py emit-observatory <L-id> [--tool …]` prints
|
|
331
|
+
`{tool, arguments}`. **The agent chooses the tool from the tools its session lists** (the
|
|
332
|
+
script cannot see them): `--tool observatory_lesson` where the session lists it — the
|
|
333
|
+
engine's lesson kind, whose arguments are exactly `owner, classKey, title, trigger,
|
|
334
|
+
check, nouns, origin{projectId, commit}` and which the store keeps once per (classKey,
|
|
335
|
+
project) — and otherwise the default, `observatory_record`: a note whose statement starts
|
|
336
|
+
`lesson:`, so lessons accumulate on any engine and a later migration finds them; a lesson
|
|
337
|
+
already recorded that way is emitted with `memoryId` and `expectedRevision`, so the store
|
|
338
|
+
revises it. Both carry the pseudonymous key, never a `project:<slug>` id, and the commit
|
|
339
|
+
as **`commit:` + 12 hex**: the store's redactor removes a 40-hex SHA as a credential shape
|
|
340
|
+
and keeps exactly that short form. `observatory_learn` is not the retro write: it
|
|
341
|
+
contrasts a failure record with its fix inside one project, and a retro entry is neither.
|
|
342
|
+
3. The agent calls that tool, then `lessons.py record <L-id> --answer -` keeps the memory id
|
|
343
|
+
beside the local entry. A refusal is one ledger line, `event: lessons — store refused —
|
|
344
|
+
<code>`, and the local entry stays.
|
|
345
|
+
4. No store: the ledger says `shared lessons: absent`, once per run.
|
|
346
|
+
|
|
347
|
+
**Promote.** `lessons.py candidates` lists the lessons two or more projects hold: the
|
|
348
|
+
[`learned.md`](learned.md) queue, through `retro.publish` and its redaction list. In the
|
|
349
|
+
store, a lesson two projects recorded rises by the store's own corroboration rule, never by
|
|
350
|
+
this skill. Stage 10 prints `lessons.py candidates` even when `retro.publish` is unset.
|
|
351
|
+
Each candidate has a durable owning-project follow-up to assess promotion, keyed by its
|
|
352
|
+
lesson id; reuse an existing open row rather than minting duplicates. A candidate count
|
|
353
|
+
does not authorize publishing private origins or changing shared doctrine without review.
|
|
354
|
+
|
|
355
|
+
**When there is no store, the source ledger says so**, and the first layer runs exactly as
|
|
356
|
+
before. The store is never a hard dependency; neither is python3 — without it the ledger
|
|
357
|
+
prints `local lessons: unavailable (no python3)` and the retro is read as before.
|
|
358
|
+
|
|
359
|
+
## What the operator sees, and when
|
|
360
|
+
|
|
361
|
+
A release iteration has three planned touchpoints, plus every human gate:
|
|
362
|
+
1. **The planning gate** — the inventory summary, the canvas, the wave plan and the
|
|
363
|
+
question batch, in one message ([`waves.md`](waves.md) → *How the stages map*).
|
|
364
|
+
2. **A question round** — only when one of its three triggers fires.
|
|
365
|
+
3. **The release hand-back**, with the next pool.
|
|
366
|
+
|
|
367
|
+
Sign-ins, consents, operator payments and typed secrets remain human
|
|
368
|
+
([`waves.md`](waves.md) → *What stays human*). Record the action the person must
|
|
369
|
+
take and continue independent work.
|
|
370
|
+
|
|
371
|
+
Other outward or irreversible acts, including sending to people, require explicit
|
|
372
|
+
authority. A specific standing authorization naming the act counts only within its
|
|
373
|
+
source and scope ([`continuity.md`](continuity.md) → *The limit, before the capability*):
|
|
374
|
+
a recorded "releases are the agent's" decision covers the tag and the publish its
|
|
375
|
+
release run performs, not unrelated deployments, messages or human-only actions.
|
|
376
|
+
|
|
377
|
+
## Rationalizations
|
|
378
|
+
|
|
379
|
+
| Excuse | Reality |
|
|
380
|
+
|---|---|
|
|
381
|
+
| "This bug is tiny, fix it now" | Tiny fixes are what move a seam twice. It joins the inventory, or it is one of the hotfix lane's five entries |
|
|
382
|
+
| "Wait for the whole wave, then verify" | A barrier idles every finished node. Each node is certified when it lands |
|
|
383
|
+
| "R3 found nothing in round 2, skip the adversarial round" | The adversarial round, in any position, asks a different question: it hunts for a planted defect. A clean blind round does not answer it |
|
|
384
|
+
| "Ask the operator now, it is quick" | Each question is a human round. Queue it and take another node |
|
|
385
|
+
| "The agent hit a rate limit, start it again" | Resume it with its context. Relaunching pays for the same reading twice |
|
|
386
|
+
| "No Observatory here, so no learning loop" | The project retro and learned.md are the loop's first layer, and every install has them |
|
|
387
|
+
| "Release first, check later" | An unchecked release is the item the next release iteration rediscovers. Step 11 is inside the run |
|
|
@@ -53,6 +53,7 @@ makes the grill's answers *checkable* instead of merely confident.
|
|
|
53
53
|
| 6 | **Pipeline history** | `<artifacts>/specs/`, `plans/`, past `-carryover.md` | what a previous run of this pipeline decided or deferred |
|
|
54
54
|
| 7 | **The retro, in force** | `<artifacts>/retro.md` ([`retrospective.md`](retrospective.md)) | what previous runs got wrong here — **read in full**: standing instructions (capped at ten) and run stamps (one line each), both bounded by construction; the *Recent log* is **queried** by the task's nouns, like the archive — nothing caps it |
|
|
55
55
|
| 7a | **The retro archive** | `<artifacts>/retro/YYYY-QN.md` | *have we been bitten by this class before?* — **queried** by the task's nouns, never read end to end |
|
|
56
|
+
| 7b | **Lessons from other projects** | the local index every install has (`scripts/lessons.py query`), and a shared lesson store only where the session lists one — an Observatory — detected by its tools, never by a path ([`iteration.md`](iteration.md) → *The learning loop*, the read recipe) | *what did other projects' agents learn about this class?* — **queried** by the task's 3–6 nouns, at most ten lines from both; a lesson that fires is cited `[lesson L-…]` or `[lesson mem:…]`. The ledger names each layer's state: `local lessons: N match | absent | unavailable (no python3)`; `shared lessons: N match | 0 match (filter unknown) | unfilterable | absent`, where the first `observatory_search` call is the probe — and nothing else changes |
|
|
56
57
|
| 8 | **The knowledge wiki** | see below | distilled cross-project knowledge, prior sessions, why decisions were made |
|
|
57
58
|
| 9 | **Other doc repos the project names** | a docs repo URL or submodule in `CLAUDE.md`/`README`, a sibling checkout, a `docs/` monorepo package | specs, contracts and runbooks that live outside this repo |
|
|
58
59
|
| 10 | **Hosted doc systems the project names** | Notion / Confluence / Google Docs referenced in the project | the same, when the team keeps them there |
|
|
@@ -109,7 +109,10 @@ that was not.
|
|
|
109
109
|
|
|
110
110
|
**Rounds are counted from the ledger, never from memory**: distinct `pass N` values on
|
|
111
111
|
`touch:` lines at the review stage. A round that finds nothing ends the loop by
|
|
112
|
-
definition and needs no counting
|
|
112
|
+
definition and needs no counting — **except a round a node's risk class owes**: an R2 or R3
|
|
113
|
+
node's certification runs every owed round even after a clean one
|
|
114
|
+
([`certification.md`](certification.md) → *Rounds owed by risk class*). Those rounds are
|
|
115
|
+
the plan, not churn, and this guard does not count them.
|
|
113
116
|
|
|
114
117
|
## The re-render loop — a budget of one, two at most
|
|
115
118
|
|
|
@@ -72,6 +72,7 @@ a row pointing outside the bundle is the defect this file exists to catch.
|
|
|
72
72
|
| The design conversation, its hard gate, UI detection, user paths | `references/brainstorm.md` |
|
|
73
73
|
| Cutting a platform into modules, brick criteria, build order | `references/decomposition.md` |
|
|
74
74
|
| **Wave mode** — the whole board: the inventory, the canvas, the one question batch, the wave cut and the close pass | `references/waves.md` |
|
|
75
|
+
| **Release iteration** — the loop around the waves; the shared lesson store is optional, detected by the tools the session lists | `references/iteration.md` |
|
|
75
76
|
| What a spec must lock, the UX-track order, the module dossier | `references/spec.md` |
|
|
76
77
|
| The zero-context plan format, parallel groups, set equality | `references/planning.md` |
|
|
77
78
|
| The work graph: its fields, the verbs and their exit codes, and the three invariants a schema cannot state | `references/work-graph.md`, `scripts/graph.py`, `graph.schema.json` |
|
|
@@ -44,6 +44,7 @@ file exists to stop.
|
|
|
44
44
|
- A fix to one call site closes a call site, not a class
|
|
45
45
|
- The loop closes at stage 0
|
|
46
46
|
- Where a lesson goes when it is not about this project
|
|
47
|
+
- Recording to a shared lesson store, where the host has one
|
|
47
48
|
- Publishing the insight — the skill learns from every project that runs it
|
|
48
49
|
- The improvement iteration — what happens to a published insight next
|
|
49
50
|
- What may leave the project — the redaction list
|
|
@@ -416,6 +417,28 @@ file that accumulates universal rules is a fork of the skill that nobody named.
|
|
|
416
417
|
trigger and no authorization** — an instruction on rung 1 that everybody read as done.
|
|
417
418
|
The rest of this section is the mechanism.
|
|
418
419
|
|
|
420
|
+
## Recording to a shared lesson store, where the host has one
|
|
421
|
+
|
|
422
|
+
**One job: let the next agent on another project read this lesson at its stage 0.** An
|
|
423
|
+
entry whose class would be true in another project carries a `**Lesson:**` line — the class
|
|
424
|
+
key, the class in one sentence, when it applies, 3–8 search nouns. That line makes it a
|
|
425
|
+
*lesson* ([`lesson.schema.json`](../templates/lesson.schema.json), lesson/1): with task-pipeline installed, its `scripts/lessons.py add` puts it in the
|
|
426
|
+
local index every install has, and where the session lists a shared lesson store (an
|
|
427
|
+
Observatory) it is also recorded there, as a proposal. An entry without the line stays
|
|
428
|
+
private to this project.
|
|
429
|
+
|
|
430
|
+
**A lesson is not a publication, but the redaction list below binds it too — with one
|
|
431
|
+
relaxation.** On this machine or estate (the local index, a shared store), rule 2 is relaxed
|
|
432
|
+
for exactly two fields: the evidence commit and the pseudonymous project key (`p-` and 12 hex
|
|
433
|
+
of the root commit, the same key in the store, never the repository's name or a
|
|
434
|
+
`project:<slug>` id). A private repository's SHA names nothing; a public repository's SHA is
|
|
435
|
+
searchable, so it stays in its field and never in text. Rules 1, 3, 4 and 5 still bind every
|
|
436
|
+
text field: no path at all, no code or value, no person's, company's, customer's or
|
|
437
|
+
product's name, a title that states the class. `retro.publish` strips the whole origin.
|
|
438
|
+
The read side is source 7b of [`knowledge-sources.md`](knowledge-sources.md). Where no store exists the run
|
|
439
|
+
says `shared lessons: absent` rather than nothing.
|
|
440
|
+
Mechanics and the read and write recipes: [`iteration.md`](iteration.md) → *The learning loop*.
|
|
441
|
+
|
|
419
442
|
## Publishing the insight — the skill learns from every project that runs it
|
|
420
443
|
|
|
421
444
|
**One job: stop a lesson dying in the repository that learned it.**
|
|
@@ -507,7 +530,9 @@ is what you want them to do.
|
|
|
507
530
|
## What may leave the project — the redaction list
|
|
508
531
|
|
|
509
532
|
An issue is a **public artefact in someone else's repository**. What travels is the
|
|
510
|
-
*class*; what stays is everything that identifies where it happened.
|
|
533
|
+
*class*; what stays is everything that identifies where it happened. This list binds
|
|
534
|
+
whatever leaves the estate, and every lesson too; *Recording to a shared lesson store*
|
|
535
|
+
above names the one relaxation a lesson kept on this machine or estate gets.
|
|
511
536
|
|
|
512
537
|
| Goes | Stays |
|
|
513
538
|
|---|---|
|
|
@@ -4,8 +4,9 @@ For each stage: what it does, what to invoke, artifacts, and the **GATE** that
|
|
|
4
4
|
must pass before advancing. Each gate is tagged with its **type** — `auto` (the
|
|
5
5
|
orchestrator verifies the check itself, pass/fail), `judgment` (a named judge
|
|
6
6
|
rules where no complete deterministic check exists — [`gates.md`](gates.md) →
|
|
7
|
-
*The judgment gate*) or `manual` (
|
|
8
|
-
|
|
7
|
+
*The judgment gate*) or `manual` (requires the operator's explicit go, including a
|
|
8
|
+
specific standing authorization naming the act, cited with its source and scope;
|
|
9
|
+
otherwise wait). These stages (0 intake + 1→10) are the plugin's
|
|
9
10
|
**example** flow, encoded in `pipeline.example.json` against the universal contract
|
|
10
11
|
`pipeline.schema.json`; a host project replaces it with its own
|
|
11
12
|
stages/agents/types (see SKILL.md → *Bring your own skills*).
|
|
@@ -108,7 +109,10 @@ never that the work was skipped quietly.
|
|
|
108
109
|
read **in full** — ten standing rows and ten stamps, both bounded **by a cap**, and they bind this
|
|
109
110
|
run; stamp each instruction as it fires. Its *Recent log* is **queried** by the
|
|
110
111
|
task's nouns, not read: uncapped narrative inside a binding source is what makes the
|
|
111
|
-
capped part get skimmed, [`retrospective.md`](retrospective.md)),
|
|
112
|
+
capped part get skimmed, [`retrospective.md`](retrospective.md)), **lessons from other
|
|
113
|
+
projects** (source 7b, [`knowledge-sources.md`](knowledge-sources.md): `scripts/lessons.py
|
|
114
|
+
query` over the local index, then a shared store where the session lists one — each
|
|
115
|
+
layer's state printed in the source ledger), the **knowledge wiki** if one is
|
|
112
116
|
installed
|
|
113
117
|
([obsidian-wiki](https://github.com/ar9av/obsidian-wiki) — recommended,
|
|
114
118
|
never required), and any **other repo or hosted doc system the project names as
|
|
@@ -180,8 +184,10 @@ never that the work was skipped quietly.
|
|
|
180
184
|
names the target and the preconditions.
|
|
181
185
|
- **The run mode is settled here too** ([`continuity.md`](continuity.md)): does
|
|
182
186
|
the run advance item-by-item with no check-in between items, and on what
|
|
183
|
-
interval?
|
|
184
|
-
answer and is not re-asked
|
|
187
|
+
interval? Exactly three sources arm it: `pipeline.json` → `run.loop` (read
|
|
188
|
+
first — a recorded mode is the answer and is not re-asked), the operator's answer
|
|
189
|
+
here, or a standing instruction the host already carries. The ledger names which, once:
|
|
190
|
+
`loop: armed by <source:line>`. **Absent all three, it is off.** It buys pacing, never
|
|
185
191
|
authorization: no loop mode collapses a `manual` gate or an outward act. The
|
|
186
192
|
same file carries the other half of pacing — the context budget, which fires
|
|
187
193
|
only on a harness signal or the operator's word, never on an estimate.
|
|
@@ -251,7 +257,10 @@ never that the work was skipped quietly.
|
|
|
251
257
|
own churn detection mechanical and reads the `touch:` lines, and
|
|
252
258
|
[`progress.md`](progress.md) derives the rail and the iteration counter from the
|
|
253
259
|
`stage:` and `iter:` lines. The header goes out before the interview because a run
|
|
254
|
-
that announces its position only at the end announced it to nobody.
|
|
260
|
+
that announces its position only at the end announced it to nobody. A new ledger starts a new
|
|
261
|
+
run's memory state: a previous run's "memory unavailable" marker is honoured only while
|
|
262
|
+
its own ledger line is in the ledger, so a reseeded ledger — even the same day — is nudged
|
|
263
|
+
again, and the stale marker is cleared at the next `stage_checkpoint.py record`.
|
|
255
264
|
- **The board is read, or seeded.** `<artifacts>/backlog.md` ([`backlog.md`](backlog.md)) — its **open count is quoted in the brief**, measured by a command at the top of the run rather than inherited from the last run's report. Absent ⇒ seeded from the template and said so; an empty board and no board are the same thing to work on, and only one of them can be appended to.
|
|
256
265
|
|
|
257
266
|
## 1 — Docs study
|
|
@@ -304,8 +313,11 @@ never that the work was skipped quietly.
|
|
|
304
313
|
can name is a node the seam and product tiers cannot scope, so `touches` and `serves` are
|
|
305
314
|
what make the two outer readings possible at all.
|
|
306
315
|
- **The queue exists here, so the loop arms here** ([`continuity.md`](continuity.md) →
|
|
307
|
-
*Part 1a*). Where `run.loop
|
|
308
|
-
|
|
316
|
+
*Part 1a*). Where the loop is armed — `run.loop` with `arm: after-decomposition`, the
|
|
317
|
+
operator's preflight answer, or a standing instruction the host carries (which arms here
|
|
318
|
+
unless it says otherwise) — and the map holds more than one module, arm the mode at the
|
|
319
|
+
close of this stage, cite the source as `loop: armed by <source:line>`, and print one
|
|
320
|
+
line: the mode, and
|
|
309
321
|
either the job id and its cancel command (`interval`) or the delay chosen and why
|
|
310
322
|
(`dynamic`). Arming collapses no gate and authorizes no outward act; it decides only
|
|
311
323
|
that the run does not stop to ask *"shall I take the next one?"*. Single-module work
|
|
@@ -589,7 +601,12 @@ never that the work was skipped quietly.
|
|
|
589
601
|
`partial` ships only with the operator's explicit acceptance. A gap is cheapest to
|
|
590
602
|
close before it ships, and the operator is already present at this gate. **The
|
|
591
603
|
carry-over count is printed beside this verdict.** Deploy is outward → explicit
|
|
592
|
-
operator go
|
|
604
|
+
operator go, or a specific standing authorization naming that act, cited with its source
|
|
605
|
+
and scope. Authorization to release does not silently authorize a separate deploy. If
|
|
606
|
+
`release.authorized_by` is present, read that source and verify its authority,
|
|
607
|
+
currentness and scope for the tag and publication; the locator is not authorization.
|
|
608
|
+
If absent, inspect existing recorded authority before asking for a missing grant.
|
|
609
|
+
**Before the tag, read
|
|
593
610
|
the release's approval mode** (`pipeline.json` → `release.approval`; then
|
|
594
611
|
[`deploy-targets.md`](deploy-targets.md) → *When the release waits for an approval*):
|
|
595
612
|
`checks` — push the tag, watch the run selected by the tag, report; `reviewers` — the
|
|
@@ -790,7 +807,11 @@ never that the work was skipped quietly.
|
|
|
790
807
|
commit that retired it**), then **write an entry only if
|
|
791
808
|
the run diverged** (symptom · the stage it surfaced at · the stage that *owned* it
|
|
792
809
|
· root cause · fix, mechanical before instruction before expiring note · the check
|
|
793
|
-
that catches it next time).
|
|
810
|
+
that catches it next time). An entry whose class would be true in another project
|
|
811
|
+
carries a `**Lesson:**` line, and **`scripts/lessons.py add --retro <retro> --entry
|
|
812
|
+
<anchor>` puts it in the local lesson index** — and, where the session lists a shared
|
|
813
|
+
store, `emit-observatory` → the tool → `record` ([`iteration.md`](iteration.md) → *The
|
|
814
|
+
learning loop*). Every run stamps and prunes; a retro left empty after
|
|
794
815
|
a messy run is the failure the file exists to stop. Stage 0 reads the standing
|
|
795
816
|
instructions in full next time, which is why the cap is not negotiable.
|
|
796
817
|
- **GATE (manual):** the ladder walk ran and its absences became REQ rows before
|
|
@@ -814,7 +835,10 @@ never that the work was skipped quietly.
|
|
|
814
835
|
the same archive, and both counts print beside the verdict** (the stamp table is read
|
|
815
836
|
in full at stage 0, so *one line per run* is a slope the prune has to stop), the run
|
|
816
837
|
stamped with its commit, every SHA in either file resolvable, and the
|
|
817
|
-
counts printed beside this verdict**; **
|
|
838
|
+
counts printed beside this verdict**; **every entry with a `**Lesson:**` line is in the
|
|
839
|
+
local index (`lessons.py add` printed its id) or its refusal is printed, and the shared
|
|
840
|
+
store's state is one ledger line** (`recorded`, `store refused — <code>`, or `shared
|
|
841
|
+
lessons: absent`); **where `pipeline.json` → `retro.publish` is
|
|
818
842
|
set, the skill-level insight is published as an issue on the skill's repository —
|
|
819
843
|
its body printed in full first, the printed string and the sent string being one
|
|
820
844
|
string, and the five redaction rules applied
|