task-pipeline-skill 1.97.1 → 1.99.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/CHANGELOG.md +283 -0
- package/HOW-IT-WORKS.md +17 -13
- package/README.md +13 -10
- package/SKILL-CARD.md +1 -1
- package/cursor/rules/task-pipeline.mdc +22 -14
- package/evals/task-pipeline.evals.json +3 -3
- package/package.json +1 -1
- package/plugins/task-pipeline/.claude-plugin/plugin.json +2 -2
- package/plugins/task-pipeline/commands/task-pipeline.md +14 -219
- package/plugins/task-pipeline/skills/evidence-docs/SKILL.md +8 -1
- package/plugins/task-pipeline/skills/evidence-docs/references/GENERATED.md +1 -1
- package/plugins/task-pipeline/skills/evidence-docs/references/learned.md +24 -1
- package/plugins/task-pipeline/skills/evidence-docs/references/retrospective.md +21 -576
- package/plugins/task-pipeline/skills/project-audit/SKILL.md +8 -1
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +162 -230
- package/plugins/task-pipeline/skills/task-pipeline/graph.schema.json +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +9 -9
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +11 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/acceptance.md +2 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/brainstorm.md +3 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/build.md +22 -7
- package/plugins/task-pipeline/skills/task-pipeline/references/certification.md +49 -44
- package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +6 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/doctrine-map.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/finish-line.md +13 -8
- package/plugins/task-pipeline/skills/task-pipeline/references/iteration.md +188 -32
- package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +5 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/learned.md +24 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md +17 -7
- package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +49 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/spec.md +5 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/stage-00-intake.md +39 -25
- package/plugins/task-pipeline/skills/task-pipeline/references/stage-05-dev.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +36 -28
- package/plugins/task-pipeline/skills/task-pipeline/references/waves.md +8 -5
- package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +79 -22
- package/plugins/task-pipeline/skills/task-pipeline/scripts/lessons.py +1229 -110
- package/plugins/task-pipeline/skills/task-pipeline/scripts/retro_budget.py +305 -0
- package/plugins/task-pipeline/skills/task-pipeline/scripts/short_path.py +70 -0
- package/plugins/task-pipeline/skills/task-pipeline/scripts/waves.py +22 -10
- package/plugins/task-pipeline/skills/task-pipeline/templates/canvas.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/templates/lesson.schema.json +38 -10
- package/plugins/task-pipeline/skills/task-pipeline/templates/retro-archive.md +9 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/retro.md +10 -1
|
@@ -1,576 +1,21 @@
|
|
|
1
|
-
# Retrospective
|
|
2
|
-
|
|
3
|
-
The
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
| the same file's **Recent log** | narrative entries, **uncapped by design** — the heading said *entries from the last five run stamps* until 2026-08-20 while the section held 25 going back nine days, a bound in a heading that nothing enforced | stage 0, **queried** by the task's nouns. It said *in full* until 2026-08-10, when it measured **74%** of the file: an uncapped section inside a binding source is what makes the capped part get skimmed |
|
|
23
|
-
| `docs/evidence/retro/YYYY-QN.md` — the archive | every entry and every retirement ever written, append-only | **queried** by the task's nouns; never read end to end |
|
|
24
|
-
|
|
25
|
-
Seed the archive from [`../templates/retro-archive.md`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/templates/retro-archive.md).
|
|
26
|
-
|
|
27
|
-
Every run writes a **stamp** and runs the **prune**. Only a run that *diverged*
|
|
28
|
-
writes an entry. A retro that is empty after a messy run is the exact failure this
|
|
29
|
-
file exists to stop.
|
|
30
|
-
|
|
31
|
-
## Contents
|
|
32
|
-
|
|
33
|
-
- Write the entry only for a divergence — and name the layer that owned it
|
|
34
|
-
- Every lesson carries its commit
|
|
35
|
-
- Never amend a commit a record already names
|
|
36
|
-
- A release stamp names the TAG, and the merge commit — never the branch's own SHA
|
|
37
|
-
- The stamp table is capped at ten, and *one line per run* was never a cap
|
|
38
|
-
- `publish:` is a line in the verdict, not a silence
|
|
39
|
-
- Rotation — the archive is how pruning stops losing things
|
|
40
|
-
- Three grades of fix — take the highest one that can work
|
|
41
|
-
- Stamp first, then prune, then write
|
|
42
|
-
- The prune — mandatory, and it runs after the stamp
|
|
43
|
-
- When the prune cannot run, say so — it is not the same as nothing to prune
|
|
44
|
-
- A fix to one call site closes a call site, not a class
|
|
45
|
-
- The loop closes at stage 0
|
|
46
|
-
- Where a lesson goes when it is not about this project
|
|
47
|
-
- Recording to a shared lesson store, where the host has one
|
|
48
|
-
- Publishing the insight — the skill learns from every project that runs it
|
|
49
|
-
- The improvement iteration — what happens to a published insight next
|
|
50
|
-
- What may leave the project — the redaction list
|
|
51
|
-
- Rationalizations
|
|
52
|
-
|
|
53
|
-
## Write the entry only for a divergence — and name the layer that owned it
|
|
54
|
-
|
|
55
|
-
An entry is owed when the run did not go as planned: a gate reopened, a stage was
|
|
56
|
-
re-entered, a fix broke something it wasn't touching, an estimate was wrong by a
|
|
57
|
-
factor, the operator had to intervene where the brief said they wouldn't.
|
|
58
|
-
|
|
59
|
-
Required fields, and none of them is optional:
|
|
60
|
-
|
|
61
|
-
| Field | The rule |
|
|
62
|
-
|---|---|
|
|
63
|
-
| **Symptom** | what actually happened, in one line, with the evidence (a command, a `file:line`, the gate that reopened) |
|
|
64
|
-
| **Surfaced at** | the stage where it became visible |
|
|
65
|
-
| **Owned by** | the stage that *let it through* — usually an earlier one. A finding belongs to the layer that owns it; recording it against the stage that tripped over it is how the same defect returns |
|
|
66
|
-
| **Root cause** | why the pipeline permitted it. "The agent was careless" is not a cause — it is the absence of one, and it produces no fix |
|
|
67
|
-
| **Fix** | one of the three grades below |
|
|
68
|
-
| **The check** | what would have caught this the first time. If the honest answer is "nothing yet", that is the fix, and it is grade 1 |
|
|
69
|
-
| **Commit** | the short SHA of the change that fixed it |
|
|
70
|
-
|
|
71
|
-
## Every lesson carries its commit
|
|
72
|
-
|
|
73
|
-
A standing instruction carries **two** SHAs — `Commit` (the change that introduced
|
|
74
|
-
it) and `Fired at` (the last run in which it fired) — and every log entry and every
|
|
75
|
-
retirement carries one.
|
|
76
|
-
|
|
77
|
-
**Why a SHA and not a `file:line`.** A line number rots at the next edit, and then
|
|
78
|
-
the evidence points at something that has moved or gone; the reader is left with a
|
|
79
|
-
claim and no way to check it. A commit is immutable and carries the diff, the
|
|
80
|
-
message and the parent, so `git show <sha>` reconstructs the entire incident two
|
|
81
|
-
months later — which is exactly when the same class comes back and somebody needs
|
|
82
|
-
to know whether this was already understood.
|
|
83
|
-
|
|
84
|
-
**Every SHA must resolve — and resolving is not enough.** This is
|
|
85
|
-
[`learned.md`](learned.md) rule 14 — *a document may not send a reader to something
|
|
86
|
-
absent* — applied to history, and it is mechanical: the documentation gate runs
|
|
87
|
-
`git rev-parse --verify --quiet <sha>^{commit}` over every backticked SHA in the retro
|
|
88
|
-
and its archive ([`gates.md`](gates.md)).
|
|
89
|
-
|
|
90
|
-
**But a commit that was amended away still resolves on the machine that amended it**,
|
|
91
|
-
and exists in no clone. The gate therefore also requires each SHA to be **reachable
|
|
92
|
-
from `HEAD`** — `git merge-base --is-ancestor <sha> HEAD` — because that is the
|
|
93
|
-
question a reader two months from now is actually asking, and the weaker one passes
|
|
94
|
-
for as long as the object survives locally.
|
|
95
|
-
|
|
96
|
-
## Never amend a commit a record already names
|
|
97
|
-
|
|
98
|
-
Measured 2026-08-16, twice in one close-out and twenty minutes apart. The sequence is
|
|
99
|
-
seductive because each step is right on its own: stamp the run with its commit → the
|
|
100
|
-
stamp is part of the run, so fold it in with `--amend` → the amend mints a new SHA →
|
|
101
|
-
the stamp now names a commit that will never reach the remote.
|
|
102
|
-
|
|
103
|
-
The rule is one line and it is absolute: **once a file names a SHA, that commit is
|
|
104
|
-
frozen.** A correction goes in a *follow-up commit*, never a second amend — amending to
|
|
105
|
-
repair a stamp is the loop that produced the problem, and the second attempt lands in the
|
|
106
|
-
same place as the first.
|
|
107
|
-
|
|
108
|
-
Practically, that makes the order:
|
|
109
|
-
|
|
110
|
-
1. commit the work;
|
|
111
|
-
2. **then** stamp, in a commit of its own, naming the commit from step 1;
|
|
112
|
-
3. prune and write the entry in that same second commit, or a third.
|
|
113
|
-
|
|
114
|
-
The stamp costs one line and one commit. A run that folds it back into the work to keep
|
|
115
|
-
the history tidy is trading a reader's ability to find the incident for the appearance of
|
|
116
|
-
tidiness — and the reader is the entire reason the stamp exists.
|
|
117
|
-
|
|
118
|
-
## A release stamp names the TAG, and the merge commit — never the branch's own SHA
|
|
119
|
-
|
|
120
|
-
The sibling of the amend rule, one level up, and it recurred three times after the amend
|
|
121
|
-
rule was written because each step is right on its own: stamp the run on the PR branch →
|
|
122
|
-
the stamp cites that branch's HEAD → `main` is **rebase-merge-only** by ruleset → the
|
|
123
|
-
rebase mints new SHAs → the object the stamp names dies with the branch.
|
|
124
|
-
|
|
125
|
-
Measured across `v1.79.1` (twice) and `v1.80.0`, and again on 2026-09-05 in the umbrella
|
|
126
|
-
repository, where it burned **two tags** in one afternoon. Each recurrence costs the same
|
|
127
|
-
two refusals, both of them correct:
|
|
128
|
-
|
|
129
|
-
1. the **unstamped-release guard** reads the tag's own commit range, finds no stamp that
|
|
130
|
-
resolves, and refuses the release;
|
|
131
|
-
2. the **documentation gate** resolves every backticked commit reference against a *fresh
|
|
132
|
-
clone*, where the branch SHA has never existed, and refuses again.
|
|
133
|
-
|
|
134
|
-
Then it costs one extra PR and one full CI round — measured at 57–97 minutes here.
|
|
135
|
-
|
|
136
|
-
**The shape that survives a rebase-merge is the tag.** A SHA the author can know before
|
|
137
|
-
the merge is precisely the SHA the merge is about to destroy, so the rule cannot be
|
|
138
|
-
"write a better SHA". Two spellings are allowed and nothing else:
|
|
139
|
-
|
|
140
|
-
- **cite the release tag** — `v1.84.1` is immutable, it is the thing being released, and
|
|
141
|
-
it resolves in any clone;
|
|
142
|
-
- **or write the stamp AFTER the merge**, in a second PR, naming the **merge commit**.
|
|
143
|
-
This is the default, because it is what the amend rule already forces one level down:
|
|
144
|
-
once a file names a SHA, that commit is frozen, so the SHA has to exist first.
|
|
145
|
-
|
|
146
|
-
The order for a release run is therefore:
|
|
147
|
-
|
|
148
|
-
1. merge the work; the merge commit is now a real object on `main`;
|
|
149
|
-
2. **then** stamp, in a second PR, naming that merge commit;
|
|
150
|
-
3. cut the tag **locally**, run `npm run test:all` against the tag's own tree, and push
|
|
151
|
-
the tag only if that run is green — `git merge-base --is-ancestor <stamp-sha> HEAD`
|
|
152
|
-
is what the release gate will ask, so ask it first.
|
|
153
|
-
|
|
154
|
-
Step 3 is not optional and it is not the same as a green branch build: the branch tree
|
|
155
|
-
and the tag's tree differ by whatever the merge did, and **a tag cannot be repaired** —
|
|
156
|
-
deleting or re-pointing one is refused by repository rule, correctly, because a tag is a
|
|
157
|
-
promise about a tree. A tag pushed on a green branch build is a promise about a tree
|
|
158
|
-
nobody ran.
|
|
159
|
-
|
|
160
|
-
## The stamp table is capped at ten, and *one line per run* was never a cap
|
|
161
|
-
|
|
162
|
-
Measured 2026-08-10: standing instructions **~1 234 tok** behind a cap of ten, run stamps
|
|
163
|
-
**~2 099 tok over 27 rows** behind nothing. Both are read in full at stage 0 and both
|
|
164
|
-
were described as *bounded by construction*. One line per run is a **slope**: at a
|
|
165
|
-
hundred runs the stamp table alone is ~7 800 tokens of a floor the doctrine believes is
|
|
166
|
-
bounded.
|
|
167
|
-
|
|
168
|
-
This is the same shape the 2026-08-10 audit found in the narrative log and moved out of
|
|
169
|
-
the floor — and it left the neighbour in the same file, with the same property, because
|
|
170
|
-
the neighbour's growth is *tidy*. A tidy slope is still a slope.
|
|
171
|
-
|
|
172
|
-
**The cap is ten and the trigger is why.** The cold rule reads *the last five run
|
|
173
|
-
stamps*; ten is that with a margin, so a stamp rotating out can never be one the trigger
|
|
174
|
-
needed. At the eleventh, the oldest row moves — whole, with its verdict and its retro
|
|
175
|
-
column — into `docs/evidence/retro/YYYY-QN.md` under `## Run stamps`, append-only,
|
|
176
|
-
like every other rotation. **The count is printed at the prune**, beside the standing
|
|
177
|
-
instructions' own count, so a table that stops rotating is visible rather than merely
|
|
178
|
-
large.
|
|
179
|
-
|
|
180
|
-
## `publish:` is a line in the verdict, not a silence
|
|
181
|
-
|
|
182
|
-
Reported from another project, and the report is about this file: an operator asked, after
|
|
183
|
-
many runs, why nothing had ever been published.
|
|
184
|
-
|
|
185
|
-
The floor is right and does not move — publishing is opt-in per project, off by default,
|
|
186
|
-
because an outward act taken from a generic flag is an outward act nobody authorized. The
|
|
187
|
-
gap is what happens next. A project with no configuration produces, run after run,
|
|
188
|
-
retrospective entries carrying lessons about **the skill**, and the mechanism reports
|
|
189
|
-
nothing, because it was never armed. **An unarmed mechanism and a mechanism with nothing
|
|
190
|
-
to say are indistinguishable from the outside** — [`gates.md`](gates.md)'s false success,
|
|
191
|
-
applied to this bundle's own learning path. In the reporting project several runs had
|
|
192
|
-
produced skill-level lessons over months; the count of published insights was zero and no
|
|
193
|
-
gate had ever mentioned it.
|
|
194
|
-
|
|
195
|
-
Stage 10 prints ratchets and two disclosures — what the run declined to claim, and what a
|
|
196
|
-
check never looked at. Publishing is neither, so its absence is not observable at the only
|
|
197
|
-
moment anyone is reading.
|
|
198
|
-
|
|
199
|
-
**So stage 10's block carries one line for publication:**
|
|
200
|
-
|
|
201
|
-
```
|
|
202
|
-
publish: <issue url> — opened this run
|
|
203
|
-
publish: 0 (configured, nothing insight-grade)
|
|
204
|
-
publish: not configured (N insight-grade entries stayed local)
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
It arms nothing and authorizes nothing; it makes the silence legible. A count of zero
|
|
208
|
-
beside *configured* is a fine answer. A **blank** where configuration is absent is how an
|
|
209
|
-
instruction went unread for eight releases.
|
|
210
|
-
|
|
211
|
-
**The failure mode survived its own fix, in a quieter form.** This section once said
|
|
212
|
-
*"open an issue upstream"* while naming no repository, no trigger and no authorization,
|
|
213
|
-
and every reader took it as done. The mechanism that replaced it is correct — and until
|
|
214
|
-
this line existed it still had no way to say it never ran.
|
|
215
|
-
|
|
216
|
-
## Rotation — the archive is how pruning stops losing things
|
|
217
|
-
|
|
218
|
-
At the prune, entries older than the last five run stamps **move** to
|
|
219
|
-
`docs/evidence/retro/YYYY-QN.md`. Moving is not deleting.
|
|
220
|
-
|
|
221
|
-
- The archive is **append-only**, and a retirement writes its line **there**, with
|
|
222
|
-
the trigger that retired it and the commit.
|
|
223
|
-
- A retired rule that comes back as a real failure is a grade-1 fix — **with its
|
|
224
|
-
history attached**, which is the whole return on having archived it.
|
|
225
|
-
- Nothing is ever removed from the archive to keep it tidy. It is not read in full,
|
|
226
|
-
so its size costs nothing; its completeness is what it is for.
|
|
227
|
-
|
|
228
|
-
## Three grades of fix — take the highest one that can work
|
|
229
|
-
|
|
230
|
-
**Grade 1 — mechanical.** A test, a lint rule, a gate criterion, a CI step, a hook.
|
|
231
|
-
The check *is* the memory: nothing has to be read, remembered or pruned later. Log
|
|
232
|
-
it as `landed` and move on — it never becomes a standing instruction and never
|
|
233
|
-
costs a slot.
|
|
234
|
-
|
|
235
|
-
**Grade 2 — a standing instruction.** A rule an agent must read, for the cases no
|
|
236
|
-
check can decide (a judgement, a precedence, a "ask before X"). It costs one of the
|
|
237
|
-
ten slots and it is **only accepted with its retirement trigger written at birth**
|
|
238
|
-
(below).
|
|
239
|
-
|
|
240
|
-
**Grade 3 — a note with an expiry.** For something still being understood. Maximum
|
|
241
|
-
**two runs**. At the second stamp it is promoted to grade 1 or 2, or deleted. A note
|
|
242
|
-
with no expiry is how a file becomes unreadable one honest line at a time.
|
|
243
|
-
|
|
244
|
-
Prefer grade 1 whenever a check can decide it. This is the same law as
|
|
245
|
-
[`audit.md`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/references/audit.md) → *A class that repeats twice becomes a gate, not a note*: a
|
|
246
|
-
rule that could have been a check gets read twice and obeyed once.
|
|
247
|
-
|
|
248
|
-
## Stamp first, then prune, then write
|
|
249
|
-
|
|
250
|
-
`learned.md` rule 21. This order used to be *prune first*, and that was a **deadlock**, not a
|
|
251
|
-
preference: the cold trigger below reads *the last five run stamps*, and the stamp was written after
|
|
252
|
-
the prune. The trigger read a counter the same stage produced later, so on any list it had never run
|
|
253
|
-
on real data — and it stays unreadable for exactly as long as nobody stamps.
|
|
254
|
-
|
|
255
|
-
Measured on a real project: last entry five days old; stamps per day 33, 20, 26, **3, 0** — the zero
|
|
256
|
-
on a day with 107 commits — and the list sitting at **10 of 10**. Every run arrived at a stage that
|
|
257
|
-
opened with a full list, an unusable trigger and a mandatory deletion. It was not skipped out of
|
|
258
|
-
laziness; its first step could not be performed, and the cheap step that would have made it
|
|
259
|
-
performable was queued behind it.
|
|
260
|
-
|
|
261
|
-
**The stamp is one line and costs nothing.** It is also the only thing that makes the prune
|
|
262
|
-
computable, which is why it goes first:
|
|
263
|
-
|
|
264
|
-
```bash
|
|
265
|
-
printf '%s · %s\n' "$(date +%F)" "$(git rev-parse --short HEAD)" >> docs/evidence/retro.md
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
## The prune — mandatory, and it runs after the stamp
|
|
269
|
-
|
|
270
|
-
A lesson that lands in a cluttered file is a lesson nobody will reach — so the prune still runs
|
|
271
|
-
before the entry is written. It runs *after* the stamp, because it reads it.
|
|
272
|
-
|
|
273
|
-
Every row carries its own trigger in a **`Retire when`** column AND a **class**,
|
|
274
|
-
both written at birth — a rule whose retirement condition is decided later is a
|
|
275
|
-
rule the prune can only argue about. Three classes, because coldness means
|
|
276
|
-
three different things:
|
|
277
|
-
|
|
278
|
-
- **permanent** — a safety invariant, a contract, a recovery procedure. The
|
|
279
|
-
absence of the rare event it guards is not absence of value: **coldness never
|
|
280
|
-
retires a permanent rule** — only *became a check* or *surface gone* do.
|
|
281
|
-
- **situational** — fires when a named EXPOSURE occurs (a payment run, a
|
|
282
|
-
migration, a release). Its row names the exposure, and the prune counts
|
|
283
|
-
**exposure opportunities, not raw runs**: five runs that never touched
|
|
284
|
-
payments say nothing about a payment rule.
|
|
285
|
-
- **temporary** — a workaround with a **TTL** and the replacing mechanism named
|
|
286
|
-
at birth. TTL only exists on this class.
|
|
287
|
-
|
|
288
|
-
Check **every** standing instruction against three triggers:
|
|
289
|
-
|
|
290
|
-
| Trigger | Test | Then |
|
|
291
|
-
|---|---|---|
|
|
292
|
-
| **It became a check** | the rule is now enforced by a test, lint, gate or hook | delete it — the check is the memory, and keeping both means it is read twice and obeyed once |
|
|
293
|
-
| **Its surface is gone** | resolve every path, command, stage and tool it names; any that no longer exists | delete it — it now describes a system nobody is running |
|
|
294
|
-
| **It went cold** | *situational only*: it fired in none of the last **five exposure opportunities** — or no exposure occurred in **sixty days** | mark **review-needed** and put the question to the operator with the exposure counts — cold is evidence worth reviewing, never an automatic deletion. *temporary*: TTL expired → archive it **once the replacing mechanism is verified present** (its check resolves and runs); an expired workaround whose replacement is absent is a live defect, not a retirement. *permanent*: not applicable |
|
|
295
|
-
|
|
296
|
-
Every removal stays explainable: the archive line names the class, the trigger
|
|
297
|
-
and the evidence (the exposure counts, or the replacing mechanism's check).
|
|
298
|
-
|
|
299
|
-
**Each trigger is a command, not a judgement.** A retirement condition nobody can run is a
|
|
300
|
-
condition nobody applies, which is how a list reaches ten and stops being read:
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
# became a check — the rule's own words appear in something that runs
|
|
304
|
-
grep -rl "$RULE_KEYWORD" scripts/ test/ .github/workflows/ Makefile* 2>/dev/null
|
|
305
|
-
|
|
306
|
-
# surface is gone — every path, command and tool it names, resolved
|
|
307
|
-
grep -oE '`[^`]+`' <<<"$RULE_TEXT" | tr -d '`' | while read -r t; do
|
|
308
|
-
[ -e "$t" ] || command -v "$t" >/dev/null || echo "MISSING: $t"; done
|
|
309
|
-
|
|
310
|
-
# went cold — fired in none of the last five stamps
|
|
311
|
-
tail -n 200 docs/evidence/retro.md | grep -c "$RULE_ID"
|
|
312
|
-
|
|
313
|
-
# ...OR in the last 60 days, whichever comes first — see below for why both
|
|
314
|
-
git log -1 --format=%cd --date=short -S"$RULE_ID" -- docs/evidence/retro.md
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
Anything the first two print is a deletion. A zero from the third — counted against
|
|
318
|
-
**exposure opportunities** for a situational rule (`grep -c "$EXPOSURE_MARK"` over the same
|
|
319
|
-
stamps, so the denominator is runs that COULD have fired it) — marks **review-needed**, and the
|
|
320
|
-
run states the counts rather than the conclusion (`learned.md` rule 19 — an empty result and an
|
|
321
|
-
unrun command look identical). Only the operator's answer, or a temporary rule's verified
|
|
322
|
-
replacement, turns review-needed into a removal.
|
|
323
|
-
|
|
324
|
-
**Why the cold trigger needs two units, and it is not belt-and-braces.** A run stamp is written by
|
|
325
|
-
a run *of this pipeline*. Where a project ships some of its work another way, the stamp counter
|
|
326
|
-
stops while the work does not — so "the last five stamps" can span an arbitrary amount of change,
|
|
327
|
-
and a rule sits unexamined for exactly as long as the pipeline goes unused. Measured on this
|
|
328
|
-
repository: **ten consecutive releases, `v1.16.0` through `v1.23.0`, carry no stamp at all.** Over
|
|
329
|
-
that stretch the trigger was not strict or lenient; it was **unreadable**, and a list capped at ten
|
|
330
|
-
with an unreadable retirement condition fills up and stops being pruned.
|
|
331
|
-
|
|
332
|
-
The wall-clock alternative fires on elapsed time, which nothing can stall. Keep both: the stamp
|
|
333
|
-
count is the better signal when the pipeline is in use, and the date is the one that still works
|
|
334
|
-
when it is not.
|
|
335
|
-
|
|
336
|
-
**The stamp gap is itself a number worth printing.** A retro whose newest stamp is far behind the
|
|
337
|
-
repository's newest release is telling you the retro is describing a smaller world than the one
|
|
338
|
-
that shipped — the same failure `learned.md` rule 16 records for a work-list. State it beside the
|
|
339
|
-
retro counts:
|
|
340
|
-
|
|
341
|
-
```bash
|
|
342
|
-
git tag --sort=-v:refname | head -1 # newest release
|
|
343
|
-
grep -m1 -oE '`[0-9a-f]{7,}`' docs/evidence/retro.md # newest stamped commit
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
Then the cap: **ten standing instructions, hard.** At eleven you do not get to keep
|
|
347
|
-
them all — the oldest never-fired one goes. "But all of them matter" is precisely
|
|
348
|
-
the state in which the list stopped being read, and the ninth stale rule is what
|
|
349
|
-
discredits the two that are load-bearing.
|
|
350
|
-
|
|
351
|
-
**Every deletion writes one line in the archive** — id, date, which trigger fired,
|
|
352
|
-
and the commit. Silent deletion is forbidden: the record is what survives, the
|
|
353
|
-
instruction is what leaves.
|
|
354
|
-
|
|
355
|
-
**Print the counts beside the gate verdict**, the same way the carry-over ledger
|
|
356
|
-
does ([`audit.md`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/references/audit.md) → *What can't be fixed now becomes a ratchet, never a TODO*):
|
|
357
|
-
|
|
358
|
-
```
|
|
359
|
-
GATE 10 acceptance: PASS — 14/14 REQ verified
|
|
360
|
-
carry-over: 0 unresolved · retro: 7 standing (was 9) · retired 3 · added 1
|
|
361
|
-
abstained: 2 · unlooked: 3
|
|
362
|
-
holds: 10 — none — enumerated 8/8 classes
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
A pruned list that nobody prints is a list that quietly grows back.
|
|
366
|
-
|
|
367
|
-
## When the prune cannot run, say so — it is not the same as nothing to prune
|
|
368
|
-
|
|
369
|
-
The prune retires a standing instruction against its triggers, and the cold trigger counts
|
|
370
|
-
run stamps. Meeting a list whose rows carry **no id and no retirement condition**, the
|
|
371
|
-
honest behaviour was undefined — so a careful run printed counts and stopped while a
|
|
372
|
-
careless one could delete nine rules, and both looked like *the prune ran*.
|
|
373
|
-
|
|
374
|
-
- **Backfill first, once.** On meeting rows with no id, the prune's first act is to mint
|
|
375
|
-
ids and propose a retirement condition per row **from the row's own text**, printed for
|
|
376
|
-
the operator to accept. One pass, and the trigger works forever after.
|
|
377
|
-
- **`blocked` is a named outcome**, printed like any other: what could not be evaluated
|
|
378
|
-
and why. An undefined case is where the spread between two runs lives.
|
|
379
|
-
- **A hard cap plus an unrunnable retirement condition is a deadlock.** Either the cap
|
|
380
|
-
yields until the backfill is done, or the backfill is a precondition of enforcing it —
|
|
381
|
-
but not both, and the file says which.
|
|
382
|
-
|
|
383
|
-
## A fix to one call site closes a call site, not a class
|
|
384
|
-
|
|
385
|
-
- **A fix other call sites will need is exported before it is used.** The test is
|
|
386
|
-
mechanical: if the same defect could exist in a second file, the fix does not stay
|
|
387
|
-
private to the first.
|
|
388
|
-
- **Closing a defect includes a census of its siblings, by BEHAVIOUR rather than by a list
|
|
389
|
-
of names** — *every script that spawns a browser*, found by searching for the spawn, not
|
|
390
|
-
by remembering the five. A list of names is how the previous pass missed four of them.
|
|
391
|
-
- **The entry states how many call sites the class had and how many were fixed.** *Fixed*
|
|
392
|
-
and *fixed in one of five* currently read identically, and only one of them closes a row.
|
|
393
|
-
|
|
394
|
-
## The loop closes at stage 0
|
|
395
|
-
|
|
396
|
-
The standing instructions are an **instruction source**, not background reading:
|
|
397
|
-
[`knowledge-sources.md`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md) reads them in full at the harvest —
|
|
398
|
-
they are short by construction — and records the file as a ledger row. Every
|
|
399
|
-
instruction that actually *fires* during the run gets its **last-fired date and
|
|
400
|
-
commit stamped** as it fires. That stamp is the only thing that makes the cold-rule
|
|
401
|
-
honest; without it "five runs without firing" is a guess, and the prune becomes a
|
|
402
|
-
mood.
|
|
403
|
-
|
|
404
|
-
**The archive is queried at the same moment**, by the task's own nouns. It is the
|
|
405
|
-
one source that answers *"have we been bitten by this class before?"* — and that
|
|
406
|
-
question is worth asking precisely when the in-force list says nothing, because a
|
|
407
|
-
rule that was retired for going cold is exactly the rule about to be re-learned.
|
|
408
|
-
|
|
409
|
-
## Where a lesson goes when it is not about this project
|
|
410
|
-
|
|
411
|
-
A lesson that would be true in any repository does not belong in one project's
|
|
412
|
-
retro — it belongs in the pipeline's own doctrine
|
|
413
|
-
([`learned.md`](learned.md), which is exactly that list, earned the same way). A local
|
|
414
|
-
file that accumulates universal rules is a fork of the skill that nobody named.
|
|
415
|
-
|
|
416
|
-
**This said *"open an issue upstream"* for eight releases and named no repository, no
|
|
417
|
-
trigger and no authorization** — an instruction on rung 1 that everybody read as done.
|
|
418
|
-
The rest of this section is the mechanism.
|
|
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`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/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`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/references/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`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/references/iteration.md) → *The learning loop*.
|
|
441
|
-
|
|
442
|
-
## Publishing the insight — the skill learns from every project that runs it
|
|
443
|
-
|
|
444
|
-
**One job: stop a lesson dying in the repository that learned it.**
|
|
445
|
-
|
|
446
|
-
A retro entry is written per project and read by that project's next stage 0. The
|
|
447
|
-
pipeline itself never sees it. So a defect in the **skill** — a gate that loops, a
|
|
448
|
-
doctrine promising what nothing enforces, a rule firing on the wrong shape — is
|
|
449
|
-
rediscovered independently in every project and fixed in none of them.
|
|
450
|
-
|
|
451
|
-
**Opt-in, per project, off by default** — `pipeline.json` → `retro.publish`:
|
|
452
|
-
|
|
453
|
-
```json
|
|
454
|
-
"retro": {
|
|
455
|
-
"publish": {
|
|
456
|
-
"repo": "ssheleg/task-pipeline",
|
|
457
|
-
"label": "retro-insight",
|
|
458
|
-
"redact": "strict"
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
Absent, nothing is published, and nothing is asked. **Silence arms nothing** — the same
|
|
464
|
-
floor deploy authorization uses, and for the same reason: this is an **outward act**,
|
|
465
|
-
and an outward act taken from a generic flag is an outward act nobody authorized
|
|
466
|
-
([`continuity.md`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/references/continuity.md) → *The limit, before the capability*).
|
|
467
|
-
|
|
468
|
-
**The body is printed in full before the issue is opened, every time.** Not a summary
|
|
469
|
-
of it, not its title — the string that will be sent. The operator standing at stage
|
|
470
|
-
10's manual gate is already reading; showing them what leaves the machine costs one
|
|
471
|
-
block and is the only moment anyone can stop it.
|
|
472
|
-
|
|
473
|
-
```
|
|
474
|
-
── would open issue ──────────────────────────────────────────
|
|
475
|
-
repo: ssheleg/task-pipeline
|
|
476
|
-
label: retro-insight
|
|
477
|
-
title: [retro] a queue is not a diagnosis
|
|
478
|
-
<the whole body, verbatim>
|
|
479
|
-
──────────────────────────────────────────────────────────────
|
|
480
|
-
opening… → #24
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
**No `gh`, no network, no permission?** Print the body, say the issue was **not**
|
|
484
|
-
opened, and carry the exact text in the carry-over ledger. That is the honest
|
|
485
|
-
degradation; a second transport is not.
|
|
486
|
-
|
|
487
|
-
## The improvement iteration — what happens to a published insight next
|
|
488
|
-
|
|
489
|
-
Publishing is half a loop. An issue that is opened and never triaged is a lesson
|
|
490
|
-
that cost a run and bought nothing, and a tracker full of those teaches everyone
|
|
491
|
-
that publishing is where findings go to be filed.
|
|
492
|
-
|
|
493
|
-
**The loop, and every arrow in it is somebody's obligation:**
|
|
494
|
-
|
|
495
|
-
```
|
|
496
|
-
a run diverges → a retro entry → retro.publish → an issue on the skill
|
|
497
|
-
↓
|
|
498
|
-
a board row ← measured against the tree ← triaged in a later cycle
|
|
499
|
-
↓
|
|
500
|
-
doctrine + the guard that proves it → a release → the next run reads it
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
**Resolve what you worked, let the rest accumulate — visibly.** At the close of a
|
|
504
|
-
cycle that consumed published issues:
|
|
505
|
-
|
|
506
|
-
- **An issue closes when the behaviour changed**, not when the lesson was
|
|
507
|
-
understood (that is R-006, and it applies to issues exactly as it applies to
|
|
508
|
-
findings). The closing comment names **where** it landed — a file and a line —
|
|
509
|
-
and the guard that now holds it. A close with no address is a close nobody can
|
|
510
|
-
audit.
|
|
511
|
-
- **An issue nobody worked stays open.** It is not triaged into silence, not
|
|
512
|
-
relabelled, not closed as stale. The pile is the queue, and its depth is the
|
|
513
|
-
honest measure of how far behind the doctrine is.
|
|
514
|
-
- **Nothing is deleted.** A closed issue keeps its number, and the number is what
|
|
515
|
-
the CHANGELOG and the retro entry point at. Deleting one severs both, and the
|
|
516
|
-
cost lands on whoever next asks *why is this rule here*.
|
|
517
|
-
|
|
518
|
-
**Where the queue comes from at the start of a cycle**, in this order: open issues
|
|
519
|
-
published by any project running this skill, then the board's open rows by computed
|
|
520
|
-
priority, then the open-questions register. A run that finishes its brief early
|
|
521
|
-
takes the top of that list rather than inventing work — and a run that *cannot*
|
|
522
|
-
take it says so, which is a fuller queue rather than a quiet one.
|
|
523
|
-
|
|
524
|
-
**The measurement that keeps this honest.** Print, at the close: issues consumed,
|
|
525
|
-
issues resolved with an address, issues left open. Three numbers, no floor, no
|
|
526
|
-
target, and the third one rising is information rather than a failure — it means
|
|
527
|
-
the projects running this skill are finding more than one cycle can absorb, which
|
|
528
|
-
is what you want them to do.
|
|
529
|
-
|
|
530
|
-
## What may leave the project — the redaction list
|
|
531
|
-
|
|
532
|
-
An issue is a **public artefact in someone else's repository**. What travels is the
|
|
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.
|
|
536
|
-
|
|
537
|
-
| Goes | Stays |
|
|
538
|
-
|---|---|
|
|
539
|
-
| the class of failure, stated in the abstract | the file, the function, the line it happened in |
|
|
540
|
-
| which stage owned it and which stage surfaced it | the repository, organisation, branch or commit |
|
|
541
|
-
| the doctrine or guard that missed it, by its name **in this skill** | any host path, absolute or relative |
|
|
542
|
-
| the fix by grade, and the check that would catch it | the code, the config values, the data |
|
|
543
|
-
| whether an existing standing instruction fired | any person, company, customer or product name |
|
|
544
|
-
|
|
545
|
-
Five rules, numbered so a reader can point at one:
|
|
546
|
-
|
|
547
|
-
1. **No host paths.** Only paths inside task-pipeline itself — `references/…`,
|
|
548
|
-
`templates/…`, `test/validate.py`. An absolute path names a machine.
|
|
549
|
-
2. **No host identifiers**: repository, organisation, branch, commit, tag, issue or PR
|
|
550
|
-
number belonging to the project the run happened in.
|
|
551
|
-
3. **No code, no configuration values, no data** — not a snippet, not a redacted
|
|
552
|
-
snippet. A shape can be described in a sentence.
|
|
553
|
-
4. **No names**: person, company, customer, employer, product.
|
|
554
|
-
5. **The title states the class, not the incident** — *"a queue is not a diagnosis"*,
|
|
555
|
-
never *"our export job looped"*.
|
|
556
|
-
|
|
557
|
-
**The printed text and the sent text are one string.** Redacting after the print, or
|
|
558
|
-
printing a cleaned-up version of what is actually sent, is the false-success shape this
|
|
559
|
-
bundle names outright ([`gates.md`](gates.md) → *False success*): a mechanism reporting
|
|
560
|
-
on itself rather than on what it did.
|
|
561
|
-
|
|
562
|
-
**When in doubt the rule is subtraction, not judgement.** An insight that survives
|
|
563
|
-
losing a detail is still an insight; a detail that leaks cannot be recalled from an
|
|
564
|
-
index. If removing it makes the entry incomprehensible, the entry was about the project
|
|
565
|
-
and not about the skill — keep it local.
|
|
566
|
-
|
|
567
|
-
## Rationalizations
|
|
568
|
-
|
|
569
|
-
| Excuse | Reality |
|
|
570
|
-
|---|---|
|
|
571
|
-
| "Nothing really went wrong this run" | Then the stamp says so in one line and you are done in ten seconds. The runs that "went fine" are where a repeated class hides — it never cost enough to remember. |
|
|
572
|
-
| "I'll write the retro later, with a clear head" | Later is when you remember the outcome and not the seam. The cause is legible for about an hour after the run. |
|
|
573
|
-
| "Don't delete it, it might still be useful" | That sentence is the entire failure mode. Every rule kept "just in case" spends attention that the load-bearing ones needed, and the file stops being read at all. |
|
|
574
|
-
| "Pruning loses knowledge" | The Log keeps the incident forever; only the *instruction* leaves. If it recurs you get a grade-1 fix with its own history attached. |
|
|
575
|
-
| "The list is at eleven but they're all important" | Then one of them is doctrine and belongs in `CLAUDE.md`, one has become a check, and one has not fired in a year. Ten is the budget precisely because ranking is uncomfortable. |
|
|
576
|
-
| "I'll note it as a reminder for next time" | A note is grade 3 and expires in two runs. If it is worth remembering it is worth a check or a slot; if it is worth neither, it was never going to be read. |
|
|
1
|
+
# Retrospective: pointer
|
|
2
|
+
|
|
3
|
+
The retrospective doctrine has one home:
|
|
4
|
+
[task-pipeline's `references/retrospective.md`](https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md).
|
|
5
|
+
With the task-pipeline plugin installed, the same file sits beside this skill at
|
|
6
|
+
`skills/task-pipeline/references/retrospective.md`. Until 1.98.0 this file was a
|
|
7
|
+
34.7 KB copy of it with no delta of its own; a copy is a second thing to keep true.
|
|
8
|
+
|
|
9
|
+
What evidence-docs relies on from it, in short:
|
|
10
|
+
|
|
11
|
+
- **One file per project**, `docs/evidence/retro.md`, written as stage 10's last act:
|
|
12
|
+
stamp the run first, then prune, then write an entry only if the run diverged.
|
|
13
|
+
- **The floor stage 0 reads in full** (standing instructions, max ten rows; run stamps,
|
|
14
|
+
max ten lines) stays under a 12 000-byte budget: a standing row at most 600
|
|
15
|
+
characters, a stamp at most 300. `scripts/retro_budget.py check` measures it.
|
|
16
|
+
- **Every lesson carries its commit**, and every SHA must resolve from `HEAD`.
|
|
17
|
+
- **Each entry names its Class and its Cost**; recurrence is counted by class.
|
|
18
|
+
- **A universal rule is cited by its [`learned.md`](learned.md) number**, never
|
|
19
|
+
restated; a lesson standing in two projects goes upstream in the same run.
|
|
20
|
+
|
|
21
|
+
evidence-docs-specific delta: none.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: project-audit
|
|
3
|
-
description: "Use when someone asks what is
|
|
3
|
+
description: "Use when someone asks what is true of a whole project: finished, half-built, broken, unexplored ('project audit', 'аудит проекта'). Discovers the project, runs probes, reads production evidence, leaves a JSON sidecar, an HTML report on request. Read-only: proposes board rows, commits nothing. Not for: one deliverable inside a run, a diff review, a skill's construction; 'без диагностики' opts out."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: "The collector (scripts/audit.py) needs python3 and reads committed state, so it needs git. Probes needing gh, npm, network or a browser declare it and report blind when it is absent — degraded, never silent."
|
|
6
6
|
---
|
|
@@ -318,3 +318,10 @@ requested, not a fixed page-and-browser:
|
|
|
318
318
|
| "Findings should just be fixed while I'm in there" | An audit that edits while it reads cannot re-run to check itself, and the next run cannot tell a fix from a rewording. |
|
|
319
319
|
| "It's the same nine checks every time, so I can skip discovery" | Then the instrument returns the same answer for every input, which is a fact about the instrument and not about the project. |
|
|
320
320
|
| "No findings — clean bill" | Only against the probes that ran. Read the blind list before saying that sentence out loud. |
|
|
321
|
+
|
|
322
|
+
## Triggers
|
|
323
|
+
|
|
324
|
+
The description is the short form. The full trigger list it carried until 1.98.0,
|
|
325
|
+
moved here verbatim, still routes to this skill:
|
|
326
|
+
|
|
327
|
+
"Use when someone asks what is actually true of a whole project right now — what is finished, what is half-built, what is broken, and what nobody has looked at. Walks a cold start: discover what the project is, run a registry of probes chosen from that, read production evidence (published artefact against source, CI history, telemetry present or absent), then leave a JSON sidecar so the next audit can say what moved, and a self-contained HTML report on request. Read-only: it proposes board rows and commits nothing. Triggers - 'project audit', 'audit the project', 'codebase audit', 'state of the project', 'what is unfinished', 'project health check', 'аудит проекта', 'проаудируй проект', 'состояние проекта', 'что не доделано', 'аудит кодовой базы'. Not for: auditing one deliverable inside a run (that is the pipeline's own ladder), reviewing a diff, or checking a skill's construction — say 'без диагностики' to opt out."
|