@zalom/plastic 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PLASTIC.md +4 -2
- package/README.md +3 -3
- package/agents/plastic-enforcer.md +5 -2
- package/agents/plastic-future-intent-researcher.md +1 -1
- package/hooks/check-update +1 -1
- package/hooks/continue +1 -1
- package/package.json +1 -1
- package/scripts/dashboard.rb +29 -24
- package/scripts/doctor.rb +136 -5
- package/scripts/hook-continue +3 -3
- package/scripts/install.rb +2 -1
- package/scripts/lib/bridge.rb +81 -0
- package/scripts/lib/dashboard_banner.rb +8 -9
- package/scripts/lib/installer_core.rb +10 -3
- package/scripts/lib/legacy_bookend_amnesty.rb +35 -0
- package/scripts/lib/release_guard.rb +62 -0
- package/scripts/lib/roadmap_queue.rb +285 -0
- package/scripts/lib/roadmap_savepoint.rb +213 -0
- package/scripts/lib/worktree.rb +21 -0
- package/scripts/new-intent +1 -0
- package/scripts/read-config +3 -3
- package/scripts/roadmap-next +44 -0
- package/scripts/roadmap-savepoint +64 -0
- package/skills/auto/SKILL.md +22 -4
- package/skills/continuing/SKILL.md +34 -0
- package/skills/continuing/evals/evals.json +91 -0
- package/skills/dashboard/SKILL.md +17 -14
- package/skills/dashboard/references/classification.md +3 -3
- package/skills/dashboard/templates/dashboard-global.md +8 -23
- package/skills/dashboard/templates/dashboard-project.md +7 -26
- package/skills/doctor/SKILL.md +1 -1
- package/skills/install/SKILL.md +10 -10
- package/skills/intent-continuing/SKILL.md +26 -68
- package/skills/intent-continuing/evals/evals.json +26 -26
- package/skills/intent-continuing/references/context-management.md +15 -19
- package/skills/intent-savepoint/SKILL.md +12 -0
- package/skills/intent-starting/evals/evals.json +1 -1
- package/skills/project-continuing/SKILL.md +104 -0
- package/skills/project-continuing/evals/evals.json +100 -0
- package/skills/project-continuing/references/board-fill.md +33 -0
- package/skills/releasing/SKILL.md +48 -0
- package/skills/releasing/references/release-lines.md +105 -0
- package/skills/roadmap/SKILL.md +7 -1
- package/skills/roadmap/references/file-format.md +30 -1
- package/skills/roadmap/references/operations.md +26 -6
- package/skills/roadmap-continuing/SKILL.md +85 -0
- package/skills/roadmap-continuing/evals/evals.json +82 -0
- package/skills/roadmap-continuing/references/liveness-ranking.md +56 -0
- package/skills/skill-evaluating/evals/evals.json +1 -1
- package/skills/tutorial/references/track-2-auto.md +1 -1
- package/skills/uninstall/SKILL.md +2 -2
- package/skills/update/SKILL.md +2 -2
- package/templates/config.yml +2 -1
- package/templates/index.md +4 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"skill_name": "plastic-intent-continuing",
|
|
3
|
-
"notes": "Intent
|
|
3
|
+
"notes": "Intent 158a1: slimmed to the specific-intent resume route of plastic-continuing (D8, extract not duplicate). Bare 'continue' now belongs to the router; triggering evals 1-4 were recast so this skill's positives always name a specific intent, and eval 4 asserts bare 'continue' does NOT settle here directly. Behavior eval 11 (dashboard selection) was removed, relocated to plastic-project-continuing's evals. Behavior eval 15 is new: the terminal handoff goes to plastic-intent-starting, not directly to plastic-auto. Scopes: description triggering (1-8) and behavior/convention compliance of the ledger-resume skill (9-15).",
|
|
4
4
|
"results": {
|
|
5
|
-
"triggering": { "cases": 8, "passed": 8, "run": "2026-
|
|
6
|
-
"behavior": { "cases":
|
|
5
|
+
"triggering": { "cases": 8, "passed": 8, "run": "2026-07-10, one subagent per case" },
|
|
6
|
+
"behavior": { "cases": 7, "passed": 7, "evidence": "convention checks against skills/intent-continuing/SKILL.md after the 158a1 extraction" }
|
|
7
7
|
},
|
|
8
8
|
"evals": [
|
|
9
9
|
{
|
|
10
10
|
"id": 1, "scope": "triggering", "set": "train",
|
|
11
|
-
"prompt": "continue",
|
|
12
|
-
"expected_output": "Activates plastic-intent-continuing (
|
|
11
|
+
"prompt": "continue intent 4a",
|
|
12
|
+
"expected_output": "Activates plastic-intent-continuing (a specific intent named by id).",
|
|
13
13
|
"files": [],
|
|
14
14
|
"assertions": [
|
|
15
15
|
{ "type": "code", "check": "router CHOICE == plastic-intent-continuing", "observed": "plastic-intent-continuing", "result": "pass" }
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"id": 2, "scope": "triggering", "set": "train",
|
|
20
|
-
"prompt": "resume
|
|
21
|
-
"expected_output": "Activates plastic-intent-continuing.",
|
|
20
|
+
"prompt": "resume the statusline-coloring intent",
|
|
21
|
+
"expected_output": "Activates plastic-intent-continuing (a specific intent named by description).",
|
|
22
22
|
"files": [],
|
|
23
23
|
"assertions": [
|
|
24
24
|
{ "type": "code", "check": "router CHOICE == plastic-intent-continuing", "observed": "plastic-intent-continuing", "result": "pass" }
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"id": 3, "scope": "triggering", "set": "validation",
|
|
29
|
-
"prompt": "
|
|
30
|
-
"expected_output": "Activates plastic-intent-continuing.",
|
|
29
|
+
"prompt": "continuing --intent 96",
|
|
30
|
+
"expected_output": "Activates plastic-intent-continuing (the --intent form).",
|
|
31
31
|
"files": [],
|
|
32
32
|
"assertions": [
|
|
33
33
|
{ "type": "code", "check": "router CHOICE == plastic-intent-continuing", "observed": "plastic-intent-continuing", "result": "pass" }
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"id": 4, "scope": "triggering", "set": "train",
|
|
38
|
-
"prompt": "
|
|
39
|
-
"expected_output": "
|
|
38
|
+
"prompt": "continue",
|
|
39
|
+
"expected_output": "Does NOT settle on plastic-intent-continuing directly. Bare 'continue' with no specific intent named belongs to the plastic-continuing router, which defaults to plastic-project-continuing.",
|
|
40
40
|
"files": [],
|
|
41
41
|
"assertions": [
|
|
42
|
-
{ "type": "code", "check": "router CHOICE
|
|
42
|
+
{ "type": "code", "check": "router CHOICE != plastic-intent-continuing", "observed": "plastic-continuing", "result": "pass" }
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
{
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
{
|
|
73
73
|
"id": 8, "scope": "triggering", "set": "validation",
|
|
74
74
|
"prompt": "what's the dashboard look like right now",
|
|
75
|
-
"expected_output": "
|
|
75
|
+
"expected_output": "Does NOT activate plastic-intent-continuing directly; an overview request without a specific intent named is a dashboard/project-board task.",
|
|
76
76
|
"files": [],
|
|
77
77
|
"assertions": [
|
|
78
78
|
{ "type": "code", "check": "router CHOICE != plastic-intent-continuing", "observed": "plastic-dashboard", "result": "pass" }
|
|
@@ -97,16 +97,7 @@
|
|
|
97
97
|
]
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
|
-
"id": 11, "scope": "behavior", "set": "
|
|
101
|
-
"prompt": "Is dashboard selection project-aware?",
|
|
102
|
-
"expected_output": "Project loaded -> `dashboard.rb project <slug>`; otherwise -> `dashboard.rb continue`. Skill only invokes, does not render.",
|
|
103
|
-
"files": ["skills/intent-continuing/SKILL.md"],
|
|
104
|
-
"assertions": [
|
|
105
|
-
{ "type": "convention", "check": "both dashboard invocations present and gated on project detection", "observed": "'dashboard.rb project <slug>' and 'dashboard.rb continue' in the 'Continue (present the dashboard)' section", "result": "pass" }
|
|
106
|
-
]
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"id": 12, "scope": "behavior", "set": "validation",
|
|
100
|
+
"id": 11, "scope": "behavior", "set": "validation",
|
|
110
101
|
"prompt": "Is ledger-resume conditional and ledger-driven?",
|
|
111
102
|
"expected_output": "Resume fires only when a specific intent is named; reads savepoint.md last line as stage, verifies the stage file, rebuilds on drift, derives next step from first unchecked checklist item.",
|
|
112
103
|
"files": ["skills/intent-continuing/SKILL.md"],
|
|
@@ -115,7 +106,7 @@
|
|
|
115
106
|
]
|
|
116
107
|
},
|
|
117
108
|
{
|
|
118
|
-
"id":
|
|
109
|
+
"id": 12, "scope": "behavior", "set": "validation",
|
|
119
110
|
"prompt": "Are the stale prose-savepoint fields gone?",
|
|
120
111
|
"expected_output": "No 'In progress' / 'Blockers' fields read from a prose savepoint remain; the ledger model is used instead.",
|
|
121
112
|
"files": ["skills/intent-continuing/SKILL.md"],
|
|
@@ -124,7 +115,7 @@
|
|
|
124
115
|
]
|
|
125
116
|
},
|
|
126
117
|
{
|
|
127
|
-
"id":
|
|
118
|
+
"id": 13, "scope": "behavior", "set": "validation",
|
|
128
119
|
"prompt": "Is the 'Determine Store' local-store gap filled?",
|
|
129
120
|
"expected_output": "Step 2 of Determine Store describes local/project-store detection (no empty step).",
|
|
130
121
|
"files": ["skills/intent-continuing/SKILL.md"],
|
|
@@ -133,13 +124,22 @@
|
|
|
133
124
|
]
|
|
134
125
|
},
|
|
135
126
|
{
|
|
136
|
-
"id":
|
|
127
|
+
"id": 14, "scope": "behavior", "set": "validation",
|
|
137
128
|
"prompt": "QMD is present. The user says: continue the work on the statusline coloring (names the intent by description, not id).",
|
|
138
129
|
"expected_output": "Before scanning the store with grep/Read to locate the named intent, runs `ruby ~/.plastic/scripts/qmd-sync search \"statusline coloring\"` to surface the candidate intent, then opens the authoritative intent file for the hit it resumes. No-op fallback to INDEX.md / file scan when QMD is absent.",
|
|
139
130
|
"files": ["skills/intent-continuing/SKILL.md"],
|
|
140
131
|
"assertions": [
|
|
141
132
|
{ "type": "human", "check": "qmd-sync search is run before grep/Read when the intent is named by description; authoritative file opened for the hit", "observed": "Conditional Ledger-Resume carries the QMD-first step: run qmd-sync search before grep/Read, then open the authoritative file; no-op fallback when QMD is absent", "result": "pass" }
|
|
142
133
|
]
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"id": 15, "scope": "behavior", "set": "train",
|
|
137
|
+
"prompt": "Does the ledger-resume's terminal handoff go to plastic-intent-starting or directly to plastic-auto?",
|
|
138
|
+
"expected_output": "Step 5 announces state then hands off to plastic-intent-starting, which takes the lock and asks 'auto or guided?' exactly once; plastic-intent-starting's own auto branch is the one that hands to plastic-auto. This skill never hands to plastic-auto directly.",
|
|
139
|
+
"files": ["skills/intent-continuing/SKILL.md"],
|
|
140
|
+
"assertions": [
|
|
141
|
+
{ "type": "convention", "check": "step 5 hands off to plastic-intent-starting, not plastic-auto; the single ask is attributed to plastic-intent-starting", "observed": "present in 'Conditional Ledger-Resume' step 5", "result": "pass" }
|
|
142
|
+
]
|
|
143
143
|
}
|
|
144
144
|
]
|
|
145
145
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Context Management (
|
|
1
|
+
# Context Management (Save, then resume-flow debugging)
|
|
2
2
|
|
|
3
3
|
## Save Point
|
|
4
4
|
Triggered by PreCompact hook or manually:
|
|
@@ -7,26 +7,22 @@ Triggered by PreCompact hook or manually:
|
|
|
7
7
|
3. Update active intent's `savepoint.md` (in-progress, next steps, blockers, discoveries)
|
|
8
8
|
4. Add observations to `## Insights`
|
|
9
9
|
5. Update INDEX.md
|
|
10
|
-
6. Commit: `cd ~/.plastic && git add . && git commit -m "chore: savepoint
|
|
10
|
+
6. Commit: `cd ~/.plastic && git add . && git commit -m "chore: savepoint - [intent name]"`
|
|
11
11
|
7. Notify user to `/clear`
|
|
12
12
|
|
|
13
|
-
##
|
|
14
|
-
Triggered by UserPromptSubmit hook when user says "continue". Priority order:
|
|
13
|
+
## Debugging the resume flow
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
2. Read active intent's `intent.md` → what and why
|
|
19
|
-
3. Read active intent's `savepoint.md` → where we left off
|
|
20
|
-
4. Read active intent's `checklist.md` → what's next
|
|
21
|
-
5. Announce: intent name, current state, next step, blockers
|
|
22
|
-
6. Resume
|
|
15
|
+
When a resume looks wrong (the announced stage does not match what is on disk, or the next
|
|
16
|
+
step looks stale):
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
2.
|
|
27
|
-
|
|
18
|
+
1. Read `savepoint.md`'s last line directly; that line alone is the source of truth for stage
|
|
19
|
+
(see `SKILL.md`'s `## Conditional Ledger-Resume` for the full state table).
|
|
20
|
+
2. Confirm the artifact that line implies (`plan.md`, `checklist.md`, `outcome.md`, ...) is
|
|
21
|
+
present and non-empty on disk.
|
|
22
|
+
3. If the two disagree, the ledger has drifted: rebuild it rather than hand-editing:
|
|
23
|
+
`ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.rebuild_savepoint("<intent_dir>")'`
|
|
24
|
+
4. Re-read the rebuilt last line and re-derive the next step from `checklist.md`'s first
|
|
25
|
+
unchecked item.
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
- **abandon** — mark as abandoned
|
|
32
|
-
- **defer to agent** — implement, research, or ideate
|
|
27
|
+
The general "land on the board / priority order / stale future intents" flow now lives in
|
|
28
|
+
`plastic-project-continuing`; this file no longer duplicates it.
|
|
@@ -42,6 +42,18 @@ Read the active store's `INDEX.md` and extract intents under `## Active`.
|
|
|
42
42
|
- Confirm the **last line's stage** matches the stage derived from files-on-disk
|
|
43
43
|
(`Bridge.derive_stage`). If they disagree, or the file is missing/empty, the ledger has
|
|
44
44
|
drifted.
|
|
45
|
+
- Run `Bridge.savepoint_phantom_lines(intent_dir)` (pure, disk-only, no bridge or session
|
|
46
|
+
resolution; intent 134). It flags a line disk evidence contradicts: a file-landing milestone
|
|
47
|
+
whose file is absent or still a sentinel placeholder, a duplicate `(stage, milestone)` pair, or
|
|
48
|
+
a state line (`How started` / `Exec started`) whose stage prerequisites are absent. If it
|
|
49
|
+
returns any lines:
|
|
50
|
+
- **LIVE intent (INDEX `## Active`):** rebuild via step 3 below.
|
|
51
|
+
- **INDEX `## Completed`/`## Abandoned` intent:** REPORT the phantom lines and STOP. Do not
|
|
52
|
+
rewrite. Completed intents are immutable. A repair is available only under an explicit human
|
|
53
|
+
grant, following the 124a precedent: rebuild the file-landing skeleton (step 3), then
|
|
54
|
+
re-append the terminal `Done <disposition>` bookend with the disposition read from
|
|
55
|
+
`outcome.md`'s `disposition:` frontmatter and the timestamp recovered from the merge commit or
|
|
56
|
+
the file's mtime, never invented.
|
|
45
57
|
|
|
46
58
|
### 3. Rebuild on drift
|
|
47
59
|
Reconstruct from the filesystem rather than hand-editing:
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"scope": "triggering",
|
|
41
41
|
"set": "validation",
|
|
42
42
|
"prompt": "continue",
|
|
43
|
-
"expected_output": "Does NOT activate plastic-intent-starting; bare 'continue' is plastic-
|
|
43
|
+
"expected_output": "Does NOT activate plastic-intent-starting; bare 'continue' is plastic-continuing (the router, no specific intent named).",
|
|
44
44
|
"files": [],
|
|
45
45
|
"assertions": [
|
|
46
46
|
{ "type": "code", "check": "router CHOICE != plastic-intent-starting", "result": "expect-pass" }
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plastic-project-continuing
|
|
3
|
+
description: >-
|
|
4
|
+
Use when the user wants to land on the project board, asks "what should I work on" in this
|
|
5
|
+
project, or wants active intents plus the most-valuable next work surfaced. This is the
|
|
6
|
+
default route of plastic-continuing for a bare "continue" with no specific intent or roadmap
|
|
7
|
+
named. It presents state and stops without asking how to proceed - that single mode question
|
|
8
|
+
lives in plastic-intent-starting, once the user names a specific intent to work.
|
|
9
|
+
user-invocable: true
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Project Continuing - land on the board
|
|
13
|
+
|
|
14
|
+
`plastic-project-continuing` is the project route of `plastic-continuing`. It lands on the
|
|
15
|
+
Markdown board, presents active work and the most-valuable next work, and stops. It does not
|
|
16
|
+
resume a specific intent by ledger (that is `plastic-intent-continuing`) and it asks nothing.
|
|
17
|
+
|
|
18
|
+
## Continue (present the board)
|
|
19
|
+
|
|
20
|
+
Land on the Markdown board via the `plastic-dashboard` skill. Rendering belongs there, not
|
|
21
|
+
here - run the data payload and fill + present the matching template:
|
|
22
|
+
- Project loaded -> `ruby ~/.plastic/scripts/dashboard.rb project <slug> --data`
|
|
23
|
+
- Otherwise (no project loaded, the global fallback) -> `ruby ~/.plastic/scripts/dashboard.rb continue --data`
|
|
24
|
+
|
|
25
|
+
Fill the matching template from `plastic-dashboard`'s `templates/` and **present the filled
|
|
26
|
+
Markdown in your reply** (every time, non-optional). If the reply does not contain the filled
|
|
27
|
+
Markdown, the user sees nothing - tool-call stdout and hook `additionalContext` are both
|
|
28
|
+
invisible to them. `hook-continue` also emits a one-line `systemMessage` summary as a
|
|
29
|
+
hook-owned fallback; treat it as a floor only, never as a substitute for presenting the full
|
|
30
|
+
board here. See `plastic-dashboard` for the fill mechanics (`references/board-fill.md` has the
|
|
31
|
+
scoped detail).
|
|
32
|
+
|
|
33
|
+
The board load runs the scoped store check on every load (`doctor --store <scope>`): the
|
|
34
|
+
global board runs `--store global` and a project board runs `--store <slug>`. The result
|
|
35
|
+
arrives in the payload as `store_health`; surface it as a one-line store-health note. It is
|
|
36
|
+
non-fatal (a warn or fail is shown as data, it does not block continuing).
|
|
37
|
+
|
|
38
|
+
## Priority Order
|
|
39
|
+
|
|
40
|
+
1. **Active intents first** - surface work in progress.
|
|
41
|
+
2. **Project context** - if in a registered project, show governing + tactical intents.
|
|
42
|
+
3. **Stale future intents** - surface for triage (see below).
|
|
43
|
+
4. **Fresh future intents** - offer as next work.
|
|
44
|
+
|
|
45
|
+
## Stale Future Intents
|
|
46
|
+
|
|
47
|
+
If a future intent's `created` date is older than the configured `stale_threshold_days`
|
|
48
|
+
(default 3), surface it for triage without taking action:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Stale future intents (no action taken):
|
|
52
|
+
|
|
53
|
+
- [ID - name] (X days old)
|
|
54
|
+
a) Activate - start working on it now
|
|
55
|
+
b) Abandon - mark as abandoned
|
|
56
|
+
c) Defer to agent: implement | research | ideate
|
|
57
|
+
d) Auto - go fully autonomous (invokes plastic-auto)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
When the user activates a future intent, move it to `## Active` in INDEX.md and auto-commit.
|
|
61
|
+
|
|
62
|
+
**Defer to agent: research.** Selecting `research` is a real dispatch, not a label. Resolve
|
|
63
|
+
`plastic-future-intent-researcher`'s model via `read-config agents.models.<basename> --project
|
|
64
|
+
<repo>` (project override, then global, then the shipped tier default) rather than relying on
|
|
65
|
+
bare frontmatter, which also honors a sanctioned `agents.models.<name>` override if one is
|
|
66
|
+
configured. Dispatch the agent (Agent tool, `subagent_type: "plastic-future-intent-researcher"`)
|
|
67
|
+
on the selected stale future intent, passing the resolved model explicitly. The agent writes its
|
|
68
|
+
findings into that intent's `## Context` (see `agents/plastic-future-intent-researcher.md`); it
|
|
69
|
+
does not itself dispatch further sub-agents. Once it returns, re-present the stale-future-intent
|
|
70
|
+
triage so the user can act on the fresh findings now on record.
|
|
71
|
+
|
|
72
|
+
## Deciding rules
|
|
73
|
+
|
|
74
|
+
The board's ranked next-work order is deterministic, computed by `dashboard.rb`; this skill
|
|
75
|
+
never re-derives it. Cite the rule NAMES only when explaining a ranking or disposition: Effort,
|
|
76
|
+
Value, Flags, Override, Caps. Read `plastic-dashboard`'s `references/classification.md` for the
|
|
77
|
+
definitions; do not restate or copy them here.
|
|
78
|
+
|
|
79
|
+
## Then stop
|
|
80
|
+
|
|
81
|
+
Present "here is the state, what next?" and wait. Offer active intents first, then future
|
|
82
|
+
intents. Do not start executing work. The only follow-up from here:
|
|
83
|
+
- User/agent names a specific intent to continue -> hand to `plastic-intent-continuing`
|
|
84
|
+
(which reads the ledger and, in turn, hands to `plastic-intent-starting` for the single
|
|
85
|
+
auto-or-guided ask).
|
|
86
|
+
|
|
87
|
+
## Coordination
|
|
88
|
+
|
|
89
|
+
Intent 149 has landed: the dashboard is demoted to prose (no Value x Effort grid). This skill
|
|
90
|
+
was built against the live INDEX.md-parsing `--data` path (147, the DB cutover, has not
|
|
91
|
+
landed). Its rule-name citations (`classification.md`, cited by name, not logic) and the
|
|
92
|
+
`dashboard.rb project <slug> --data` -> `dashboard-project.md` path still resolve.
|
|
93
|
+
|
|
94
|
+
Intent 148 landed: roadmaps are the primary planning surface. When the tier has a mid-flight
|
|
95
|
+
roadmap (`ruby ~/.plastic/scripts/roadmap-next --roadmaps-dir <tier>/roadmaps` reports a `state`
|
|
96
|
+
other than `none`), the roadmap route (`plastic-roadmap-continuing`) is the live surface for
|
|
97
|
+
"what to work next", and `plastic-continuing` routes there. This board still presents project
|
|
98
|
+
state and stops, asking nothing (unchanged): it does not itself dispatch, re-rank, or resume a
|
|
99
|
+
roadmap. The global store and any project with no roadmap report `none`, so this board stays the
|
|
100
|
+
default route for them.
|
|
101
|
+
|
|
102
|
+
## References
|
|
103
|
+
|
|
104
|
+
- `references/board-fill.md` - the template-fill mechanics and store-health surfacing detail.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "plastic-project-continuing",
|
|
3
|
+
"notes": "Intent 158a1. New project route, absorbing intent-continuing's Continue/Priority Order/Stale Future Intents sections (move, not duplicate). Scopes: description triggering (1-3) and behavior/convention compliance (4-10), the latter checked against SKILL.md.",
|
|
4
|
+
"results": {
|
|
5
|
+
"triggering": { "cases": 3, "passed": 3, "run": "2026-07-10, one subagent per case" },
|
|
6
|
+
"behavior": { "cases": 7, "passed": 7, "evidence": "convention checks against skills/project-continuing/SKILL.md after the 158a1 extraction; id 10 (intent 170) added for the research-dispatch wiring of plastic-future-intent-researcher" }
|
|
7
|
+
},
|
|
8
|
+
"evals": [
|
|
9
|
+
{
|
|
10
|
+
"id": 1, "scope": "triggering", "set": "train",
|
|
11
|
+
"prompt": "what should I work on in this project",
|
|
12
|
+
"expected_output": "Activates plastic-project-continuing.",
|
|
13
|
+
"files": [],
|
|
14
|
+
"assertions": [
|
|
15
|
+
{ "type": "code", "check": "router CHOICE == plastic-project-continuing", "observed": "plastic-project-continuing", "result": "pass" }
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 2, "scope": "triggering", "set": "train",
|
|
20
|
+
"prompt": "land me on the project board",
|
|
21
|
+
"expected_output": "Activates plastic-project-continuing.",
|
|
22
|
+
"files": [],
|
|
23
|
+
"assertions": [
|
|
24
|
+
{ "type": "code", "check": "router CHOICE == plastic-project-continuing", "observed": "plastic-project-continuing", "result": "pass" }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": 3, "scope": "triggering", "set": "validation",
|
|
29
|
+
"prompt": "continue",
|
|
30
|
+
"expected_output": "Bare 'continue' with no specific intent or roadmap named reaches plastic-project-continuing via plastic-continuing's default route.",
|
|
31
|
+
"files": [],
|
|
32
|
+
"assertions": [
|
|
33
|
+
{ "type": "code", "check": "router CHOICE == plastic-project-continuing", "observed": "plastic-project-continuing", "result": "pass" }
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": 4, "scope": "behavior", "set": "train",
|
|
38
|
+
"prompt": "Is dashboard selection project-aware?",
|
|
39
|
+
"expected_output": "Project loaded -> `dashboard.rb project <slug> --data` -> fills dashboard-project.md; otherwise -> `dashboard.rb continue --data`. Skill only invokes, does not render.",
|
|
40
|
+
"files": ["skills/project-continuing/SKILL.md"],
|
|
41
|
+
"assertions": [
|
|
42
|
+
{ "type": "convention", "check": "both dashboard invocations present and gated on project detection", "observed": "'dashboard.rb project <slug>' and 'dashboard.rb continue' in the 'Continue (present the board)' section", "result": "pass" }
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": 5, "scope": "behavior", "set": "train",
|
|
47
|
+
"prompt": "Does the skill cite the classification rules by name instead of copying the logic?",
|
|
48
|
+
"expected_output": "SKILL.md's 'Deciding rules' section names Effort/Value/Flags/Override/Caps and points to plastic-dashboard's references/classification.md; it does not restate the rule definitions.",
|
|
49
|
+
"files": ["skills/project-continuing/SKILL.md"],
|
|
50
|
+
"assertions": [
|
|
51
|
+
{ "type": "convention", "check": "rule names cited, no copied Effort/Value/Flags/Override/Caps definitions", "observed": "present in 'Deciding rules'", "result": "pass" }
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": 6, "scope": "behavior", "set": "train",
|
|
56
|
+
"prompt": "Does the project route ask 'auto or guided?' before stopping?",
|
|
57
|
+
"expected_output": "No. The project route presents state then stops; it asks nothing. The single ask lives in plastic-intent-starting once a specific intent is named.",
|
|
58
|
+
"files": ["skills/project-continuing/SKILL.md"],
|
|
59
|
+
"assertions": [
|
|
60
|
+
{ "type": "convention", "check": "no 'auto or guided' ask anywhere in SKILL.md", "observed": "absent", "result": "pass" }
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"id": 7, "scope": "behavior", "set": "validation",
|
|
65
|
+
"prompt": "Does the skill present the board before stopping?",
|
|
66
|
+
"expected_output": "The filled Markdown board is presented in the reply (non-optional), then a 'Then stop' section waits for the user.",
|
|
67
|
+
"files": ["skills/project-continuing/SKILL.md"],
|
|
68
|
+
"assertions": [
|
|
69
|
+
{ "type": "convention", "check": "presentation is mandatory and precedes the 'Then stop' wait", "observed": "present", "result": "pass" }
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": 8, "scope": "behavior", "set": "validation",
|
|
74
|
+
"prompt": "Does the skill carry the intent-149 coordination note?",
|
|
75
|
+
"expected_output": "A 'Coordination' section records that intent 149 has landed (dashboard demoted to prose, no Value x Effort grid) and confirms the rule-name citations and the dashboard.rb project <slug> --data -> dashboard-project.md path still resolve.",
|
|
76
|
+
"files": ["skills/project-continuing/SKILL.md"],
|
|
77
|
+
"assertions": [
|
|
78
|
+
{ "type": "convention", "check": "'149' present with the landed coordination note", "observed": "present: 'Intent 149 has landed' with confirmed rule-name citations and dashboard.rb path", "result": "pass" }
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": 9, "scope": "behavior", "set": "validation",
|
|
83
|
+
"prompt": "Are Priority Order and Stale Future Intents present here and only here (not in intent-continuing)?",
|
|
84
|
+
"expected_output": "Both sections are present verbatim in this skill; they no longer exist in skills/intent-continuing/SKILL.md.",
|
|
85
|
+
"files": ["skills/project-continuing/SKILL.md", "skills/intent-continuing/SKILL.md"],
|
|
86
|
+
"assertions": [
|
|
87
|
+
{ "type": "convention", "check": "'## Priority Order' and '## Stale Future Intents' present here, absent from intent-continuing", "observed": "present here, absent there", "result": "pass" }
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": 10, "scope": "behavior", "set": "validation",
|
|
92
|
+
"prompt": "In the stale-future-intent triage, the user picks option c) Defer to agent: research. What happens?",
|
|
93
|
+
"expected_output": "A real dispatch, not a label: plastic-future-intent-researcher's model is resolved via `read-config agents.models.<basename> --project <repo>` (never bare frontmatter, sanctioned-override-aware), the agent is dispatched via the Agent tool on the selected stale future intent and writes its findings into that intent's `## Context`, then the skill re-presents the stale-future-intent triage.",
|
|
94
|
+
"files": ["skills/project-continuing/SKILL.md"],
|
|
95
|
+
"assertions": [
|
|
96
|
+
{ "type": "convention", "check": "the 'Defer to agent: research' paragraph under Stale Future Intents resolves the model via read-config (not bare frontmatter), dispatches plastic-future-intent-researcher via the Agent tool, and re-presents the triage after the agent returns", "observed": "'Defer to agent: research.' paragraph present: resolves via read-config agents.models.<basename> --project <repo>, dispatches subagent_type plastic-future-intent-researcher, findings land in ## Context, then re-presents the triage", "result": "pass" }
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Board Fill Mechanics
|
|
2
|
+
|
|
3
|
+
Depth reference for the "Continue (present the board)" section of `SKILL.md`. The fill itself
|
|
4
|
+
is mechanical; `plastic-dashboard` owns the rules, this page is a pointer plus the detail that
|
|
5
|
+
would otherwise bloat the SKILL.md body.
|
|
6
|
+
|
|
7
|
+
## Fill rules (owned by plastic-dashboard, summarized here for convenience)
|
|
8
|
+
|
|
9
|
+
- `{{a.b.count}}` -> the integer (e.g. `counts.active` is that count).
|
|
10
|
+
- `{{...lines}}` -> join the list's `.line` strings with real newlines (one per line). These
|
|
11
|
+
are ordinary prose lines; never add a `-` prefix, never emit `<br>`. An empty list renders
|
|
12
|
+
`_(none)_`.
|
|
13
|
+
- `next_work.lines` -> the most-valuable next work, already ranked and capped.
|
|
14
|
+
- `active.lines` / `future.lines` (project board) -> one line per intent, already formatted.
|
|
15
|
+
- Scalars (`{{date}}`, `{{slug}}`, `{{description}}`) -> substitute verbatim.
|
|
16
|
+
|
|
17
|
+
No re-sorting, no re-summarizing, no hand-written prose replacing a line the payload already
|
|
18
|
+
supplies. Same store state produces a byte-identical payload regardless of model.
|
|
19
|
+
|
|
20
|
+
## Store-health surfacing
|
|
21
|
+
|
|
22
|
+
Every board load runs the scoped `doctor --store <scope>` check server-side (inside
|
|
23
|
+
`dashboard.rb`, not this skill). The payload carries the result as `store_health` (`{scope,
|
|
24
|
+
status, summary, failing_checks}`). Render it as a single line, for example `store health:
|
|
25
|
+
pass (3/3)` or `store health: warn (orphaned_intents)`. A warn or fail is informational only;
|
|
26
|
+
it never blocks presenting the board.
|
|
27
|
+
|
|
28
|
+
## Project vs. global fallback
|
|
29
|
+
|
|
30
|
+
The project route's default target is the project board. When no project is loaded (the rare
|
|
31
|
+
case where this route is reached without a registered project in scope), fall back to the
|
|
32
|
+
global board payload (`dashboard.rb continue --data`) rather than failing. This mirrors the
|
|
33
|
+
router's D6 default: a bare "continue" always lands somewhere useful.
|
|
@@ -15,6 +15,7 @@ Project configuration drives the workflow - no hardcoded assumptions.
|
|
|
15
15
|
- [ ] All tests pass (or verification skipped per config)
|
|
16
16
|
- [ ] Merge feature branch to main
|
|
17
17
|
- [ ] Bump version in configured version files
|
|
18
|
+
- [ ] Stable-cut guard passes (version files agree, no pre-release suffix; stable/latest cuts only)
|
|
18
19
|
- [ ] Commit version bump
|
|
19
20
|
- [ ] Create annotated tag
|
|
20
21
|
- [ ] Push to remote with tags
|
|
@@ -94,6 +95,25 @@ the full rationale and the already-merged-by-hand no-op case, read
|
|
|
94
95
|
|
|
95
96
|
### 4. Bump Version
|
|
96
97
|
|
|
98
|
+
**Stable-cut guard.** Before touching any version file for a stable (no pre-release suffix,
|
|
99
|
+
`latest`) cut, run the guard in `scripts/lib/release_guard.rb`:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
require "./scripts/lib/release_guard"
|
|
103
|
+
result = ReleaseGuard.check(
|
|
104
|
+
package_json: "package.json",
|
|
105
|
+
plugin_json: ".claude-plugin/plugin.json",
|
|
106
|
+
marketplace_json: ".claude-plugin/marketplace.json",
|
|
107
|
+
stable: true
|
|
108
|
+
)
|
|
109
|
+
raise "release guard failed: #{result.mismatches} #{result.prerelease_suffix}" unless result.ok?
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If it reports a mismatch or a pre-release-suffix violation, stop and resolve it before bumping
|
|
113
|
+
any file. For a beta or alpha cut, pass `stable: false`; only version-file agreement is checked,
|
|
114
|
+
a pre-release suffix is expected. Read `references/release-lines.md` for the stable-line
|
|
115
|
+
guarantees this guard protects.
|
|
116
|
+
|
|
97
117
|
Determine which files to update from project.yml:
|
|
98
118
|
|
|
99
119
|
- If `release.version_files` is set: update ALL listed files (they must stay in sync).
|
|
@@ -252,6 +272,32 @@ prune` in the affected repo if you hit a stale reference. For why this is the on
|
|
|
252
272
|
merge-vs-remove policy lands on merge, and the fail-open/idempotent guarantees of `finish`,
|
|
253
273
|
read `references/promotion-and-tagging.md`.
|
|
254
274
|
|
|
275
|
+
## Release lines and channels
|
|
276
|
+
|
|
277
|
+
Two lanes get code to a release, on top of the workflow above.
|
|
278
|
+
|
|
279
|
+
- **Default lane.** Branch, merge to main, cut stable, publish to npm `latest`. This is the
|
|
280
|
+
workflow in the steps above, unchanged. Use it for additive, suite-verifiable,
|
|
281
|
+
low-blast-radius work.
|
|
282
|
+
- **Beta-verified lane.** Branch, merge to `beta`, publish to the npm `beta` channel, verify in
|
|
283
|
+
real use, then merge to main and cut stable. Use it for work that changes operational
|
|
284
|
+
substrate, or carries data, migration, lock, or state-format risk, or that a hermetic suite
|
|
285
|
+
cannot fully validate on its own.
|
|
286
|
+
|
|
287
|
+
**Stable-line guarantees.** An external `latest` user can rely on:
|
|
288
|
+
|
|
289
|
+
- `main` is always green and releasable; no pending revert awaiting re-land sits on `main`.
|
|
290
|
+
- A stable release carries no pre-release suffix, publishes to `latest`, and the newest release
|
|
291
|
+
always carries the GitHub "Latest" badge.
|
|
292
|
+
- The three version files always agree, checked by `scripts/lib/release_guard.rb` (see Bump
|
|
293
|
+
Version above).
|
|
294
|
+
- A stable cut collects only intents that cleared their lane's verification bar.
|
|
295
|
+
- Channel semantics are fixed: `latest` is stable, `beta` is the verification line, `alpha` is
|
|
296
|
+
experimental.
|
|
297
|
+
|
|
298
|
+
Read `references/release-lines.md` for the full lane-routing detail, the version-line map, and
|
|
299
|
+
the intent-41 re-land playbook.
|
|
300
|
+
|
|
255
301
|
## Conventions
|
|
256
302
|
|
|
257
303
|
- **Annotated tags only** - `git tag -a`, never lightweight tags
|
|
@@ -266,6 +312,8 @@ read `references/promotion-and-tagging.md`.
|
|
|
266
312
|
|
|
267
313
|
## References
|
|
268
314
|
|
|
315
|
+
- Read `references/release-lines.md` for the two release lanes, the stable-line guarantees,
|
|
316
|
+
the version-line map, and the intent-41 re-land playbook before starting any release
|
|
269
317
|
- When promoting a pre-release across channels (`--promote beta`/`--promote stable`) or
|
|
270
318
|
tagging a historical release retroactively, read `references/promotion-and-tagging.md`
|
|
271
319
|
for the exact commands and rules first
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Release Lines and Channels
|
|
2
|
+
|
|
3
|
+
The two release lanes, the version-line map, and the intent-41 re-land playbook: the deep
|
|
4
|
+
material behind SKILL.md's "Release lines and channels" section.
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
- [The two lanes](#the-two-lanes)
|
|
9
|
+
- [Routing rule](#routing-rule)
|
|
10
|
+
- [Stable-line guarantees](#stable-line-guarantees)
|
|
11
|
+
- [Version-line map](#version-line-map)
|
|
12
|
+
- [Intent 41 re-land playbook](#intent-41-re-land-playbook)
|
|
13
|
+
|
|
14
|
+
## The two lanes
|
|
15
|
+
|
|
16
|
+
**Default lane.** Branch, merge to `main` with `--no-ff`, cut stable, publish to npm `latest`.
|
|
17
|
+
This is the workflow SKILL.md documents step by step. It is the path for additive,
|
|
18
|
+
suite-verifiable, low-blast-radius work: new skills, prose, deterministic scripts, anything a
|
|
19
|
+
green Minitest run can fully vouch for.
|
|
20
|
+
|
|
21
|
+
**Beta-verified lane.** Branch, merge to the `beta` branch, publish to the npm `beta` dist-tag,
|
|
22
|
+
verify in real use, then merge `beta` into `main` and cut stable. It sits on top of the existing
|
|
23
|
+
promotion mechanics (`--promote beta` / `--promote stable`, linear only, see
|
|
24
|
+
`promotion-and-tagging.md`); it names when to use them, not new machinery.
|
|
25
|
+
|
|
26
|
+
## Routing rule
|
|
27
|
+
|
|
28
|
+
Work rides the beta-verified lane when it changes operational substrate, carries data,
|
|
29
|
+
migration, lock, or state-format risk, or cannot be fully validated by a hermetic suite alone.
|
|
30
|
+
Everything else merges straight to main.
|
|
31
|
+
|
|
32
|
+
Intent 41's DB layer is the archetypal beta-lane case: it replaces the bridge, lock, and session
|
|
33
|
+
file formats with a new persistent SQLite substrate. A green suite proves the code correct; it
|
|
34
|
+
cannot prove the new substrate survives real, uncontrolled usage, so real-use verification on
|
|
35
|
+
beta comes first.
|
|
36
|
+
|
|
37
|
+
The manual-first roadmap's eight 1.1.0 intents (158a, 163, 161, 164, 165, 168, 166, 159) are all
|
|
38
|
+
default-lane: skill directory renames, prose rewrites, deterministic step scripts. Additive, and
|
|
39
|
+
fully suite-verified.
|
|
40
|
+
|
|
41
|
+
## Stable-line guarantees
|
|
42
|
+
|
|
43
|
+
What an external `latest` user can rely on:
|
|
44
|
+
|
|
45
|
+
1. `main` is always green and releasable. No pending revert awaiting re-land sits on `main`.
|
|
46
|
+
When something needs beta verification, it comes out of `main` the same day that need is
|
|
47
|
+
found (the 226023f precedent), never left half-landed.
|
|
48
|
+
2. A stable release carries no pre-release suffix, publishes to npm `latest`, and the newest
|
|
49
|
+
stable release always carries the GitHub "Latest" badge (`gh release create --latest` on
|
|
50
|
+
every cut).
|
|
51
|
+
3. The three repo version files (`package.json`, `.claude-plugin/plugin.json`,
|
|
52
|
+
`.claude-plugin/marketplace.json`) always agree. Checked mechanically by
|
|
53
|
+
`scripts/lib/release_guard.rb`.
|
|
54
|
+
4. A stable cut collects only intents that cleared their lane's bar: default-lane intents by a
|
|
55
|
+
green suite, beta-lane intents by suite green plus their lane's own verification (real-use
|
|
56
|
+
signal, owner sign-off).
|
|
57
|
+
5. Channel semantics are fixed: `latest` is stable and what an external user should run; `beta`
|
|
58
|
+
is the verification line, published but expected to move; `alpha` is experimental,
|
|
59
|
+
pre-verification.
|
|
60
|
+
|
|
61
|
+
## Version-line map
|
|
62
|
+
|
|
63
|
+
| Line | State | What lands here |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `1.1.x` | Current stable line (main) | Additive or low-risk work merged straight to main; interim stable cuts, including 171's wave-6 cut, stay in this line |
|
|
66
|
+
| `1.2.0-beta.1` | On beta (`9ec194b`, unpublished) | Intent 41's DB layer, restored over 1.1.0 by revert-of-revert (`c48601a` then `9ec194b`) |
|
|
67
|
+
| `1.2.0` | Reserved | The stable graduation of the DB layer, once beta verification passes; not claimed by any interim `1.1.x` cut |
|
|
68
|
+
|
|
69
|
+
A beta-graduated substrate change claims its reserved minor at the moment it actually merges to
|
|
70
|
+
main, not before. Nothing else on the `1.1.x` line is blocked waiting for `1.2.0`.
|
|
71
|
+
|
|
72
|
+
## Intent 41 re-land playbook
|
|
73
|
+
|
|
74
|
+
Written for the wave-6 cut intent (171) and any future reader to pick a version without
|
|
75
|
+
re-deriving this decision.
|
|
76
|
+
|
|
77
|
+
**Current state.** The revert-of-revert already sits on the `beta` branch at `9ec194b`, on top
|
|
78
|
+
of 1.1.0, versioned `1.2.0-beta.1` (`c48601a`). There is nothing left to execute on the git side;
|
|
79
|
+
this playbook describes what happens next, not a pending action.
|
|
80
|
+
|
|
81
|
+
**Preconditions**, both required before any npm publish of `1.2.0-beta.1`:
|
|
82
|
+
|
|
83
|
+
- (a) One documentation pass over beta-line skills and docs for the hybrid savepoint contract:
|
|
84
|
+
on beta, only the terminal Done bookend still writes a live `savepoint.md`; every other
|
|
85
|
+
milestone lives in `savepoint_events` plus a committed JSONL export. Beta-line prose that
|
|
86
|
+
still assumes an always-live ledger needs updating first, so a beta-line reader does not
|
|
87
|
+
mistake an empty ledger for a broken one.
|
|
88
|
+
- (b) The owner's manual verification of the DB layer in real use. This is a dogfood signal,
|
|
89
|
+
distinct from the independently-reviewed green suite that already exists on beta.
|
|
90
|
+
|
|
91
|
+
**Trigger**, owner-gated: the owner publishes `1.2.0-beta.1` to the npm `beta` dist-tag. This is
|
|
92
|
+
explicitly not this intent's, nor any agent's, call to make.
|
|
93
|
+
|
|
94
|
+
**Verification.** An external tester plus the owner verify the DB layer on the beta channel.
|
|
95
|
+
|
|
96
|
+
**Completion.** Once verified, `beta` merges into `main`, `1.2.0` is cut stable, and it publishes
|
|
97
|
+
to npm `latest`.
|
|
98
|
+
|
|
99
|
+
**Version mechanics.** `1.2.0` is reserved for this graduation. The `1.1.x` line stays the
|
|
100
|
+
stable line until `1.2.0` actually lands. `1.2.0-beta.1` graduates to `1.2.0` stable by dropping
|
|
101
|
+
the pre-release suffix; nothing else about the version number changes.
|
|
102
|
+
|
|
103
|
+
**Consumed by 171.** The wave-6 consistency-dividend cut stays in the `1.1.x` line. It does not
|
|
104
|
+
ride intent 41 and needs no further derivation: intent 41 keeps its own `1.2.0` line on beta,
|
|
105
|
+
independent of whatever `1.1.x` number 171 lands on.
|