@zalom/plastic 1.0.0-alpha.22 → 1.0.0-alpha.23

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.22",
3
+ "version": "1.0.0-alpha.23",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,6 +7,8 @@ require "json"
7
7
  require "date"
8
8
  require "yaml"
9
9
  require_relative "lib/bridge"
10
+ require_relative "lib/boot_banner"
11
+ require_relative "doctor"
10
12
 
11
13
  index_path, store_root, mode, plugin_root = ARGV
12
14
  exit 0 unless index_path && store_root && mode
@@ -172,6 +174,18 @@ if File.exist?(cache_file)
172
174
  end
173
175
  end
174
176
 
177
+ # --- Run core health in-process (intent 36a) ---
178
+ # Reuse Doctor's own --core checks so there is one source of truth for "core
179
+ # health" (no second process spawn, no duplicated check list). Never blocks the
180
+ # session: any failure or exception degrades to a banner and we continue.
181
+
182
+ core_health = begin
183
+ Doctor.new(plastic_home: store_root).run_core_checks("claude")
184
+ rescue
185
+ nil
186
+ end
187
+ core_banner = BootBanner.render(health: core_health, version: current_version)
188
+
175
189
  # --- Assemble session context ---
176
190
 
177
191
  all_active = active + project_active
@@ -184,6 +198,12 @@ all_future = future + project_future
184
198
 
185
199
  parts = []
186
200
 
201
+ # Boot banner first: every session start surfaces that Plastic loaded, with the
202
+ # version (clean) or the first failing core check (degraded). Owned by the hook
203
+ # so it runs by construction — the plastic-continuing skill no longer does this.
204
+ parts << core_banner
205
+ parts << ""
206
+
187
207
  if plastic_md
188
208
  # Conventions always loaded first
189
209
  parts << plastic_md
@@ -0,0 +1,36 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Renders the SessionStart boot banner from a core-health result (intent 36a).
5
+ #
6
+ # Pure and dependency-injected: it takes the health hash (the return value of
7
+ # Doctor#run_core_checks) and the version string, and returns a single banner
8
+ # line. No I/O, no globals, no ENV — so it is unit-testable in isolation while
9
+ # the hook feeds it real data.
10
+ module BootBanner
11
+ module_function
12
+
13
+ # health: the Hash returned by Doctor#run_core_checks, or nil if the check
14
+ # itself raised (degraded to an error banner).
15
+ # version: the installed Plastic version string, or nil.
16
+ def render(health:, version:)
17
+ return "Plastic Core: health check error — run /plastic-doctor" if health.nil?
18
+
19
+ if health[:status] == "pass"
20
+ "Plastic Core loaded — v#{version || "unknown"}"
21
+ else
22
+ bad = first_problem(health[:checks])
23
+ if bad
24
+ "Plastic Core loaded with issues — #{bad[:name]}: #{bad[:message]} — run /plastic-doctor"
25
+ else
26
+ "Plastic Core loaded with issues — run /plastic-doctor"
27
+ end
28
+ end
29
+ end
30
+
31
+ # First failing check, else first warning, else nil.
32
+ def first_problem(checks)
33
+ checks = checks || []
34
+ checks.find { |c| c[:status] == "fail" } || checks.find { |c| c[:status] == "warn" }
35
+ end
36
+ end
@@ -1,59 +1,37 @@
1
1
  ---
2
2
  name: plastic-continuing
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).
3
+ description: Use when the user says "continue", "resume", or "pick up where we left off", or when starting a new session. Continues work with the latest project context: lands on the right dashboard, then presents choices. Boot (health check, core context, version, statusline) is owned by the SessionStart hook, not this skill. 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).
8
+ `plastic-continuing` continues work. It presents the latest state via the dashboard and offers
9
+ choices, then stops. It does NOT execute work autonomously (that is `plastic-auto`) and does
10
+ NOT render the dashboard itself (it only invokes it).
11
+
12
+ **Boot is not this skill's job.** The `hook-session-start` hook already runs by construction on
13
+ every session start: it runs the core health check (`doctor --core`), primes `PLASTIC.md` +
14
+ store/project state, and prints the `Plastic Core loaded — v{version}` banner. The
15
+ `plastic-statusline` hook sets the statusline. So by the time this skill runs, core is loaded
16
+ and healthy (or the banner already warned otherwise). This skill picks up from there and
17
+ continues work. This is the seam future continue-flags build on (see [[39]]).
11
18
 
12
19
  ## When to Use
13
20
  - UserPromptSubmit hook detects "continue" (automatic)
14
21
  - User says "continue", "resume", or "pick up where we left off"
15
- - Starting a new session with an existing Plastic store
22
+ - Starting a new session and you want to resume work with the latest context
16
23
 
17
24
  ## Determine Store
18
25
 
19
26
  1. **Global store** — `~/.plastic/INDEX.md` exists → global mode.
20
27
  2. **Local store** — a project store under `~/.plastic/projects/{slug}/` whose registered
21
28
  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.
29
+ The SessionStart hook already detects this; here you only need the slug to scope the
30
+ dashboard.
24
31
  3. If neither exists → announce "No Plastic store found. Run /plastic-install."
25
32
 
26
- ## Boot Sequence (run in this fixed order)
27
-
28
- ### 1. Core doctor (health first)
29
- Run the fast runtime-liveness check synchronously (it returns in well under a second):
30
-
31
- ```bash
32
- ruby ~/.plastic/scripts/doctor.rb --core
33
- ```
34
-
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.
33
+ ## Continue (present the dashboard)
55
34
 
56
- ### 4. Dashboard
57
35
  Land on the Markdown board via the `plastic-dashboard` skill. Rendering belongs there, not
58
36
  here — run the data payload and fill + present the matching template:
59
37
  - Project loaded → `ruby ~/.plastic/scripts/dashboard.rb project <slug> --data`
@@ -1,6 +1,6 @@
1
1
  {
2
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.",
3
+ "notes": "Intent 36, updated for 36a (boot moved into hook-session-start; the skill is now work-continuation only). Scopes: description triggering (1-8) and behavior/convention compliance of the work-continuation skill (9-14). Triggering assertions follow the plastic-auto eval style (one subagent router per case). Behavior assertions are convention checks against the SKILL.md.",
4
4
  "results": {
5
5
  "triggering": { "cases": 8, "passed": 8, "run": "2026-06-16, one subagent per case" },
6
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" }
@@ -36,7 +36,7 @@
36
36
  {
37
37
  "id": 4, "scope": "triggering", "set": "train",
38
38
  "prompt": "boot plastic and show me where things stand",
39
- "expected_output": "Activates plastic-continuing (boot + present state is the skill's purpose).",
39
+ "expected_output": "Activates plastic-continuing (continuing work + presenting state is the skill's purpose; boot itself is hook-owned).",
40
40
  "files": [],
41
41
  "assertions": [
42
42
  { "type": "code", "check": "router CHOICE == plastic-continuing", "observed": "plastic-continuing", "result": "pass" }
@@ -80,20 +80,20 @@
80
80
  },
81
81
  {
82
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.",
83
+ "prompt": "Does the skill defer boot to the SessionStart hook rather than running it itself?",
84
+ "expected_output": "SKILL.md states boot (health check, core context, version banner, statusline) is owned by hook-session-start / plastic-statusline, not this skill.",
85
85
  "files": ["skills/continuing/SKILL.md"],
86
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" }
87
+ { "type": "convention", "check": "SKILL.md says boot is owned by the SessionStart hook and the skill does not run it", "observed": "'Boot is not this skill's job.' paragraph naming hook-session-start and plastic-statusline", "result": "pass" }
88
88
  ]
89
89
  },
90
90
  {
91
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.",
92
+ "prompt": "Does the skill avoid running doctor / setting the statusline itself?",
93
+ "expected_output": "SKILL.md does NOT instruct running doctor.rb --core or setting the statusline; it attributes those to the hooks.",
94
94
  "files": ["skills/continuing/SKILL.md"],
95
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" }
96
+ { "type": "convention", "check": "no in-skill boot step invokes doctor.rb --core or sets the statusline; both are attributed to hooks", "observed": "doctor --core and statusline mentioned only as hook-owned in the 'Boot is not this skill's job' note", "result": "pass" }
97
97
  ]
98
98
  },
99
99
  {
@@ -102,7 +102,7 @@
102
102
  "expected_output": "Project loaded -> `dashboard.rb project <slug>`; otherwise -> `dashboard.rb continue`. Skill only invokes, does not render.",
103
103
  "files": ["skills/continuing/SKILL.md"],
104
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" }
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
106
  ]
107
107
  },
108
108
  {