@zalom/plastic 1.0.0-alpha.9 → 1.0.0-beta.2

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 (99) hide show
  1. package/PLASTIC.md +163 -469
  2. package/README.md +95 -58
  3. package/agents/plastic-brainstorming.md +37 -0
  4. package/agents/plastic-enforcer.md +36 -0
  5. package/agents/plastic-executor.md +37 -0
  6. package/agents/{future-intent-researcher.md → plastic-future-intent-researcher.md} +1 -1
  7. package/agents/{intent-curator.md → plastic-intent-curator.md} +1 -1
  8. package/agents/plastic-planner.md +37 -0
  9. package/agents/plastic-spec-specialist.md +37 -0
  10. package/bin/plastic.js +57 -0
  11. package/bin/test +28 -0
  12. package/deprecations.yml +1 -10
  13. package/hooks/auto-arm +5 -0
  14. package/hooks/bash-gate +3 -0
  15. package/hooks/check-update +12 -8
  16. package/hooks/code-gate +12 -0
  17. package/hooks/create-gate +3 -0
  18. package/hooks/gate-check +3 -1
  19. package/hooks/hooks.json +52 -0
  20. package/hooks/qmd-search +8 -0
  21. package/package.json +2 -2
  22. package/scripts/dashboard.rb +687 -0
  23. package/scripts/doctor.rb +1054 -628
  24. package/scripts/hook-auto-arm +51 -0
  25. package/scripts/hook-bash-gate +41 -0
  26. package/scripts/hook-code-gate +27 -0
  27. package/scripts/hook-continue +15 -114
  28. package/scripts/hook-create-gate +59 -0
  29. package/scripts/hook-gate-check +47 -32
  30. package/scripts/hook-qmd-search +44 -0
  31. package/scripts/hook-session-start +106 -38
  32. package/scripts/install.rb +91 -529
  33. package/scripts/lib/boot_banner.rb +28 -0
  34. package/scripts/lib/bridge.rb +404 -19
  35. package/scripts/lib/installer_core.rb +807 -0
  36. package/scripts/lib/intent_validator.rb +235 -0
  37. package/scripts/lib/qmd_hook.rb +44 -0
  38. package/scripts/lib/qmd_sync.rb +209 -0
  39. package/scripts/lib/store_provisioning.rb +100 -0
  40. package/scripts/migrate-to-global +1 -1
  41. package/scripts/new-intent +226 -0
  42. package/scripts/provision-project-store +53 -0
  43. package/scripts/qmd-sync +92 -0
  44. package/scripts/select-update-target +93 -0
  45. package/scripts/spawn-preamble +121 -0
  46. package/scripts/uninstall.rb +53 -0
  47. package/scripts/update.rb +164 -0
  48. package/scripts/validate-intent +54 -0
  49. package/scripts/versions.rb +141 -0
  50. package/skills/_active-intent-gate.md +1 -1
  51. package/skills/add-project-store/SKILL.md +54 -0
  52. package/skills/auto/SKILL.md +77 -7
  53. package/skills/auto/evals/evals.json +207 -0
  54. package/skills/auto/references/agent-architecture.md +135 -0
  55. package/skills/brainstorming/SKILL.md +9 -9
  56. package/skills/brainstorming-grill-me/SKILL.md +6 -6
  57. package/skills/continuing/SKILL.md +92 -82
  58. package/skills/continuing/evals/evals.json +136 -0
  59. package/skills/continuing/references/context-management.md +32 -0
  60. package/skills/creating-intent/SKILL.md +74 -36
  61. package/skills/creating-intent/evals/evals.json +56 -0
  62. package/skills/creating-intent/references/lifecycle.md +76 -0
  63. package/skills/creating-intent/references/wikilinks.md +8 -0
  64. package/skills/creating-project/SKILL.md +40 -8
  65. package/skills/creating-project/references/hubs-projects.md +55 -0
  66. package/skills/dashboard/SKILL.md +121 -0
  67. package/skills/dashboard/templates/dashboard-global.md +31 -0
  68. package/skills/dashboard/templates/dashboard-project.md +40 -0
  69. package/skills/doctor/SKILL.md +51 -4
  70. package/skills/doctor/references/gates-stuck-detection.md +38 -0
  71. package/skills/doctor/report.md +4 -0
  72. package/skills/evaluating-skills/SKILL.md +140 -0
  73. package/skills/evaluating-skills/assets/eval-template.json +12 -0
  74. package/skills/evaluating-skills/evals/evals.json +75 -0
  75. package/skills/evaluating-skills/references/convention-checks.md +76 -0
  76. package/skills/evaluating-skills/references/eval-methodology.md +154 -0
  77. package/skills/executing-plan/SKILL.md +5 -3
  78. package/skills/install/SKILL.md +69 -8
  79. package/skills/intent-curator/SKILL.md +3 -3
  80. package/skills/linking-intents/SKILL.md +16 -7
  81. package/skills/linking-intents/references/zettelkasten.md +38 -0
  82. package/skills/managing-index/SKILL.md +5 -1
  83. package/skills/managing-index/references/zettelkasten-linking.md +1 -1
  84. package/skills/releasing/SKILL.md +80 -23
  85. package/skills/releasing/references/deprecations.md +60 -0
  86. package/skills/research/SKILL.md +2 -2
  87. package/skills/savepoint/SKILL.md +46 -37
  88. package/skills/savepoint/references/context-management.md +32 -0
  89. package/skills/uninstall/SKILL.md +39 -28
  90. package/skills/update/SKILL.md +41 -44
  91. package/skills/versions/SKILL.md +65 -0
  92. package/skills/writing-instructions/SKILL.md +159 -0
  93. package/skills/writing-instructions/references/agentskills-spec.md +135 -0
  94. package/skills/writing-plans/SKILL.md +5 -5
  95. package/templates/agents.md +7 -7
  96. package/templates/outcome.md +13 -0
  97. package/templates/savepoint.md +14 -13
  98. package/templates/spec.md +25 -0
  99. package/bin/install.js +0 -29
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+
5
+ # Plastic — `update` verb. Runs via `npx @zalom/plastic update` (bin/plastic.js) or directly.
6
+ # Usage: ruby scripts/update.rb [--beta|--latest|--alpha] [--claude|--codex|--hermes|--all] [--help]
7
+ #
8
+ # Forward-only version transitions, sourced from npm dist-tags:
9
+ # - no flag = next version on the CURRENT channel (derived from VERSION)
10
+ # - --beta/--latest/--alpha = switch channel. Toward stability (alpha->beta->latest) is
11
+ # frictionless; toward bleeding edge requires confirmation.
12
+ # "Already up to date" is a clean no-op. Performs the switch by delegating the file-sync to
13
+ # `install --reinstall --ledger-action update` for the chosen version via npx.
14
+
15
+ require_relative "lib/installer_core"
16
+ require_relative "doctor"
17
+
18
+ class Update < InstallerCore
19
+ PKG = "@zalom/plastic"
20
+
21
+ def cli(argv = ARGV)
22
+ if argv.include?("--help") || argv.include?("-h")
23
+ show_help
24
+ return 0
25
+ end
26
+
27
+ iv = installed_version
28
+ unless iv
29
+ warn "Plastic is not installed. Run: npx #{PKG} install --claude"
30
+ return 1
31
+ end
32
+
33
+ requested = requested_channel(argv)
34
+ tags = fetch_dist_tags
35
+ unless tags
36
+ warn "Could not query npm dist-tags. Are you online?"
37
+ return 1
38
+ end
39
+
40
+ res = compute_target(installed_version: iv, dist_tags: tags, requested_channel: requested)
41
+
42
+ case res[:status]
43
+ when :up_to_date
44
+ puts "\u{2705} Plastic v#{iv} is already up to date on the #{channel_for(iv)} channel."
45
+ return 0
46
+ when :unknown_channel
47
+ warn "No published version on the #{requested} channel."
48
+ return 1
49
+ when :ok
50
+ if res[:kind] == :cross_bleeding && !confirm_bleeding(iv, res[:target])
51
+ puts "Aborted."
52
+ return 1
53
+ end
54
+ puts "\u{2b06}\u{fe0f} Updating Plastic #{iv} \u{2192} #{res[:target]}"
55
+ exit_code = perform_switch(res[:target], agent_args(argv))
56
+ run_post_update_doctor if exit_code == 0
57
+ exit_code
58
+ end
59
+ end
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)
65
+ doctor ||= Doctor.new
66
+ out.puts "\nRunning full doctor after update..."
67
+ result = doctor.run_checks("claude")
68
+ s = result[:summary]
69
+ out.puts " Doctor status: #{result[:status]} " \
70
+ "(pass: #{s[:pass]}, warn: #{s[:warn]}, fail: #{s[:fail]}, total: #{s[:total]})"
71
+ out.puts " Run /plastic-doctor for details." unless result[:status] == "pass"
72
+ result
73
+ rescue StandardError => e
74
+ # Non-blocking: a crash here (e.g. malformed file in the real store) must not
75
+ # undo or fail an update that already succeeded. Report and move on.
76
+ out.puts " doctor could not run: #{e.message} — run /plastic-doctor"
77
+ nil
78
+ end
79
+
80
+ # Pure decision logic (hermetically testable). Returns a status hash.
81
+ def compute_target(installed_version:, dist_tags:, requested_channel: nil)
82
+ installed_ch = channel_for(installed_version)
83
+ target_ch = requested_channel || installed_ch
84
+ cand = dist_tags[target_ch]
85
+ return { status: :unknown_channel } unless cand
86
+
87
+ if target_ch == installed_ch
88
+ return { status: :up_to_date } unless semver_gt?(cand, installed_version)
89
+ { status: :ok, target: cand, kind: :in_channel }
90
+ else
91
+ bleeding = stability_rank(target_ch) < stability_rank(installed_ch)
92
+ { status: :ok, target: cand, kind: bleeding ? :cross_bleeding : :cross_stable }
93
+ end
94
+ end
95
+
96
+ def installed_version
97
+ path = File.join(plastic_home, "VERSION")
98
+ File.exist?(path) ? File.read(path).strip : nil
99
+ end
100
+
101
+ private
102
+
103
+ def requested_channel(argv)
104
+ return "alpha" if argv.include?("--alpha")
105
+ return "beta" if argv.include?("--beta")
106
+ return "latest" if argv.include?("--latest")
107
+ nil
108
+ end
109
+
110
+ # Agent flags to pass through to the delegated install (default --claude).
111
+ def agent_args(argv)
112
+ flags = agents.map { |a| a[:flag] }.select { |f| argv.include?(f) }
113
+ flags << "--all" if argv.include?("--all")
114
+ flags.empty? ? ["--claude"] : flags
115
+ end
116
+
117
+ def fetch_dist_tags
118
+ raw = `npm view #{PKG} dist-tags --json 2>/dev/null`
119
+ return nil if raw.nil? || raw.strip.empty?
120
+ JSON.parse(raw)
121
+ rescue JSON::ParserError, StandardError
122
+ nil
123
+ end
124
+
125
+ def confirm_bleeding(current, target)
126
+ return false unless $stdin.tty?
127
+ print "Switch from #{current} to the less-stable #{target}? [y/N]: "
128
+ ($stdin.gets&.strip || "").downcase.start_with?("y")
129
+ end
130
+
131
+ # Thin npx-exec glue (not unit-tested; the decision above is). Delegates the file-sync to
132
+ # the target version's install verb, recording the ledger action as `update`.
133
+ def perform_switch(target, agent_flags)
134
+ cmd = ["npx", "#{PKG}@#{target}", "install", "--reinstall", "--ledger-action", "update", *agent_flags]
135
+ puts " $ #{cmd.join(" ")}"
136
+ system(*cmd) ? 0 : 1
137
+ end
138
+
139
+ def show_help
140
+ puts <<~HELP
141
+
142
+ plastic update — upgrade Plastic to a newer version
143
+
144
+ Usage:
145
+ npx @zalom/plastic update [options]
146
+
147
+ Channel options (default: stay on the current channel):
148
+ --latest Switch to / advance the stable channel
149
+ --beta Switch to / advance the beta channel
150
+ --alpha Switch to / advance the alpha channel (bleeding edge — confirmed)
151
+
152
+ Agent options (default: --claude):
153
+ --claude --codex --hermes --all
154
+
155
+ Behaviour:
156
+ No flag advances to the next version on your current channel. Switching toward a
157
+ more stable channel is frictionless; switching toward bleeding edge is confirmed.
158
+ Use `versions` to roll back to a previously-installed version.
159
+
160
+ HELP
161
+ end
162
+ end
163
+
164
+ exit(Update.new(package_root: ENV["PLASTIC_PACKAGE_ROOT"] || File.expand_path("..", __dir__)).cli(ARGV)) if $PROGRAM_NAME == __FILE__
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+
5
+ # validate-intent — deterministic CLI over IntentValidator (intent 60).
6
+ #
7
+ # Checks whether a single intent directory is born complete: required frontmatter
8
+ # fields present, and `sources`/`chain` well-formed arrays of id strings. The
9
+ # creating-intent skill self-verifies a freshly written intent with this CLI
10
+ # before announcing or committing; any agent or human can run it on one dir.
11
+ #
12
+ # Usage:
13
+ # validate-intent <intent_dir> [--home <path>]
14
+ #
15
+ # Exit codes: 0 (born complete), 1 (incomplete; report on stderr), 2 (usage).
16
+ # --home <path> overrides the Plastic home (default: ~/.plastic).
17
+
18
+ require_relative "lib/intent_validator"
19
+
20
+ def plastic_home(args)
21
+ if (i = args.index("--home")) && args[i + 1]
22
+ File.expand_path(args[i + 1])
23
+ else
24
+ File.expand_path(ENV["PLASTIC_HOME"] || "~/.plastic")
25
+ end
26
+ end
27
+
28
+ home = plastic_home(ARGV)
29
+
30
+ # First positional arg, skipping any flag and the value that follows --home.
31
+ home_value_index = (i = ARGV.index("--home")) ? i + 1 : nil
32
+ intent_dir = ARGV.each_index.find do |idx|
33
+ arg = ARGV[idx]
34
+ !arg.start_with?("--") && idx != home_value_index
35
+ end
36
+ intent_dir = intent_dir && ARGV[intent_dir]
37
+
38
+ if intent_dir.nil?
39
+ warn "usage: validate-intent <intent_dir> [--home <path>]"
40
+ exit 2
41
+ end
42
+
43
+ dir = File.expand_path(intent_dir)
44
+ result = IntentValidator.validate(dir, plastic_home: home)
45
+
46
+ if result[:ok]
47
+ puts "OK: #{dir}"
48
+ exit 0
49
+ end
50
+
51
+ warn "INCOMPLETE: #{dir}"
52
+ result[:missing].each { |field| warn "missing: #{field}" }
53
+ result[:errors].each { |error| warn error }
54
+ exit 1
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+
5
+ # Plastic — `versions` verb. Runs via `npx @zalom/plastic versions` (bin/plastic.js) or directly.
6
+ # Usage: ruby scripts/versions.rb [--downgrade [--version <v>] | --upgrade] [--help]
7
+ #
8
+ # Local time-machine over the append-only versions.json ledger. Navigates ONLY versions you
9
+ # have actually run (the ledger), never npm's full list — fetching brand-new versions is
10
+ # `update`'s job. Lets you roll back to a known-good build after a bad release.
11
+ # - no flag print the ledger table (and offer to continue a rollback)
12
+ # - --downgrade step back one version in your history
13
+ # - --downgrade --version V jump to a specific previously-run version
14
+ # - --upgrade step forward one version (after a rollback)
15
+
16
+ require_relative "lib/installer_core"
17
+
18
+ class Versions < InstallerCore
19
+ PKG = "@zalom/plastic"
20
+
21
+ def cli(argv = ARGV)
22
+ if argv.include?("--help") || argv.include?("-h")
23
+ show_help
24
+ return 0
25
+ end
26
+
27
+ ledger = ledger_read
28
+ if ledger.empty?
29
+ puts "No version history yet (versions.json is empty). Install or update first."
30
+ return 0
31
+ end
32
+
33
+ timeline = version_timeline(ledger)
34
+ current = installed_version || timeline.last
35
+
36
+ if argv.include?("--downgrade")
37
+ explicit = flag_value(argv, "--version")
38
+ target = explicit || step_target(timeline, current, :back)
39
+ return no_target("No earlier version in your history.") unless target
40
+ unless timeline.include?(target)
41
+ warn "#{target} is not in your version history. Choose one of: #{timeline.join(", ")}"
42
+ return 1
43
+ end
44
+ return switch_to(target, current)
45
+ elsif argv.include?("--upgrade")
46
+ target = step_target(timeline, current, :forward)
47
+ return no_target("No later version in your history. Use `update` for new releases.") unless target
48
+ return switch_to(target, current)
49
+ end
50
+
51
+ # No flag: show the table, and detect an in-progress rollback.
52
+ print_table(ledger, current)
53
+ if ledger.last && ledger.last["action"] == "downgrade"
54
+ prev = step_target(timeline, current, :back)
55
+ if prev && $stdin.tty?
56
+ print "\nYou recently rolled back. Go back further to #{prev}? [y/N]: "
57
+ return switch_to(prev, current) if ($stdin.gets&.strip || "").downcase.start_with?("y")
58
+ end
59
+ end
60
+ 0
61
+ end
62
+
63
+ # Unique versions in first-seen (chronological) order — the user's personal timeline.
64
+ def version_timeline(ledger)
65
+ ledger.map { |e| e["version"] }.compact.uniq
66
+ end
67
+
68
+ # Neighbour of `current` in the timeline. direction :back (older) or :forward (newer).
69
+ def step_target(timeline, current, direction)
70
+ i = timeline.index(current)
71
+ return nil if i.nil?
72
+ j = direction == :back ? i - 1 : i + 1
73
+ return nil if j < 0 || j >= timeline.length
74
+ timeline[j]
75
+ end
76
+
77
+ # Direction-derived ledger action for moving current -> target.
78
+ def action_for(target, current)
79
+ cmp = semver_compare(target, current)
80
+ cmp == -1 ? "downgrade" : "update"
81
+ end
82
+
83
+ def installed_version
84
+ path = File.join(plastic_home, "VERSION")
85
+ File.exist?(path) ? File.read(path).strip : nil
86
+ end
87
+
88
+ private
89
+
90
+ def flag_value(argv, name)
91
+ i = argv.index(name)
92
+ return nil unless i && argv[i + 1]
93
+ argv[i + 1]
94
+ end
95
+
96
+ def no_target(msg)
97
+ puts msg
98
+ 0
99
+ end
100
+
101
+ def switch_to(target, current)
102
+ action = action_for(target, current)
103
+ puts "#{action == "downgrade" ? "\u{23ea}" : "\u{23e9}"} #{current} \u{2192} #{target} (#{action})"
104
+ cmd = ["npx", "#{PKG}@#{target}", "install", "--reinstall", "--ledger-action", action, "--claude"]
105
+ puts " $ #{cmd.join(" ")}"
106
+ system(*cmd) ? 0 : 1
107
+ end
108
+
109
+ def print_table(ledger, current)
110
+ puts "\nPlastic version history (versions.json — append-only):\n\n"
111
+ puts " %-22s %-10s %s" % ["version", "action", "at"]
112
+ ledger.each do |e|
113
+ marker = e["version"] == current ? "\u{2192} " : " "
114
+ puts " #{marker}%-22s %-10s %s" % [e["version"], e["action"], e["at"]]
115
+ end
116
+ puts "\n \u{2192} = currently installed (v#{current})\n\n"
117
+ end
118
+
119
+ def show_help
120
+ puts <<~HELP
121
+
122
+ plastic versions — manage and roll back your local Plastic versions
123
+
124
+ Usage:
125
+ npx @zalom/plastic versions [options]
126
+
127
+ Options:
128
+ (none) Show the version-history table
129
+ --downgrade Roll back one version in your history
130
+ --downgrade --version V Roll back to a specific previously-run version
131
+ --upgrade Step forward one version in your history
132
+ -h, --help Show this help
133
+
134
+ Navigates only versions you have already run (the append-only ledger). To move to a
135
+ brand-new release, use `update`.
136
+
137
+ HELP
138
+ end
139
+ end
140
+
141
+ exit(Versions.new(package_root: ENV["PLASTIC_PACKAGE_ROOT"] || File.expand_path("..", __dir__)).cli(ARGV)) if $PROGRAM_NAME == __FILE__
@@ -13,7 +13,7 @@ Before proceeding, resolve the active intent:
13
13
  - Look under `## Active` for intent entries
14
14
  - If exactly one active intent → use it
15
15
  - If multiple active intents → ask user which one
16
- - If no active intent → refuse: "No active intent. Create one first with /plastic:creating-intent"
16
+ - If no active intent → refuse: "No active intent. Create one first with /plastic-creating-intent"
17
17
 
18
18
  3. **Resolve paths:**
19
19
  - Intent directory: `{store}/store/{id}--{slug}/`
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: plastic-add-project-store
3
+ description: >-
4
+ Add an intent store to a project that is already registered in projects.yml
5
+ but has no store on disk. Use when a project is registered but has no store,
6
+ when you need to provision a store, or when doctor reports a missing project
7
+ store (project_store_dir). Thin wrapper around provision-project-store plus an
8
+ optional qmd register step.
9
+ ---
10
+
11
+ # Add a Project Store
12
+
13
+ Provision the intent store for a project that is already registered in
14
+ `projects.yml` but whose `~/.plastic/projects/{slug}/store/` is missing. This is
15
+ the standalone path; `plastic-creating-project` provisions the store for brand
16
+ new projects.
17
+
18
+ The provisioner is the single source of truth for store creation. It is pure
19
+ filesystem and idempotent, so re-running is always safe. The project must already
20
+ be registered (this skill does not edit `projects.yml`).
21
+
22
+ ## 1. Resolve the slug
23
+
24
+ The slug is the project's key under `projects` in `~/.plastic/projects.yml`. If
25
+ the user names a project, map it to its slug. If the slug is missing from
26
+ `projects.yml`, stop: the provisioner requires the project to already be
27
+ registered. If ambiguous, list the registered slugs and ask which one.
28
+
29
+ ## 2. Run the provisioner
30
+
31
+ ```bash
32
+ ruby ~/.plastic/scripts/provision-project-store <slug>
33
+ ```
34
+
35
+ This creates `~/.plastic/projects/<slug>/store/` with `.gitkeep`, writes
36
+ `INDEX.md` and `project.yml` only if they are missing, and never clobbers
37
+ existing files. An unregistered slug exits non-zero with a clear error and
38
+ creates nothing.
39
+
40
+ ## 3. Register with QMD (separate, optional step)
41
+
42
+ ```bash
43
+ ruby ~/.plastic/scripts/qmd-sync register --store ~/.plastic/projects/<slug>/store
44
+ ```
45
+
46
+ This is the only qmd mutation; the provisioner itself performs none. It no-ops
47
+ when qmd is absent, so it is safe to run unconditionally.
48
+
49
+ ## Scope
50
+
51
+ - Provisioning and qmd registration are two distinct steps. Do not fold qmd into
52
+ the provisioner.
53
+ - No `projects.yml` edits, no project-tree edits, no tactical-mirror or
54
+ founding-intent seeding.
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: plastic:auto
2
+ name: plastic-auto
3
3
  description: >-
4
4
  Autonomous intent delivery — agent takes over How and Exec. Use when user says
5
5
  "auto", "take it from here", "deliver this", or when brainstorming-grill-me concludes
@@ -12,14 +12,68 @@ Announce: "Taking over intent [ID] — [name] for autonomous delivery."
12
12
 
13
13
  ## Precondition
14
14
 
15
- An active intent MUST exist in INDEX.md. If none exists, refuse: "No active intent found. Create one first with /plastic:creating-intent."
15
+ An active intent MUST exist in INDEX.md. If none exists, refuse: "No active intent found. Create one first with /plastic-creating-intent."
16
16
 
17
17
  If multiple active intents exist, ask the user which one to deliver (this is the only question auto asks).
18
18
 
19
+ **Picking work when no intent is specified.** If the user says "auto" without naming an
20
+ intent and none is active, consult the dashboard's machine-readable queue to choose the
21
+ next dispatchable intent:
22
+
23
+ ```bash
24
+ ruby ~/.plastic/scripts/dashboard.rb all --json
25
+ ```
26
+
27
+ Work `dispatchable_queue` in `rank` order (these are `defer`/`research` dispositions —
28
+ safe to deliver autonomously). Leave `human_only` and `next_big_thing` for the user — those
29
+ are `drive`/`triage` items the human should lead. See the `plastic-dashboard` skill.
30
+
31
+ ## Arm the Lifecycle Gate (do this FIRST)
32
+
33
+ Immediately after selecting the intent — before any other work — arm auto mode. This
34
+ writes the session bridge that makes the code-edit gate live, so project code cannot be
35
+ edited before the plan exists (the gate applies to YOU, the orchestrator):
36
+
37
+ ```bash
38
+ ruby -r ~/.plastic/scripts/lib/bridge -e \
39
+ 'Bridge.arm_auto(ENV["CLAUDE_SESSION_ID"], intent_id: "<ID>", intent_dir: "<STORE>/<dir>", store: "<STORE>", name: "<name>")'
40
+ ```
41
+
42
+ Replace `<ID>`, `<STORE>` (e.g. `~/.plastic/projects/<slug>/store` or `~/.plastic/store`),
43
+ `<dir>` (the `ID--slug` directory), and `<name>`. If `CLAUDE_SESSION_ID` is unset, `arm_auto`
44
+ falls back to a deterministic derived bridge key (a hash of the store and intent id), so the
45
+ gate still engages; arming prints a one-line notice to stderr in that case.
46
+
47
+ **Hard rule for the rest of this run:** do NOT edit project code (anything outside the
48
+ intent directory / `~/.plastic/`) until `plan.md` AND `checklist.md` exist for the intent.
49
+ Honor the cycle: What → Why (spec.md) → How (plan.md + actions/ + checklist.md) → Exec.
50
+
19
51
  ## Flags
20
52
 
21
53
  - `--skip-permissions` — bypass hard stops on destructive actions on existing projects. Full trust mode. Default: off.
22
54
 
55
+ ## Team Spin-Up
56
+
57
+ 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.
58
+
59
+ Roster (one role per cycle stage):
60
+
61
+ - **plastic-brainstorming** (Why exploration): enriches `## Context` + `### Decisions`
62
+ - **plastic-spec-specialist** (`spec.md`)
63
+ - **plastic-planner** (`plan.md` + `actions/` + `checklist.md`)
64
+ - **plastic-executor** (code + checklist + `## Insights`)
65
+ - **plastic-enforcer** (orchestrates + gates; that is YOU)
66
+
67
+ Dispatch rule: sequential, one specialist per stage on one branch (the deliverables share files). Gate each deliverable against the stage's exit criteria before handing off. The How and Exec phases below default to Plastic's native dispatch (`plastic-executing-plan`) and delegate to the superpowers skills only when they are available or the user asks; do not restate the phase mechanics here.
68
+
69
+ 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`).
70
+
71
+ Final-gate review: dispatch an independent reviewer subagent at the final gate only, not as a standing role.
72
+
73
+ 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).
74
+
75
+ 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.
76
+
23
77
  ## Stage-Aware Entry
24
78
 
25
79
  Read the active intent's directory. Determine current lifecycle stage from filesystem state:
@@ -67,7 +121,7 @@ If the plan calls for creating a new project (the intent is an implementation in
67
121
  1. Determine project path from `~/.plastic/config.yml` `project_roots` or from intent context
68
122
  2. **Confirm path with user** — this is the ONE human interaction during auto delivery:
69
123
  > "Creating project `<slug>` at `<path>`. Confirm path, or provide alternative."
70
- 3. Invoke `plastic:creating-project` skill
124
+ 3. Invoke `plastic-creating-project` skill
71
125
  4. The global intent is now Completed (creating-project handles this)
72
126
  5. The tactical mirror in the project store becomes the active intent
73
127
  6. Continue execution from the project directory using the tactical intent
@@ -75,7 +129,7 @@ If the plan calls for creating a new project (the intent is an implementation in
75
129
  ## Exec Phase
76
130
 
77
131
  1. If `superpowers:subagent-driven-development` or `superpowers:executing-plans` is available, delegate execution to it
78
- 2. Otherwise invoke `plastic:executing-plan`
132
+ 2. Otherwise invoke `plastic-executing-plan`
79
133
  3. Execute actions from checklist sequentially
80
134
  4. Check off items in `checklist.md` as completed
81
135
  5. Append observations to `## Insights` with `(autonomous)` marker
@@ -124,13 +178,25 @@ During initial project creation, all decisions are non-destructive by definition
124
178
  - `fix_and_retry` — attempt to fix the failure, re-run verify (max 2 retries)
125
179
  - `stop` — write `savepoint.md` with current state, notify user: "Verify failed — savepoint written.", **STOP**
126
180
  - `manual` — notify user: "Verify failed: [summary]. Resolve manually."
127
- 5. If `release.on_green` has items, invoke `plastic:releasing` to handle them (tag, changelog, publish, etc.). Do NOT duplicate release logic — delegate entirely.
181
+ 5. If `release.on_green` has items, invoke `plastic-releasing` to handle them (tag, changelog, publish, etc.). Do NOT duplicate release logic — delegate entirely.
128
182
  5. Review `## Insights` for observations that should spawn future intents. If any:
129
- - Create them (using `plastic:creating-intent` conventions)
183
+ - Create them (using `plastic-creating-intent` conventions)
130
184
  - Update `chain` in the current intent's frontmatter
131
185
  6. Move intent from `## Active` to `## Completed` in INDEX.md (with today's date)
132
186
  7. Auto-commit: `cd <store-root> && git add . && git commit -m "feat: deliver intent <ID> — <name>"`
133
- 8. Notify user: "Intent [ID] [name] delivered. [1-2 sentence summary]. See outcome.md for details."
187
+ 8. Refresh the QMD search index for this store (optional, no-op when QMD is absent):
188
+ ```bash
189
+ ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root>
190
+ ```
191
+ Delivery is the lifecycle event that keeps the search index fresh. `<store-root>` is the
192
+ store that holds this intent (the global store or the project store).
193
+ 9. Disarm the lifecycle gate (auto delivery is finished):
194
+ ```bash
195
+ ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.disarm_auto(ENV["CLAUDE_SESSION_ID"])'
196
+ ```
197
+ Disarming also purges stale bridge files from the temp directory automatically (it keeps the
198
+ current bridge and any live run), so no manual `/tmp` cleanup is needed.
199
+ 10. Notify user: "Intent [ID] — [name] delivered. [1-2 sentence summary]. See outcome.md for details."
134
200
 
135
201
  ## Error Handling
136
202
 
@@ -139,3 +205,7 @@ If the agent gets stuck (can't resolve a gap, dependency is missing, tests fail
139
205
  2. Write `savepoint.md` with current state
140
206
  3. Notify user: "Blocked on intent [ID] — [name]: [description]. Savepoint written."
141
207
  4. **STOP.** Do not attempt workarounds that could leave the project in a broken state.
208
+
209
+ ## References
210
+
211
+ - 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