@zalom/plastic 1.0.0-beta.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/PLASTIC-reference.md +133 -0
  2. package/PLASTIC.md +398 -82
  3. package/README.md +99 -82
  4. package/agents/plastic-brainstorming.md +12 -9
  5. package/agents/plastic-enforcer.md +52 -12
  6. package/agents/plastic-executor.md +10 -10
  7. package/agents/plastic-future-intent-researcher.md +4 -8
  8. package/agents/plastic-intent-curator.md +12 -14
  9. package/agents/plastic-intent-discovery.md +35 -0
  10. package/agents/plastic-planner.md +15 -10
  11. package/agents/plastic-spec-specialist.md +15 -10
  12. package/bin/plastic.js +7 -3
  13. package/hooks/code-gate +10 -1
  14. package/hooks/hooks.json +28 -3
  15. package/hooks/lock-gate +21 -0
  16. package/hooks/retrieval-gate +10 -0
  17. package/hooks/savepoint-pre +10 -0
  18. package/hooks/statusline +48 -6
  19. package/package.json +2 -1
  20. package/scripts/agent-report +21 -0
  21. package/scripts/dashboard.rb +98 -23
  22. package/scripts/doctor.rb +232 -5
  23. package/scripts/hook-auto-arm +1 -1
  24. package/scripts/hook-bash-gate +22 -4
  25. package/scripts/hook-code-gate +11 -6
  26. package/scripts/hook-continue +17 -0
  27. package/scripts/hook-create-gate +45 -9
  28. package/scripts/hook-gate-check +24 -23
  29. package/scripts/hook-lock-gate +83 -0
  30. package/scripts/hook-retrieval-gate +148 -0
  31. package/scripts/hook-savepoint-pre +32 -0
  32. package/scripts/hook-session-start +1 -1
  33. package/scripts/insight-append +51 -0
  34. package/scripts/install.rb +44 -7
  35. package/scripts/lib/agent_models.rb +43 -0
  36. package/scripts/lib/bridge.rb +924 -84
  37. package/scripts/lib/dashboard_banner.rb +42 -0
  38. package/scripts/lib/hook_registry.rb +95 -0
  39. package/scripts/lib/insights.rb +86 -0
  40. package/scripts/lib/installer_core.rb +100 -60
  41. package/scripts/lib/link_suggestions.rb +319 -0
  42. package/scripts/lib/lock.rb +375 -0
  43. package/scripts/lib/power_tools.rb +22 -21
  44. package/scripts/lib/preflight.rb +79 -0
  45. package/scripts/lib/qmd_sync.rb +15 -0
  46. package/scripts/lib/retrieval_gate.rb +211 -0
  47. package/scripts/lib/worktree.rb +384 -0
  48. package/scripts/link-suggest +213 -0
  49. package/scripts/new-intent +9 -1
  50. package/scripts/plastic-lock +164 -0
  51. package/scripts/read-config +4 -0
  52. package/scripts/spawn-preamble +11 -3
  53. package/scripts/update.rb +16 -7
  54. package/skills/auto/SKILL.md +126 -19
  55. package/skills/auto/references/agent-architecture.md +7 -4
  56. package/skills/auto/references/agent-report-contract.md +36 -1
  57. package/skills/auto/references/end-tail.md +56 -0
  58. package/skills/auto/references/human-report-contract.md +55 -0
  59. package/skills/auto/references/tiers.md +77 -0
  60. package/skills/brainstorming/SKILL.md +7 -34
  61. package/skills/brainstorming/references/design-principles.md +49 -0
  62. package/skills/continuing/SKILL.md +26 -7
  63. package/skills/creating-intent/SKILL.md +13 -28
  64. package/skills/creating-project/SKILL.md +11 -74
  65. package/skills/creating-project/references/project-scaffolding.md +97 -0
  66. package/skills/creating-skills/SKILL.md +65 -0
  67. package/skills/creating-skills/evals/evals.json +108 -0
  68. package/skills/creating-skills/references/agents.md +168 -0
  69. package/skills/creating-skills/references/evals.md +41 -0
  70. package/skills/creating-skills/references/hooks.md +248 -0
  71. package/skills/creating-skills/references/progressive-disclosure.md +176 -0
  72. package/skills/creating-skills/references/scripts.md +166 -0
  73. package/skills/creating-skills/references/skills.md +165 -0
  74. package/skills/creating-skills/scripts/scaffold.rb +313 -0
  75. package/skills/dashboard/SKILL.md +13 -11
  76. package/skills/dashboard/references/classification.md +22 -0
  77. package/skills/doctor/SKILL.md +10 -7
  78. package/skills/executing-plan/SKILL.md +4 -4
  79. package/skills/humanizer/SKILL.md +39 -0
  80. package/skills/humanizer/evals/evals.json +70 -0
  81. package/skills/humanizer/references/always-on-snippet.md +9 -0
  82. package/skills/humanizer/references/examples.md +48 -0
  83. package/skills/install/SKILL.md +75 -84
  84. package/skills/intent-curator/SKILL.md +5 -1
  85. package/skills/intent-discovery/SKILL.md +47 -0
  86. package/skills/intent-starting/SKILL.md +130 -0
  87. package/skills/intent-starting/evals/evals.json +117 -0
  88. package/skills/intent-starting/references/boarding-matrix.md +35 -0
  89. package/skills/linking-intents/SKILL.md +48 -12
  90. package/skills/lock/SKILL.md +41 -0
  91. package/skills/managing-index/SKILL.md +6 -0
  92. package/skills/releasing/SKILL.md +44 -29
  93. package/skills/releasing/references/promotion-and-tagging.md +60 -0
  94. package/skills/roadmap/SKILL.md +51 -0
  95. package/skills/roadmap/references/file-format.md +91 -0
  96. package/skills/roadmap/references/operations.md +78 -0
  97. package/skills/uninstall/SKILL.md +29 -11
  98. package/skills/update/SKILL.md +34 -23
  99. package/skills/versions/SKILL.md +27 -12
  100. package/skills/writing-plans/SKILL.md +10 -88
  101. package/skills/writing-plans/references/plan-format.md +102 -0
  102. package/templates/config.yml +8 -0
  103. package/templates/outcome.md +3 -0
  104. package/templates/revisions.md +58 -0
  105. package/templates/roadmap.md +30 -0
  106. package/skills/writing-instructions/SKILL.md +0 -159
  107. package/skills/writing-instructions/references/agentskills-spec.md +0 -135
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+ #
5
+ # plastic-lock: inspect and repair the durable delivery lock (intent 108, D5),
6
+ # and take/free per-artifact claim tokens (intent 111 D1/D5).
7
+ #
8
+ # Usage: plastic-lock <status|fix|release|reclaim|delegate|claim|release-claim>
9
+ # [--intent-dir DIR] [--session SID] [--delegate SID] [--artifact NAME]
10
+ #
11
+ # Verbs:
12
+ # status report the lock file, the bridge cache, their agreement,
13
+ # and any live per-artifact claims
14
+ # fix idempotent repair: rebuild lock + bridge from disk truth for
15
+ # the current session; never touches a fresh foreign lock
16
+ # release owner clears the lock (End tail / abandoning a boarding)
17
+ # reclaim explicit takeover of a stale lock, audited in savepoint.md
18
+ # delegate owner registers a subagent session under the lock (D4)
19
+ # claim acquire the named --artifact's claim token (intent 111);
20
+ # rejected (exit 1) while a fresh claim is held by anyone,
21
+ # including this same session; takes over a stale claim
22
+ # release-claim free the named --artifact's claim token
23
+ #
24
+ # Without --intent-dir the intent is resolved from this session's bridge.
25
+ # Exit 0 on success/report; exit 1 when the verb is blocked (held elsewhere).
26
+
27
+ require "json"
28
+ require_relative "lib/bridge"
29
+ require_relative "lib/lock"
30
+
31
+ def usage!
32
+ warn "usage: plastic-lock <status|fix|release|reclaim|delegate|claim|release-claim> " \
33
+ "[--intent-dir DIR] [--session SID] [--delegate SID] [--artifact NAME]"
34
+ exit 1
35
+ end
36
+
37
+ verb = ARGV.shift
38
+ usage! unless %w[status fix release reclaim delegate claim release-claim].include?(verb)
39
+
40
+ opts = {}
41
+ until ARGV.empty?
42
+ case (flag = ARGV.shift)
43
+ when "--intent-dir" then opts[:dir] = ARGV.shift
44
+ when "--session" then opts[:session] = ARGV.shift
45
+ when "--delegate" then opts[:delegate] = ARGV.shift
46
+ when "--artifact" then opts[:artifact] = ARGV.shift
47
+ else
48
+ warn "unknown flag #{flag}"
49
+ usage!
50
+ end
51
+ end
52
+
53
+ session = opts[:session]
54
+ session = ENV["CLAUDE_CODE_SESSION_ID"] if session.nil? || session.strip.empty?
55
+
56
+ dir = opts[:dir]
57
+ if dir.nil? || dir.strip.empty?
58
+ bridge = Bridge.discover_bridge(session: session, cwd: Dir.pwd)
59
+ dir = Bridge.bridge_intent_dir(bridge)
60
+ end
61
+ if dir.nil?
62
+ warn "plastic-lock: no intent resolved; pass --intent-dir <intent dir>"
63
+ exit 1
64
+ end
65
+ dir = File.expand_path(dir)
66
+
67
+ intent_id = Bridge.intent_id_from_dir(dir)
68
+ store = File.dirname(dir)
69
+ name = File.basename(dir)
70
+ key = Bridge.resolve_session(session, intent_id: intent_id, store: store)
71
+
72
+ case verb
73
+ when "status"
74
+ lock = Lock.read(dir)
75
+ bridge = Bridge.read(key, intent_id: intent_id)
76
+ report = {
77
+ "intent_dir" => dir,
78
+ "session" => key,
79
+ "lock" => lock,
80
+ "lock_fresh" => Lock.fresh?(dir),
81
+ "lock_corrupt" => Lock.corrupt?(dir),
82
+ "bridge_present" => !bridge.nil?,
83
+ "agreement" => (lock && bridge) ?
84
+ (lock["owner_session"] == bridge.dig("lock", "owner_session")) : nil,
85
+ "claims" => Claim.claims_status(dir),
86
+ }
87
+ puts JSON.pretty_generate(report)
88
+ when "fix"
89
+ report = Bridge.repair_lock(key, intent_id: intent_id, intent_dir: dir,
90
+ store: store, name: name)
91
+ puts JSON.pretty_generate(report)
92
+ unless report["status"] == "repaired"
93
+ warn "plastic-lock: #{report['status']} by #{report['owner']}" \
94
+ "#{report['hint'] ? " (#{report['hint']})" : ''}"
95
+ exit 1
96
+ end
97
+ when "release"
98
+ result = Lock.release(dir, session: key)
99
+ if result == :not_owner
100
+ warn "plastic-lock: not the owner; run plastic-lock status"
101
+ exit 1
102
+ end
103
+ data = Bridge.read(key, intent_id: intent_id)
104
+ if data
105
+ data["lock"] = { "owner_session" => nil, "acquired_at" => nil,
106
+ "host" => nil, "type" => nil, "delegates" => [] }
107
+ Bridge.write(key, data)
108
+ end
109
+ puts "released (#{result})"
110
+ when "reclaim"
111
+ status, lock_data = Lock.takeover(dir, session: key)
112
+ if status == :fresh
113
+ warn "plastic-lock: lock is FRESH and held by #{lock_data['owner_session']}; " \
114
+ "back off (no silent reclaim)"
115
+ exit 1
116
+ end
117
+ report = Bridge.repair_lock(key, intent_id: intent_id, intent_dir: dir,
118
+ store: store, name: name)
119
+ puts JSON.pretty_generate(report)
120
+ when "delegate"
121
+ usage! if opts[:delegate].nil?
122
+ ok = Lock.add_delegate(dir, delegate: opts[:delegate], session: key)
123
+ unless ok
124
+ warn "plastic-lock: only the lock owner may delegate; run plastic-lock status"
125
+ exit 1
126
+ end
127
+ puts "delegated #{opts[:delegate]} under #{key}"
128
+ when "claim"
129
+ usage! if opts[:artifact].nil?
130
+ status, data = Claim.acquire_claim(dir, opts[:artifact], session: key,
131
+ delegate: opts[:delegate])
132
+ case status
133
+ when :acquired
134
+ puts "claimed #{opts[:artifact]} under #{key}"
135
+ when :held
136
+ warn "plastic-lock: #{opts[:artifact]} is claimed by " \
137
+ "#{data['owner_session']} since #{data['acquired_at']}; back off"
138
+ exit 1
139
+ when :stale
140
+ old = data && data["owner_session"]
141
+ Claim.release_claim(dir, opts[:artifact], session: key, force: true)
142
+ status2, = Claim.acquire_claim(dir, opts[:artifact], session: key,
143
+ delegate: opts[:delegate])
144
+ if status2 == :acquired
145
+ warn "plastic-lock: took over stale claim on #{opts[:artifact]} from #{old}"
146
+ puts "claimed #{opts[:artifact]} under #{key}"
147
+ else
148
+ warn "plastic-lock: could not take over #{opts[:artifact]} (#{status2})"
149
+ exit 1
150
+ end
151
+ when :corrupt
152
+ Claim.release_claim(dir, opts[:artifact], session: key, force: true)
153
+ Claim.acquire_claim(dir, opts[:artifact], session: key, delegate: opts[:delegate])
154
+ puts "claimed #{opts[:artifact]} under #{key} (repaired corrupt claim)"
155
+ end
156
+ when "release-claim"
157
+ usage! if opts[:artifact].nil?
158
+ result = Claim.release_claim(dir, opts[:artifact], session: key)
159
+ if result == :not_owner
160
+ warn "plastic-lock: not the claim owner; run plastic-lock status"
161
+ exit 1
162
+ end
163
+ puts "released-claim #{opts[:artifact]} (#{result})"
164
+ end
@@ -9,6 +9,7 @@
9
9
 
10
10
  require "yaml"
11
11
  require "json"
12
+ require_relative "lib/agent_models"
12
13
 
13
14
  DEFAULTS = {
14
15
  "version" => 3,
@@ -23,6 +24,9 @@ DEFAULTS = {
23
24
  "type" => "claude-code",
24
25
  "parallel_mode" => "linear"
25
26
  },
27
+ "agents" => {
28
+ "models" => AgentModels::TIER_DEFAULTS
29
+ },
26
30
  "architect" => {
27
31
  "style" => nil
28
32
  }
@@ -41,10 +41,18 @@ REPORT_CONTRACT =
41
41
  "FINAL MESSAGE (your return value), not a side-channel file. Do not go idle or " \
42
42
  "finish silently. The report carries a common envelope: role, intent id, stage, " \
43
43
  "status (delivered or blocked), artifacts written, verification or tests run, " \
44
- "checklist deltas, deviations from spec, and blockers or handoff notes; plus a " \
44
+ "checklist deltas, deviations from spec, blockers or handoff notes, and an " \
45
+ "insights field carrying 0..N durable nuggets (what I discovered worth keeping, " \
46
+ "the most interesting residue of this turn; none if there were none); plus a " \
45
47
  "role-specific payload that fulfils your place in the What, Why, How, Exec cycle " \
46
- "(for example the planner explains the plan back to the orchestrator). See " \
47
- "skills/auto/references/agent-report-contract.md for the per-role format."
48
+ "(for example the planner explains the plan back to the orchestrator). Populate " \
49
+ "the insights field even when you cannot write the intent file yourself: a " \
50
+ "background or dispatched agent carries each nugget home in the report and the " \
51
+ "orchestrator persists it via scripts/insight-append, so an insight never " \
52
+ "depends on the discovering session's file-write access. Keep the report " \
53
+ "prose-stripped: the envelope and payload only, no greeting, no preamble, " \
54
+ "no end-recap, no restating of the task; reasoning stays in the thinking channel. " \
55
+ "See skills/auto/references/agent-report-contract.md for the per-role format."
48
56
 
49
57
  def parse_args(argv)
50
58
  role = nil
package/scripts/update.rb CHANGED
@@ -53,18 +53,22 @@ class Update < InstallerCore
53
53
  end
54
54
  puts "\u{2b06}\u{fe0f} Updating Plastic #{iv} \u{2192} #{res[:target]}"
55
55
  exit_code = perform_switch(res[:target], agent_args(argv))
56
- run_post_update_doctor if exit_code == 0
56
+ run_post_update_doctor(full: argv.include?("--full-doctor")) if exit_code == 0
57
57
  exit_code
58
58
  end
59
59
  end
60
60
 
61
- # Run the full doctor after a successful update and print a human-readable
62
- # summary. Informational only: does not raise and does not affect the update's
63
- # exit code. Accepts injected `doctor` and `out` for hermetic unit tests.
64
- def run_post_update_doctor(doctor: nil, out: $stdout)
61
+ # Run doctor after a successful update and print a human-readable summary.
62
+ # Defaults to the fast core tier (agent registration + core files + manifest
63
+ # sync, binary pass|fail, no store walk) so a newcomer's first post-update
64
+ # run is not buried in convention warns they cannot act on. `full: true`
65
+ # (via `--full-doctor`) runs the complete store walk instead. Informational
66
+ # only: does not raise and does not affect the update's exit code. Accepts
67
+ # injected `doctor` and `out` for hermetic unit tests.
68
+ def run_post_update_doctor(doctor: nil, out: $stdout, full: false)
65
69
  doctor ||= Doctor.new
66
- out.puts "\nRunning full doctor after update..."
67
- result = doctor.run_checks("claude")
70
+ out.puts full ? "\nRunning full doctor after update..." : "\nRunning core doctor after update..."
71
+ result = full ? doctor.run_checks("claude") : doctor.run_core_checks("claude")
68
72
  s = result[:summary]
69
73
  out.puts " Doctor status: #{result[:status]} " \
70
74
  "(pass: #{s[:pass]}, warn: #{s[:warn]}, fail: #{s[:fail]}, total: #{s[:total]})"
@@ -152,6 +156,11 @@ class Update < InstallerCore
152
156
  Agent options (default: --claude):
153
157
  --claude --codex --hermes --all
154
158
 
159
+ Post-update doctor:
160
+ By default, a successful update runs the fast core doctor sync (agent
161
+ registration, core files, manifest — binary pass|fail, no store walk).
162
+ --full-doctor Run the full doctor (complete store walk) after updating.
163
+
155
164
  Behaviour:
156
165
  No flag advances to the next version on your current channel. Switching toward a
157
166
  more stable channel is frictionless; switching toward bleeding edge is confirmed.
@@ -10,6 +10,12 @@ description: >-
10
10
 
11
11
  Announce: "Taking over intent [ID] — [name] for autonomous delivery."
12
12
 
13
+ **Advisory (not a gate).** At auto-mode start, recommend once that the user run this
14
+ orchestrating main session on the best available thinking model (Fable, Opus, or whatever
15
+ supersedes them) for the sharpest gating and synthesis. This is advice only: it changes no
16
+ behavior and blocks nothing if ignored. It concerns the human's MAIN session; dispatched
17
+ subagents keep their pinned tier and never resolve to Fable.
18
+
13
19
  ## Precondition
14
20
 
15
21
  An active intent MUST exist in INDEX.md. If none exists, refuse: "No active intent found. Create one first with /plastic-creating-intent."
@@ -35,6 +41,27 @@ intents, then open the authoritative intent file for the hit you take over. The
35
41
  when QMD is absent, so fall back to the existing INDEX.md / file scan. (This is discovery; the
36
42
  reindex step under Completion is separate.)
37
43
 
44
+ ## Tiers (proportional auto sizing)
45
+
46
+ Auto mode sizes every intent S/M/L at Why, deterministically, then matches agent topology
47
+ and artifact depth to that size. Extended walkthrough: `references/tiers.md`.
48
+
49
+ 1. **Sizing rule.** S = single mechanism or file cluster (hours). M = one subsystem (a
50
+ day). L = cross-cutting or novel design.
51
+ 2. **Two levers.** Speed comes only from artifact content DEPTH and agent TOPOLOGY. The
52
+ same-structure invariant (same file set, stage order, gates, savepoint ledger) holds at
53
+ every tier and in both modes. A three-line spec.md is still a spec.md, in the same
54
+ place, under the same gate.
55
+ 3. **Per-tier topology.** S/M: one thinker agent, one boot, two stations, sonnet
56
+ executor, `actions/` skipped; S may also skip the QMD discovery deposit when chain and
57
+ sources are both empty. L: today's full team (`## Team Spin-Up` below).
58
+ 4. **Never-cut list**, any tier or mode: the independent reviewer (separate agent, fresh
59
+ context, never the maker), `outcome.md` as truth of delivery, the delivery lock,
60
+ worktree isolation, intent creation via skill, INDEX as status truth, the QMD reindex
61
+ at End. Lightness is about ceremony, never about these guarantees.
62
+ 5. **Tier record.** `Tier: S|M|L` at the top of spec.md. Convention-only: read by the
63
+ orchestrator, never validated by any gate or by doctor.
64
+
38
65
  ## Arm the Lifecycle Gate (do this FIRST)
39
66
 
40
67
  Immediately after selecting the intent — before any other work — arm auto mode. This
@@ -43,13 +70,16 @@ edited before the plan exists (the gate applies to YOU, the orchestrator):
43
70
 
44
71
  ```bash
45
72
  ruby -r ~/.plastic/scripts/lib/bridge -e \
46
- 'Bridge.arm_auto(ENV["CLAUDE_SESSION_ID"], intent_id: "<ID>", intent_dir: "<STORE>/<dir>", store: "<STORE>", name: "<name>")'
73
+ 'Bridge.arm_auto(ENV["CLAUDE_CODE_SESSION_ID"], intent_id: "<ID>", intent_dir: "<STORE>/<dir>", store: "<STORE>", name: "<name>")'
47
74
  ```
48
75
 
49
76
  Replace `<ID>`, `<STORE>` (e.g. `~/.plastic/projects/<slug>/store` or `~/.plastic/store`),
50
- `<dir>` (the `ID--slug` directory), and `<name>`. If `CLAUDE_SESSION_ID` is unset, `arm_auto`
51
- falls back to a deterministic derived bridge key (a hash of the store and intent id), so the
52
- gate still engages; arming prints a one-line notice to stderr in that case.
77
+ `<dir>` (the `ID--slug` directory), and `<name>`. The first argument is the session id you
78
+ want the bridge keyed by: pass the hook stdin `session_id` when you have it, otherwise
79
+ `ENV["CLAUDE_CODE_SESSION_ID"]`, otherwise `nil`. Arming always succeeds and acquires the
80
+ durable `delivery.lock` in the intent dir. For the `resolve_session` fallback chain
81
+ (why arming never needs a non-empty session env var, and what the lock ownership model
82
+ implies for later tool calls) read `references/end-tail.md`.
53
83
 
54
84
  **Hard rule for the rest of this run:** do NOT edit project code (anything outside the
55
85
  intent directory / `~/.plastic/`) until `plan.md` AND `checklist.md` exist for the intent.
@@ -61,6 +91,8 @@ Honor the cycle: What → Why (spec.md) → How (plan.md + actions/ + checklist.
61
91
 
62
92
  ## Team Spin-Up
63
93
 
94
+ This is the L-tier shape (see `## Tiers` above); S/M collapse it to one thinker agent.
95
+
64
96
  Auto mode spins up exactly ONE enforcer-led team per intent. The plastic-enforcer IS this orchestrator (you), not a separately dispatched agent, which avoids the who-gates-the-gater regress.
65
97
 
66
98
  Roster (one role per cycle stage):
@@ -75,17 +107,46 @@ Dispatch rule: sequential, one specialist per stage on one branch (the deliverab
75
107
 
76
108
  Spawn preamble (live-state injection): before dispatching any specialist, run `scripts/spawn-preamble <intent_dir> --role <role>` and PREPEND its output to that specialist's prompt. The preamble is a deterministic, filesystem-only snapshot of the active intent (id, intent line, current stage) plus the honoring instruction, so every spawned agent boots with accurate live state instead of guessing. This is the authoritative L2 mechanism for harnesses whose sub-agents do not inherit a top-level session event (see `docs/reference/harness-adapters.md`).
77
109
 
110
+ Dispatch-time model contract (belt-and-braces): alongside the preamble, resolve each specialist's model through the config chain (`read-config agents.models.<basename> --project <repo>`: project override, then global, then the shipped tier default) and pass it explicitly at dispatch. Never rely on the dispatched role's frontmatter alone; a resolved subagent model is never Fable.
111
+
78
112
  Completion report (require-then-synthesize): every dispatched specialist MUST end with a structured completion report as its final message. The preamble's `REPORT_CONTRACT` injects this and the role prompts carry the per-role format (see `references/agent-report-contract.md`). Because child-agent honor is best-effort across harnesses, this is decision-shaping, not a hard block. When a specialist returns no usable report (it went idle, emitted only a bare ping, or its message was lost to a mid-run interjection), run `scripts/agent-report <intent_dir> --role <role>` to synthesize a deterministic filesystem-derived report so the handoff account always exists. Use the agent-authored report when present, the synthesized one otherwise.
79
113
 
80
114
  Final-gate review: dispatch an independent reviewer subagent at the final gate only, not as a standing role.
81
115
 
82
- Headless manual gate: when running headless or in the background, enforce gates manually and do not rely on hooks, because `CLAUDE_SESSION_ID` may be unset (this ties to the arm-gate fallback above).
116
+ ### Delegation (subagents writing under the owner's lock)
117
+
118
+ The enforcer's session owns the delivery lock. Per-stage specialists run in
119
+ their own sessions and would be denied by the lock gate, so register each one
120
+ as a delegate before (or when) it needs to write into the intent dir:
121
+
122
+ 1. Instruct each spawned specialist to report its session id
123
+ (`CLAUDE_CODE_SESSION_ID`) in its first message.
124
+ 2. As the lock owner, run:
125
+ `ruby ~/.plastic/scripts/plastic-lock delegate --delegate <specialist-session-id>`
126
+ 3. If a specialist hits a lock-gate deny, the deny message names this exact
127
+ command; run it and have the specialist retry.
128
+
129
+ Only the owner can delegate. Delegates cannot re-delegate or release.
130
+
131
+ Headless manual gate: when running headless or in the background, still enforce gates manually rather than relying on hooks alone. The PostToolUse gate hook reads `session_id` from hook stdin, and the savepoint ledger write is decoupled from the bridge (derived from the file path, so it fires even with no session id) - these do NOT no-op. What can degrade is the bridge-keyed stage enforcement: if no session id reaches the bridge and no matching bridge is discovered, the stage-gate enforcement step exits without acting, so verify state yourself. The bridge still resolves arming via `CLAUDE_CODE_SESSION_ID` or the derived-key fallback (see the arm-gate note above).
83
132
 
84
133
  Solo fallback: if the harness has no subagent dispatch, fall back to a single agent walking the full What, Why, How, Exec cycle yourself. This preserves current behavior.
85
134
 
86
135
  ## Stage-Aware Entry
87
136
 
88
- Read the active intent's directory. Determine current lifecycle stage from filesystem state:
137
+ Read the active intent's `savepoint.md` FIRST (intent 81): the last line classifies the stage,
138
+ and you then verify only that line's artifact before entering. Fall back to the filesystem probe
139
+ below only when the ledger is missing (then rebuild it with `Bridge.rebuild_savepoint`).
140
+
141
+ | Ledger last line | Enter |
142
+ |---|---|
143
+ | `What {id}--{slug}.md` (born) or no spec | Start / complete Why (write spec.md) |
144
+ | `Why spec.md created` | Enter How |
145
+ | `How plan.md created` / `How checklist.md created` / `Exec started` | Enter Exec (verify plan + checklist) |
146
+ | `Exec outcome.md created` | Exec done; complete the intent |
147
+ | `Done delivered|abandoned` | Terminal; do not resume |
148
+
149
+ Filesystem fallback (ledger missing only):
89
150
 
90
151
  | Check (in order) | Stage |
91
152
  |---|---|
@@ -97,6 +158,10 @@ Read the active intent's directory. Determine current lifecycle stage from files
97
158
 
98
159
  Announce which stage you're entering and why.
99
160
 
161
+ Notify user (What briefing): brief per `references/human-report-contract.md`
162
+ (State: the work picked up and why it matters now; Risk: scope uncertainty; Call: confirm
163
+ this is worth doing, or proceed).
164
+
100
165
  ## Why Completion (Autonomous)
101
166
 
102
167
  When entering at Why stage:
@@ -111,15 +176,26 @@ When entering at Why stage:
111
176
  5. Make decisions — pick best option, document in `## Context > ### Decisions` with rationale
112
177
  6. Log all autonomous decisions in `## Insights` with `(autonomous)` marker: "Decision: chose X because Y (autonomous)"
113
178
  7. Write `spec.md` — consolidated specification
179
+ 8. Notify user (Why briefing): brief per `references/human-report-contract.md`
180
+ (State: the approach chosen, one line; Risk: the main trade-off; Call: the one decision
181
+ needed, approve or pick an option).
114
182
 
115
183
  Then proceed to How.
116
184
 
117
185
  ## How Phase
118
186
 
187
+ This is the L-tier shape (see `## Tiers` above); S/M skip step 3 and fold the checklist
188
+ rationale into plan.md inline. The `actions/` directory itself is scaffolded empty at
189
+ intent birth and persists at every tier; only writing `ACTION_N.md` files into it is L
190
+ only (S/M leave the directory empty).
191
+
119
192
  1. If `superpowers:writing-plans` is available as a skill, delegate plan creation to it. Tell it the plan saves to the active intent's directory (not `docs/superpowers/plans/`).
120
193
  2. Otherwise, write `plan.md` directly — implementation plan with numbered tasks
121
- 3. Create `actions/` directory with `ACTION_N.md` files (one per task, self-contained)
194
+ 3. Write `ACTION_N.md` files into the existing `actions/` directory (one per task, self-contained) — L only
122
195
  4. Write `checklist.md` — execution registry with checkboxes covering all actions
196
+ 5. Notify user (How briefing): brief per `references/human-report-contract.md`
197
+ (State: the plan shape, task count and what it builds; Risk: the riskiest task or
198
+ dependency; Call: approve the plan to build).
123
199
 
124
200
  Then proceed to Exec.
125
201
 
@@ -143,6 +219,9 @@ If the plan calls for creating a new project (the intent is an implementation in
143
219
  4. Check off items in `checklist.md` as completed
144
220
  5. Append observations to `## Insights` with `(autonomous)` marker
145
221
  6. Sub-agents can be spawned for parallel actions (one agent per action)
222
+ 7. Notify user (Exec briefing): brief per `references/human-report-contract.md`
223
+ (State: what got built and the test result; Risk: residual failures or deviations;
224
+ Call: go to review, or done).
146
225
 
147
226
  ## Permission Model — Safe-by-Default
148
227
 
@@ -172,7 +251,10 @@ During initial project creation, all decisions are non-destructive by definition
172
251
  ## Completion
173
252
 
174
253
  1. Verify all checklist items are checked
175
- 2. Write `outcome.md` with detailed results
254
+ 2. Write `outcome.md` with detailed results, from `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md`.
255
+ Set the frontmatter `disposition: delivered` (this is the delivered terminal). `outcome.md`
256
+ is mandatory at every terminal and self-declares its disposition (see the canonical done-marker
257
+ and End tail in PLASTIC.md `## Delivery Isolation and the Single-Owner Lock`).
176
258
  3. Write `## Outcome` summary in the intent file (1-2 sentences)
177
259
  4. **Release (if configured)**
178
260
  1. Detect project — match CWD against paths in `~/.plastic/projects.yml` to find the project slug. If no match, skip to step 5 (default commit-only behavior).
@@ -191,22 +273,41 @@ During initial project creation, all decisions are non-destructive by definition
191
273
  5. Review `## Insights` for observations that should spawn future intents. If any:
192
274
  - Create them (using `plastic-creating-intent` conventions)
193
275
  - Update `chain` in the current intent's frontmatter
194
- 6. Move intent from `## Active` to `## Completed` in INDEX.md (with today's date)
276
+ 6. Move intent from `## Active` to `## Completed` in INDEX.md (with today's date). As the
277
+ closing act of the transfer, stamp the terminal ledger bookend (intent 81) so the savepoint's
278
+ last line records delivery:
279
+ ```bash
280
+ ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.append_terminal_savepoint("<intent_dir>", "delivered")'
281
+ ```
282
+ (Use `"abandoned"` instead when the intent is being moved to `## Abandoned`.) Idempotent.
195
283
  7. Auto-commit: `cd <store-root> && git add . && git commit -m "feat: deliver intent <ID> — <name>"`
196
- 8. On completion, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent).
197
- It runs in the background so it never blocks the turn:
284
+ 8. Disarm the lifecycle gate (auto delivery is finished). Substitute the intent's own id for
285
+ `<ID>` (a session can be delivering more than one intent at once, intent 131, so disarm must
286
+ name which of the session's bridges to clear):
198
287
  ```bash
199
- ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async
288
+ ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.disarm_auto(ENV["CLAUDE_CODE_SESSION_ID"], intent_id: "<ID>")'
200
289
  ```
201
- Completion is the lifecycle event that keeps the search index fresh. `<store-root>` is the
202
- store that holds this intent (the global store or the project store).
203
- 9. Disarm the lifecycle gate (auto delivery is finished):
290
+ Disarm runs the ordered End tail (release worktrees, then clear the `delivery.lock`,
291
+ then the bridge becomes purge-eligible) and performs the mandatory worktree cleanup
292
+ (intent 73c3): both per-intent worktrees are removed and both repos pruned. This is
293
+ the plain remove path (no merge); when the work ships through a release, the release
294
+ path merges the branch BEFORE the worktree is removed instead of relying on this step.
295
+ Never leave an orphaned worktree, and run `git worktree prune` if you hit a stale
296
+ reference. For the full ordering rationale and the release-vs-plain-disarm
297
+ distinction, read `references/end-tail.md`.
298
+ 9. QMD reindex LAST (canonical End tail), run only after disarm has released the
299
+ worktrees, cleared the `delivery.lock`, and purged the bridge. It runs in the
300
+ background so it never blocks the turn:
204
301
  ```bash
205
- ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.disarm_auto(ENV["CLAUDE_SESSION_ID"])'
302
+ ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async
206
303
  ```
207
- Disarming also purges stale bridge files from the temp directory automatically (it keeps the
208
- current bridge and any live run), so no manual `/tmp` cleanup is needed.
209
- 10. Notify user: "Intent [ID] [name] delivered. [1-2 sentence summary]. See outcome.md for details."
304
+ `<store-root>` is the store that holds this intent (the global store or the project
305
+ store); the command is a no-op when QMD is absent. For why the reindex must be last
306
+ (so the index never references a bridge or lock about to disappear), read
307
+ `references/end-tail.md`.
308
+ 10. Notify user (Done briefing): brief per `references/human-report-contract.md`
309
+ (State: the delivered impact; Risk: residual risk; Call: the decision left to you, merge,
310
+ release, or accept). See `outcome.md` for details.
210
311
 
211
312
  ## Error Handling
212
313
 
@@ -219,3 +320,9 @@ If the agent gets stuck (can't resolve a gap, dependency is missing, tests fail
219
320
  ## References
220
321
 
221
322
  - Read `references/agent-architecture.md` for the full team model (the 5-role enforcer-led team, per-stage handoffs, gate ownership, headless note, solo fallback) and the orchestrator hierarchy (Main Orchestrator, Project Orchestrators, coordination loop) when spinning up the team or understanding autonomous delivery scope
323
+ - Read `references/tiers.md` for the extended per-tier walkthrough (S/M/L worked examples, the collapsed one-thinker flow, the QMD-skip case for S) and rationale
324
+ - Read `references/human-report-contract.md` for the human-facing per-stage briefing (the
325
+ State/Risk/Call skeleton used at each "Notify user" step above, and how it differs from the
326
+ internal `agent-report-contract.md`)
327
+ - Read `references/end-tail.md` for the `resolve_session` fallback chain and the disarm
328
+ ordering / worktree cleanup / QMD reindex rationale referenced above
@@ -90,10 +90,13 @@ permanent sixth role, it exists only for the final review.
90
90
 
91
91
  ### Headless Manual Gate
92
92
 
93
- When running headless or in the background, the enforcer enforces gates manually and
94
- does not rely on hooks, because `CLAUDE_SESSION_ID` may be unset in those runs (the
95
- gate-check and savepoint hooks no-op without it). The enforcer arms via the bridge's
96
- derived-key fallback and verifies state itself.
93
+ When running headless or in the background, the enforcer enforces gates manually rather
94
+ than relying on hooks alone. The savepoint ledger and PostToolUse gate hook still fire
95
+ (the gate hook reads `session_id` from stdin; the savepoint write is path-derived and
96
+ bridge-independent), so they do not blanket no-op. Only the bridge-keyed stage-enforcement
97
+ step degrades when no session id reaches the bridge and no bridge is discovered. The
98
+ enforcer arms via `CLAUDE_CODE_SESSION_ID` or the bridge's derived-key fallback and
99
+ verifies state itself.
97
100
 
98
101
  ### Delegation
99
102
 
@@ -17,6 +17,14 @@ only. In-flight observations still go in `## Insights`; the report does not add
17
17
  completed its handoff: the agent that did the work is the cheapest, most accurate source of the
18
18
  account.
19
19
 
20
+ ## Prose-stripped (intent 84)
21
+
22
+ The report is the envelope and the per-role payload, nothing else. Dispatched and background
23
+ subagents report and do their job; they do not narrate. Strip conversational prose: no
24
+ greeting, no preamble, no "Here is what I did" framing, no end-recap, no restating of the task.
25
+ Reasoning belongs in the thinking channel, not the report body. This tightens the FORM (the
26
+ fields stay exactly as below); it does not remove any required field.
27
+
20
28
  ## Common envelope
21
29
 
22
30
  Every role report, whatever the stage, carries these fields:
@@ -31,6 +39,11 @@ Every role report, whatever the stage, carries these fields:
31
39
  - **Checklist deltas**: which checklist items this turn checked off (executor), or `n/a`.
32
40
  - **Deviations from spec**: anything done differently from the spec or plan, and why, or `none`.
33
41
  - **Blockers / handoff notes**: what the next stage must watch for, or `none`.
42
+ - **Insights**: 0..N durable nuggets discovered this turn (the most interesting residue),
43
+ each one a `## Insights`-worthy line; `none` if there were none. Background and dispatched
44
+ agents MUST populate this: they carry each nugget home in the report and the orchestrator
45
+ persists it (see Insights delivery below), so an insight never depends on the discovering
46
+ session having file-write access.
34
47
 
35
48
  ## Per-role payload
36
49
 
@@ -41,11 +54,13 @@ D2). The payload is what makes the report useful to the orchestrator beyond the
41
54
  - Decisions recorded in `### Decisions`, each with its one-line rationale.
42
55
  - Context enriched: what was researched and the key findings.
43
56
  - Open questions resolved, and any deliberately left for the spec.
57
+ - Insights: durable discoveries from the Why exploration, reported in the `insights:` field.
44
58
 
45
59
  ### spec-specialist (Why to How boundary)
46
60
  - Spec sections produced (Problem, Goals, Non-Goals, Approach, Decisions, Acceptance Criteria).
47
61
  - How the recorded decisions resolved into the chosen approach.
48
62
  - Acceptance-criteria count, so the planner knows the surface to cover.
63
+ - Insights: durable discoveries from consolidating the spec, reported in the `insights:` field.
49
64
 
50
65
  ### planner (How): worked exemplar
51
66
  The planner report EXPLAINS THE PLAN BACK TO THE ORCHESTRATOR. It carries:
@@ -54,17 +69,20 @@ The planner report EXPLAINS THE PLAN BACK TO THE ORCHESTRATOR. It carries:
54
69
  - Checklist coverage: item count and that every action plus suite-green is covered.
55
70
  This is the exemplar because the plan is an argument, and the orchestrator gates on whether that
56
71
  argument is sound before any code is written.
72
+ - Insights: durable discoveries from planning, reported in the `insights:` field.
57
73
 
58
74
  ### executor (Exec)
59
75
  - Actions implemented this turn, mapped to checklist items checked off (checked / total).
60
76
  - A summary of the code changed (files and the shape of the change).
61
77
  - Test result: the full-suite command and its pass / fail counts.
62
- - Insights appended, with the `(autonomous)` marker.
78
+ - Insights reported in the `insights:` field (each with the `(autonomous)` marker); the
79
+ executor or the orchestrator persists them to `## Insights` via the `insight-append` helper.
63
80
 
64
81
  ### final reviewer (final gate)
65
82
  - Verdict: `pass` or `blockers found`.
66
83
  - Each acceptance criterion checked, with the evidence that confirms or refutes it.
67
84
  - Gaps or risks found, ranked, with a recommended disposition.
85
+ - Insights: durable discoveries from the review, reported in the `insights:` field.
68
86
 
69
87
  ## Fallback: always a report
70
88
 
@@ -84,3 +102,20 @@ ledger, the lifecycle artifacts present, the checklist checked / total, and the
84
102
  line, and emits a filesystem-derived report labelled `synthesized`. So a handoff account always
85
103
  exists: authored by the agent when possible, reconstructed deterministically when not. This
86
104
  formalizes the by-hand reconstruction the orchestrator did while delivering intent 68.
105
+
106
+ ## Insights delivery
107
+
108
+ Insights ride home in the completion report. Every agent reports its durable nuggets in the
109
+ `insights:` field; the orchestrator (or any agent that can write the intent file) then persists
110
+ each one via the helper:
111
+
112
+ ```
113
+ scripts/insight-append <intent_dir> <text> --stage S --author A
114
+ ```
115
+
116
+ The helper formats the `{utc-iso8601} · {stage} · {author}` prefix (the same timestamp
117
+ convention as the savepoint ledger), validates it, and appends the entry at the bottom of the
118
+ `## Insights` section, newest last. This is the fix for dropped background and sub-agent
119
+ insights: a session that cannot write the intent file still returns its report, so the insight
120
+ survives and the orchestrator writes it on receipt. Hand-editing `## Insights` is an escape
121
+ hatch; the helper is the default so the prefix format cannot drift.