@zalom/plastic 1.0.0-beta.2 → 1.0.0-beta.3

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.
@@ -27,7 +27,15 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
27
27
  2. Read the intent file, its `## Links`, and related code or docs
28
28
  3. Research with the adaptive budget the enforcer set (simple 2-3, medium 5-8, complex 10-15 steps)
29
29
  4. Produce (output handoff): an enriched `## Context` plus `### Decisions` with rationale
30
- 5. Log autonomous calls in `## Insights` with the `(autonomous)` marker, then report for gating
30
+ 5. Log autonomous calls in `## Insights` with the `(autonomous)` marker, then report for gating (see `## Completion Report`)
31
+
32
+ ## Completion Report
33
+
34
+ END your turn with a structured completion report as your final message, per the spawn preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.md`. Do not finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written, verification, checklist deltas, deviations, blockers) plus the brainstorming payload:
35
+
36
+ - Decisions recorded in `### Decisions`, each with its one-line rationale
37
+ - Context enriched: what was researched and the key findings
38
+ - Open questions resolved, and any deliberately left for the spec
31
39
 
32
40
  ## Constraints
33
41
 
@@ -28,6 +28,16 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
28
28
  3. Edit project code (the gate is open now that plan and checklist exist)
29
29
  4. Run the full suite, iterate to zero failures and zero errors
30
30
  5. Produce (output handoff): the code changes, a checked-off `checklist.md`, and `## Insights`
31
+ 6. Report for gating (see `## Completion Report`); the enforcer reviews the work at the final gate
32
+
33
+ ## Completion Report
34
+
35
+ END your turn with a structured completion report as your final message, per the spawn preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.md`. Do not finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written, verification, checklist deltas, deviations, blockers) plus the executor payload:
36
+
37
+ - Actions implemented this turn, mapped to checklist items checked off (checked / total)
38
+ - A summary of the code changed (files and the shape of the change)
39
+ - Test result: the full-suite command and its pass / fail counts
40
+ - Insights appended, with the `(autonomous)` marker
31
41
 
32
42
  ## Constraints
33
43
 
@@ -27,7 +27,17 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
27
27
  2. Read `spec.md` and the plan/checklist templates; match their FORM
28
28
  3. Write `plan.md`, the `actions/` directory, and `checklist.md` into the intent directory
29
29
  4. Produce (output handoff): `plan.md` plus `actions/` plus `checklist.md`
30
- 5. Report for gating; the enforcer verifies plan and checklist exist before Exec is allowed
30
+ 5. Report for gating (see `## Completion Report`); the enforcer verifies plan and checklist exist before Exec is allowed
31
+
32
+ ## Completion Report
33
+
34
+ END your turn with a structured completion report as your final message, per the spawn preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.md`. Do not finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written, verification, checklist deltas, deviations, blockers) plus the planner payload, which EXPLAINS THE PLAN BACK TO THE ORCHESTRATOR:
35
+
36
+ - The ordered actions, one line each: what the action does and how it is verified
37
+ - Decomposition rationale: why this order, and why the actions are independent
38
+ - Checklist coverage: the item count, and that every action plus suite-green is covered
39
+
40
+ The plan is an argument; the orchestrator gates on whether that argument is sound before any code is written, so make the report make that case.
31
41
 
32
42
  ## Constraints
33
43
 
@@ -27,7 +27,15 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
27
27
  2. Read the spec template (`templates/spec.md`) and match its FORM exactly
28
28
  3. Write `spec.md` into the intent directory, resolving the decisions into a coherent approach
29
29
  4. Produce (output handoff): a complete `spec.md`
30
- 5. Report for gating; the enforcer checks the spec against its exit criteria before How begins
30
+ 5. Report for gating (see `## Completion Report`); the enforcer checks the spec against its exit criteria before How begins
31
+
32
+ ## Completion Report
33
+
34
+ END your turn with a structured completion report as your final message, per the spawn preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.md`. Do not finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written, verification, checklist deltas, deviations, blockers) plus the spec payload:
35
+
36
+ - The spec sections produced (Problem, Goals, Non-Goals, Approach, Decisions, Acceptance Criteria)
37
+ - How the recorded decisions resolved into the chosen approach
38
+ - The acceptance-criteria count, so the planner knows the surface to cover
31
39
 
32
40
  ## Constraints
33
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalom/plastic",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.3",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+
5
+ # agent-report - deterministic filesystem-derived completion report (intent 74).
6
+ #
7
+ # The auto-mode report contract is decision-shaped (the spawn preamble's
8
+ # REPORT_CONTRACT plus the role prompts ask every dispatched agent to END with a
9
+ # structured completion report). Because child-agent honor is best-effort across
10
+ # harnesses (Tier B/C, docs/reference/harness-adapters.md), the contract is never a
11
+ # hard block. This script is the always-a-report fallback: when a dispatched agent
12
+ # returns no usable report, the enforcer runs it to synthesize one from the intent
13
+ # directory, so the handoff account always exists.
14
+ #
15
+ # Like scripts/spawn-preamble, it is a PURE function of the intent dir: no network,
16
+ # no randomness, no wall-clock reads. Two runs over the same on-disk state produce
17
+ # byte-identical output.
18
+ #
19
+ # Usage:
20
+ # agent-report <intent_dir> [--role ROLE]
21
+ #
22
+ # Exit codes: 0 (report emitted), 2 (usage).
23
+
24
+ require_relative "lib/bridge"
25
+
26
+ def parse_args(argv)
27
+ role = nil
28
+ positional = []
29
+ i = 0
30
+ while i < argv.length
31
+ case argv[i]
32
+ when "--role"
33
+ role = argv[i + 1]
34
+ i += 2
35
+ else
36
+ positional << argv[i]
37
+ i += 1
38
+ end
39
+ end
40
+ [positional.first, role]
41
+ end
42
+
43
+ # Read the intent file frontmatter via the same path spawn-preamble uses, so the
44
+ # id/intent we report match what the rest of Plastic sees. Never raises.
45
+ def frontmatter_for(intent_dir)
46
+ ifile = Bridge.intent_file(intent_dir)
47
+ return {} unless File.exist?(ifile)
48
+
49
+ content = File.read(ifile)
50
+ return {} unless content.start_with?("---")
51
+
52
+ parts = content.split("---", 3)
53
+ return {} if parts.length < 3
54
+
55
+ require "yaml"
56
+ require "date"
57
+ require "time"
58
+ YAML.safe_load(parts[1], permitted_classes: [Date, Time]) || {}
59
+ rescue StandardError
60
+ {}
61
+ end
62
+
63
+ # Current stage: prefer the last non-empty savepoint line (the ledger encodes the
64
+ # furthest-reached milestone), else the file-derived stage. Same logic as
65
+ # spawn-preamble's current_stage.
66
+ STAGE_LABELS = {
67
+ "what" => "What", "why" => "Why", "how" => "How",
68
+ "exec" => "Exec", "done" => "Done"
69
+ }.freeze
70
+
71
+ def current_stage(intent_dir)
72
+ ledger = File.join(intent_dir, Bridge::SAVEPOINT_FILE)
73
+ if File.exist?(ledger)
74
+ last = File.read(ledger).each_line.map(&:strip).reject(&:empty?).last
75
+ return last if last
76
+ end
77
+ STAGE_LABELS.fetch(Bridge.derive_stage(intent_dir), Bridge.derive_stage(intent_dir))
78
+ end
79
+
80
+ # checklist.md checked/total: count GFM task-list items. Returns "n/a" when there
81
+ # is no checklist (a checklist is only meaningful from How onward).
82
+ def checklist_progress(intent_dir)
83
+ path = File.join(intent_dir, "checklist.md")
84
+ return "n/a" unless Bridge.stage_file_present?(path)
85
+
86
+ lines = File.readlines(path)
87
+ total = lines.count { |l| l =~ /^\s*- \[[ xX]\]/ }
88
+ checked = lines.count { |l| l =~ /^\s*- \[[xX]\]/ }
89
+ return "n/a" if total.zero?
90
+
91
+ "#{checked}/#{total}"
92
+ end
93
+
94
+ # The outcome line: first content line under outcome.md's "## Summary" (or the first
95
+ # non-heading content line), else a placeholder. Pure read of the intent dir.
96
+ def outcome_line(intent_dir)
97
+ path = File.join(intent_dir, "outcome.md")
98
+ return "(no outcome yet)" unless Bridge.stage_file_present?(path)
99
+
100
+ lines = File.readlines(path).map(&:rstrip)
101
+ start = lines.index { |l| l.strip.downcase == "## summary" }
102
+ scan = start ? lines[(start + 1)..] : lines
103
+ (scan || []).each do |l|
104
+ s = l.strip
105
+ next if s.empty? || s.start_with?("#") || s.start_with?("<!--")
106
+ return s
107
+ end
108
+ "(no outcome yet)"
109
+ end
110
+
111
+ intent_dir_arg, role = parse_args(ARGV)
112
+
113
+ if intent_dir_arg.nil? || intent_dir_arg.empty?
114
+ warn "usage: agent-report <intent_dir> [--role ROLE]"
115
+ exit 2
116
+ end
117
+
118
+ intent_dir = File.expand_path(intent_dir_arg)
119
+
120
+ fm = frontmatter_for(intent_dir)
121
+ id = fm["id"].to_s.strip
122
+ intent_name = fm["intent"].to_s.strip
123
+ id = "(unknown)" if id.empty?
124
+ intent_name = "(unknown)" if intent_name.empty?
125
+
126
+ stage = current_stage(intent_dir)
127
+ role_label = (role && !role.empty?) ? role : stage
128
+ artifacts = Bridge.has_files(intent_dir)
129
+ artifacts_str = artifacts.empty? ? "(none)" : artifacts.join(", ")
130
+
131
+ lines = []
132
+ lines << "=== Plastic agent report (synthesized) ==="
133
+ lines << "Intent: #{id} - #{intent_name}"
134
+ lines << "Stage: #{stage}"
135
+ lines << "Role: #{role_label}"
136
+ lines << "Status: synthesized (filesystem-derived; no agent-authored report)"
137
+ lines << "Artifacts present: #{artifacts_str}"
138
+ lines << "Checklist: #{checklist_progress(intent_dir)}"
139
+ lines << "Outcome: #{outcome_line(intent_dir)}"
140
+ lines << "=== end report ==="
141
+
142
+ puts lines.join("\n")
@@ -29,7 +29,22 @@ require_relative "lib/bridge"
29
29
  HONOR_INSTRUCTION =
30
30
  "You are operating inside Plastic. Use it as your operating scaffold. " \
31
31
  "Emit VALID lifecycle artifacts; do not hallucinate intents or stages. " \
32
- "Your output is a deliverable, not a message."
32
+ "Your primary output is valid lifecycle artifacts; you close with a structured report about them."
33
+
34
+ # Verbatim completion-report contract (intent 74). Kept as one constant so the
35
+ # contract doc (skills/auto/references/agent-report-contract.md), the role prompts,
36
+ # and the test assert against the exact same string. SINGLE SOURCE OF TRUTH for the
37
+ # report wording: the work output is lifecycle artifacts, the final message is a
38
+ # structured report about them. Both are required and they do not contradict.
39
+ REPORT_CONTRACT =
40
+ "Before you finish, END your turn with a structured completion report as your " \
41
+ "FINAL MESSAGE (your return value), not a side-channel file. Do not go idle or " \
42
+ "finish silently. The report carries a common envelope: role, intent id, stage, " \
43
+ "status (delivered or blocked), artifacts written, verification or tests run, " \
44
+ "checklist deltas, deviations from spec, and blockers or handoff notes; plus a " \
45
+ "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."
33
48
 
34
49
  def parse_args(argv)
35
50
  role = nil
@@ -116,6 +131,8 @@ lines << "Current stage: #{stage}"
116
131
  lines << "Cycle step / role: #{cycle}"
117
132
  lines << ""
118
133
  lines << HONOR_INSTRUCTION
134
+ lines << ""
135
+ lines << REPORT_CONTRACT
119
136
  lines << "=== end preamble ==="
120
137
 
121
138
  puts lines.join("\n")
@@ -68,6 +68,8 @@ Dispatch rule: sequential, one specialist per stage on one branch (the deliverab
68
68
 
69
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
70
 
71
+ 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.
72
+
71
73
  Final-gate review: dispatch an independent reviewer subagent at the final gate only, not as a standing role.
72
74
 
73
75
  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).
@@ -61,6 +61,26 @@ and not hallucinate intents or stages. This is the standard L2 live-state mechan
61
61
  for harnesses whose spawned sub-agents do not inherit the top-level session event. See
62
62
  `docs/reference/harness-adapters.md` for how it slots into the per-harness contract.
63
63
 
64
+ ### Completion Reports
65
+
66
+ Every dispatched specialist ends its turn with a structured completion report as its final
67
+ message (its return value), so the agent that did the work is the one that accounts for it. The
68
+ report carries a common envelope plus a role-specific payload that fulfils the agent's place in
69
+ the cycle; the planner explains the plan back to the orchestrator, the executor reports what was
70
+ built and the test result, and so on. The format lives in `references/agent-report-contract.md`,
71
+ and the verbatim instruction is injected once via the spawn preamble's `REPORT_CONTRACT`
72
+ constant, which the role prompts reproduce.
73
+
74
+ Enforcement is require-report then synthesize-fallback. The preamble and prompts make the report
75
+ mandatory (decision-shaping), but child-agent honor is best-effort across harnesses (Tier B/C),
76
+ so it is never a hard block. When a specialist returns no usable report, the enforcer runs
77
+ `scripts/agent-report <intent_dir> --role <role>`, a pure function of the intent dir (no network,
78
+ clock, or randomness, mirroring `spawn-preamble`) that emits a filesystem-derived report from the
79
+ savepoint, the artifacts present, the checklist checked/total, and the outcome line. A handoff
80
+ account therefore always exists: agent-authored when present, deterministically reconstructed
81
+ otherwise. This structures the finish notification only; in-flight observations stay in
82
+ `## Insights`, no progress chatter is added.
83
+
64
84
  ### Gate Ownership
65
85
 
66
86
  The enforcer arms and verifies the lifecycle gate, then gates every stage transition.
@@ -0,0 +1,86 @@
1
+ # Agent Completion Report Contract
2
+
3
+ Every agent dispatched by the auto-mode enforcer MUST end its turn with a structured
4
+ completion report. This doc defines that report: one common envelope plus a per-role payload.
5
+ It is the format the `REPORT_CONTRACT` constant in `scripts/spawn-preamble` points at, the
6
+ role prompts (`agents/plastic-*.md`) reproduce, and the deterministic fallback
7
+ (`scripts/agent-report`) approximates. Keep all four in agreement; the constant in
8
+ `scripts/spawn-preamble` is the single source of truth for the injected wording.
9
+
10
+ ## Purpose
11
+
12
+ The report is the agent's FINAL MESSAGE (its return value), not a side-channel file. Every
13
+ harness hands a spawned agent's final text back to the dispatcher, so the final message is the
14
+ one carrier that works everywhere (decision D1). The report structures the FINISH notification
15
+ only. In-flight observations still go in `## Insights`; the report does not add progress chatter
16
+ (decision D5). An agent that finishes correct artifacts but goes idle without a report has not
17
+ completed its handoff: the agent that did the work is the cheapest, most accurate source of the
18
+ account.
19
+
20
+ ## Common envelope
21
+
22
+ Every role report, whatever the stage, carries these fields:
23
+
24
+ - **Role**: which specialist produced this (brainstorming, spec, planner, executor, reviewer).
25
+ - **Intent id and stage**: the active intent id and the cycle stage just completed.
26
+ - **Status**: `delivered` or `blocked`.
27
+ - **Artifacts written**: the files produced or changed (store paths, and project paths for the
28
+ executor).
29
+ - **Verification / tests run**: the command run and its result, or `n/a` for stages that write
30
+ no code.
31
+ - **Checklist deltas**: which checklist items this turn checked off (executor), or `n/a`.
32
+ - **Deviations from spec**: anything done differently from the spec or plan, and why, or `none`.
33
+ - **Blockers / handoff notes**: what the next stage must watch for, or `none`.
34
+
35
+ ## Per-role payload
36
+
37
+ Each role appends a payload that fulfils its place in the What, Why, How, Exec cycle (decision
38
+ D2). The payload is what makes the report useful to the orchestrator beyond the envelope.
39
+
40
+ ### brainstorming (Why exploration)
41
+ - Decisions recorded in `### Decisions`, each with its one-line rationale.
42
+ - Context enriched: what was researched and the key findings.
43
+ - Open questions resolved, and any deliberately left for the spec.
44
+
45
+ ### spec-specialist (Why to How boundary)
46
+ - Spec sections produced (Problem, Goals, Non-Goals, Approach, Decisions, Acceptance Criteria).
47
+ - How the recorded decisions resolved into the chosen approach.
48
+ - Acceptance-criteria count, so the planner knows the surface to cover.
49
+
50
+ ### planner (How): worked exemplar
51
+ The planner report EXPLAINS THE PLAN BACK TO THE ORCHESTRATOR. It carries:
52
+ - The ordered actions, one line each: what the action does and how it is verified.
53
+ - Decomposition rationale: why this order, and why the actions are independent.
54
+ - Checklist coverage: item count and that every action plus suite-green is covered.
55
+ This is the exemplar because the plan is an argument, and the orchestrator gates on whether that
56
+ argument is sound before any code is written.
57
+
58
+ ### executor (Exec)
59
+ - Actions implemented this turn, mapped to checklist items checked off (checked / total).
60
+ - A summary of the code changed (files and the shape of the change).
61
+ - Test result: the full-suite command and its pass / fail counts.
62
+ - Insights appended, with the `(autonomous)` marker.
63
+
64
+ ### final reviewer (final gate)
65
+ - Verdict: `pass` or `blockers found`.
66
+ - Each acceptance criterion checked, with the evidence that confirms or refutes it.
67
+ - Gaps or risks found, ranked, with a recommended disposition.
68
+
69
+ ## Fallback: always a report
70
+
71
+ Decision-shaping (the preamble plus these prompts) makes the report mandatory, but child-agent
72
+ honor is best-effort across harnesses (Tier B/C in `docs/reference/harness-adapters.md`), so the
73
+ contract is never a hard block (decision D3). When a dispatched agent returns no usable report
74
+ (it went idle, emitted only a bare ping, or its message was lost to a mid-run interjection), the
75
+ enforcer synthesizes one:
76
+
77
+ ```
78
+ scripts/agent-report <intent_dir> --role <role>
79
+ ```
80
+
81
+ `scripts/agent-report` is a pure function of the intent directory (no network, clock, or
82
+ randomness, mirroring `scripts/spawn-preamble`): it reads the current stage from the savepoint
83
+ ledger, the lifecycle artifacts present, the checklist checked / total, and the `## Outcome`
84
+ line, and emits a filesystem-derived report labelled `synthesized`. So a handoff account always
85
+ exists: authored by the agent when possible, reconstructed deterministically when not. This
86
+ formalizes the by-hand reconstruction the orchestrator did while delivering intent 68.