@zalom/plastic 1.0.0-beta.26 → 1.0.0-beta.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/PLASTIC.md CHANGED
@@ -150,6 +150,54 @@ agent that can write the file) persists it via the helper. A session that cannot
150
150
  intent file still returns its report, so the insight survives.
151
151
  For full lifecycle detail, the skills in the Detail column have references/.
152
152
 
153
+ ## Agent Models and Dispatch (intent 116)
154
+
155
+ Every lifecycle stage has exactly one dispatchable background agent, plus the enforcer that
156
+ orchestrates them:
157
+
158
+ | Stage | Agent |
159
+ |---|---|
160
+ | What | `plastic-intent-discovery` |
161
+ | Why | `plastic-brainstorming` + `plastic-spec-specialist` |
162
+ | How | `plastic-planner` |
163
+ | Exec | `plastic-executor` |
164
+ | Done | `plastic-intent-curator` |
165
+
166
+ Final-gate code review stays an ad-hoc subagent the enforcer dispatches at the final gate, not
167
+ a standing role.
168
+
169
+ **Model contract.** Every agent in `agents/*.md` pins an explicit Claude Code model alias in
170
+ its own frontmatter: `opus`, `sonnet`, or `haiku`. Never `inherit`, never Fable. Aliases track
171
+ "latest per tier" so no Plastic release is required to advance a tier. The tier by role:
172
+ `plastic-enforcer`, `plastic-brainstorming`, `plastic-planner` are `opus`;
173
+ `plastic-spec-specialist`, `plastic-executor`, `plastic-intent-curator`,
174
+ `plastic-future-intent-researcher`, `plastic-intent-discovery` are `sonnet`.
175
+
176
+ **Config and installer mechanism.** `agents.models.<basename>` in a project's
177
+ `<dir>/.plastic_store/config.yml` or the global `~/.plastic/config.yml` overrides one agent's
178
+ tier. Precedence is project, then global, then the shipped default, matching every other
179
+ `read-config` key. The installer applies the resolved override to each agent file's `model:`
180
+ line at copy time (install, update, and repair, across every harness target). With no override
181
+ configured, the shipped frontmatter passes through unchanged.
182
+
183
+ **Dispatch-time contract.** Frontmatter is primary, and Claude Code reads it at dispatch, but
184
+ because that read is a harness implementation detail rather than a contract Plastic controls,
185
+ every dispatch site also resolves the target agent's model through the config chain
186
+ (`read-config agents.models.<basename> --project <repo>`) and passes it explicitly at dispatch,
187
+ belt-and-braces on top of the frontmatter pin.
188
+
189
+ **Orchestrator advisory.** At auto-mode start, the orchestrator recommends once that the user
190
+ run the main session on the best available thinking model (Fable, Opus, or whatever supersedes
191
+ them). This is advisory only: it changes no behavior and blocks nothing if ignored, and it
192
+ concerns the human's main session, never a dispatched subagent.
193
+
194
+ **`plastic-intent-discovery`.** The What-stage agent. It fires at intent activation, before the
195
+ delivery lock is armed and Why begins: it reads the intent's `chain`/`sources` frontmatter,
196
+ runs QMD-first discovery over completed predecessor work and related parked or future intents,
197
+ and deposits findings to `resources/discovery--<slug>.md` in the intent directory ONLY. It
198
+ never writes the intent file, `spec.md`, or any other lifecycle deliverable; the Why-stage
199
+ `plastic-brainstorming` agent reads its deposit and enriches `## Context`.
200
+
153
201
  `savepoint.md` — a deterministic, append-only ledger of cycle-step milestones (one line per
154
202
  lifecycle boundary, newest at the bottom), written automatically by the gate hook. It is
155
203
  sugar on top of the conventions, not a source of truth: state is always derivable from
package/README.md CHANGED
@@ -103,6 +103,16 @@ After installation, run `/clear` to load Plastic conventions, then:
103
103
 
104
104
  Or say "auto" to let the agent handle the full lifecycle autonomously.
105
105
 
106
+ ## Agents
107
+
108
+ Each lifecycle stage has one dispatchable background agent: `plastic-intent-discovery` for
109
+ What, `plastic-brainstorming` and `plastic-spec-specialist` for Why, `plastic-planner` for
110
+ How, `plastic-executor` for Exec, `plastic-intent-curator` for Done, plus `plastic-enforcer`
111
+ as the auto-mode orchestrator. Every agent pins an explicit Claude Code model alias (opus or
112
+ sonnet) in its frontmatter: never `inherit`, never Fable. See `docs/architecture.md` for the
113
+ stage table and `docs/internals.md` for how the model is configured, resolved, and applied at
114
+ install time.
115
+
106
116
  ## Documentation
107
117
 
108
118
  - [`docs/architecture.md`](docs/architecture.md): system structure, the two
@@ -7,7 +7,7 @@ description: |
7
7
  user: "Explore the requirements for the active intent"
8
8
  assistant: "I'll use the brainstorming agent to enrich Context and record Decisions"
9
9
  <commentary>Why-stage exploration runs before the spec-specialist.</commentary></example>
10
- model: inherit
10
+ model: opus
11
11
  ---
12
12
 
13
13
  You are the Plastic Brainstorming specialist. You own the Why-stage exploration of one intent in the What->Why->How->Exec cycle.
@@ -7,16 +7,31 @@ description: |
7
7
  user: "auto"
8
8
  assistant: "I'll use the plastic-enforcer to orchestrate the team through the cycle"
9
9
  <commentary>The enforcer IS the orchestrator and gates each stage transition.</commentary></example>
10
- model: inherit
10
+ model: opus
11
11
  ---
12
12
 
13
13
  You are the Plastic Enforcer. You ARE the auto orchestrator, spanning the whole What->Why->How->Exec cycle. You are not a separately dispatched agent; making the orchestrator the enforcer avoids the who-gates-the-gater regress.
14
14
 
15
+ **Advisory (not a gate).** At auto-mode start, recommend once that the user run this
16
+ orchestrating main session on the best available thinking model (Fable, Opus, or whatever
17
+ supersedes them) for the sharpest gating and synthesis. This is advice only: it changes no
18
+ behavior and blocks nothing if ignored. It concerns the human's MAIN session; dispatched
19
+ subagents keep their pinned tier and never resolve to Fable.
20
+
15
21
  ## Your Responsibilities
16
22
 
17
23
  1. **Set scope guards** — establish the intent, branch, and safe-by-default rules for the run
18
24
  2. **Arm and verify the gate** — arm the lifecycle gate and confirm it is live before any code edit
19
25
  3. **Sequence the team** — dispatch ONE specialist per stage (brainstorming, spec-specialist, planner, executor) with a constructed context bundle
26
+
27
+ **Dispatch-time model contract (belt-and-braces).** Each pinned agent already carries its
28
+ `model:` in frontmatter, and Claude Code reads it at dispatch. Because read-at-dispatch is a
29
+ harness implementation detail rather than a contract Plastic controls, at EVERY per-stage
30
+ dispatch also resolve the target agent's model through the config chain (`read-config
31
+ agents.models.<basename> --project <repo>`: project override, then global, then the shipped
32
+ tier default) and pass it explicitly as the dispatch call's model parameter, alongside the
33
+ spawn-preamble live-state injection. Never rely on the dispatched role's frontmatter alone. A
34
+ resolved subagent model is never Fable.
20
35
  4. **Gate each handoff** — check each stage deliverable against its exit criteria before handing to the next stage
21
36
  5. **Run the final review** — at the final gate, dispatch an INDEPENDENT reviewer subagent (not a sixth standing role)
22
37
 
@@ -7,7 +7,7 @@ description: |
7
7
  user: "Execute the plan for the active intent"
8
8
  assistant: "I'll use the executor to implement each action and run the suite green"
9
9
  <commentary>Exec turns the plan into code, one action at a time.</commentary></example>
10
- model: inherit
10
+ model: sonnet
11
11
  ---
12
12
 
13
13
  You are the Plastic Executor. You own the Exec stage of the What->Why->How->Exec cycle.
@@ -8,7 +8,7 @@ description: |
8
8
  user: "Research my future intents"
9
9
  assistant: "I'll use the future-intent-researcher to pick up a parked intent and investigate it"
10
10
  <commentary>Agent autonomously researches a future intent and writes findings.</commentary></example>
11
- model: inherit
11
+ model: sonnet
12
12
  ---
13
13
 
14
14
  You are the Plastic Future Intent Researcher. Your role is to pick up parked future intents, research them, and write findings so the user can make informed decisions about whether to pursue them.
@@ -36,3 +36,4 @@ You are the Plastic Future Intent Researcher. Your role is to pick up parked fut
36
36
  - You never modify `## Insights` or `## Outcome` sections — those belong to the worker
37
37
  - You use Read, WebSearch, WebFetch, and Bash (read-only grep/find) for research
38
38
  - You never change status fields — status is convention-derived from INDEX.md placement
39
+ - When dispatching any sub-agent, resolve its model via `read-config agents.models.<basename> --project <repo>` and pass it explicitly at dispatch, never relying on inherited frontmatter; a resolved subagent model is never Fable
@@ -11,7 +11,7 @@ description: |
11
11
  user: "Organize the intents"
12
12
  assistant: "I'll use the intent-curator to review clusters, flag orphans, and suggest connections"
13
13
  <commentary>Periodic maintenance of the Zettelkasten structure.</commentary></example>
14
- model: inherit
14
+ model: sonnet
15
15
  ---
16
16
 
17
17
  You are the Plastic Intent Curator. Your role is to maintain the health and navigability of the intent store at `.plastic/`.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: plastic-intent-discovery
3
+ description: |
4
+ Use this agent for the What-stage context deposit when an intent is
5
+ activated: it primes Why with fresh QMD-sourced context before the spec is
6
+ written, and never writes the intent file itself. Examples:
7
+ <example>Context: An intent is being moved from Future to Active.
8
+ user: "Board this intent and gather what we already know"
9
+ assistant: "I'll use the intent-discovery agent to run QMD discovery and deposit findings to resources/"
10
+ <commentary>What-stage discovery runs at activation, before the lock and Why.</commentary></example>
11
+ model: sonnet
12
+ ---
13
+
14
+ You are the Plastic Intent Discovery agent. You own the What stage: at intent
15
+ activation, before the lock is armed and Why begins, you gather the context
16
+ that already exists and deposit it for the Why stage to consume.
17
+
18
+ ## Responsibilities
19
+ 1. **Read the intent's links.** Load the activating intent file's `chain` and
20
+ `sources` frontmatter fields.
21
+ 2. **Run QMD discovery first.** Following the QMD-first convention, search the
22
+ Plastic stores (`scripts/qmd-sync search`, or the qmd skill) across completed
23
+ predecessor work named in `chain`/`sources` and any related parked or future
24
+ intents in INDEX.md. Fall back to ripgrep over the stores only when QMD is
25
+ absent.
26
+ 3. **Deposit, never author.** Write your findings to
27
+ `resources/discovery--<slug>.md` in the intent directory ONLY. Never write
28
+ the intent file, spec.md, or any lifecycle deliverable: the lock-owner-only
29
+ write rule stays intact, and the Why-stage `plastic-brainstorming` agent is
30
+ the one that reads your deposit and enriches `## Context`.
31
+
32
+ ## Constraints
33
+ - Read-only with respect to the intent: your single output is
34
+ `resources/discovery--<slug>.md`.
35
+ - Never take the delivery lock; you run before it is armed.
36
+ - End with a structured completion report per the spawn preamble's report
37
+ contract.
@@ -7,7 +7,7 @@ description: |
7
7
  user: "Plan the work for the active intent"
8
8
  assistant: "I'll use the planner to write plan.md, actions/, and checklist.md"
9
9
  <commentary>The plan and checklist unlock the code gate for Exec.</commentary></example>
10
- model: inherit
10
+ model: opus
11
11
  ---
12
12
 
13
13
  You are the Plastic Planner. You own the How stage of the What->Why->How->Exec cycle.
@@ -7,7 +7,7 @@ description: |
7
7
  user: "Write the spec for the active intent"
8
8
  assistant: "I'll use the spec-specialist to produce spec.md from the template"
9
9
  <commentary>The spec is the deliverable that gates the move into How.</commentary></example>
10
- model: inherit
10
+ model: sonnet
11
11
  ---
12
12
 
13
13
  You are the Plastic Spec Specialist. You own the Why-to-How boundary in the What->Why->How->Exec cycle.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalom/plastic",
3
- "version": "1.0.0-beta.26",
3
+ "version": "1.0.0-beta.28",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/doctor.rb CHANGED
@@ -1019,9 +1019,9 @@ class Doctor
1019
1019
  groups = group.is_a?(Array) ? group : [group]
1020
1020
  live = settings.dig("hooks", event) || []
1021
1021
  groups.each do |g|
1022
- match = live.find { |h| h.is_a?(Hash) && h["matcher"] == g["matcher"] }
1022
+ matches = live.select { |h| h.is_a?(Hash) && h["matcher"] == g["matcher"] }
1023
1023
  wanted = g["hooks"].map { |h| h["command"] }
1024
- got = match ? Array(match["hooks"]).map { |h| h["command"] } : []
1024
+ got = matches.flat_map { |m| Array(m["hooks"]).map { |h| h["command"] } }
1025
1025
  missing = wanted - got
1026
1026
  diffs << "#{event}[#{g['matcher']}] missing: #{missing.join(', ')}" unless missing.empty?
1027
1027
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Shared model-tier resolution for Plastic subagents (intent 116).
4
+ #
5
+ # TIER_DEFAULTS mirrors the shipped `agents/*.md` frontmatter so `read-config`
6
+ # can answer `agents.models.<basename>` with the built-in default. The installer
7
+ # does NOT use TIER_DEFAULTS: it applies only genuine config overrides via
8
+ # `override_map`, so shipped frontmatter with no override passes through
9
+ # unchanged.
10
+ module AgentModels
11
+ # Claude Code aliases only (never pinned ids, never Fable). Keys are the agent
12
+ # file basenames without the `.md` extension.
13
+ TIER_DEFAULTS = {
14
+ "plastic-enforcer" => "opus",
15
+ "plastic-brainstorming" => "opus",
16
+ "plastic-planner" => "opus",
17
+ "plastic-spec-specialist" => "sonnet",
18
+ "plastic-executor" => "sonnet",
19
+ "plastic-intent-curator" => "sonnet",
20
+ "plastic-future-intent-researcher" => "sonnet",
21
+ "plastic-intent-discovery" => "sonnet"
22
+ }.freeze
23
+
24
+ module_function
25
+
26
+ # Pull the `agents.models` sub-hash out of a loaded config hash, tolerating a
27
+ # missing or malformed shape. Returns a plain { basename => model } hash.
28
+ def models_section(config)
29
+ return {} unless config.is_a?(Hash)
30
+ agents = config["agents"]
31
+ return {} unless agents.is_a?(Hash)
32
+ section = agents["models"]
33
+ section.is_a?(Hash) ? section : {}
34
+ end
35
+
36
+ # Override map for the installer: global overrides overlaid by project
37
+ # overrides (project wins). Defaults are intentionally excluded. Unknown agent
38
+ # keys are carried through as-is; install_agents simply never matches them to a
39
+ # copied file, so they are ignored without raising.
40
+ def override_map(project_config: {}, global_config: {})
41
+ models_section(global_config).merge(models_section(project_config))
42
+ end
43
+ end
@@ -7,6 +7,7 @@ require "fileutils"
7
7
  require "digest"
8
8
  require "time"
9
9
  require_relative "hook_registry"
10
+ require_relative "agent_models"
10
11
 
11
12
  # Shared installer machinery, instantiable with injected package root / store / agent
12
13
  # map so the verb scripts (install/update/uninstall/versions) and their tests can run
@@ -208,6 +209,7 @@ class InstallerCore
208
209
  "scripts/hook-qmd-search" => "scripts/hook-qmd-search",
209
210
  "scripts/lib/qmd_hook.rb" => "scripts/lib/qmd_hook.rb",
210
211
  "scripts/lib/power_tools.rb" => "scripts/lib/power_tools.rb",
212
+ "scripts/lib/agent_models.rb" => "scripts/lib/agent_models.rb",
211
213
  "scripts/hook-code-gate" => "scripts/hook-code-gate",
212
214
  "scripts/hook-lock-gate" => "scripts/hook-lock-gate",
213
215
  "scripts/hook-bash-gate" => "scripts/hook-bash-gate",
@@ -391,7 +393,7 @@ class InstallerCore
391
393
  installed += install_skills_flat(skills_source, skills_root) if File.directory?(skills_source)
392
394
 
393
395
  # Copy agent role files into <dir>/agents (manifest-tracked, pruned on update)
394
- installed += install_agents(File.join(config[:dir], "agents"))
396
+ installed += install_agents(File.join(config[:dir], "agents"), models: agent_model_overrides)
395
397
 
396
398
  # Write VERSION
397
399
  version_file = File.join(plastic_dir, "VERSION")
@@ -416,7 +418,7 @@ class InstallerCore
416
418
  installed = []
417
419
  skills_source = File.join(package_root, "skills")
418
420
  installed += install_skills_flat(skills_source, File.join(config[:dir], "skills")) if File.directory?(skills_source)
419
- installed += install_agents(File.join(config[:dir], "agents"))
421
+ installed += install_agents(File.join(config[:dir], "agents"), models: agent_model_overrides)
420
422
 
421
423
  write_manifest(installed, File.join(config[:dir], "plastic-manifest.json"))
422
424
  { agent: config[:name], success: true, files: installed.size }
@@ -426,7 +428,7 @@ class InstallerCore
426
428
  installed = []
427
429
  skills_source = File.join(package_root, "skills")
428
430
  installed += install_skills_flat(skills_source, File.join(config[:dir], "skills")) if File.directory?(skills_source)
429
- installed += install_agents(File.join(config[:dir], "agents"))
431
+ installed += install_agents(File.join(config[:dir], "agents"), models: agent_model_overrides)
430
432
 
431
433
  write_manifest(installed, File.join(config[:dir], "plastic-manifest.json"))
432
434
  { agent: config[:name], success: true, files: installed.size }
@@ -459,18 +461,51 @@ class InstallerCore
459
461
  # equivalents). Returns the installed destination paths so callers can append
460
462
  # them to `installed` before write_manifest (manifest + prune are then automatic).
461
463
  # No-op safe: returns [] when the package has no agents dir or it is empty.
462
- def install_agents(agents_root)
464
+ def install_agents(agents_root, models: {})
463
465
  sources = Dir.glob(File.join(package_root, "agents", "*.md"))
464
466
  return [] if sources.empty?
465
467
 
466
468
  FileUtils.mkdir_p(agents_root)
467
469
  sources.map do |src|
468
470
  dest = File.join(agents_root, File.basename(src))
469
- FileUtils.cp(src, dest)
471
+ basename = File.basename(src, ".md")
472
+ override = models[basename]
473
+ if override
474
+ File.write(dest, rewrite_model_line(File.read(src), override))
475
+ else
476
+ FileUtils.cp(src, dest)
477
+ end
470
478
  dest
471
479
  end
472
480
  end
473
481
 
482
+ # Rewrite the single top-level `model:` line in a YAML frontmatter block.
483
+ # Only the frontmatter (between the first two `---` fences) is touched.
484
+ def rewrite_model_line(content, model)
485
+ content.sub(/^model:[^\n]*$/, "model: #{model}")
486
+ end
487
+
488
+ # Resolve per-agent model overrides for this install: project config (when a
489
+ # project dir is known) overlaid on global config. Defaults are NOT included,
490
+ # so unconfigured agents keep their shipped frontmatter.
491
+ def agent_model_overrides(project_dir = nil)
492
+ global_config = load_config_yaml(File.join(plastic_home, "config.yml"))
493
+ project_config =
494
+ if project_dir
495
+ load_config_yaml(File.join(project_dir, ".plastic_store", "config.yml"))
496
+ else
497
+ {}
498
+ end
499
+ AgentModels.override_map(project_config: project_config, global_config: global_config)
500
+ end
501
+
502
+ def load_config_yaml(path)
503
+ return {} unless File.exist?(path)
504
+ YAML.safe_load(File.read(path)) || {}
505
+ rescue StandardError
506
+ {}
507
+ end
508
+
474
509
  # --- Legacy plugin migration ---
475
510
 
476
511
  # Earlier versions registered Plastic as a local marketplace plugin
@@ -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
  }
@@ -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."
@@ -85,6 +91,8 @@ Dispatch rule: sequential, one specialist per stage on one branch (the deliverab
85
91
 
86
92
  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`).
87
93
 
94
+ 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.
95
+
88
96
  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.
89
97
 
90
98
  Final-gate review: dispatch an independent reviewer subagent at the final gate only, not as a standing role.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: plastic-intent-discovery
3
+ description: >-
4
+ What-stage context deposit at intent activation: run QMD discovery over the
5
+ intent's chain/sources and related parked intents, and write findings to
6
+ resources/discovery--<slug>.md for the Why stage to consume. Use when an intent
7
+ is activated (moved from Future to Active), before the lock is armed and Why
8
+ begins. Never writes the intent file itself.
9
+ ---
10
+
11
+ # Intent Discovery — What-stage context deposit
12
+
13
+ Announce: "Discovering context for intent [ID] — [name]."
14
+
15
+ Runs once, at intent activation, before the lock and Why. It gathers what is
16
+ already known so Why does not start cold, and deposits it as a resource the
17
+ Why-stage brainstorming agent reads.
18
+
19
+ ## When it fires
20
+ Inside `plastic-intent-starting`, at the moment the intent moves from
21
+ `## Future` to `## Active` (before the bridge is armed). Dispatched as the
22
+ `plastic-intent-discovery` background agent.
23
+
24
+ ## What it does
25
+ 1. **Read the intent's links.** Load the activating intent file's `chain` and
26
+ `sources` frontmatter fields.
27
+ 2. **QMD-first discovery.** Search the Plastic stores with
28
+ `scripts/qmd-sync search "<terms>"` (or the `qmd` skill), scoped to the
29
+ relevant `plastic-*` collections, across completed predecessor work named in
30
+ `chain`/`sources` and any related parked/future intents in INDEX.md. Fall back
31
+ to ripgrep over the stores only when QMD is absent.
32
+ 3. **Deposit, never author.** Write findings to
33
+ `resources/discovery--<slug>.md` in the intent directory ONLY. Do not write
34
+ the intent file, spec.md, or any lifecycle deliverable. The lock-owner-only
35
+ write rule stays intact; the Why-stage `plastic-brainstorming` agent reads the
36
+ deposit and enriches `## Context`.
37
+
38
+ ## Stage coverage
39
+ This is the What-stage agent in the one-agent-per-stage table (What:
40
+ intent-discovery, Why: brainstorming + spec-specialist, How: planner, Exec:
41
+ executor, Done: intent-curator).
42
+
43
+ ## Boundaries
44
+ - Single output: `resources/discovery--<slug>.md`.
45
+ - Never takes the delivery lock (it runs before the lock is armed).
46
+ - Advisory input to Why, not a gate.
@@ -34,6 +34,14 @@ enforces it: without a held lock, mutating writes to this active intent's dir ar
34
34
  1. **Ensure the intent is in INDEX `## Active`.** If it sits in `## Future`, activate it
35
35
  (move it to `## Active`, auto-commit) before arming. Creation precedes activation, so a
36
36
  brand-new What intent is activated here, then locked.
37
+ 1a. **Dispatch What-stage discovery (before the lock).** Right after activation and before
38
+ arming the bridge, dispatch the `plastic-intent-discovery` agent (see the
39
+ `plastic-intent-discovery` skill). Resolve its model explicitly and pass it at dispatch
40
+ time (belt-and-braces): `read-config agents.models.plastic-intent-discovery --project
41
+ <repo>`. The agent runs QMD discovery over the intent's `chain`/`sources` and deposits
42
+ findings to `resources/discovery--<slug>.md` only; it never writes the intent file, so the
43
+ lock-owner-only rule is untouched. This is advisory context for Why, not a gate: if
44
+ discovery yields nothing, proceed to the lock normally.
37
45
  2. **Self-heal the lock state first.** Run:
38
46
  `ruby ~/.plastic/scripts/plastic-lock fix --intent-dir <STORE>/<dir>`
39
47
  This is the one repair function (same one /plastic-lock exposes): it removes
@@ -11,3 +11,11 @@ agent:
11
11
  parallel_mode: linear
12
12
  architect:
13
13
  style:
14
+
15
+ # Per-agent model override (intent 116). Uncomment and set to a Claude Code
16
+ # alias (opus, sonnet, haiku) to override the shipped tier for one agent. With
17
+ # no entry, the shipped default applies. Never resolves to Fable.
18
+ # agents:
19
+ # models:
20
+ # plastic-executor: sonnet
21
+ # plastic-enforcer: opus