tiny-spec 1.1.0__tar.gz → 1.2.0__tar.gz
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.
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/PKG-INFO +44 -14
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/README.md +43 -13
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/agents/tiny-spec-build-executor.md +6 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/agents/tiny-spec-build-reviewer.md +46 -34
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/pyproject.toml +1 -1
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny-spec-build/SKILL.md +74 -16
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny-spec-plan/SKILL.md +20 -15
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny-spec-run/SKILL.md +139 -87
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny-spec-scope/SKILL.md +20 -1
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny_spec/__init__.py +1 -1
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/.gitignore +0 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/LICENSE +0 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/docs/eval/README.md +0 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/examples/todo-cli/README.md +0 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny-spec-adopt/SKILL.md +0 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny-spec-create/SKILL.md +0 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny-spec-design/SKILL.md +0 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny_spec/cli.py +0 -0
- {tiny_spec-1.1.0 → tiny_spec-1.2.0}/tiny_spec/manifest.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tiny-spec
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: A tiny, opinionated take on spec-driven development.
|
|
5
5
|
Project-URL: Homepage, https://github.com/GrayMa77er/tiny-spec
|
|
6
6
|
Project-URL: Source, https://github.com/GrayMa77er/tiny-spec
|
|
@@ -498,15 +498,38 @@ resumes from the checklist state.
|
|
|
498
498
|
### Working a whole list
|
|
499
499
|
|
|
500
500
|
Ask `/tiny-spec-run` to build — "build the backlog", "work through the breakdown",
|
|
501
|
-
"spec it out and build it" — and it takes a list of stories and works them
|
|
502
|
-
|
|
501
|
+
"spec it out and build it" — and it takes a list of stories and works them in batches.
|
|
502
|
+
Per story it does the same four moves:
|
|
503
503
|
|
|
504
504
|
```
|
|
505
505
|
cut a branch from main → walk the chain → tiny-spec-build → merge back to main
|
|
506
506
|
```
|
|
507
507
|
|
|
508
|
-
|
|
509
|
-
|
|
508
|
+
Each branch is cut **fresh from main**, so a later story sees the earlier ones already
|
|
509
|
+
merged — which is what makes an ordered list build correctly.
|
|
510
|
+
|
|
511
|
+
**Independent stories build at the same time.** A story can declare what it must follow
|
|
512
|
+
with a `needs:` line in `BREAKDOWN.md`; everything with no unmet `needs:` forms a batch
|
|
513
|
+
and builds **concurrently, one git worktree per story**, three at a time by default.
|
|
514
|
+
The batch merges, then the next one starts.
|
|
515
|
+
|
|
516
|
+
```
|
|
517
|
+
## Feature: interface
|
|
518
|
+
|
|
519
|
+
- Story: expose both helpers on a CLI slug: cli
|
|
520
|
+
- AC: `textkit slugify "Hi There"` prints "hi-there"
|
|
521
|
+
- needs: slugify, wordwrap
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Omit `needs:` when a story stands alone — that's the common case, and the field is meant
|
|
525
|
+
to be rare. A `needs:` you didn't need costs you parallelism forever; one you missed
|
|
526
|
+
costs a single merge conflict, which the run already catches and halts on. You can also
|
|
527
|
+
just name the set yourself at invocation ("build these three at once"), which overrides
|
|
528
|
+
the graph. A cycle, or a `needs:` naming a story that isn't there, stops the run rather
|
|
529
|
+
than being guessed past.
|
|
530
|
+
|
|
531
|
+
**Tasks *inside* a story never run in parallel.** They share files and each one assumes
|
|
532
|
+
the last landed, so they stay strictly sequential. Parallelism is across stories only.
|
|
510
533
|
|
|
511
534
|
**The list is `BREAKDOWN.md` by default** — its `- Story:` entries, in file order,
|
|
512
535
|
each already carrying a `slug:` (the branch and directory name) and `AC:` lines. Paste
|
|
@@ -529,9 +552,13 @@ honest trade: a breakdown runs unattended, a pasted list is supervised.
|
|
|
529
552
|
Stopping at story 2 of 7 and reporting "done" is what autonomous loops get wrong most
|
|
530
553
|
often, so the state is always named alongside what merged and what's still untouched.
|
|
531
554
|
|
|
532
|
-
**A halt stops the
|
|
533
|
-
|
|
534
|
-
|
|
555
|
+
**A halt stops the lane it happened in, and ends the run after that batch.** Its siblings
|
|
556
|
+
were declared independent, so they finish and merge — killing working lanes because one
|
|
557
|
+
failed throws away good work. But the run does not start the next batch: later stories
|
|
558
|
+
usually assume the earlier ones landed, so skipping ahead past a failure just produces a
|
|
559
|
+
second, more confusing failure downstream. With more than one lane you get each story's
|
|
560
|
+
own state, and the run's state is the worst of them — four green lanes and one `blocked`
|
|
561
|
+
is a `blocked` run.
|
|
535
562
|
|
|
536
563
|
**Pause points are technical, not per-story.** Any task can carry a `pause:` line, and
|
|
537
564
|
the build halts *before* running it:
|
|
@@ -547,12 +574,15 @@ destructive file operations, a new dependency, an auth boundary, a public API co
|
|
|
547
574
|
You can also give the run a standing policy up front ("halt before anything that touches
|
|
548
575
|
auth") and it gets applied as each story's tasks are sliced.
|
|
549
576
|
|
|
550
|
-
**What it will not do to your repo.** It runs exactly
|
|
551
|
-
`switch -c`, `merge --no-ff`, `merge --abort`,
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
the
|
|
577
|
+
**What it will not do to your repo.** It runs exactly seven git commands — `switch`,
|
|
578
|
+
`switch -c`, `merge --no-ff`, `merge --abort`, `worktree add`, `worktree list`, and
|
|
579
|
+
reads. It refuses to start on a dirty tree, or if a worktree path it needs already
|
|
580
|
+
exists. It **never pushes**, never rebases, never resets, never deletes a branch, never
|
|
581
|
+
removes a worktree, and never opens a PR. Merges are local, so a bad run is one
|
|
582
|
+
`git reset` away; publishing stays yours. If the gate goes red after a merge it leaves
|
|
583
|
+
the merge alone and tells you the undo command rather than running it — and it hands
|
|
584
|
+
back the `git worktree remove` commands for the lanes instead of running those either,
|
|
585
|
+
since a halted lane's worktree is the tree you need to look at.
|
|
556
586
|
|
|
557
587
|
**Walk away and come back.** Progress isn't written down, it's derived: a story whose
|
|
558
588
|
ticked `tasks.md` is on `main` is done, a `.spec/<slug>/` with an unchecked task is in
|
|
@@ -461,15 +461,38 @@ resumes from the checklist state.
|
|
|
461
461
|
### Working a whole list
|
|
462
462
|
|
|
463
463
|
Ask `/tiny-spec-run` to build — "build the backlog", "work through the breakdown",
|
|
464
|
-
"spec it out and build it" — and it takes a list of stories and works them
|
|
465
|
-
|
|
464
|
+
"spec it out and build it" — and it takes a list of stories and works them in batches.
|
|
465
|
+
Per story it does the same four moves:
|
|
466
466
|
|
|
467
467
|
```
|
|
468
468
|
cut a branch from main → walk the chain → tiny-spec-build → merge back to main
|
|
469
469
|
```
|
|
470
470
|
|
|
471
|
-
|
|
472
|
-
|
|
471
|
+
Each branch is cut **fresh from main**, so a later story sees the earlier ones already
|
|
472
|
+
merged — which is what makes an ordered list build correctly.
|
|
473
|
+
|
|
474
|
+
**Independent stories build at the same time.** A story can declare what it must follow
|
|
475
|
+
with a `needs:` line in `BREAKDOWN.md`; everything with no unmet `needs:` forms a batch
|
|
476
|
+
and builds **concurrently, one git worktree per story**, three at a time by default.
|
|
477
|
+
The batch merges, then the next one starts.
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
## Feature: interface
|
|
481
|
+
|
|
482
|
+
- Story: expose both helpers on a CLI slug: cli
|
|
483
|
+
- AC: `textkit slugify "Hi There"` prints "hi-there"
|
|
484
|
+
- needs: slugify, wordwrap
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Omit `needs:` when a story stands alone — that's the common case, and the field is meant
|
|
488
|
+
to be rare. A `needs:` you didn't need costs you parallelism forever; one you missed
|
|
489
|
+
costs a single merge conflict, which the run already catches and halts on. You can also
|
|
490
|
+
just name the set yourself at invocation ("build these three at once"), which overrides
|
|
491
|
+
the graph. A cycle, or a `needs:` naming a story that isn't there, stops the run rather
|
|
492
|
+
than being guessed past.
|
|
493
|
+
|
|
494
|
+
**Tasks *inside* a story never run in parallel.** They share files and each one assumes
|
|
495
|
+
the last landed, so they stay strictly sequential. Parallelism is across stories only.
|
|
473
496
|
|
|
474
497
|
**The list is `BREAKDOWN.md` by default** — its `- Story:` entries, in file order,
|
|
475
498
|
each already carrying a `slug:` (the branch and directory name) and `AC:` lines. Paste
|
|
@@ -492,9 +515,13 @@ honest trade: a breakdown runs unattended, a pasted list is supervised.
|
|
|
492
515
|
Stopping at story 2 of 7 and reporting "done" is what autonomous loops get wrong most
|
|
493
516
|
often, so the state is always named alongside what merged and what's still untouched.
|
|
494
517
|
|
|
495
|
-
**A halt stops the
|
|
496
|
-
|
|
497
|
-
|
|
518
|
+
**A halt stops the lane it happened in, and ends the run after that batch.** Its siblings
|
|
519
|
+
were declared independent, so they finish and merge — killing working lanes because one
|
|
520
|
+
failed throws away good work. But the run does not start the next batch: later stories
|
|
521
|
+
usually assume the earlier ones landed, so skipping ahead past a failure just produces a
|
|
522
|
+
second, more confusing failure downstream. With more than one lane you get each story's
|
|
523
|
+
own state, and the run's state is the worst of them — four green lanes and one `blocked`
|
|
524
|
+
is a `blocked` run.
|
|
498
525
|
|
|
499
526
|
**Pause points are technical, not per-story.** Any task can carry a `pause:` line, and
|
|
500
527
|
the build halts *before* running it:
|
|
@@ -510,12 +537,15 @@ destructive file operations, a new dependency, an auth boundary, a public API co
|
|
|
510
537
|
You can also give the run a standing policy up front ("halt before anything that touches
|
|
511
538
|
auth") and it gets applied as each story's tasks are sliced.
|
|
512
539
|
|
|
513
|
-
**What it will not do to your repo.** It runs exactly
|
|
514
|
-
`switch -c`, `merge --no-ff`, `merge --abort`,
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
the
|
|
540
|
+
**What it will not do to your repo.** It runs exactly seven git commands — `switch`,
|
|
541
|
+
`switch -c`, `merge --no-ff`, `merge --abort`, `worktree add`, `worktree list`, and
|
|
542
|
+
reads. It refuses to start on a dirty tree, or if a worktree path it needs already
|
|
543
|
+
exists. It **never pushes**, never rebases, never resets, never deletes a branch, never
|
|
544
|
+
removes a worktree, and never opens a PR. Merges are local, so a bad run is one
|
|
545
|
+
`git reset` away; publishing stays yours. If the gate goes red after a merge it leaves
|
|
546
|
+
the merge alone and tells you the undo command rather than running it — and it hands
|
|
547
|
+
back the `git worktree remove` commands for the lanes instead of running those either,
|
|
548
|
+
since a halted lane's worktree is the tree you need to look at.
|
|
519
549
|
|
|
520
550
|
**Walk away and come back.** Progress isn't written down, it's derived: a story whose
|
|
521
551
|
ticked `tasks.md` is on `main` is done, a `.spec/<slug>/` with an unchecked task is in
|
|
@@ -15,6 +15,12 @@ pleasantries.
|
|
|
15
15
|
|
|
16
16
|
Everything you need and nothing you don't:
|
|
17
17
|
|
|
18
|
+
- **the working directory** to operate in. Every path you read, write, or run a command
|
|
19
|
+
against resolves against it. It may be a **git worktree** rather than the main checkout
|
|
20
|
+
— a build can run several stories at once, each in its own worktree. **Never read or
|
|
21
|
+
write outside the directory you were given**, and never `cd` to a sibling worktree to
|
|
22
|
+
"check something": another story is being built there right now, and what you find will
|
|
23
|
+
be wrong by the time you act on it;
|
|
18
24
|
- the **task id**, **description**, and **acceptance** (the outcome that proves it done);
|
|
19
25
|
- a **`files:` hint** — likely paths to touch (guidance, not a hard boundary);
|
|
20
26
|
- the full **constitution** (`constitution.md`): Style, Engineering standards,
|
|
@@ -13,6 +13,11 @@ back to `tiny-spec-build`; return data, not pleasantries.
|
|
|
13
13
|
|
|
14
14
|
## What you receive (the context contract)
|
|
15
15
|
|
|
16
|
+
- **the working directory** to operate in — every path and every gate command resolves
|
|
17
|
+
against it. It may be a **git worktree** rather than the main checkout, since a build
|
|
18
|
+
can run several stories at once. **Never read, write, or run a gate outside the
|
|
19
|
+
directory you were given**: a sibling worktree holds a different story mid-build, and
|
|
20
|
+
measuring it would make your verdict meaningless;
|
|
16
21
|
- the **task id**, **description**, and **acceptance** (the outcome that must hold);
|
|
17
22
|
- the full **constitution** (`constitution.md`) — especially **Guiding invariants**,
|
|
18
23
|
**Definition of Done**, and **Verification commands**, plus the **Design system**
|
|
@@ -54,6 +59,18 @@ acceptance and the constitution — verified, not inferred?**
|
|
|
54
59
|
task on evidence you found insufficient is not** — that is the one thing this whole
|
|
55
60
|
role exists to prevent. Never narrow a `full` gate to `scoped`.
|
|
56
61
|
|
|
62
|
+
**A Verification command that cannot run in this environment is a `FAIL` and a
|
|
63
|
+
blocker — never a `PASS`.** If a documented command is red for a reason the task's
|
|
64
|
+
code cannot fix (the interpreter is too old, a tool isn't installed, the command was
|
|
65
|
+
never runnable as written), say exactly that in `GATE:`, return `FAIL`, and name the
|
|
66
|
+
**constitution** as the upstream document to fix. Proving the task green *somewhere
|
|
67
|
+
else* — a fresh venv, a container, an install you fixed by hand — is **not** a pass:
|
|
68
|
+
you verified a different environment than the one the gate names, and the next task
|
|
69
|
+
will hit the identical red. Diagnosing the cause and demonstrating the task's own code
|
|
70
|
+
is sound is genuinely useful; put it in `FINDINGS` so the fix is cheap. It does not
|
|
71
|
+
change the verdict. A constitution whose gate cannot run is the single most dangerous
|
|
72
|
+
file in the project, because every later task inherits the same false signal.
|
|
73
|
+
|
|
57
74
|
Say which scope you actually ran in `GATE:`, including that you escalated and why.
|
|
58
75
|
3. **Exercise the acceptance.** Trigger the task's stated outcome the most
|
|
59
76
|
black-box way available (CLI > HTTP > public API) with realistic input,
|
|
@@ -78,25 +95,23 @@ acceptance and the constitution — verified, not inferred?**
|
|
|
78
95
|
- **A selector that matches nothing is a `FAIL`, never a skip.** Either the code
|
|
79
96
|
didn't build the element or it named it something else — both are real, and both
|
|
80
97
|
are invisible if you quietly move on. Report the selector and that it was absent.
|
|
81
|
-
- Compare each element's numbers to the tokens its row names
|
|
82
|
-
deltas ("heading is 28px, `type.heading.lg` is 24px"; "padding 19px is not on
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- **Check `layout:`** — the arrangement, max width, and the **order** it names
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
command printed, `Read` the `D<n>`'s `export:` image, and grade **every state you
|
|
99
|
-
have a screenshot for**, not just the happy path, on four lines:
|
|
98
|
+
- Compare each element's numbers to the tokens its row names, reporting concrete
|
|
99
|
+
deltas ("heading is 28px, `type.heading.lg` is 24px"; "padding 19px is not on the
|
|
100
|
+
`space.*` scale"). **Do not pixel-diff the screenshot** — font antialiasing makes
|
|
101
|
+
image comparison flaky enough that the check gets ignored, which is how visual
|
|
102
|
+
gates die.
|
|
103
|
+
- **Check `layout:`** — the arrangement, max width, and the **order** it names, from
|
|
104
|
+
the bounding rectangles. Every token can be correct on an element in the wrong place.
|
|
105
|
+
- **Exercise every state the entry names** — empty, loading, error, success. Drive the
|
|
106
|
+
UI into each one and observe what changes; **finding the word in the source is not
|
|
107
|
+
evidence**, and it false-passes routinely (a comment saying the loading state is
|
|
108
|
+
missing contains "loading"). A surface that renders its happy path and nothing else
|
|
109
|
+
is a fail, not a nit.
|
|
110
|
+
- Finally, **judge the render against the export.** Everything above proves the numbers
|
|
111
|
+
are right; none of it can see an element that is present, on-token, and invisible —
|
|
112
|
+
so now look. `Read` each `SCREENSHOT <state> <path>` the `visual:` command printed
|
|
113
|
+
and the `D<n>`'s `export:` image, and grade **every state you have a screenshot
|
|
114
|
+
for**, not just the happy path, on four lines:
|
|
100
115
|
|
|
101
116
|
1. **Presence** — is every `elements:` row actually *visible* in the render? At
|
|
102
117
|
`opacity: 0`, zero height, clipped out of view, hidden behind a sibling, or the
|
|
@@ -104,24 +119,21 @@ acceptance and the constitution — verified, not inferred?**
|
|
|
104
119
|
not there. **FAIL** — this is why the step exists.
|
|
105
120
|
2. **Legibility & occlusion** — text clipped, truncated mid-word, overlapping
|
|
106
121
|
another element, or on a background it can't be read against. **FAIL.**
|
|
107
|
-
3. **Correspondence** —
|
|
108
|
-
|
|
109
|
-
|
|
122
|
+
3. **Correspondence** — the same regions as the export, in the reading order
|
|
123
|
+
`layout:` names? A whole region missing is a **FAIL**; a stylistic difference is
|
|
124
|
+
a **flag**.
|
|
110
125
|
4. **Hierarchy & polish** — emphasis, balance, crowding, alignment. **Always a
|
|
111
126
|
flag**, never a fail.
|
|
112
127
|
|
|
113
|
-
Three rules bound it
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- **Cite what you saw.** Name the state whose screenshot the finding came from and
|
|
123
|
-
what was in it ("state `error`: caption present in DOM but renders at opacity 0").
|
|
124
|
-
An uncited visual claim reads as an opinion and gets ignored.
|
|
128
|
+
Three rules bound it. **The numbers beat your eye on anything they already
|
|
129
|
+
measured** — on-scale padding that looks cramped, or an exact token that looks
|
|
130
|
+
washed out, is a `flag:`; you may fail only on what measurement *cannot* see, since
|
|
131
|
+
contradicting your own numbers sends the executor a task it cannot fix and the loop
|
|
132
|
+
is bounded at two attempts. **This is still not a pixel diff** — the export is
|
|
133
|
+
usually a wireframe, so judge structure and legibility, never visual identity. And
|
|
134
|
+
**cite what you saw**, naming the state the finding came from ("state `error`:
|
|
135
|
+
caption present in DOM but renders at opacity 0") — an uncited visual claim reads as
|
|
136
|
+
an opinion and gets ignored.
|
|
125
137
|
|
|
126
138
|
**If the command printed no `SCREENSHOT` line**, do not run this sub-step and do
|
|
127
139
|
not eyeball a substitute. Grade on steps 1–3 above, write `judge: not run — visual:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tiny-spec-build
|
|
3
|
-
description: Build the spec — run the per-task loop plan→implement→review→commit, one task at a time. Implements with a fresh executor, grades with an independent reviewer running the real gate, commits per passed task, keeps a lean memory. Halts on a blocker, a spent convergence budget, a task's pause: point, or a genuine fork, and names which. Resumes from the checkbox state.
|
|
3
|
+
description: Build the spec — run the per-task loop plan→implement→review→commit, one task at a time. Implements with a fresh executor, grades with an independent reviewer running the real gate, commits per passed task, keeps a lean memory. Can be handed several lanes (one worktree + slug per independent story) and runs them concurrently, one task at a time within each. Halts on a blocker, a spent convergence budget, a task's pause: point, or a genuine fork, and names which. Resumes from the checkbox state.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# tiny-spec-build
|
|
@@ -19,16 +19,14 @@ alongside this skill (see the suite README).
|
|
|
19
19
|
|
|
20
20
|
## Inputs
|
|
21
21
|
|
|
22
|
-
1. **Resolve the active ticket dir**
|
|
23
|
-
whose slug matches the branch
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
**degraded** case, not an ask case — branch match is simply unavailable, so fall
|
|
31
|
-
through to sole-dir and ask as written.
|
|
22
|
+
1. **Resolve the active ticket dir** (call it `<active>`), in order: the `.spec/<slug>/`
|
|
23
|
+
whose slug matches the current git branch (one branch per ticket); else the sole
|
|
24
|
+
ticket dir if exactly one exists; else ask. **Ask instead** when more than one dir
|
|
25
|
+
matches the branch, or when ticket dirs exist while you are on `main`/`master` with no
|
|
26
|
+
name match — neither has a safe tie-break. Detached HEAD or no git repo is
|
|
27
|
+
**degraded**, not an ask: fall through to sole-dir and ask as written.
|
|
28
|
+
In **multi-lane mode** (below) this step does not run: each lane is handed its
|
|
29
|
+
worktree and slug explicitly.
|
|
32
30
|
2. Read `.spec/constitution.md` (**the shared constitution**), `.spec/memory.md` if
|
|
33
31
|
it exists (**shared**), and `.spec/<active>/tasks.md`. The constitution + memory
|
|
34
32
|
get injected **whole** into every executor and reviewer. Also note the `ticket`
|
|
@@ -37,6 +35,52 @@ alongside this skill (see the suite README).
|
|
|
37
35
|
`tiny-spec-plan` to reconcile first.
|
|
38
36
|
4. Pick the **first unchecked `[ ]`** task. If all are `[x]`, jump to **Completion**.
|
|
39
37
|
|
|
38
|
+
## Multi-lane mode (optional — several stories at once)
|
|
39
|
+
|
|
40
|
+
A caller may hand you **lanes** instead of a single active dir: one or more
|
|
41
|
+
`(worktree path, slug)` pairs, each an independent story in its own git worktree.
|
|
42
|
+
`tiny-spec-run` does this for a batch of stories whose `needs:` say they don't depend on
|
|
43
|
+
each other. **With one lane, or none, everything below is inert and this skill behaves
|
|
44
|
+
exactly as it always has** — that is the common case and it must not drift.
|
|
45
|
+
|
|
46
|
+
Given lanes, run them **in rounds**:
|
|
47
|
+
|
|
48
|
+
1. For every live lane, pick its first `[ ]` task and build the executor brief exactly as
|
|
49
|
+
step 2 describes — resolved against **that lane's worktree**, not the main checkout.
|
|
50
|
+
2. **Dispatch every lane's executor in one message**, so they run concurrently.
|
|
51
|
+
3. **Dispatch every lane's reviewer in one message**, once the executors are back.
|
|
52
|
+
4. Commit and tick per passing lane, in that lane's worktree.
|
|
53
|
+
|
|
54
|
+
Then start the next round with whichever lanes are still live. A fast lane waits at the
|
|
55
|
+
round boundary for a slow one; that barrier is deliberate, and much simpler to follow than
|
|
56
|
+
letting lanes free-run.
|
|
57
|
+
|
|
58
|
+
**Within a lane nothing changes.** Tasks still run one at a time, top to bottom, with the
|
|
59
|
+
same convergence bound, the same gate scope rules, and the same halting states. There is
|
|
60
|
+
no parallelism *inside* a story and no `owns:` contract — tasks in one story share files
|
|
61
|
+
and assume their predecessors landed, which is exactly why they stay sequential.
|
|
62
|
+
|
|
63
|
+
**A halt stops that lane only.** The other lanes run to completion; you report each lane's
|
|
64
|
+
terminal state separately. The stories were declared independent, so killing working lanes
|
|
65
|
+
because one failed throws away finished work for nothing.
|
|
66
|
+
|
|
67
|
+
**Every dispatched agent is told its working directory**, and that all paths resolve
|
|
68
|
+
against it. An executor or reviewer must never read or write another lane's worktree.
|
|
69
|
+
|
|
70
|
+
### `memory.md` in multi-lane mode
|
|
71
|
+
|
|
72
|
+
`.spec/memory.md` is **shared at the `.spec/` root**, and step 6 below writes to it. Two
|
|
73
|
+
lanes appending to their own copy in their own worktree would conflict on *every* parallel
|
|
74
|
+
batch — a guaranteed merge failure that has nothing to do with the code.
|
|
75
|
+
|
|
76
|
+
So: **in multi-lane mode, lanes do not write `memory.md` at all.** Collect each lane's
|
|
77
|
+
distilled lessons as it goes, and write them **once, in the main checkout on the
|
|
78
|
+
integration branch, after the batch has merged** — pruning superseded entries there, as
|
|
79
|
+
step 6 describes. Single-lane runs are unaffected and write it in place as before.
|
|
80
|
+
|
|
81
|
+
The other artifacts are safe and need no special handling: `tasks.md` and `decisions.md`
|
|
82
|
+
are per-story, and `constitution.md` is read-only for the whole build.
|
|
83
|
+
|
|
40
84
|
## The per-task loop
|
|
41
85
|
|
|
42
86
|
For the selected task, run these steps in order. **Do not tick a task until its
|
|
@@ -60,6 +104,9 @@ it short and concrete.
|
|
|
60
104
|
### 2. IMPLEMENT (dispatch `tiny-spec-build-executor`)
|
|
61
105
|
Spawn one **`tiny-spec-build-executor`** with a fresh, self-contained prompt:
|
|
62
106
|
|
|
107
|
+
- **the working directory** it must operate in — this lane's worktree, or the project
|
|
108
|
+
root in a single-lane run. Every path resolves against it, and it must never read or
|
|
109
|
+
write another lane's worktree;
|
|
63
110
|
- the task id, description, and **acceptance**;
|
|
64
111
|
- the `files:` hint;
|
|
65
112
|
- the **whole** `.spec/constitution.md`;
|
|
@@ -69,10 +116,15 @@ Spawn one **`tiny-spec-build-executor`** with a fresh, self-contained prompt:
|
|
|
69
116
|
design instead of guessing at it;
|
|
70
117
|
- only the specific existing files the task starts from, named explicitly (so it
|
|
71
118
|
edits with the real current contents, not blind);
|
|
72
|
-
- the **
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
119
|
+
- the **accumulated `CHANGES` paths from every passed task in this story so far**,
|
|
120
|
+
labelled as the ground this story has already moved. Every executor starts cold and
|
|
121
|
+
re-derives the codebase from scratch — that re-derivation is the **largest single cost
|
|
122
|
+
in the loop**, far larger than the prompt text around it — and naming the files this
|
|
123
|
+
story has already touched is the cheapest way to cut it. You already have the lists in
|
|
124
|
+
hand. **Paths only**, deduplicated, most-recently-touched first — never the earlier
|
|
125
|
+
tasks' descriptions, findings, or reports. (The reviewer does *not* get this list: it
|
|
126
|
+
is scoped to the task's own changed files on purpose, and widening it would dilute the
|
|
127
|
+
independence that makes its verdict worth anything.)
|
|
76
128
|
|
|
77
129
|
Do **not** pass the plan, sibling tasks, or other chatter. It returns a structured
|
|
78
130
|
report (`STATUS`, `CHANGES`, `DECISIONS`, `BLOCKER`). A `STATUS: blocked` →
|
|
@@ -81,6 +133,8 @@ report (`STATUS`, `CHANGES`, `DECISIONS`, `BLOCKER`). A `STATUS: blocked` →
|
|
|
81
133
|
### 3. REVIEW (dispatch `tiny-spec-build-reviewer` — independent)
|
|
82
134
|
Spawn one **`tiny-spec-build-reviewer`**, **blind to step 2**, with:
|
|
83
135
|
|
|
136
|
+
- **the working directory** it must operate in — this lane's worktree, or the project
|
|
137
|
+
root in a single-lane run. It runs the gate there and touches no other lane;
|
|
84
138
|
- the task id, description, and **acceptance**;
|
|
85
139
|
- the **whole** `.spec/constitution.md`;
|
|
86
140
|
- the list of changed files (from the executor's `CHANGES`) to read;
|
|
@@ -169,6 +223,9 @@ entry to the **shared** `.spec/memory.md` (the root — lessons are project-wide
|
|
|
169
223
|
pruning any entry the new one supersedes. Skip code-style rules (→ shared
|
|
170
224
|
`constitution.md`) and one-off history (→ the ticket's `decisions.md`). Keep it lean.
|
|
171
225
|
|
|
226
|
+
**In multi-lane mode, do not write the file here.** Hold the entry and write it once
|
|
227
|
+
after the batch merges — see **Multi-lane mode** above for why.
|
|
228
|
+
|
|
172
229
|
On first use, create the file with this structure:
|
|
173
230
|
|
|
174
231
|
```markdown
|
|
@@ -212,7 +269,8 @@ way, which is why a loop and a hand-driven build can't drift apart.
|
|
|
212
269
|
## Halting
|
|
213
270
|
|
|
214
271
|
Every build run ends in exactly **one** of five terminal states. Name it out loud in
|
|
215
|
-
the closing report
|
|
272
|
+
the closing report — **per lane**, when there is more than one; a lane's halt never
|
|
273
|
+
rounds up into another lane's `done`, and the run's own state is the worst of them:
|
|
216
274
|
|
|
217
275
|
| State | Cause |
|
|
218
276
|
|---|---|
|
|
@@ -18,19 +18,15 @@ sets `tasks.md`'s `status:`.
|
|
|
18
18
|
|
|
19
19
|
Artifacts live under `.spec/`: the **shared** constitution at the root
|
|
20
20
|
(`.spec/constitution.md`), the per-ticket `SPEC.md`/`PLAN.md`/`tasks.md` under
|
|
21
|
-
`.spec/<slug>/`.
|
|
22
|
-
`.spec/<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
`main`/`master` with no name match (the usual cause is a forgotten `git switch`, and
|
|
31
|
-
the sole-dir fallback would otherwise swallow it). Detached HEAD or no git repo is a
|
|
32
|
-
**degraded** case, not an ask case — branch match is simply unavailable, so fall
|
|
33
|
-
through to sole-dir and ask as written.
|
|
21
|
+
`.spec/<slug>/`. Both skeletons are inline below — write them from there, no file to
|
|
22
|
+
read. Requires `.spec/<active>/SPEC.md`.
|
|
23
|
+
|
|
24
|
+
**Resolve the active ticket dir**, in order: the `.spec/<slug>/` whose slug matches the
|
|
25
|
+
current git branch (one branch per ticket); else the sole ticket dir if exactly one
|
|
26
|
+
exists; else ask. **Ask instead** when more than one dir matches the branch, or when
|
|
27
|
+
ticket dirs exist while you are on `main`/`master` with no name match — neither has a
|
|
28
|
+
safe tie-break. Detached HEAD or no git repo is **degraded**, not an ask: branch match is
|
|
29
|
+
simply unavailable, so fall through to sole-dir and ask as written.
|
|
34
30
|
|
|
35
31
|
## Step 1 — harden the constitution (`constitution.md`)
|
|
36
32
|
|
|
@@ -175,10 +171,19 @@ task it belongs to:
|
|
|
175
171
|
- a task that only adds tests for the task before it (the constitution's **Definition of
|
|
176
172
|
Done** already requires the tests to ship with the code);
|
|
177
173
|
- one task per file, or one task per function;
|
|
178
|
-
- a "wire it up" / "integrate the pieces" task trailing the pieces it wires
|
|
174
|
+
- a "wire it up" / "integrate the pieces" task trailing the pieces it wires;
|
|
175
|
+
- a **leading pure-scaffold task** — a skeleton, a dispatch stub, a module that imports
|
|
176
|
+
cleanly and does nothing. Fold it into the first task that gives it behavior;
|
|
177
|
+
- a **trailing end-to-end verification task**. `tiny-spec-build`'s Completion step already
|
|
178
|
+
runs the whole gate against the whole project from a clean state, exercised the way a
|
|
179
|
+
user would. A task that re-does it buys nothing and costs the full per-task overhead.
|
|
180
|
+
|
|
181
|
+
The shipped `examples/todo-cli` task list predates these two smells and shows both: its
|
|
182
|
+
`T1` is a pure scaffold and its `T5` is an end-to-end verification pass. Today `T1` folds
|
|
183
|
+
into `T2` and `T5` doesn't exist. Read that list for `T4`'s sizing, not for its edges.
|
|
179
184
|
|
|
180
185
|
**Count is a smell, not a cap.** A story sized the way `tiny-spec-scope` describes
|
|
181
|
-
usually lands in **
|
|
186
|
+
usually lands in **2–4 tasks**. If you are past about six, re-read the list: you have
|
|
182
187
|
either sliced below the commit line, or the story itself was too big and should have been
|
|
183
188
|
split upstream. Check the list against that; do **not** enforce a number, and never drop
|
|
184
189
|
or merge coverage just to hit one.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tiny-spec-run
|
|
3
|
-
description: The one router — read each artifact's status flag to work out where work stands, then invoke the skill that moves it forward. Two stop points, chosen once at the start: by default it walks tiny-spec-adopt/create → design → plan and STOPS before tiny-spec-build; asked to build ("spec it out and build it") or handed a story list, it drives each story branch → plan → build → merge until the list ends or something halts it. Use for "run the spec flow", "pick the chain back up", "build the backlog", "work through the breakdown". NOT for a single stage — for that invoke tiny-spec-create, tiny-spec-plan, or tiny-spec-design directly.
|
|
3
|
+
description: The one router — read each artifact's status flag to work out where work stands, then invoke the skill that moves it forward. Two stop points, chosen once at the start: by default it walks tiny-spec-adopt/create → design → plan and STOPS before tiny-spec-build; asked to build ("spec it out and build it") or handed a story list, it drives each story branch → plan → build → merge until the list ends or something halts it. Independent stories — grouped by their BREAKDOWN.md needs: field, or named by the user — build concurrently, one git worktree per lane, merging batch by batch. Use for "run the spec flow", "pick the chain back up", "build the backlog", "work through the breakdown". NOT for a single stage — for that invoke tiny-spec-create, tiny-spec-plan, or tiny-spec-design directly.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# tiny-spec-run
|
|
@@ -53,8 +53,8 @@ return to Step 2 after a stage, or between stories.
|
|
|
53
53
|
the branch for the user.
|
|
54
54
|
|
|
55
55
|
2. **Resolve the story list** (below) and play it back in order — slug and title — with
|
|
56
|
-
the state you derived for each
|
|
57
|
-
before anything is cut.
|
|
56
|
+
the state you derived for each, **grouped into the batches you will run**. This is the
|
|
57
|
+
user's chance to reorder, drop, narrow, or correct a dependency before anything is cut.
|
|
58
58
|
|
|
59
59
|
3. **Take the pause policy.** Ask for, or accept, standing technical stop points for
|
|
60
60
|
this run — *"halt before anything that touches auth"*, *"stop before any schema
|
|
@@ -63,6 +63,17 @@ return to Step 2 after a stage, or between stories.
|
|
|
63
63
|
**technical**, not per-story: the thing worth looking at is a migration or an auth
|
|
64
64
|
boundary, not a feature heading.
|
|
65
65
|
|
|
66
|
+
4. **Fix the lane count.** Default **3**; take a number from the user if they gave one.
|
|
67
|
+
This is a concurrency limit on the machine, not a budget — the story list is still the
|
|
68
|
+
budget, and a batch wider than the lane count simply runs in successive fills. **One
|
|
69
|
+
lane must stay available**: any phrasing asking for one story at a time ("one at a
|
|
70
|
+
time", "no parallelism", "serially") sets it to 1, which collapses Step 4 to the
|
|
71
|
+
sequential behavior — no worktrees, no batching.
|
|
72
|
+
|
|
73
|
+
Then check one more precondition, once you know the slugs in the first batch: **no
|
|
74
|
+
target worktree path already exists.** Name the collisions and stop; do not reuse or
|
|
75
|
+
remove a directory you found.
|
|
76
|
+
|
|
66
77
|
## Step 1 — resolve the scope
|
|
67
78
|
|
|
68
79
|
### Stop-before-build: the active ticket dir
|
|
@@ -126,7 +137,26 @@ on the integration branch at all, so "does the directory exist" can't tell "not
|
|
|
126
137
|
apart from "built on a branch you haven't merged" — reading each ref explicitly can.
|
|
127
138
|
Sourcing step 1 from the integration branch is also what survives a deleted branch: once
|
|
128
139
|
a story is merged, its ticked task list is part of `main` whether or not the branch that
|
|
129
|
-
built it still exists.
|
|
140
|
+
built it still exists. **This survives parallelism unchanged** — a worktree is just a
|
|
141
|
+
branch, so a story built in a worktree but not yet merged lands on rule 2 exactly as one
|
|
142
|
+
built in place.
|
|
143
|
+
|
|
144
|
+
**Group the remaining stories into batches.** Level 0 is every unbuilt story whose
|
|
145
|
+
`needs:` are empty or already built-and-merged; level 1 is every story whose `needs:` are
|
|
146
|
+
all in level 0; and so on. Batches run **in order**; the stories inside one batch run
|
|
147
|
+
**concurrently**, capped at the lane count.
|
|
148
|
+
|
|
149
|
+
- **A parallel set the user named at invocation overrides the graph** for this run —
|
|
150
|
+
"build these three at once", or a pasted list they called independent. Explicit beats
|
|
151
|
+
derived, the same way a pasted story list already wins over `BREAKDOWN.md`. Say that you
|
|
152
|
+
are overriding, and name what the graph would have done.
|
|
153
|
+
- **A cycle in `needs:` is a stop.** Print the cycle and stop. Never break it by picking
|
|
154
|
+
an order — `tiny-spec-scope` says two stories that need each other are really one, and
|
|
155
|
+
choosing for the user hides a carve that needs fixing.
|
|
156
|
+
- **A `needs:` naming a slug that isn't in the list is a stop**, not a shrug. It is either
|
|
157
|
+
a typo or a story someone dropped, and both want a human.
|
|
158
|
+
- **A batch of one is the sequential behavior** — Step 4 collapses to exactly what it did
|
|
159
|
+
before lanes existed. This is the common case for a single ticket and must not change.
|
|
130
160
|
|
|
131
161
|
## Step 2 — the ladder
|
|
132
162
|
|
|
@@ -146,67 +176,62 @@ re-evaluate against the new state on disk.
|
|
|
146
176
|
| — | **no rung matched** | **stop** — report the exact state you found and ask; never improvise a stage |
|
|
147
177
|
|
|
148
178
|
**Upstream beats downstream — that is what the table order encodes.** Always fix the
|
|
149
|
-
earliest artifact in the chain that needs attention,
|
|
150
|
-
|
|
151
|
-
|
|
179
|
+
earliest artifact in the chain that needs attention, stale *or* missing. Deriving a task
|
|
180
|
+
list from a design you already know is wrong wastes the run, and the reconcile then has
|
|
181
|
+
to uncheck completed work all over again.
|
|
152
182
|
|
|
153
183
|
**L0/L1 carry the greenfield/brownfield fork.** "Does this repo already contain source?"
|
|
154
|
-
means:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
**L1 is a reseed, not a fresh start
|
|
161
|
-
missing while a perfectly good `SPEC.md` sits next to it.
|
|
162
|
-
**reseed/refresh mode** — say so explicitly, and add: *do not
|
|
163
|
-
do not create a ticket dir, and do not touch `SPEC.md`.* If the
|
|
164
|
-
doesn't exist afterwards, stop and tell the user; do not loop.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
is
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
whether two documents agree. A design that moved under a finished spec is otherwise
|
|
182
|
-
completely invisible — no status flips, and the build reviews against a screen that no
|
|
183
|
-
longer exists.
|
|
184
|
+
means: real code beyond config and docs. If yes, the constitution must be *derived from
|
|
185
|
+
that code* (`tiny-spec-adopt`) rather than interviewed out of the user — an interviewed
|
|
186
|
+
constitution on an existing codebase is how you get a gate that names commands the
|
|
187
|
+
project doesn't have. Starting from an idea with no code yet, `tiny-spec-scope` is the
|
|
188
|
+
user's to run first; it is not in the ladder (see below).
|
|
189
|
+
|
|
190
|
+
**L1 is a reseed, not a fresh start**, and it **outranks L2**. The constitution is
|
|
191
|
+
project-wide, so it can go missing while a perfectly good `SPEC.md` sits next to it.
|
|
192
|
+
Invoke the chosen skill in **reseed/refresh mode** — say so explicitly, and add: *do not
|
|
193
|
+
re-interview from scratch, do not create a ticket dir, and do not touch `SPEC.md`.* If the
|
|
194
|
+
constitution still doesn't exist afterwards, stop and tell the user; do not loop. The next
|
|
195
|
+
pass lands on L2 and creates the spec properly — a *different rung*, so the same-skill
|
|
196
|
+
bound below does not fire.
|
|
197
|
+
|
|
198
|
+
**L3 is a stop, not a stage.** Nothing in the suite ever *sets* `SPEC.md` to stale — it is
|
|
199
|
+
the root of the chain — so a stale SPEC means someone hand-edited it. Surface it and offer
|
|
200
|
+
`tiny-spec-create` in update mode rather than assuming intent. A go-ahead **in the same
|
|
201
|
+
turn** is enough to invoke it and carry on down the ladder; the stop exists to get a human
|
|
202
|
+
decision, not to force a second command. (Update mode *clears* the flag on its way out, so
|
|
203
|
+
this rung resolves rather than repeating.)
|
|
204
|
+
|
|
205
|
+
**L4 is the one thing `run` checks that isn't a `status:` flag**, and it is deliberately
|
|
206
|
+
*not* a validator: for each `D<n>` in the active `SPEC.md`, run `shasum -a 256 <export>`
|
|
207
|
+
and compare with the `sha256:` the entry declares. You are checking whether an anchor
|
|
208
|
+
still points at what it says it points at, not inferring whether two documents agree. A
|
|
209
|
+
design that moved under a finished spec is otherwise completely invisible — no status
|
|
210
|
+
flips, and the build reviews against a screen that no longer exists.
|
|
184
211
|
|
|
185
212
|
- **Mismatch** → `tiny-spec-design` in re-anchor mode.
|
|
186
213
|
- **Missing file** → **stop and tell the user**, naming the entry and the path. Don't
|
|
187
|
-
route it: a deleted export can mean a rename, a move, or a design
|
|
188
|
-
|
|
214
|
+
route it: a deleted export can mean a rename, a move, or a withdrawn design, and each
|
|
215
|
+
wants a different answer. Never quietly drop the entry.
|
|
189
216
|
- No `## Design` section, or every hash matches → the rung doesn't fire; fall through.
|
|
190
217
|
|
|
191
218
|
**Any `status:` that isn't exactly `current`** — `stale`, missing, unreadable, or an
|
|
192
219
|
unrecognized value like `draft` — counts as **stale**, on `SPEC.md`, `PLAN.md`, and
|
|
193
|
-
`tasks.md` alike. Say so out loud.
|
|
194
|
-
|
|
195
|
-
`SPEC.md` it means L3 — stop and ask.
|
|
220
|
+
`tasks.md` alike. Say so out loud. `PLAN.md`/`tasks.md` → reconcile (update mode preserves
|
|
221
|
+
existing ids, the non-destructive way to be wrong); `SPEC.md` → L3, stop and ask.
|
|
196
222
|
|
|
197
|
-
**A `tasks.md` with no tasks at all is not "built"** —
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
time, stop and tell the user — the plan has nothing derivable in it.
|
|
223
|
+
**A `tasks.md` with no tasks at all is not "built"** — L6 and L7 both require at least one
|
|
224
|
+
task, so an empty checklist matches neither and L5 catches it. Empty a second time → stop
|
|
225
|
+
and tell the user; the plan has nothing derivable in it.
|
|
201
226
|
|
|
202
227
|
`run` trusts the `status:` flags. It does **not** second-guess hand edits, diff
|
|
203
|
-
timestamps, or validate the chain — there is no validator in this suite by design. (L4
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
228
|
+
timestamps, or validate the chain — there is no validator in this suite by design. (L4 is
|
|
229
|
+
not an exception: a hash is a value the spec itself declares about a file it names, so
|
|
230
|
+
checking it is reading state, not judging consistency. Not a precedent for adding
|
|
231
|
+
cross-document checks.) So it inherits each stage's propagation: if `tiny-spec-plan`
|
|
232
|
+
update mode doesn't flip `tasks.md`, `run` walks right past it. That trust is the price of
|
|
233
|
+
having no validator; when a run's result looks wrong, suspect the stage's propagation
|
|
234
|
+
before the ladder.
|
|
210
235
|
|
|
211
236
|
**`tiny-spec-scope` is not in the ladder.** It is a pre-spec on-ramp that writes
|
|
212
237
|
`BREAKDOWN.md` at the project root, carries no `status:` frontmatter, and is the user's
|
|
@@ -253,39 +278,60 @@ to enter `tiny-spec-build` in a stop-before-build run.**
|
|
|
253
278
|
## Step 4 — build-through only: build and merge
|
|
254
279
|
|
|
255
280
|
Reached only when Step 0 fixed the stop point at build-through and the ladder is at L6
|
|
256
|
-
(or L7 with an unmerged branch).
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
281
|
+
(or L7 with an unmerged branch). Work **one batch at a time**, in batch order. For every
|
|
282
|
+
story in the current batch that isn't already built and merged:
|
|
283
|
+
|
|
284
|
+
1. **Lane.** Give each story its own **git worktree**, so lanes cannot collide on the
|
|
285
|
+
filesystem: `git worktree add ../<repo>-<slug> -b <slug> <integration>`, or
|
|
286
|
+
`git worktree add ../<repo>-<slug> <slug>` when the branch already exists. Cut **fresh
|
|
287
|
+
from the integration branch** so the story sees every story merged before it — that is
|
|
288
|
+
what makes an ordered backlog build correctly, and it is why batches merge before the
|
|
289
|
+
next one starts.
|
|
290
|
+
|
|
291
|
+
With a single lane you may stay in the main checkout and `git switch` as before; a
|
|
292
|
+
worktree is only required when a batch has more than one story.
|
|
293
|
+
|
|
294
|
+
2. **Ladder.** Walk Steps 1–3 for each story, in its own worktree, until it reaches L6.
|
|
295
|
+
A story that stops anywhere else does **not** enter the build — its lane is halted;
|
|
296
|
+
see the halt rule below. **Never walk the ladder twice to push past its own stop:**
|
|
297
|
+
each of those is a human decision it deliberately declined to make, and running it
|
|
298
|
+
again declines again.
|
|
299
|
+
|
|
300
|
+
3. **Build.** Invoke **`tiny-spec-build`** **once for the whole batch**, briefed to run
|
|
301
|
+
through, and hand it every live lane as a `(worktree path, slug)` pair. It owns the
|
|
302
|
+
per-task loop, runs the lanes concurrently, and writes each story's halt record.
|
|
303
|
+
Do not invoke it a second time for a lane that halted: it resumes from the checkbox
|
|
304
|
+
state, so it lands on the very task that just halted and halts there again.
|
|
305
|
+
|
|
306
|
+
4. **Merge — only the lanes that returned `done`, one at a time, in batch order.**
|
|
307
|
+
For each, in the main checkout:
|
|
275
308
|
- `git switch <integration>`
|
|
276
309
|
- `git merge --no-ff <slug>` — the merge commit keeps each story legible in history.
|
|
277
|
-
- **Conflict** → `git merge --abort`,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
310
|
+
- **Conflict** → `git merge --abort`, halt that story `conflict`, and carry on with
|
|
311
|
+
the rest of the batch. Report every conflicted story at the end.
|
|
312
|
+
- **Run the constitution's Verification commands on the merged result**, exercised the
|
|
313
|
+
way a user would. Red → halt `blocked`. **This check is load-bearing under
|
|
314
|
+
parallelism and is never skipped:** every lane gated against a tree that did not
|
|
315
|
+
contain its siblings, so the merged result is the first time they meet.
|
|
281
316
|
- **Never push.** Merging locally keeps a bad run one `git reset` away; sending it to
|
|
282
317
|
a remote is the user's call, and this suite makes no network calls.
|
|
283
318
|
|
|
284
|
-
5. **Next
|
|
319
|
+
5. **Next batch.** If **every** lane in this batch merged cleanly, return to Step 1 for
|
|
320
|
+
the next batch. Do not re-run Step 0. **If any lane halted, stop the run here** — a
|
|
321
|
+
later batch may well depend on the story that failed, and working out which stories
|
|
322
|
+
are still safe is exactly the machinery this suite refuses to grow. Report and let the
|
|
323
|
+
user decide.
|
|
324
|
+
|
|
325
|
+
6. **Leave the worktrees.** Do **not** run `git worktree remove`. Cleanup is the user's,
|
|
326
|
+
for the same reason branch deletion is — a halted lane's worktree holds the tree they
|
|
327
|
+
need to look at. List every path you created in the closing report, with the
|
|
328
|
+
`git worktree remove` commands, and let them run it.
|
|
285
329
|
|
|
286
|
-
**A halt stops the
|
|
287
|
-
|
|
288
|
-
|
|
330
|
+
**A halt stops the lane it happened in, and ends the run at the end of that batch.**
|
|
331
|
+
Within a batch the other lanes run to completion: the stories were declared independent,
|
|
332
|
+
so stopping the ones that are working buys nothing and throws away finished work. Across
|
|
333
|
+
batches the old rule stands unchanged — later stories usually assume earlier ones landed,
|
|
334
|
+
so never skip ahead past a failure.
|
|
289
335
|
|
|
290
336
|
`tiny-spec-build` records its own halts (`blocked`, `exhausted`, `fork`) in the story's
|
|
291
337
|
`decisions.md`. **You record nothing** — the merge-stage halts are already legible
|
|
@@ -309,11 +355,15 @@ L3 or on a bound, say exactly what stopped you and what the user needs to decide
|
|
|
309
355
|
**Build-through.** Name, in this order:
|
|
310
356
|
|
|
311
357
|
1. **The terminal state** — exactly one of `done`, `blocked`, `exhausted`, `paused`,
|
|
312
|
-
`fork`, `conflict`. Use the word.
|
|
313
|
-
|
|
314
|
-
|
|
358
|
+
`fork`, `conflict`. Use the word. With more than one lane, report **each story's own
|
|
359
|
+
state**, and give the run's state as the **worst** of them: four green lanes and one
|
|
360
|
+
`blocked` is a `blocked` run, never a `done` one with a footnote.
|
|
361
|
+
2. **Stories built and merged**, in batch order, with their merge commits.
|
|
362
|
+
3. **The story or stories it stopped on**, the task within each, and why in one line.
|
|
315
363
|
4. **Stories never started** — say how many are left, by name.
|
|
316
|
-
5. **
|
|
364
|
+
5. **Worktrees left on disk**, with the `git worktree remove` command for each. You do
|
|
365
|
+
not run them.
|
|
366
|
+
6. **The one command that resolves it** — `tiny-spec-create`/`tiny-spec-plan` in update
|
|
317
367
|
mode for `blocked`/`exhausted`, this skill again for `paused`, the decision the user
|
|
318
368
|
owes you for a `fork`, or the conflicted paths for a `conflict`.
|
|
319
369
|
|
|
@@ -338,9 +388,11 @@ ticket that legitimately has no visual surface.
|
|
|
338
388
|
- **Never write, edit, or flip anything.** No `status:`, no checkbox, no `decisions.md`
|
|
339
389
|
entry, no code, no `BREAKDOWN.md` edit. Delegate or stop. A router that regenerates
|
|
340
390
|
documents is a second source of truth.
|
|
341
|
-
- **Never push, force, rebase, reset, or
|
|
342
|
-
skill issues are `switch`, `switch -c`, `merge --no-ff`,
|
|
343
|
-
|
|
391
|
+
- **Never push, force, rebase, reset, delete a branch, or remove a worktree.** The only
|
|
392
|
+
git commands this skill issues are `switch`, `switch -c`, `merge --no-ff`,
|
|
393
|
+
`merge --abort`, `worktree add`, `worktree list`, and read-only queries. Everything
|
|
394
|
+
outward-facing or destructive stays the user's — `worktree remove` included, which is
|
|
395
|
+
why Step 4 hands those commands over instead of running them.
|
|
344
396
|
- **Never merge a story whose build didn't return `done`.**
|
|
345
397
|
- **Never invoke `tiny-spec-run`.** Re-entering means re-reading these steps, not
|
|
346
398
|
calling yourself. Self-invocation compounds context and does not terminate.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tiny-spec-scope
|
|
3
|
-
description: The greenfield on-ramp — turn a rough idea, or an existing PRD, into BREAKDOWN.md at the project root - a flat list of Features → user Stories, each with draft acceptance criteria
|
|
3
|
+
description: The greenfield on-ramp — turn a rough idea, or an existing PRD, into BREAKDOWN.md at the project root - a flat list of Features → user Stories, each with draft acceptance criteria, a slug, and an optional needs: naming the stories it must follow, plus a shared Decisions block. The one skill that works from a blank page. Does not scaffold .spec/ or touch the constitution — tiny-spec-create reads BREAKDOWN.md in seeded mode and does that. For an existing codebase use tiny-spec-adopt instead. The suite works without either.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# tiny-spec-scope
|
|
@@ -135,6 +135,7 @@ Write `BREAKDOWN.md` at the **project root** (the user's cwd) with the structure
|
|
|
135
135
|
- Story: <one user-observable capability> slug: <ado-__ | kebab>
|
|
136
136
|
- AC: <a single user-observable, testable outcome — becomes REQ-1 in this story's SPEC.md>
|
|
137
137
|
- AC: <another — keep each atomic; no "and" hiding two capabilities>
|
|
138
|
+
- needs: <slug, slug> <!-- optional; stories this one must be built after. Omit when independent. -->
|
|
138
139
|
- design: <path(s) to the wireframe(s) covering this story — omit if it has no visual surface>
|
|
139
140
|
- Story: <one user-observable capability> slug: <ado-__ | kebab>
|
|
140
141
|
- AC: <…>
|
|
@@ -171,6 +172,24 @@ story it belongs to, so `tiny-spec-design` knows which files to open for that sp
|
|
|
171
172
|
instead of re-deriving the mapping from filenames. A wireframe covering several stories
|
|
172
173
|
is listed on each of them.
|
|
173
174
|
|
|
175
|
+
**`needs:` is what lets stories build in parallel — so keep it honest and keep it rare.**
|
|
176
|
+
It names the stories this one must be built *after*. `tiny-spec-run` uses it to group the
|
|
177
|
+
backlog into batches: stories with no unmet `needs:` build concurrently, each in its own
|
|
178
|
+
git worktree, and the next batch starts once they merge. Three rules:
|
|
179
|
+
|
|
180
|
+
- **Name a story only for a real code dependency** — this story reads or edits files that
|
|
181
|
+
one writes, or builds on a schema, API, or type it introduces. A shared theme, a shared
|
|
182
|
+
`## Feature:` heading, or "it feels like it comes second" is **not** a dependency.
|
|
183
|
+
- **When unsure, omit it.** The two errors are not symmetric. A `needs:` you didn't need
|
|
184
|
+
costs parallelism on every future run of this backlog, silently and forever. A `needs:`
|
|
185
|
+
you missed costs one merge conflict, which `tiny-spec-run` already detects and halts on.
|
|
186
|
+
- **Never point across a cycle.** If two stories each need the other, they are one story —
|
|
187
|
+
merge them, or split the shared part out into a third that both need.
|
|
188
|
+
|
|
189
|
+
A `## Feature:` heading carries no `needs:` — it is a grouping, not a work item. Most
|
|
190
|
+
backlogs need the field on a minority of stories; a `needs:` on nearly every story means
|
|
191
|
+
the carve is really a sequence, and you should say so rather than encode it here.
|
|
192
|
+
|
|
174
193
|
**Slugs** mirror `tiny-spec-create`: derive from the platform key when bound — ADO
|
|
175
194
|
`AB#77`→`ado-77`, GitHub `#42`→`gh-42`, Monday item→`monday-<id>`, Jira `PROJ-123`
|
|
176
195
|
verbatim; **ad-hoc** → a short kebab-case slug (`email-login`). The tracker id is
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|