@zalom/plastic 1.0.0-alpha.20 → 1.0.0-alpha.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalom/plastic",
3
- "version": "1.0.0-alpha.20",
3
+ "version": "1.0.0-alpha.21",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/doctor.rb CHANGED
@@ -62,6 +62,7 @@ class Doctor
62
62
  def parse_args(argv)
63
63
  agent = "claude"
64
64
  help = false
65
+ core = false
65
66
 
66
67
  i = 0
67
68
  while i < argv.length
@@ -74,6 +75,9 @@ class Doctor
74
75
  $stderr.puts "Error: --agent requires one of: #{agents.keys.join(", ")}"
75
76
  exit 2
76
77
  end
78
+ when "--core"
79
+ core = true
80
+ i += 1
77
81
  when "--help", "-h"
78
82
  help = true
79
83
  i += 1
@@ -82,7 +86,7 @@ class Doctor
82
86
  end
83
87
  end
84
88
 
85
- { agent: agent, help: help }
89
+ { agent: agent, help: help, core: core }
86
90
  end
87
91
 
88
92
  def show_help
@@ -95,6 +99,8 @@ class Doctor
95
99
 
96
100
  Options:
97
101
  --agent NAME Agent to check: claude (default), codex, hermes
102
+ --core Fast runtime-liveness check only (hooks, scripts, core files);
103
+ skips the slow store/conventions/project inventory walks.
98
104
  -h, --help Show this help
99
105
 
100
106
  Output:
@@ -903,6 +909,23 @@ class Doctor
903
909
  all_checks += check_project_stores
904
910
  all_checks += check_deprecations
905
911
 
912
+ summarize(all_checks, agent_key)
913
+ end
914
+
915
+ # Fast runtime-liveness check: only the plumbing that proves Plastic can
916
+ # operate (hooks, skills, scripts, core files). Skips the slow inventory
917
+ # walks (global store refs, per-intent conventions, project stores,
918
+ # deprecations) so it returns near-instantly. Used by `doctor.rb --core`.
919
+ def run_core_checks(agent_key)
920
+ all_checks = []
921
+ all_checks += check_agent_registration(agent_key)
922
+ all_checks += check_core_files(agent_key)
923
+
924
+ summarize(all_checks, agent_key)
925
+ end
926
+
927
+ # Roll a list of checks up into the standard result envelope.
928
+ def summarize(all_checks, agent_key)
906
929
  summary = { pass: 0, warn: 0, fail: 0, total: all_checks.size }
907
930
  all_checks.each { |c| summary[c[:status].to_sym] += 1 }
908
931
 
@@ -934,7 +957,7 @@ class Doctor
934
957
  exit 0
935
958
  end
936
959
 
937
- result = run_checks(flags[:agent])
960
+ result = flags[:core] ? run_core_checks(flags[:agent]) : run_checks(flags[:agent])
938
961
 
939
962
  puts JSON.pretty_generate(result)
940
963
 
@@ -1,120 +1,129 @@
1
1
  ---
2
2
  name: plastic-continuing
3
- description: Use when the user says "continue" after a /clear, or when resuming work in a new session. Reads intent state from global store (~/.plastic/) or local store, offers active intents first, then future intents, and surfaces stale intents for triage.
3
+ description: Use when the user says "continue", "resume", or "pick up where we left off", or when starting a new session. Boots Plastic runtime health check, loads core context + store/project state, prints version + statusline, and lands on the right dashboard — then presents choices. Does not drive work autonomously (that is plastic-auto).
4
4
  ---
5
5
 
6
6
  # Continuing
7
7
 
8
+ `plastic-continuing` is a deterministic **boot orchestrator**. It loads and presents choices,
9
+ then stops. It does NOT execute work autonomously (that is `plastic-auto`) and does NOT render
10
+ the dashboard itself (it only invokes it).
11
+
8
12
  ## When to Use
9
13
  - UserPromptSubmit hook detects "continue" (automatic)
10
14
  - User says "continue", "resume", or "pick up where we left off"
11
- - Starting a new session with existing active intents
15
+ - Starting a new session with an existing Plastic store
12
16
 
13
17
  ## Determine Store
14
18
 
15
- 1. Check `~/.plastic/INDEX.md` → global mode
16
- 2.
19
+ 1. **Global store** — `~/.plastic/INDEX.md` exists → global mode.
20
+ 2. **Local store** — a project store under `~/.plastic/projects/{slug}/` whose registered
21
+ path (in `~/.plastic/projects.yml`) matches the current working directory → project mode.
22
+ Project detection happens in boot step 2 below; this just records that a local store is
23
+ in play.
17
24
  3. If neither exists → announce "No Plastic store found. Run /plastic-install."
18
25
 
19
- ## Workflow
26
+ ## Boot Sequence (run in this fixed order)
20
27
 
21
- ### 0. Render the dashboard (overview)
22
- For the "where are we / what's next" overview, run the deterministic dashboard and show
23
- its output verbatim instead of hand-summarizing intents:
28
+ ### 1. Core doctor (health first)
29
+ Run the fast runtime-liveness check synchronously (it returns in well under a second):
24
30
 
25
31
  ```bash
26
- ruby ~/.plastic/scripts/dashboard.rb continue
27
- ```
28
-
29
- This is the uniform, model-agnostic cockpit (active + last touched, then the Value×Effort
30
- matrix). Then continue with the steps below to actually resume a specific intent. See the
31
- `plastic-dashboard` skill for how to read the matrix.
32
-
33
- ### 1. Read INDEX.md
34
- Read the INDEX.md from the active store. Extract intents under `## Active` and `## Future`.
35
-
36
- ### 2. Detect Current Project (global mode only)
37
- Read `~/.plastic/projects.yml`, match CWD against registered project paths. If in a project:
38
- - Load the governing intent (from `parent` in projects.yml)
39
- - Load tactical intents from `~/.plastic/projects/{slug}/store/`
40
-
41
- ### 3. If Active Intents Exist → Resume
42
-
43
- For each active intent in the store:
44
-
45
- **a. Read `{ID}--{slug}.md`:**
46
- - What we're doing (`## Intent`)
47
- - Why (`## Context`)
48
- - What insights have emerged (`## Insights`)
49
-
50
- **b. Read savepoint.md** (if exists):
51
- - What was in progress, what's next, blockers
52
-
53
- **c. Read checklist.md** (if exists):
54
- - What's completed, what's next
55
-
56
- **d. Announce:**
57
- ```
58
- Resuming intent [ID] — [name]
59
- Store: [global | project:<slug> | local]
60
- Status: active
61
- Last session: [date from savepoint]
62
- In progress: [from savepoint]
63
- Next step: [from checklist or savepoint]
64
- Blockers: [from savepoint, or "none"]
65
- ```
66
-
67
- **e. Resume** — proceed with the next step.
68
-
69
- ### 3b. Detect Autonomous Resume
70
-
71
- When resuming an active intent, check `## Insights` for entries containing `(autonomous)`.
72
-
73
- If found — this intent was being delivered autonomously:
74
-
75
- **Announce:**
76
- ```
77
- Resuming autonomous delivery of intent [ID] — [name]
78
- Store: [global | project:<slug> | local]
79
- Last autonomous action: [last (autonomous) insight entry]
80
- Next step: [from checklist or savepoint]
32
+ ruby ~/.plastic/scripts/doctor.rb --core
81
33
  ```
82
34
 
83
- **Then:** Continue autonomous execution by invoking `plastic-auto`. The auto skill will pick up from the current lifecycle stage (it reads filesystem state to determine where to resume).
84
-
85
- If NOT found resume normally as described in step 3.
86
-
87
- ### 4. If No Active Intents Offer Future Intents
88
-
89
- Present future intents as options. When user picks one, move to Active in INDEX.md. Auto-commit.
90
-
91
- ### 5. Surface Stale Future Intents
92
-
93
- If any future intent has `created` date older than the configured `stale_threshold_days` (default 3):
35
+ Print one compact health line:
36
+ - All pass → `Plastic core: healthy`
37
+ - Otherwise `Plastic core: issues` followed by the failing checks (name + message).
38
+
39
+ This runs first so a broken runtime (missing hooks, scripts, core files) surfaces before any
40
+ state is loaded on top of it. For a full diagnosis, point the user at `/plastic-doctor`.
41
+
42
+ ### 2. Load core (context + state)
43
+ - Prime `PLASTIC.md` and the harness docs so the conventions are in mind.
44
+ - Load live state:
45
+ - Read the active store `INDEX.md` (`## Active`, `## Future`).
46
+ - Read `~/.plastic/projects.yml`.
47
+ - **Detect the current project** by matching CWD against registered project paths.
48
+ - If in a project: load that project's `INDEX.md`; load the governing intent (from
49
+ `parent` in projects.yml) and the tactical intents from
50
+ `~/.plastic/projects/{slug}/store/`.
51
+
52
+ ### 3. Version + statusline
53
+ - Print the current Plastic version (from `~/.plastic/VERSION`).
54
+ - Set the statusline.
55
+
56
+ ### 4. Dashboard
57
+ Invoke the dashboard. Rendering belongs to the dashboard skill, not here — only invoke:
58
+ - Project loaded → `ruby ~/.plastic/scripts/dashboard.rb project <slug>`
59
+ - Otherwise → `ruby ~/.plastic/scripts/dashboard.rb continue`
60
+
61
+ Show its output verbatim. See `plastic-dashboard` for how to read the matrix.
62
+
63
+ ### Then stop
64
+ Present "here is the state, what next?" and wait. Offer active intents first, then future
65
+ intents. Do not start executing work. The branches below are the only follow-ups:
66
+ - User/agent names a specific intent to continue → **Conditional ledger-resume** (below).
67
+ - User says "auto" / an agent is instructed to deliver → hand to `plastic-auto`.
68
+
69
+ ## Conditional Ledger-Resume
70
+
71
+ Fires ONLY when the user explicitly asks to continue a SPECIFIC intent, or an agent is
72
+ instructed to continue one. It is not part of every boot. For that intent's directory:
73
+
74
+ 1. **Read `savepoint.md`.** It is a deterministic, append-only stage ledger (one line per
75
+ milestone, newest at the bottom): `{utc-iso8601} {Stage} {milestone}`. The **last line =
76
+ current stage**.
77
+ 2. **Verify the stage file.** Confirm the file the ledger names exists and is non-empty
78
+ (ledger `How plan.md created` → `plan.md` must be present and non-empty).
79
+ 3. **Drift handling.** If the ledger's last line disagrees with files-on-disk, rebuild the
80
+ ledger from filesystem state and note the correction:
81
+ ```bash
82
+ ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.rebuild_savepoint("<intent_dir>")'
83
+ ```
84
+ 4. **Derive the next step:**
85
+ - First unchecked item in `checklist.md` if it exists, else
86
+ - "advance to the next lifecycle stage" (e.g. ledger shows Why/spec.md → next is How).
87
+ - The newest `## Insights` entry supplies human-readable context (Insights are
88
+ append-only, newest at the bottom).
89
+ 5. **Announce and stop:**
90
+ ```
91
+ Resuming intent [ID] — [name]
92
+ Store: [global | project:<slug> | local]
93
+ Stage: [from ledger last line]
94
+ Next step: [first unchecked checklist item | advance to <stage>]
95
+ Context: [newest ## Insights entry]
96
+ Drift: [none | ledger rebuilt from filesystem]
97
+ ```
98
+ Then proceed with the next step. Autonomy is `plastic-auto`'s job — if the intent's
99
+ `## Insights` contains `(autonomous)` entries, it was being delivered autonomously; hand
100
+ to `plastic-auto` to continue from the current stage.
101
+
102
+ ## Priority Order
103
+
104
+ 1. **Active intents first** — surface work in progress.
105
+ 2. **Project context** — if in a registered project, show governing + tactical intents.
106
+ 3. **Stale future intents** — surface for triage (see below).
107
+ 4. **Fresh future intents** — offer as next work.
108
+
109
+ ## Stale Future Intents
110
+
111
+ If a future intent's `created` date is older than the configured `stale_threshold_days`
112
+ (default 3), surface it for triage without taking action:
94
113
 
95
114
  ```
96
115
  Stale future intents (no action taken):
97
116
 
98
117
  - [ID — name] (X days old)
99
- Options:
100
118
  a) Activate — start working on it now
101
119
  b) Abandon — mark as abandoned
102
- c) Defer to agent:
103
- - implement: agent builds it
104
- - research: agent investigates feasibility
105
- - ideate: agent explores the problem space
106
- d) Auto — go fully autonomous (invokes plastic-auto — agent delivers the intent end-to-end)
120
+ c) Defer to agent: implement | research | ideate
121
+ d) Auto go fully autonomous (invokes plastic-auto)
107
122
  ```
108
123
 
109
- Auto-commit all triage changes.
110
-
111
- ### 6. Priority Order
112
-
113
- 1. **Active intents first** — resume work in progress
114
- 2. **Project context** — if in a registered project, show governing intent + tactical intents
115
- 3. **Stale future intents** — surface for triage
116
- 4. **Fresh future intents** — offer as next work
124
+ When the user activates a future intent, move it to `## Active` in INDEX.md and auto-commit.
117
125
 
118
126
  ## References
119
127
 
120
- - Read `references/context-management.md` for the full save/continue protocol when resuming from a savepoint or when the resume flow needs debugging
128
+ - Read `references/context-management.md` for the full save/continue protocol and for
129
+ debugging the resume flow.
@@ -0,0 +1,136 @@
1
+ {
2
+ "skill_name": "plastic-continuing",
3
+ "notes": "Intent 36. Scopes: description triggering (1-8) and behavior/convention compliance of the rewritten boot orchestrator (9-14). Triggering assertions follow the plastic-auto eval style (one subagent router per case). Behavior assertions are convention checks against the rewritten SKILL.md.",
4
+ "results": {
5
+ "triggering": { "cases": 8, "passed": 8, "run": "2026-06-16, one subagent per case" },
6
+ "behavior": { "cases": 6, "passed": 6, "evidence": "convention checks against skills/continuing/SKILL.md after the intent-36 rewrite; doctor --core verified at 0.07s with 9 liveness checks and full doctor unchanged at 30 checks" }
7
+ },
8
+ "evals": [
9
+ {
10
+ "id": 1, "scope": "triggering", "set": "train",
11
+ "prompt": "continue",
12
+ "expected_output": "Activates plastic-continuing (the bare 'continue' keyword is the documented trigger).",
13
+ "files": [],
14
+ "assertions": [
15
+ { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
16
+ ]
17
+ },
18
+ {
19
+ "id": 2, "scope": "triggering", "set": "train",
20
+ "prompt": "resume where we left off",
21
+ "expected_output": "Activates plastic-continuing.",
22
+ "files": [],
23
+ "assertions": [
24
+ { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
25
+ ]
26
+ },
27
+ {
28
+ "id": 3, "scope": "triggering", "set": "validation",
29
+ "prompt": "pick up where we left off in this project",
30
+ "expected_output": "Activates plastic-continuing.",
31
+ "files": [],
32
+ "assertions": [
33
+ { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
34
+ ]
35
+ },
36
+ {
37
+ "id": 4, "scope": "triggering", "set": "train",
38
+ "prompt": "boot plastic and show me where things stand",
39
+ "expected_output": "Activates plastic-continuing (boot + present state is the skill's purpose).",
40
+ "files": [],
41
+ "assertions": [
42
+ { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
43
+ ]
44
+ },
45
+ {
46
+ "id": 5, "scope": "triggering", "set": "train",
47
+ "prompt": "continue delivering this intent autonomously, don't ask me",
48
+ "expected_output": "Does NOT settle on plastic-continuing for execution. Shares 'continue' but the autonomous-delivery intent routes to plastic-auto.",
49
+ "files": [],
50
+ "assertions": [
51
+ { "type": "code", "check": "router CHOICE == plastic-auto", "observed": "plastic-auto", "result": "pass" }
52
+ ]
53
+ },
54
+ {
55
+ "id": 6, "scope": "triggering", "set": "validation",
56
+ "prompt": "continue the for-loop to the next iteration in this function",
57
+ "expected_output": "Does NOT activate plastic-continuing. Near-miss: shares 'continue' but is a code-editing task.",
58
+ "files": [],
59
+ "assertions": [
60
+ { "type": "code", "check": "router CHOICE != plastic-continuing", "observed": "none", "result": "pass" }
61
+ ]
62
+ },
63
+ {
64
+ "id": 7, "scope": "triggering", "set": "train",
65
+ "prompt": "resume the paused background download",
66
+ "expected_output": "Does NOT activate plastic-continuing. Near-miss: shares 'resume' but is unrelated to Plastic sessions.",
67
+ "files": [],
68
+ "assertions": [
69
+ { "type": "code", "check": "router CHOICE != plastic-continuing", "observed": "none", "result": "pass" }
70
+ ]
71
+ },
72
+ {
73
+ "id": 8, "scope": "triggering", "set": "validation",
74
+ "prompt": "what's the dashboard look like right now",
75
+ "expected_output": "May activate plastic-dashboard rather than plastic-continuing; an overview request without 'continue/resume' is a dashboard task.",
76
+ "files": [],
77
+ "assertions": [
78
+ { "type": "code", "check": "router CHOICE != plastic-continuing", "observed": "plastic-dashboard", "result": "pass" }
79
+ ]
80
+ },
81
+ {
82
+ "id": 9, "scope": "behavior", "set": "train",
83
+ "prompt": "Does the skill document the four-step boot sequence in the fixed order?",
84
+ "expected_output": "SKILL.md lists, in order: (1) core doctor, (2) load core, (3) version + statusline, (4) dashboard.",
85
+ "files": ["skills/continuing/SKILL.md"],
86
+ "assertions": [
87
+ { "type": "convention", "check": "boot steps appear in order doctor -> load core -> version/statusline -> dashboard", "observed": "headings '### 1. Core doctor', '### 2. Load core', '### 3. Version + statusline', '### 4. Dashboard'", "result": "pass" }
88
+ ]
89
+ },
90
+ {
91
+ "id": 10, "scope": "behavior", "set": "train",
92
+ "prompt": "Does step 1 run the fast core health check synchronously?",
93
+ "expected_output": "Step 1 invokes `doctor.rb --core` and prints a single health line.",
94
+ "files": ["skills/continuing/SKILL.md"],
95
+ "assertions": [
96
+ { "type": "convention", "check": "SKILL.md contains 'doctor.rb --core' and a 'Plastic core: healthy' health line", "observed": "present in '### 1. Core doctor'", "result": "pass" }
97
+ ]
98
+ },
99
+ {
100
+ "id": 11, "scope": "behavior", "set": "train",
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/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 '### 4. Dashboard'", "result": "pass" }
106
+ ]
107
+ },
108
+ {
109
+ "id": 12, "scope": "behavior", "set": "validation",
110
+ "prompt": "Is ledger-resume conditional and ledger-driven?",
111
+ "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
+ "files": ["skills/continuing/SKILL.md"],
113
+ "assertions": [
114
+ { "type": "convention", "check": "Conditional Ledger-Resume section reads last ledger line, verifies stage file, calls rebuild_savepoint on drift, uses first unchecked checklist item", "observed": "all four present in 'Conditional Ledger-Resume'", "result": "pass" }
115
+ ]
116
+ },
117
+ {
118
+ "id": 13, "scope": "behavior", "set": "validation",
119
+ "prompt": "Are the stale prose-savepoint fields gone?",
120
+ "expected_output": "No 'In progress' / 'Blockers' fields read from a prose savepoint remain; the ledger model is used instead.",
121
+ "files": ["skills/continuing/SKILL.md"],
122
+ "assertions": [
123
+ { "type": "convention", "check": "no prose-savepoint announce template (In progress / Blockers from prose)", "observed": "absent; announce uses Stage/Next step/Context/Drift derived from ledger + checklist", "result": "pass" }
124
+ ]
125
+ },
126
+ {
127
+ "id": 14, "scope": "behavior", "set": "validation",
128
+ "prompt": "Is the 'Determine Store' local-store gap filled?",
129
+ "expected_output": "Step 2 of Determine Store describes local/project-store detection (no empty step).",
130
+ "files": ["skills/continuing/SKILL.md"],
131
+ "assertions": [
132
+ { "type": "convention", "check": "Determine Store step 2 documents local/project store detection via projects.yml + CWD match", "observed": "filled", "result": "pass" }
133
+ ]
134
+ }
135
+ ]
136
+ }