task-pipeline-skill 1.57.0 → 1.59.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 +115 -0
- package/SKILL-CARD.md +1 -1
- package/package.json +1 -1
- package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +8 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/documentation.md +17 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +33 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +34 -5
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +31 -1
- package/plugins/task-pipeline/skills/task-pipeline/templates/docgate.sh +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,120 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.59.0 — never amend a commit a record already names
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **The stamping procedure invited the defect it then had to repair, twice in one
|
|
8
|
+
close-out.** `retrospective.md` says stamp the run with its own commit; the only way to
|
|
9
|
+
know that commit is to make it; so the stamp gets folded in with `--amend` — and the
|
|
10
|
+
amend mints a new SHA, leaving the stamp naming a commit that resolves on the machine
|
|
11
|
+
that wrote it and reaches no clone. It happened here on 2026-08-16 and then again in the
|
|
12
|
+
umbrella twenty minutes later, which is a procedure fault rather than two lapses.
|
|
13
|
+
|
|
14
|
+
The doctrine now says it in one line — **once a file names a SHA, that commit is
|
|
15
|
+
frozen** — states the order that removes the temptation (commit the work, *then* stamp in
|
|
16
|
+
a commit of its own), and names the only repair that does not re-enter the loop: a
|
|
17
|
+
follow-up commit, never a second amend.
|
|
18
|
+
|
|
19
|
+
- **The documentation gate asked the weaker of the two questions.** It required every
|
|
20
|
+
backticked SHA in the retro to *resolve*, and an amended-away commit resolves for as long
|
|
21
|
+
as the object survives locally. It now also requires **reachability from `HEAD`**
|
|
22
|
+
(`git merge-base --is-ancestor`), which is the question a reader two months later is
|
|
23
|
+
actually asking. Watched failing on a purpose-built repository whose stamped commit had
|
|
24
|
+
been amended away: `commit ... resolves but is NOT reachable from HEAD`.
|
|
25
|
+
|
|
26
|
+
- **Three id registers that could never allocate are removed** (`B-45`). They were declared
|
|
27
|
+
over the `fs` backend, whose `reserve` refuses by design, and `agent_sync.py check` had
|
|
28
|
+
been calling it a problem for as long as they stood. A declaration that cannot be served
|
|
29
|
+
is worse than none: it reads as a capability, so nobody writes the procedure it hides —
|
|
30
|
+
and on 2026-08-15 two sessions filed a different `B-073`. Allocation is manual, documented
|
|
31
|
+
in `CLAUDE.md`, and the guard that requires that documentation now fires on the **backend**
|
|
32
|
+
rather than on the declaration, so removing the registers could not retire it.
|
|
33
|
+
|
|
34
|
+
Guards: 344 → **344**. Property checks: 9 → 9. No new plant, and that is the honest
|
|
35
|
+
number: what changed is a gate script (`templates/docgate.sh`) rather than a validator
|
|
36
|
+
guard, and it was watched failing against a purpose-built repository whose stamped commit
|
|
37
|
+
had been amended away — the plant lives in that measurement rather than in the workflow,
|
|
38
|
+
because a gate that runs over a project's own git history cannot be planted from inside a
|
|
39
|
+
copy of this one.
|
|
40
|
+
|
|
41
|
+
- **The coordination snapshot exists and is linked.** `docs/AGENT_SYNC.md` was missing
|
|
42
|
+
entirely, which `check` had also been reporting. Generated from the live configuration and
|
|
43
|
+
linked from `CLAUDE.md`; `agent_sync.py check` → **exit 0, `setup healthy`**, for the first
|
|
44
|
+
time in this repository.
|
|
45
|
+
|
|
46
|
+
## v1.58.0 — a fan-out is not finished when its branches are
|
|
47
|
+
|
|
48
|
+
The graph model this pipeline was audited against in v1.57.0 named one defect and fixed
|
|
49
|
+
it in one place. Applying the same model to the rest of the skill found the same defect
|
|
50
|
+
in **three more**, and they are the same sentence each time: work fans out, the branches
|
|
51
|
+
each go green, and the node that consumes them trusts them because they arrived.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- **The harvest is a convergence, and now it has a check.** Stage 0 queries the code, the
|
|
56
|
+
graph, `CLAUDE.md`, the ADRs, the docs, past briefs, the wiki, the board — all
|
|
57
|
+
independently — and lands them in one brief the interview then treats as a single
|
|
58
|
+
answer. Phase 2 checks each *answer* against the harvest; **nothing compared the sources
|
|
59
|
+
with each other**, so a doc contradicting the code produced two rows that each looked
|
|
60
|
+
fine and the run followed whichever it read last. The ledger now carries a
|
|
61
|
+
`Contradictions:` line with four things to look for, and the stage-0 gate reads it.
|
|
62
|
+
`Contradictions: none` is the answer most runs write, and writing it is the point.
|
|
63
|
+
|
|
64
|
+
- **Stage 3's COPY and VISUAL tracks are a parallel layer, and their convergence has a
|
|
65
|
+
check.** Neither consumes the other — copy is written from the scenarios and the brand
|
|
66
|
+
pack, the visual from the frame and the style pack — so the order they were written in
|
|
67
|
+
was a fake edge teaching a run to wait for a result that never arrives. What they do
|
|
68
|
+
share is the screen, which is where the real failure lives: **each track is right alone
|
|
69
|
+
and they disagree together.** A label the layout has no room for, a state one drew and
|
|
70
|
+
the other never wrote, two names for one component, a tone the motion contradicts.
|
|
71
|
+
|
|
72
|
+
- **Stage 9's three artifacts are named as a convergence**, and the graph↔docs divergence
|
|
73
|
+
check as its gate rather than a nicety — it is the only thing that compares two of the
|
|
74
|
+
three outputs against each other.
|
|
75
|
+
|
|
76
|
+
Guards: 339 → **344**. Property checks: 9 → 9. Five new plants, one per branch of the
|
|
77
|
+
three new checks, each anchored on a heading and each asserting it changed something.
|
|
78
|
+
Two of the five were broken on their first run and both failures were mine: a grep
|
|
79
|
+
pattern one character short of the message it looked for — **the same class fixed hours
|
|
80
|
+
earlier in this programme and not swept into the new plants**, which is R-003 — and an
|
|
81
|
+
assertion looking for the doctrine in the file that does not hold it.
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
|
|
85
|
+
- **The stage-4 gate reads both halves of what it requires**, and the id/version
|
|
86
|
+
allocation this repository actually uses is written down. `.claude/agent-sync.json`
|
|
87
|
+
declares three id registers over an `fs` backend whose `reserve` refuses by design —
|
|
88
|
+
correctly, since *pretending would hand two agents the same id* — and the declaration
|
|
89
|
+
read as a capability, so nobody wrote the manual procedure it was hiding. On 2026-08-15
|
|
90
|
+
two sessions filed a different `B-073` and two branches claimed one version number.
|
|
91
|
+
`CLAUDE.md` now carries the three-step allocation (lease first, compute from the
|
|
92
|
+
**committed** file, commit before releasing) and the version rule (`git ls-remote --tags`,
|
|
93
|
+
because a local checkout is not where the answer lives), the shipped doctrine in
|
|
94
|
+
`documentation.md` carries the generalisable half, and a guard requires both.
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
|
|
98
|
+
- **Two concurrent runs of `test/negatives.py` no longer corrupt each other** (`B-075`).
|
|
99
|
+
Every step copies the repo to a **fixed** `/tmp` name, which is right in CI — one runner
|
|
100
|
+
per job — and wrong on a machine where a second suite is already running. The runner now
|
|
101
|
+
serialises the runs instead: an exclusive lock for the duration of the suite, so a second
|
|
102
|
+
run **waits** rather than corrupting the first and says so instead of producing a number
|
|
103
|
+
nobody can trust. The 344 workflow steps are untouched — they are the CI contract, one
|
|
104
|
+
runner per job, and they were never the ones colliding.
|
|
105
|
+
|
|
106
|
+
**The first fix was wrong and is worth recording.** It rewrote every `/tmp/...` path in a
|
|
107
|
+
step's script to a per-run name, and it broke two plants whose payload **is the workflow
|
|
108
|
+
text** — they search the copied workflow for a literal path in order to duplicate it. A
|
|
109
|
+
mechanical rewrite cannot tell a path being *used* from a path being *discussed*, which is
|
|
110
|
+
the umbrella's standing instruction #7 met for the second time in two days, both times by
|
|
111
|
+
the same author. The suite caught it; reading did not.
|
|
112
|
+
|
|
113
|
+
**Watched both ways, under real overlap.** Before, two concurrent runs of one selector
|
|
114
|
+
returned `1 guard did not fire, 7 broken` and `8 guards did not fire` — two different
|
|
115
|
+
wrong answers about a tree that was not changing. After, both return `all 8 guards
|
|
116
|
+
provably reject their planted defect`, exit 0, with the second printing that it waited.
|
|
117
|
+
|
|
3
118
|
## v1.57.0 — an arrow that carries nothing is not an arrow, and two green diffs can still contradict each other
|
|
4
119
|
|
|
5
120
|
The pipeline has drawn a dependency graph at stage 4 since it had a stage 4, and grouped
|
package/SKILL-CARD.md
CHANGED
|
@@ -12,7 +12,7 @@ harmless.
|
|
|
12
12
|
|---|---|
|
|
13
13
|
| **Purpose** | Runs a substantial task through ten gated delivery stages — intake grill, docs study, brainstorm, spec, plan, subagent build, tests, lint/deploy, post-deploy, docs+registers, acceptance — refusing to advance until each gate passes |
|
|
14
14
|
| **Owner** | ssheleg ([github.com/ssheleg/task-pipeline](https://github.com/ssheleg/task-pipeline)) |
|
|
15
|
-
| **Version** | 1.
|
|
15
|
+
| **Version** | 1.59.0 |
|
|
16
16
|
| **Surface** | Claude Code (filesystem skill + plugin) and the vercel `skills` CLI. **Not** uploaded to the Skills API; custom Skills do not sync across surfaces |
|
|
17
17
|
| **Dependencies** | None required. Optional: `context7` (MCP), `figma` (MCP), super-ux, agent-sync, graphify, obsidian-wiki, and **one of two browser channels** — `playwright` (CLI or MCP) or `chrome-devtools` (MCP); either satisfies the browser step and neither is required. Every stage's doctrine ships in-repo; the one conditional requirement is super-ux for the stage-3 UX track on a user-facing task |
|
|
18
18
|
| **Evaluation status** | Suite authored, 5 categories. One recorded run, **self-observed by the author**; **zero blind runs on zero of three models** — the split, and the numbers, live in [`evals/RESULTS.md`](evals/RESULTS.md) and are computed by `evals/run.py` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "task-pipeline-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.59.0",
|
|
4
4
|
"description": "Full-cycle delivery pipeline for coding agents: a mandatory built-in intake grill, then 10 gated stages (docs, brainstorm+decompose, spec, plan, build, tests, lint/deploy, post-deploy, docs/wiki, acceptance). Every stage's doctrine ships inside the skill — no companion plugin required. This package is the installer CLI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"task-pipeline": "bin/task-pipeline.js"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "task-pipeline",
|
|
3
3
|
"displayName": "Task Pipeline",
|
|
4
4
|
"description": "Runs a substantial task through a mandatory built-in intake grill, then 10 gated stages (docs, brainstorm+decompose, spec, plan, subagent build, tests, lint/deploy, post-deploy, docs/wiki, acceptance). Every stage's doctrine is built into the skill — no companion plugin required — with typed auto/manual gates, a frozen requirement spine that closes with evidence, a work board and a verification ledger that outlive a run, an exposure line naming what shipped unconfirmed, a progress rail computed from the project's own config, a loop guard whose review ceiling measures rather than stops, and stage-3 tracks for what a product does, how it sounds and how it looks. Two modes need no task: `checkup` (what is unverified) and `setup` (audit existing docs). Retro insights can publish upstream as issues, opt-in and redacted.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.59.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ssheleg",
|
|
8
8
|
"url": "https://x.com/sshlg93"
|
|
@@ -115,8 +115,12 @@ log and archive are **queried** by the task's nouns, because nothing caps them
|
|
|
115
115
|
outranks any document — **but only out loud**, so an override is a recorded decision
|
|
116
116
|
rather than an undetected divergence. That ledger is also stage 9's work list.
|
|
117
117
|
|
|
118
|
-
**Three artifacts close a run, not two
|
|
119
|
-
code graph** (`/graphify . --update`).
|
|
118
|
+
**Three artifacts close a run, not two — and they are a convergence, not a sequence.**
|
|
119
|
+
Stage 9 syncs the docs, the wiki **and the code graph** (`/graphify . --update`). None of
|
|
120
|
+
the three consumes another; all three consume the same change, and the **graph↔docs
|
|
121
|
+
divergence check is the gate over their convergence** rather than an extra nicety. That is
|
|
122
|
+
why it is not optional where a graph exists: it is the only thing that compares two of the
|
|
123
|
+
three outputs against each other. The graph is what the next run's harvest queries
|
|
120
124
|
first, so a stale one is a false premise **carrying the authority of a machine** —
|
|
121
125
|
a wrong doc gets argued with, a wrong graph gets believed. Refreshing it buys the
|
|
122
126
|
graph↔docs divergence check; doc-side findings are fixed at stage 9, absences become
|
|
@@ -248,10 +252,10 @@ capable available — see `references/model-tiering.md`).
|
|
|
248
252
|
|
|
249
253
|
| # | Stage | Gate | Type |
|
|
250
254
|
|---|---|---|---|
|
|
251
|
-
| 0 | Intake grill — **mandatory** | source ledger written; **the documentation inventory answered into `docs/DOCMAP.md`** — registers, single homes, the propagation matrix, the gate command — and **intent reconciled against as-built**, every divergence resolved ([`references/documentation.md`](references/documentation.md)); the retro read in full and its archive queried; shared understanding reached; autonomy sweep covered; brief locked + confirmed | manual |
|
|
255
|
+
| 0 | Intake grill — **mandatory** | source ledger written **with its `Contradictions:` line** — the harvest converges on one brief and nothing else compares the sources with each other; **the documentation inventory answered into `docs/DOCMAP.md`** — registers, single homes, the propagation matrix, the gate command — and **intent reconciled against as-built**, every divergence resolved ([`references/documentation.md`](references/documentation.md)); the retro read in full and its archive queried; shared understanding reached; autonomy sweep covered; brief locked + confirmed | manual |
|
|
252
256
|
| 1 | Docs study | contracts grounded on fetched docs | auto |
|
|
253
257
|
| 2 | Brainstorm + decompose | design approved; UI verdict recorded; every REQ answered; platform: module map approved | manual |
|
|
254
|
-
| 3 | Spec | committed + reviewed; UI: chain validated, linter green, scenarios/`SCR-` traced | manual |
|
|
258
|
+
| 3 | Spec | committed + reviewed; UI: chain validated, linter green, scenarios/`SCR-` traced; **COPY and VISUAL are a parallel layer after UX, and where both ran their convergence check is recorded** — a label the layout has no room for is right in each track and wrong on the screen ([`references/stages.md`](references/stages.md)) | manual |
|
|
255
259
|
| 4 | Plan | parallel-ready, DoD per task; **every edge names what it carries** — the fake-edge test run, its `Edges:` count computed, and no arrow left whose payload nobody can name ([`references/planning.md`](references/planning.md)) | auto |
|
|
256
260
|
| 5 | Dev | tasks DONE, TDD green per task, branch integrated per the brief; **a fanned-out group gets one convergence check over all its diffs together before the first worktree lands** — a per-task review cannot see a contradiction that exists only between two of them ([`references/build.md`](references/build.md) §4.2a); **anything generated passes its own checks, and local infrastructure does not publish the host's default ports** ([`references/learned.md`](references/learned.md)) | auto |
|
|
257
261
|
| 6 | Tests | full suite green; new/changed code covered; **every new check probed both ways and asserted on its exit code**, and the suite run once against a cold environment ; **on a web front end the surface is checked in a browser, not in the diff** — a green suite cannot see a component that renders under a fixed header, a request that 404s past its mock, or a console error — and a browser **test suite** is the other half of the pair, never a substitute for the look (`playwright` or `chrome-devtools`, either one — **how**: [`references/browser.md`](references/browser.md), which channel: [`references/companion-skills.md`](references/companion-skills.md); absent → say *verified by reading the diff* and record it as the weaker claim it is) | auto |
|
|
@@ -353,6 +353,23 @@ A register is the one file two agents will write in the same minute.
|
|
|
353
353
|
a real state, not a formality. Describing a project as protected while nothing
|
|
354
354
|
enforces it is worse than having no protection, because everyone downstream
|
|
355
355
|
believes the guarantee.
|
|
356
|
+
- **A register declared against a backend that cannot reserve is a register in name
|
|
357
|
+
only, and its presence is what stops the manual procedure being written.** Measured
|
|
358
|
+
2026-08-15: a project declared three id registers over a local-filesystem backend
|
|
359
|
+
whose `reserve` correctly refuses — *pretending would hand two agents the same id* —
|
|
360
|
+
and two sessions filed a different `B-073` on the same afternoon. The tool was honest;
|
|
361
|
+
the *declaration* was the problem, because a capability that appears to exist is one
|
|
362
|
+
nobody replaces. Where reservation is not available, write the three-step manual
|
|
363
|
+
allocation down where an agent reads it, and make it follow the lease:
|
|
364
|
+
1. take the lease on the register's file **first**;
|
|
365
|
+
2. compute the next id from the **committed** file — `git show HEAD:<file>` — never
|
|
366
|
+
from a working copy, which holds your own unpushed row and hides somebody else's;
|
|
367
|
+
3. write and **commit** before releasing the lease. An id held only in an uncommitted
|
|
368
|
+
file is an id nobody else can see you took.
|
|
369
|
+
- **A version number is the same class with no register at all.** Two branches both
|
|
370
|
+
claimed one version on that same afternoon because each incremented from what its own
|
|
371
|
+
checkout knew. Read the remote — `git ls-remote --tags` — because that list is the only
|
|
372
|
+
place the answer lives.
|
|
356
373
|
|
|
357
374
|
---
|
|
358
375
|
|
|
@@ -17,6 +17,7 @@ that goes stale when the answer changes.
|
|
|
17
17
|
- The knowledge wiki — recommended
|
|
18
18
|
- How to harvest — retrieval, not reading
|
|
19
19
|
- Record it — the source ledger
|
|
20
|
+
- The harvest is a convergence, so it needs a check across its sources
|
|
20
21
|
- The source is not the copy you have
|
|
21
22
|
- Carried-in claims — measured or recalled
|
|
22
23
|
- Phase 2 — validate the answers against the harvest
|
|
@@ -177,6 +178,38 @@ source nobody will update.
|
|
|
177
178
|
**"No sources found" is a valid, recorded outcome.** Write the row. An empty ledger
|
|
178
179
|
tells the next run that the search happened and came back empty — silence doesn't.
|
|
179
180
|
|
|
181
|
+
## The harvest is a convergence, so it needs a check across its sources
|
|
182
|
+
|
|
183
|
+
Every row above was produced independently — the code does not know what the ADR says,
|
|
184
|
+
the wiki does not know what the graph found. Then all of them arrive at one place, the
|
|
185
|
+
brief, and the interview treats that place as a single answer. **That is a fan-out with
|
|
186
|
+
a convergence, and a convergence trusts its inputs because they arrived**
|
|
187
|
+
(`agent-stack` → `agent-orchestrator/references/graph-engineering.md`, the checker node).
|
|
188
|
+
|
|
189
|
+
Phase 2 below checks each *answer* against the harvest. Nothing checks the **sources
|
|
190
|
+
against each other**, and that is the gap: a doc that contradicts the code produces two
|
|
191
|
+
rows that each look fine, and the run picks whichever it read last.
|
|
192
|
+
|
|
193
|
+
So before the first question, walk the ledger once more and write a **`Contradictions:`**
|
|
194
|
+
line under it. Four things to look for, and they are the checker's five catches with the
|
|
195
|
+
one that cannot apply here dropped:
|
|
196
|
+
|
|
197
|
+
1. **Empty** — a source that was consulted and said nothing about this task. Already a
|
|
198
|
+
valid row; the point is that it must be *written*, not omitted.
|
|
199
|
+
2. **Mutually contradictory** — two sources that cannot both be true. Name both rows and
|
|
200
|
+
which one this run will follow, **out loud**, because the operator outranks a document
|
|
201
|
+
only when the override is recorded.
|
|
202
|
+
3. **Off-topic** — a row that answers a neighbouring question. It inflates the ledger and
|
|
203
|
+
makes the next reader trust a source that was never about this.
|
|
204
|
+
4. **Stale against a fresher neighbour** — the graph built a week before the code it
|
|
205
|
+
describes, an ADR superseded by a decision nobody linked. This is the one the `Fresh?`
|
|
206
|
+
column exists for and nobody compares.
|
|
207
|
+
|
|
208
|
+
`Contradictions: none` is the answer most runs will write, and writing it is the point:
|
|
209
|
+
a check whose silence is indistinguishable from not having run is not evidence. Anything
|
|
210
|
+
found here is a stage-9 obligation — the ledger's row is what stage 9 goes back and
|
|
211
|
+
fixes.
|
|
212
|
+
|
|
180
213
|
## The source is not the copy you have
|
|
181
214
|
|
|
182
215
|
`learned.md` rule 17. The harvest reads the project's own files, and one property of them is
|
|
@@ -32,6 +32,7 @@ file exists to stop.
|
|
|
32
32
|
|
|
33
33
|
- Write the entry only for a divergence — and name the layer that owned it
|
|
34
34
|
- Every lesson carries its commit
|
|
35
|
+
- Never amend a commit a record already names
|
|
35
36
|
- The stamp table is capped at ten, and *one line per run* was never a cap
|
|
36
37
|
- `publish:` is a line in the verdict, not a silence
|
|
37
38
|
- Rotation — the archive is how pruning stops losing things
|
|
@@ -76,11 +77,39 @@ message and the parent, so `git show <sha>` reconstructs the entire incident two
|
|
|
76
77
|
months later — which is exactly when the same class comes back and somebody needs
|
|
77
78
|
to know whether this was already understood.
|
|
78
79
|
|
|
79
|
-
**Every SHA must resolve
|
|
80
|
-
document may not send a reader to something
|
|
81
|
-
mechanical: the
|
|
82
|
-
<sha>^{commit}` over every backticked SHA in the retro
|
|
83
|
-
([`gates.md`](gates.md)).
|
|
80
|
+
**Every SHA must resolve — and resolving is not enough.** This is
|
|
81
|
+
[`learned.md`](learned.md) rule 14 — *a document may not send a reader to something
|
|
82
|
+
absent* — applied to history, and it is mechanical: the documentation gate runs
|
|
83
|
+
`git rev-parse --verify --quiet <sha>^{commit}` over every backticked SHA in the retro
|
|
84
|
+
and its archive ([`gates.md`](gates.md)).
|
|
85
|
+
|
|
86
|
+
**But a commit that was amended away still resolves on the machine that amended it**,
|
|
87
|
+
and exists in no clone. The gate therefore also requires each SHA to be **reachable
|
|
88
|
+
from `HEAD`** — `git merge-base --is-ancestor <sha> HEAD` — because that is the
|
|
89
|
+
question a reader two months from now is actually asking, and the weaker one passes
|
|
90
|
+
for as long as the object survives locally.
|
|
91
|
+
|
|
92
|
+
## Never amend a commit a record already names
|
|
93
|
+
|
|
94
|
+
Measured 2026-08-16, twice in one close-out and twenty minutes apart. The sequence is
|
|
95
|
+
seductive because each step is right on its own: stamp the run with its commit → the
|
|
96
|
+
stamp is part of the run, so fold it in with `--amend` → the amend mints a new SHA →
|
|
97
|
+
the stamp now names a commit that will never reach the remote.
|
|
98
|
+
|
|
99
|
+
The rule is one line and it is absolute: **once a file names a SHA, that commit is
|
|
100
|
+
frozen.** A correction goes in a *follow-up commit*, never a second amend — amending to
|
|
101
|
+
repair a stamp is the loop that produced the problem, and the second attempt lands in the
|
|
102
|
+
same place as the first.
|
|
103
|
+
|
|
104
|
+
Practically, that makes the order:
|
|
105
|
+
|
|
106
|
+
1. commit the work;
|
|
107
|
+
2. **then** stamp, in a commit of its own, naming the commit from step 1;
|
|
108
|
+
3. prune and write the entry in that same second commit, or a third.
|
|
109
|
+
|
|
110
|
+
The stamp costs one line and one commit. A run that folds it back into the work to keep
|
|
111
|
+
the history tidy is trading a reader's ability to find the incident for the appearance of
|
|
112
|
+
tidiness — and the reader is the entire reason the stamp exists.
|
|
84
113
|
|
|
85
114
|
## The stamp table is capped at ten, and *one line per run* was never a cap
|
|
86
115
|
|
|
@@ -194,7 +194,11 @@ never that the work was skipped quietly.
|
|
|
194
194
|
an updated `CONTEXT.md` (terms written as they resolved) and any ADRs under
|
|
195
195
|
`docs/adr/` — see `grill.md` → *Domain awareness*.
|
|
196
196
|
- **GATE (manual):** shared understanding reached — **the source ledger is written
|
|
197
|
-
(every source consulted, or an explicit "none found")
|
|
197
|
+
(every source consulted, or an explicit "none found")** and **its `Contradictions:`
|
|
198
|
+
line is written under it** — the harvest is a fan-out that converges on one brief, and
|
|
199
|
+
nothing else compares the sources *with each other*; `Contradictions: none` is the
|
|
200
|
+
answer most runs give and writing it is the point
|
|
201
|
+
([`knowledge-sources.md`](knowledge-sources.md)), **where a code graph
|
|
198
202
|
exists its row carries the measured lag and the signal it was measured with — a
|
|
199
203
|
bare build date does not satisfy this, because it is the graph's own reply rather
|
|
200
204
|
than a measurement of it** ([`knowledge-graph.md`](knowledge-graph.md) →
|
|
@@ -338,6 +342,29 @@ never that the work was skipped quietly.
|
|
|
338
342
|
`sheleg-design` how it **looks**. Until 2026-08-10 this stage named only the first,
|
|
339
343
|
so a run designed a flow, then wrote its strings by taste and picked its values at the
|
|
340
344
|
keyboard — and every gate in the pipeline reported green over both.
|
|
345
|
+
- **Two of the three are a parallel layer, and the third is their only real dependency.**
|
|
346
|
+
COPY and VISUAL both consume the UX track's scenarios; **neither consumes the other**.
|
|
347
|
+
Copy is written against the brand pack and the scenarios, not against tokens; the visual
|
|
348
|
+
is built from the frame and the style pack, not from strings. Writing them in a line —
|
|
349
|
+
which this file did until 2026-08-15 — teaches a run to wait for a result that never
|
|
350
|
+
arrives. The order is `UX → { COPY ∥ VISUAL }`, and the only thing crossing each of
|
|
351
|
+
those two arrows is **the scenario set**.
|
|
352
|
+
- **Their convergence needs a check, and it has a real contradiction to catch.** Both land
|
|
353
|
+
on the same screen, so the failure is not that one is wrong: it is that each is right
|
|
354
|
+
alone and they disagree together. Before the spec is committed, compare the two outputs
|
|
355
|
+
and record the answer:
|
|
356
|
+
1. **A string the layout has no room for** — a label, an error or an empty state longer
|
|
357
|
+
than the frame's element, at the frame's own width.
|
|
358
|
+
2. **A state one track has and the other does not** — copy for an empty state the design
|
|
359
|
+
never drew, or a loading state drawn with no string.
|
|
360
|
+
3. **Two names for one thing** — the design system's component name against the
|
|
361
|
+
terminology file's noun, where a user reads both.
|
|
362
|
+
4. **A tone the visual contradicts** — a calm, plain register on a screen whose motion
|
|
363
|
+
and colour say urgency.
|
|
364
|
+
`Tracks converge: clean` is the answer most runs write, and writing it is the point —
|
|
365
|
+
a check whose silence is indistinguishable from not having run is not evidence. This is
|
|
366
|
+
the same rule the harvest applies at stage 0 and the build applies to a fanned-out group
|
|
367
|
+
at stage 5 ([`build.md`](build.md) §4.2a); one shape, three places.
|
|
341
368
|
- **Spec:** write the approved design to
|
|
342
369
|
`<artifacts>/specs/YYYY-MM-DD-<topic>-design.md` and commit it. Lock all
|
|
343
370
|
shared contracts (types, schemas, signatures, file layout). For UI tasks the
|
|
@@ -360,6 +387,9 @@ never that the work was skipped quietly.
|
|
|
360
387
|
through the COPY track or the refusal is recorded**, and **the visual layer went
|
|
361
388
|
through the VISUAL track or the refusal is recorded** — a recorded refusal passes
|
|
362
389
|
this gate and an unmentioned one does not, which is the only difference that matters.
|
|
390
|
+
**Where both tracks ran, their convergence check is recorded** — findings with the
|
|
391
|
+
ruling, or `Tracks converge: clean`; a screen where each track is right alone and they
|
|
392
|
+
disagree together is the defect neither track's own review can see.
|
|
363
393
|
No plan (stage 4)
|
|
364
394
|
starts before this — the chain comes BEFORE interface.
|
|
365
395
|
|
|
@@ -441,14 +441,22 @@ else
|
|
|
441
441
|
while IFS=: read -r ln tok; do
|
|
442
442
|
s=$(echo "$tok" | tr -d '`')
|
|
443
443
|
case ${#s} in 7|8|9|10|11|12|40) ;; *) continue ;; esac
|
|
444
|
-
git rev-parse --verify --quiet "$s^{commit}" >/dev/null 2>&1
|
|
444
|
+
if ! git rev-parse --verify --quiet "$s^{commit}" >/dev/null 2>&1; then
|
|
445
445
|
echo "$f:$ln: commit \`$s\` does not resolve" >> "$TMP/sha"
|
|
446
|
+
# Resolving is the weaker half. A commit that was AMENDED AWAY still resolves on
|
|
447
|
+
# the machine that amended it and reaches no clone — measured 2026-08-16, twice in
|
|
448
|
+
# one close-out: the stamp named the run's commit, the commit was amended to carry
|
|
449
|
+
# the stamp, and the SHA the stamp recorded stopped being reachable. Ask the
|
|
450
|
+
# question a reader actually has: is it in this history at all.
|
|
451
|
+
elif ! git merge-base --is-ancestor "$s^{commit}" HEAD >/dev/null 2>&1; then
|
|
452
|
+
echo "$f:$ln: commit \`$s\` resolves but is NOT reachable from HEAD — amended away, or on a branch this checkout does not have" >> "$TMP/sha"
|
|
453
|
+
fi
|
|
446
454
|
done
|
|
447
455
|
done
|
|
448
456
|
if [ -s "$TMP/sha" ] 2>/dev/null; then
|
|
449
|
-
err "
|
|
457
|
+
err "commit reference(s) a clone could not follow:"; sed 's/^/ /' "$TMP/sha"
|
|
450
458
|
else
|
|
451
|
-
ok "every commit reference in $RETRO_GLOB resolves"
|
|
459
|
+
ok "every commit reference in $RETRO_GLOB resolves AND is reachable from HEAD"
|
|
452
460
|
fi
|
|
453
461
|
fi
|
|
454
462
|
|