@zalom/plastic 1.7.1 → 1.9.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.
- package/PLASTIC.md +75 -535
- package/README.md +3 -2
- package/agents/plastic-intent-curator.md +2 -2
- package/deprecations.yml +10 -2
- package/hooks/{links-gate → edit-gates} +1 -1
- package/hooks/hooks.json +4 -44
- package/hooks/power-tools +8 -0
- package/package.json +1 -2
- package/scripts/codex-hook +17 -22
- package/scripts/doctor.rb +688 -186
- package/scripts/end-intent +65 -22
- package/scripts/hook-code-gate +13 -28
- package/scripts/hook-create-gate +10 -57
- package/scripts/hook-edit-gates +58 -0
- package/scripts/hook-links-gate +8 -31
- package/scripts/hook-lock-gate +13 -58
- package/scripts/hook-power-tools +38 -0
- package/scripts/hook-savepoint-pre +8 -11
- package/scripts/lib/bridge.rb +2 -2
- package/scripts/lib/edit_gates.rb +342 -0
- package/scripts/lib/hook_registry.rb +61 -29
- package/scripts/lib/installer_core.rb +100 -18
- package/scripts/lib/outcome_guard.rb +38 -0
- package/scripts/lib/qmd_hook.rb +21 -41
- package/scripts/lib/qmd_sync.rb +0 -15
- package/scripts/lib/revisions_writer.rb +1 -1
- package/scripts/maintenance-run +56 -2
- package/scripts/restore-intent-v1 +16 -11
- package/skills/auto/SKILL.md +15 -4
- package/skills/auto/evals/evals.json +2 -2
- package/skills/conventions/SKILL.md +31 -0
- package/skills/conventions/references/completion-and-done.md +40 -0
- package/skills/conventions/references/gates-and-enforcement.md +23 -0
- package/skills/conventions/references/knowledge-graph.md +47 -0
- package/skills/conventions/references/lifecycle-and-savepoints.md +11 -0
- package/skills/conventions/references/locks-and-worktrees.md +113 -0
- package/skills/conventions/references/maintenance-and-revisions.md +170 -0
- package/skills/conventions/references/roadmaps.md +44 -0
- package/skills/conventions/references/tiers-and-dispatch.md +134 -0
- package/skills/doctor/SKILL.md +59 -19
- package/skills/intent-brainstorming/SKILL.md +4 -0
- package/skills/intent-continuing/SKILL.md +4 -0
- package/skills/intent-creating/SKILL.md +7 -2
- package/skills/intent-ending/SKILL.md +20 -6
- package/skills/intent-executing/SKILL.md +10 -0
- package/skills/intent-linking/SKILL.md +4 -0
- package/skills/intent-locking/SKILL.md +4 -0
- package/skills/intent-planning/SKILL.md +7 -0
- package/skills/intent-savepoint/SKILL.md +4 -0
- package/skills/intent-speccing/SKILL.md +4 -0
- package/skills/intent-starting/SKILL.md +10 -0
- package/skills/project-creating/references/project-scaffolding.md +2 -2
- package/skills/roadmap/SKILL.md +4 -0
- package/skills/roadmap-continuing/SKILL.md +4 -0
- package/skills/skill-creating/SKILL.md +3 -0
- package/skills/skill-creating/references/defaults-first.md +23 -0
- package/skills/skill-creating/references/hooks.md +4 -1
- package/skills/store-curating/SKILL.md +8 -0
- package/skills/store-indexing/SKILL.md +8 -0
- package/skills/tutorial/references/track-2-auto.md +2 -3
- package/templates/agents.md +8 -0
- package/PLASTIC-reference.md +0 -138
- package/hooks/code-gate +0 -27
- package/hooks/create-gate +0 -3
- package/hooks/lock-gate +0 -21
- package/hooks/qmd-search +0 -8
- package/hooks/retrieval-gate +0 -10
- package/hooks/savepoint-pre +0 -10
- package/scripts/hook-qmd-search +0 -44
- package/scripts/hook-retrieval-gate +0 -148
- package/scripts/lib/legacy_bookend_amnesty.rb +0 -35
- package/scripts/lib/retrieval_gate.rb +0 -211
package/PLASTIC.md
CHANGED
|
@@ -4,7 +4,19 @@
|
|
|
4
4
|
> plugin is updated. Do not modify it: your changes will be lost.
|
|
5
5
|
> For project-specific rules, use `AGENTS.md` instead.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Deeper doctrine lives in the `plastic-conventions` skill's chapters
|
|
8
|
+
(`plastic-conventions > references/<chapter>.md`); read the one that matches your task on demand,
|
|
9
|
+
it is not injected at session start.
|
|
10
|
+
|
|
11
|
+
## Two Processes
|
|
12
|
+
|
|
13
|
+
| Process | Scope | Type | Actor |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| **Build → Observe → Repeat** | The system | Continuous loop | Coordinator |
|
|
16
|
+
| **What → Why → How → Exec** | One intent | Finite lifecycle | Agent |
|
|
17
|
+
|
|
18
|
+
B→O→R is the Coordinator's heartbeat. W→W→H→E is what happens inside each intent.
|
|
19
|
+
The connection: an intent's `## Insights` feeds the Coordinator's Observe phase.
|
|
8
20
|
|
|
9
21
|
## What is an Intent
|
|
10
22
|
|
|
@@ -31,14 +43,6 @@ roles. Supporting artifacts that aren't lifecycle deliverables (research reports
|
|
|
31
43
|
reference docs, external API snapshots, screenshots, diagrams) go in `resources/`.
|
|
32
44
|
Name files inside as `{type}--{description}.md` (e.g., `deep-research--gsd-core.md`).
|
|
33
45
|
|
|
34
|
-
`revisions.md` is an optional, append-only structural-maintenance audit trail. It is not a
|
|
35
|
-
lifecycle deliverable and is never scaffolded at intent birth. Its mere existence signals that
|
|
36
|
-
the intent underwent structural (not conceptual) change. Structural maintenance is move-and-record:
|
|
37
|
-
it removes a misplaced section, file, or ref from its artifact and preserves that content in full
|
|
38
|
-
inside `revisions.md` (newest entry at the bottom, one entry per relocated item), so no record is
|
|
39
|
-
lost and the delivered meaning is never altered. Changing what an intent delivered is a new intent,
|
|
40
|
-
not a revision.
|
|
41
|
-
|
|
42
46
|
## Frontmatter
|
|
43
47
|
|
|
44
48
|
Identity and knowledge graph only. Nothing operational.
|
|
@@ -55,41 +59,15 @@ tags: [plastic, architecture]
|
|
|
55
59
|
---
|
|
56
60
|
```
|
|
57
61
|
|
|
58
|
-
- `sources` (formative, must-load, acyclic) and `chain` (forward + relational, lighter,
|
|
59
|
-
may cycle) form the directed knowledge graph. Reciprocity is one-directional: every
|
|
60
|
-
`sources` edge has a reciprocal `chain` entry (I1), but `chain` may carry relational
|
|
61
|
-
entries with no reciprocal `sources` (I2), so the graph is not strictly symmetric.
|
|
62
62
|
- Context contract: load `sources` strongly (they are what the intent was built from);
|
|
63
63
|
traverse `chain` lightly for discovery. See
|
|
64
64
|
docs/concepts/how-plastic-sources-and-chains-intents.md for the full model.
|
|
65
|
-
- `## Links` (I5) is the human-readable projection of the graph. It mirrors the
|
|
66
|
-
frontmatter exactly: every entry is `- [[id--slug|<target's full intent: text>]]`, a
|
|
67
|
-
clickable `id--slug` wikilink target with the target intent's full `intent:` text as the
|
|
68
|
-
label (cross-store targets render `- [[store:id--slug|<target's full intent: text>]]`).
|
|
69
|
-
Ordering is mandatory: all `sources` first (top), then all `chain`, frontmatter order
|
|
70
|
-
preserved within each group. Sources never appear at the end. No source/chain tags, no
|
|
71
|
-
sub-grouping. An intent with empty `sources` and `chain` carries the empty-state comment.
|
|
72
|
-
- `## Links` is a DERIVED view, not a place to author links (Convention over Configuration).
|
|
73
|
-
It equals the projection of `sources` (first) then `chain`. Never hand-write or hand-edit a
|
|
74
|
-
`## Links` line, and never auto-delete one. The edge lives in the frontmatter graph; the
|
|
75
|
-
section is regenerated from it (doctor `graph_links_projection` enforces this identity). To
|
|
76
|
-
add a link, add the frontmatter edge, then reproject.
|
|
77
|
-
- Links are decided by CONTEXT INFLUENCE, not by shared files, shared symbols, or a topic
|
|
78
|
-
similarity score. The question is whether one intent's context actually informed another.
|
|
79
|
-
Three tiers:
|
|
80
|
-
- **sources:** the foundational context that shaped this intent's creation (a split, an idea
|
|
81
|
-
born during development, a merge). Earns an edge.
|
|
82
|
-
- **chain:** the context that materially helps DELIVER this intent. This is a HIGH bar: only
|
|
83
|
-
the genuinely delivery-moving intents, not everything in the same area. Earns an edge,
|
|
84
|
-
reflected in `## Links`.
|
|
85
|
-
- **tags:** a loose theme grouping for search. NOT a link. A shared tag is a door INTO the
|
|
86
|
-
store (filtered discovery), not a pathway BETWEEN two notes.
|
|
87
|
-
Judging influence is an agent's call, made by reading the candidate's Intent and Context. A
|
|
88
|
-
script cannot grade it, so `scripts/link-suggest` only gathers candidates with that evidence,
|
|
89
|
-
records a confirmed edge with a rating and reason, and flags drift.
|
|
90
65
|
- IDs use Luhmann's alternating convention: `1` → `1a` → `1a1` → `1a1a`
|
|
91
66
|
- Multiple branches increment: `1a`, `1b`, `1c`
|
|
92
67
|
|
|
68
|
+
See `plastic-conventions > references/knowledge-graph.md` for the linking doctrine: the tiers
|
|
69
|
+
of influence (sources/chain/tags) and the `## Links` projection rules.
|
|
70
|
+
|
|
93
71
|
## Lifecycle Stages
|
|
94
72
|
|
|
95
73
|
| Stage | Section | Deliverable | Detail |
|
|
@@ -123,40 +101,23 @@ The blessed write path is the `insight-append` helper
|
|
|
123
101
|
install and update, formats the prefix, validates it, and appends at the bottom. Hand-editing
|
|
124
102
|
`## Insights` is an escape hatch; the helper is the default so the format cannot drift.
|
|
125
103
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
intent file still returns its report, so the insight survives.
|
|
130
|
-
For full lifecycle detail, the skills in the Detail column have references/.
|
|
104
|
+
See `plastic-conventions > references/lifecycle-and-savepoints.md` for the subagent
|
|
105
|
+
report-home contract (how an insight reaches the intent when the writer cannot write the file
|
|
106
|
+
itself) and `savepoint.md`'s role.
|
|
131
107
|
|
|
132
108
|
## Tiers (proportional auto sizing)
|
|
133
109
|
|
|
134
110
|
Auto mode sizes every intent S/M/L at Why: S = single mechanism or file cluster (hours);
|
|
135
111
|
M = one subsystem (about a day); L = cross-cutting or novel design.
|
|
136
112
|
|
|
137
|
-
Speed comes from two levers only: artifact content depth and agent topology. The
|
|
138
|
-
same-structure invariant holds: same file set, stage order, gates, and savepoint ledger at
|
|
139
|
-
every tier and in both modes.
|
|
140
|
-
|
|
141
|
-
S/M collapse the topology (one thinker agent writes spec.md then plan.md plus
|
|
142
|
-
checklist.md plus at least one real action file in one context, consolidated into a single
|
|
143
|
-
actions/ACTION_1.md at S/M and one file per task at L; a sonnet executor implements). L
|
|
144
|
-
keeps the full team.
|
|
145
|
-
|
|
146
|
-
Never cut at any tier: the independent reviewer, outcome.md as truth of delivery, the
|
|
147
|
-
delivery lock, worktree isolation, intent creation via skill, INDEX as status truth, the
|
|
148
|
-
QMD reindex at End.
|
|
149
|
-
|
|
150
113
|
Tier is recorded as a `Tier: S|M|L` line at the top of spec.md. It is convention-only,
|
|
151
114
|
read by the orchestrator, not enforced by any gate or by doctor.
|
|
152
115
|
|
|
153
|
-
|
|
116
|
+
See `plastic-conventions > references/tiers-and-dispatch.md` for topology by tier, what never
|
|
117
|
+
gets cut, and guided mode.
|
|
154
118
|
|
|
155
119
|
## Agent Models and Dispatch (intent 116)
|
|
156
120
|
|
|
157
|
-
Every lifecycle stage has exactly one dispatchable background agent, plus the enforcer that
|
|
158
|
-
orchestrates them:
|
|
159
|
-
|
|
160
121
|
| Stage | Agent |
|
|
161
122
|
|---|---|
|
|
162
123
|
| What | `plastic-intent-discovery` |
|
|
@@ -165,152 +126,23 @@ orchestrates them:
|
|
|
165
126
|
| Exec | `plastic-executor` |
|
|
166
127
|
| Done | `plastic-intent-curator` |
|
|
167
128
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
**The advisor: two consultation agents, never injected (intent 185).** Neither is a stage
|
|
172
|
-
role: never in the table above, never dispatched by the auto pipeline, and neither ever
|
|
173
|
-
touches a user's own session. `plastic-advisor` is the real advisor, ships `model: fable`,
|
|
174
|
-
expensive, billed through usage credits. `plastic-faux-advisor` is the imitation advisor, ships
|
|
175
|
-
`model: opus`, an ordinary model carrying the Operating Manual's reasoning discipline inlined
|
|
176
|
-
in its own body (not injected into anything), so it reasons the same disciplined way at a
|
|
177
|
-
fraction of the cost. The `plastic-agent-advisor` skill is the one front door: it teaches when
|
|
178
|
-
consulting is worth the money (from the Advisor Protocol: buy one-way doors, plans, adversarial
|
|
179
|
-
review, deadlocks, ranking; never buy what a tool can answer, code volume, or confirmation of a
|
|
180
|
-
decision already made), routes to the configured agent, and can set the config on request. The
|
|
181
|
-
user or the main session states a TIER (S, M, or L) and an EFFORT line in the brief; shipped
|
|
182
|
-
effort is `xhigh` for `plastic-advisor` and `max` for `plastic-faux-advisor`.
|
|
183
|
-
|
|
184
|
-
Config is harness-scoped, keys matching `InstallerCore::DEFAULT_AGENTS` exactly (`claude`,
|
|
185
|
-
`codex`, never `claude_code`): `advisor.enabled` (false skips installing both agents and the
|
|
186
|
-
skill), `advisor.claude.default` (which agent the skill routes to), `advisor.claude.primary`
|
|
187
|
-
and `.secondary` (the two slots, agent NAMES never model names, so a slot can point at a
|
|
188
|
-
locally registered agent). Each agent's actual model is a plain `agents.models.claude.<name>`
|
|
189
|
-
override, the SAME harness-scoped mechanism every other agent uses, resolved through
|
|
190
|
-
`InstallerCore#agent_model_overrides(harness:)`; there is no separate advisor-model key.
|
|
191
|
-
`agents.models` is harness-scoped from this release (`agents.models.claude.*`,
|
|
192
|
-
`agents.models.codex.*`), with the pre-existing flat form (`agents.models.<name>: value`)
|
|
193
|
-
still honored as the claude harness and nested winning over flat. This closes a real latent
|
|
194
|
-
bug: previously the same override map fed both the Claude frontmatter rewrite and the Codex
|
|
195
|
-
TOML generator, so a literal Claude model id could leak into a Codex config; a model named
|
|
196
|
-
under `claude` is now never emitted to `codex`. Install asks which advisor is the default
|
|
197
|
-
(Claude Code only), with a plain description of each: Faux Fable (recommended, cheaper,
|
|
198
|
-
available on any plan) or Fable 5 (the frontier model, billed through credits). Update asks
|
|
199
|
-
the same question once when the key is unset, then never again. Claude-only for this release:
|
|
200
|
-
the owner has not evaluated the Codex reasoning-model ecosystem long enough to judge it, so
|
|
201
|
-
`generate_codex_agents` skips both agents by name, tracked at intent 186, not a permanent
|
|
202
|
-
exclusion.
|
|
203
|
-
|
|
204
|
-
**Auto-mode entry.** `plastic-auto` is the entry skill for autonomous delivery: it takes over How
|
|
205
|
-
and Exec, spins up the team above, and works the dashboard's dispatchable queue. The dashboard's
|
|
206
|
-
`--data` output splits intents into a `dispatchable_queue` (work an agent can pick up) and
|
|
207
|
-
`human_only` (intents that need a person); auto mode consumes the former.
|
|
208
|
-
|
|
209
|
-
**Model contract.** Every agent in `agents/*.md` pins an explicit Claude Code model alias in
|
|
210
|
-
its own frontmatter: `opus`, `sonnet`, or `haiku`. Never `inherit`, never Fable by default,
|
|
211
|
-
unless an explicit `agents.models.<name>` config override names Fable for that role, in which
|
|
212
|
-
case the override is honored as written. The two advisors, `plastic-advisor` and
|
|
213
|
-
`plastic-faux-advisor`, are not lifecycle stage roles: the never-Fable rule governs stage
|
|
214
|
-
agents only. Neither is ever dispatched by the auto pipeline; they are consultation roles
|
|
215
|
-
summoned deliberately by the user or the main session, and their models are user configuration
|
|
216
|
-
(fable and opus by default on Claude Code). Aliases track "latest
|
|
217
|
-
per tier" so no Plastic release is required to advance a tier. The tier by role:
|
|
218
|
-
`plastic-enforcer`, `plastic-brainstorming`, `plastic-planner` are `opus`;
|
|
219
|
-
`plastic-spec-specialist`, `plastic-executor`, `plastic-intent-curator`,
|
|
220
|
-
`plastic-future-intent-researcher`, `plastic-intent-discovery` are `sonnet`.
|
|
221
|
-
|
|
222
|
-
**Config and installer mechanism.** `agents.models.<basename>` in a project's
|
|
223
|
-
`<dir>/.plastic_store/config.yml` or the global `~/.plastic/config.yml` overrides one agent's
|
|
224
|
-
tier. Precedence is project, then global, then the shipped default, matching every other
|
|
225
|
-
`read-config` key. The installer applies the resolved override to each agent file's `model:`
|
|
226
|
-
line at copy time (install, update, and repair, across every harness target). With no override
|
|
227
|
-
configured, the shipped frontmatter passes through unchanged.
|
|
228
|
-
|
|
229
|
-
**Dispatch-time contract.** Frontmatter is primary, and Claude Code reads it at dispatch, but
|
|
230
|
-
because that read is a harness implementation detail rather than a contract Plastic controls,
|
|
231
|
-
every dispatch site also resolves the target agent's model through the config chain
|
|
232
|
-
(`read-config agents.models.<basename> --project <repo>`) and passes it explicitly at dispatch,
|
|
233
|
-
belt-and-braces on top of the frontmatter pin.
|
|
234
|
-
|
|
235
|
-
**Cross-harness portability.** The dispatch and model-tier contract above is harness-facing. The
|
|
236
|
-
adapter layer that maps Plastic's hooks and model aliases onto each supported agent runtime
|
|
237
|
-
(Claude, Codex, Hermes) is the cross-harness portability layer; see
|
|
238
|
-
docs/reference/harness-adapters.md for the adapter contract.
|
|
239
|
-
|
|
240
|
-
**Spawn preamble (intent 152).** `scripts/spawn-preamble` emits a live-state block purely from
|
|
241
|
-
filesystem state: the active intent, stage, role/cycle-step, the honor instruction, and the
|
|
242
|
-
report contract. When the intent's code worktree is resolvable and exists on disk, it also
|
|
243
|
-
appends the worktree's absolute path plus a verbatim instruction to `cd` there directly, for
|
|
244
|
-
harnesses whose `EnterWorktree` cannot discover a nested repo from a non-repo launch directory.
|
|
245
|
-
Output is byte-identical when no worktree resolves.
|
|
246
|
-
|
|
247
|
-
**Orchestrator advisory.** At auto-mode start, the orchestrator recommends once that the user
|
|
248
|
-
run the main session on the best available thinking model (Fable, Opus, or whatever supersedes
|
|
249
|
-
them). This is advisory only: it changes no behavior and blocks nothing if ignored, and it
|
|
250
|
-
concerns the human's main session, never a dispatched subagent. The two advisors,
|
|
251
|
-
`plastic-advisor` and `plastic-faux-advisor`, are not lifecycle stage roles: the never-Fable
|
|
252
|
-
rule governs stage agents only. Neither is ever dispatched by the auto pipeline; they are
|
|
253
|
-
consultation roles summoned deliberately by the user or the main session, and their models are
|
|
254
|
-
user configuration (fable and opus by default on Claude Code).
|
|
255
|
-
|
|
256
|
-
**`plastic-intent-discovery`.** The What-stage agent. It fires at intent activation, after the
|
|
257
|
-
delivery lock is armed and before Why begins, running under that lock as the owner session (it
|
|
258
|
-
does not acquire the lock itself and is not blocked by it): it reads the intent's
|
|
259
|
-
`chain`/`sources` frontmatter, runs QMD-first discovery over completed predecessor work and
|
|
260
|
-
related parked or future intents, and deposits findings to `resources/discovery--<slug>.md` in
|
|
261
|
-
the intent directory ONLY. It never writes the intent file, `spec.md`, or any other lifecycle
|
|
262
|
-
deliverable; the Why-stage `plastic-brainstorming` agent reads its deposit and enriches
|
|
263
|
-
`## Context`.
|
|
264
|
-
|
|
265
|
-
`savepoint.md`: a deterministic, append-only ledger of cycle-step milestones (one line per
|
|
266
|
-
lifecycle boundary, newest at the bottom), written automatically by the gate hook. It is
|
|
267
|
-
sugar on top of the conventions, not a source of truth: state is always derivable from
|
|
268
|
-
files-on-disk, and the ledger is rebuildable. It exists so a resuming agent reads the cycle's
|
|
269
|
-
succession at a glance (last line = where we are).
|
|
270
|
-
|
|
271
|
-
## Auto-Mode Human Reporting (intent 92)
|
|
272
|
-
|
|
273
|
-
In auto mode the orchestrator briefs the human at every lifecycle stage boundary in a fixed,
|
|
274
|
-
impact-first shape (the EM-to-CTO report contract): State, then Risk, then Call. It leads with
|
|
275
|
-
what changed and why it matters, names one risk, and leaves the decision to the human. Separately,
|
|
276
|
-
the `plastic-humanizer` skill cleans authored prose (specs, outcomes, READMEs, release notes) of
|
|
277
|
-
AI tells and slop; it is for documents, not for every reply.
|
|
129
|
+
See `plastic-conventions > references/tiers-and-dispatch.md` for the advisor, model
|
|
130
|
+
configuration, the dispatch contract, and the spawn preamble.
|
|
278
131
|
|
|
279
132
|
## Operational Skills
|
|
280
133
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
for its three scopes (`--core` for the boot integrity check, `--store` per store on dashboard
|
|
288
|
-
load, and the full no-flag walk after an update).
|
|
289
|
-
- **Lifecycle skills** (`plastic-install`, `plastic-update`, `plastic-uninstall`,
|
|
290
|
-
`plastic-rollback`, intent 55) are thin wrappers over a single pinned
|
|
291
|
-
`npx -y @zalom/plastic@<channel> <verb>` call: initialize or repair an install, advance a
|
|
292
|
-
channel, remove Plastic, and step the local versions ledger.
|
|
293
|
-
- **`plastic-feedback`** (intent 174) turns a described Plastic quirk, bug, or feature idea
|
|
294
|
-
into a redacted local report file and a prefilled GitHub issue URL; only the user can submit
|
|
295
|
-
it. `disable-model-invocation` hides its description from your own context, so if the user
|
|
296
|
-
hits a Plastic quirk, bug, or missing feature, offer to invoke the plastic-feedback skill
|
|
297
|
-
yourself instead of waiting to be asked; the user still sends it, you never do.
|
|
134
|
+
- **`plastic-feedback`** turns a described Plastic quirk, bug, or feature idea into a redacted
|
|
135
|
+
local report file and a prefilled GitHub issue URL; only the user submits it. If the user hits
|
|
136
|
+
a Plastic quirk, bug, or missing feature, offer to invoke the plastic-feedback skill yourself
|
|
137
|
+
instead of waiting to be asked.
|
|
138
|
+
- **`plastic-doctor`** checks installation health across three scopes: core, store, and full.
|
|
139
|
+
See `plastic-doctor/SKILL.md` for the contract.
|
|
298
140
|
|
|
299
141
|
## Releases and Versioning
|
|
300
142
|
|
|
301
|
-
Plastic ships as versioned releases
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
(`package.json`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`), tags
|
|
305
|
-
`v{version}`, runs `gh release create --latest`, and publishes to npm. The npm dist-tag follows
|
|
306
|
-
the version string: a `-alpha` suffix routes to the `alpha` tag, `-beta` to `beta`, and a plain
|
|
307
|
-
version with no suffix routes to `latest`. Release history lives in `CHANGELOG.md` at the repo
|
|
308
|
-
root, one line per cut. The `plastic-releasing` skill runs the whole flow.
|
|
309
|
-
|
|
310
|
-
Deprecations are declared in `deprecations.yml` and shown at SessionStart. While Plastic is
|
|
311
|
-
pre-1.0, a satisfied deprecation may be removed immediately; from `1.0.0` on the steady-state
|
|
312
|
-
grace rule applies (removal at least two minors ahead). See PLASTIC-reference.md for the
|
|
313
|
-
Deprecation Process.
|
|
143
|
+
Plastic ships as versioned releases; a release is a collection of intents. Run
|
|
144
|
+
`plastic-releasing` for the full flow (version bump, tag, GitHub release, npm publish).
|
|
145
|
+
Deprecations are declared in `deprecations.yml` and shown at SessionStart.
|
|
314
146
|
|
|
315
147
|
## Gotchas
|
|
316
148
|
|
|
@@ -364,17 +196,8 @@ Format: `ID--three-to-five-words` (all stores).
|
|
|
364
196
|
- Intent file matches directory: `1a1--slug/1a1--slug.md`
|
|
365
197
|
- Next ID: `"${CLAUDE_PLUGIN_ROOT}/scripts/folgezettel-id" <parent_id> <store_path>`
|
|
366
198
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
- **Branch (`14a`, `14b`):** a sub-task, refinement, or direct continuation of the
|
|
371
|
-
parent. It cannot stand on its own; it only makes sense as part of the parent's work.
|
|
372
|
-
- **Root (`15`, `16`)**: an independent thought, even if inspired by another intent.
|
|
373
|
-
Reserve `sources` for true created-from provenance (intents this was built out of). An
|
|
374
|
-
independent intent merely related to or inspired by another carries NO `sources`; record
|
|
375
|
-
the relation on the PREDECESSOR's `chain` (and mirror it as a
|
|
376
|
-
`[[id--slug|<target's full intent: text>]]` wikilink in `## Links`).
|
|
377
|
-
- **Rule of thumb:** if the intent could exist without its parent, it's a root.
|
|
199
|
+
See `plastic-conventions > references/knowledge-graph.md` for the branch-vs-root semantic
|
|
200
|
+
decision: when to branch versus start a root.
|
|
378
201
|
|
|
379
202
|
## INDEX.md
|
|
380
203
|
|
|
@@ -390,11 +213,9 @@ descriptions. This is a self-check, not a gate.
|
|
|
390
213
|
|
|
391
214
|
## Roadmaps
|
|
392
215
|
|
|
393
|
-
A roadmap is a named, ordered, delivery-side collection of intents
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
`INDEX.md` stays the single writer of intent status; a roadmap entry mirrors it and yields on any
|
|
397
|
-
conflict. See PLASTIC-reference.md for the full Roadmaps format.
|
|
216
|
+
A roadmap is a named, ordered, delivery-side collection of intents. Create, order, close, and
|
|
217
|
+
consume one with `plastic-roadmap`; see `plastic-conventions > references/roadmaps.md` for the
|
|
218
|
+
full format.
|
|
398
219
|
|
|
399
220
|
## Rules for Skills
|
|
400
221
|
|
|
@@ -437,42 +258,16 @@ tables-everywhere rule: simple data stays prose, and tables must not be overused
|
|
|
437
258
|
**Bullets-limit.** Use bullets only when a table genuinely does not fit the content, and
|
|
438
259
|
never more than 3-5 items.
|
|
439
260
|
|
|
440
|
-
**Why tables.**
|
|
441
|
-
|
|
442
|
-
| # | Reason |
|
|
443
|
-
|---|--------|
|
|
444
|
-
| 1 | Parallel structure makes comparison a row-vs-row scan, not a re-read. |
|
|
445
|
-
| 2 | Coverage is provable: N items means N rows, an empty cell exposes a gap while a missing bullet hides it. |
|
|
446
|
-
| 3 | Schema stated once in the headers, no repeated labels, higher density. |
|
|
447
|
-
| 4 | A column reads vertically to show every value at once. |
|
|
448
|
-
| 5 | Cells force terseness where bullets sprawl and nest. |
|
|
449
|
-
| 6 | A ruling or decision column turns the report into the decision worksheet. |
|
|
450
|
-
| 7 | Rows stay machine-readable for downstream tooling. |
|
|
451
|
-
| 8 | Uniform granularity: every item answers the same questions. |
|
|
452
|
-
| 9 | Line-scoped git diffs. |
|
|
453
|
-
|
|
454
261
|
**Exception.** The EM-to-CTO human briefing (`skills/auto/references/human-report-contract.md`)
|
|
455
262
|
keeps its deliberate prose shape (fixed State/Risk/Call, single item, nothing to tabulate) and
|
|
456
263
|
is exempt from this rule.
|
|
457
264
|
|
|
458
|
-
##
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
fires so the next turn's hint runs against a fresh index (never synchronous). QMD, Enola,
|
|
465
|
-
and Serena are recommendations, not obligations: the UserPromptSubmit power-tools hook
|
|
466
|
-
appends one recommendation line per present tool, naming Enola only, not both, when Enola
|
|
467
|
-
and Serena are both present (Enola-first, one code-navigation slot). The legacy trailing `# qmd-ok` token is still
|
|
468
|
-
accepted on Bash commands and simply silences the hint. Scope stays the agent's own tool
|
|
469
|
-
calls; Ruby `File.read` inside a script is invisible to the hook by design.
|
|
470
|
-
|
|
471
|
-
The deterministic entry point is the `scripts/qmd-sync` CLI (verbs: detect, register, reindex,
|
|
472
|
-
status, search), a clean no-op when QMD is absent. Each store indexes into its own
|
|
473
|
-
`plastic-<slug>` collection (`plastic-global` for the global store, `plastic-<slug>` per project).
|
|
474
|
-
Index mutation is lifecycle-only, and the reindex runs LAST in the End tail, after the bridge
|
|
475
|
-
purge. See `docs/internals.md` for depth.
|
|
265
|
+
## QMD Search
|
|
266
|
+
|
|
267
|
+
QMD, Enola, and Serena are recommendations, not obligations. The deterministic entry point
|
|
268
|
+
is `scripts/qmd-sync` (detect, register, reindex, status, search). Intent delivery reindexes
|
|
269
|
+
the store; see `plastic-conventions > references/gates-and-enforcement.md` for gate
|
|
270
|
+
mechanics.
|
|
476
271
|
|
|
477
272
|
## Transition Gates
|
|
478
273
|
|
|
@@ -487,293 +282,38 @@ Hard blocking: hooks exit code 2 on gate failure.
|
|
|
487
282
|
|
|
488
283
|
### The gates by name
|
|
489
284
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
- **gate
|
|
495
|
-
|
|
496
|
-
- **lock-gate**
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
`~/.plastic/.cache/gate-escapes.log`. The code gate (Write and Edit) carries the identical
|
|
504
|
-
audited `# plastic-ok` escape, logged to the same file. The escape does not extend to
|
|
505
|
-
`NotebookEdit` or MCP structural edits: they are still gated, just without an escape hatch.
|
|
506
|
-
- **retrieval-gate** is advisory only (see the Retrieval Gate section): it hints at QMD and never
|
|
507
|
-
blocks a read or search.
|
|
285
|
+
One line each. On Claude the five edit-path gates (savepoint-pre, lock-gate, code-gate,
|
|
286
|
+
links-gate, create-gate) run inside one dispatcher process per Write or Edit, in that fixed
|
|
287
|
+
order with the first deny winning; what each gate checks is unchanged.
|
|
288
|
+
|
|
289
|
+
- **create-gate** validates the proposed intent file at What write-time.
|
|
290
|
+
- **gate-check** enforces lifecycle stage order.
|
|
291
|
+
- **lock-gate** admits only the intent's lock owner or a registered delegate to write into an
|
|
292
|
+
active intent directory.
|
|
293
|
+
- **bash-gate** intercepts a write attempted through a bash or interpreter one-liner, mirrored
|
|
294
|
+
by the code gate for Write and Edit.
|
|
295
|
+
|
|
296
|
+
See `plastic-conventions > references/gates-and-enforcement.md` for the escape and logging
|
|
297
|
+
detail.
|
|
508
298
|
|
|
509
299
|
## Delivery Isolation and the Single-Owner Lock
|
|
510
300
|
|
|
511
|
-
Exactly one session or agent develops an intent's delivery at a time. Ownership is
|
|
512
|
-
session-keyed
|
|
513
|
-
(
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
a delegate, and an artifact claim are distinct evidence: controller ownership authorizes the
|
|
531
|
-
delivery, delegate registration authorizes a child session, and a claim selects one current
|
|
532
|
-
writer for one artifact. Disarm clears the lock; the End tail is ordered: verify, merge and remove
|
|
533
|
-
worktrees, clear the lock, and only then is the bridge purge-eligible. Repair is one
|
|
534
|
-
idempotent function with two entry points: the `plastic-lock` command (`who`, status, fix,
|
|
535
|
-
release, reclaim, delegate) and `/plastic-intent-starting`, so boarding self-heals. `who` is
|
|
536
|
-
read-only and reports the controller, mtime heartbeat, delegates, and claims from durable files.
|
|
537
|
-
This is
|
|
538
|
-
mandatory, not a convention.
|
|
539
|
-
|
|
540
|
-
Solo-mode gate defaults (intent 128): on a confirmed positive solo determination
|
|
541
|
-
(`Bridge.solo_delivery?`, a single owner working alone with no sign of parallel or team
|
|
542
|
-
delivery), the lock and worktree arbitration gates relax from enforced to advisory. The moment
|
|
543
|
-
any parallel or team activity appears they return to strictly enforced. This is a real behavior
|
|
544
|
-
difference, not just a message change: a solo session is not hard-blocked by these gates, a
|
|
545
|
-
shared one still is.
|
|
546
|
-
|
|
547
|
-
The bridge resolves the current session in a fixed precedence: the stdin `session_id` first, then
|
|
548
|
-
the `CLAUDE_CODE_SESSION_ID` environment variable, then a derived key when neither is present. A
|
|
549
|
-
bridge is purge-eligible by terminal state, not by age: it is removed only once its intent is no
|
|
550
|
-
longer active, never on a timer. See `docs/internals.md` for depth.
|
|
551
|
-
|
|
552
|
-
The delivery lock arbitrates at the whole-intent grain: it decides who may work
|
|
553
|
-
an intent at all. Underneath it, a per-artifact claim token (intent 111)
|
|
554
|
-
arbitrates at the file grain: it decides who, among those already holding the
|
|
555
|
-
delivery lock, is the one writer for one lifecycle file right now. A write to
|
|
556
|
-
`spec.md`, `plan.md`, `checklist.md`, or the intent file must hold both the
|
|
557
|
-
delivery lock and that file's claim. Claims live in `.claims/<artifact>.claim`
|
|
558
|
-
inside the intent directory, one small JSON file per artifact, scoped strictly
|
|
559
|
-
per-intent-per-artifact, never session-global. The claim gate is dormant
|
|
560
|
-
(allows) when no claim file exists for an artifact, so ordinary single-owner
|
|
561
|
-
work is unaffected; it engages, and denies, only when a second writer tries to
|
|
562
|
-
take a fresh claim someone else already holds. A stale or corrupt claim fails
|
|
563
|
-
open (the write proceeds, the claim yields) and the condition is surfaced in
|
|
564
|
-
`plastic-lock status`, which lists any live claims alongside the delivery
|
|
565
|
-
lock. See `plastic-lock claim`/`release-claim` and `docs/internals.md` for the
|
|
566
|
-
full mechanism.
|
|
567
|
-
|
|
568
|
-
There is exactly one lock in Plastic: `delivery.lock` (exclusive, one owner plus delegates),
|
|
569
|
-
shipped by intent 108. An earlier two-lock doctrine proposed a second `maintenance.lock`
|
|
570
|
-
(short TTL, structural move-and-record only); intent 112 built it in full and was then
|
|
571
|
-
abandoned before merge on a design pivot, so nothing from it ever shipped (`lock.rb`'s
|
|
572
|
-
`TYPES` seam is the only trace left). Intent 197 rejects the second lock outright rather than
|
|
573
|
-
reviving it: a lock held by a maintenance session could be mistaken by a resuming session
|
|
574
|
-
for an active delivery. Maintenance instead DETECTS `delivery.lock`'s freshness
|
|
575
|
-
(`Lock.fresh?`) and defers when fresh; it never acquires any lock of its own and leaves none
|
|
576
|
-
behind. See "WORK vs MAINTENANCE" below for the full doctrine.
|
|
577
|
-
|
|
578
|
-
Every code-touching intent gets its own git worktree named `{id}--{slug}`, and all code edits
|
|
579
|
-
for that intent happen only inside it. Plastic provisions the worktree deterministically: it
|
|
580
|
-
resolves the project repo from `projects.yml` and runs `git -C <repo> worktree add`, so
|
|
581
|
-
isolation never depends on the current working directory. There is one worktree per project
|
|
582
|
-
intent, the code worktree at `<repo>/.claude/worktrees/{id}--{slug}` (branch
|
|
583
|
-
`plastic/{id}--{slug}`).
|
|
584
|
-
|
|
585
|
-
Plastic does not provision a second worktree for lifecycle-doc writes. Two things cover that
|
|
586
|
-
need instead. First, the harness's own native worktree: Claude Code manages its own code
|
|
587
|
-
worktree at `<repo>/.claude/worktrees/{name}`, and Codex manages its own at
|
|
588
|
-
`$CODEX_HOME/worktrees` (default `~/.codex/worktrees`); both exist on their own, independent of
|
|
589
|
-
anything Plastic provisions. Second, intent 197's branch-from-main plus scoped commit, which
|
|
590
|
-
gives store writes their own write safety without a dedicated worktree. Plastic tried a second,
|
|
591
|
-
dedicated store worktree at `<plastic_home>/.worktrees/{id}--{slug}` first; agents never wrote
|
|
592
|
-
into it, because every delivering agent writes lifecycle docs straight to the main store
|
|
593
|
-
checkout, so intent 178 retired the store worktree in favor of the two mechanisms above.
|
|
594
|
-
|
|
595
|
-
Provisioning fails open for intents that touch no project code (pure research or decision
|
|
596
|
-
intents in the global store, or a non-git repo): those get the lock only, and the worktree
|
|
597
|
-
block stays unprovisioned. The fail-open path is always logged, never silent.
|
|
598
|
-
|
|
599
|
-
Cleanup is part of Done: the End tail merges the branch, then removes the worktree. Never leave
|
|
600
|
-
an orphaned worktree behind, and clear a stale worktree reference with `git worktree prune`.
|
|
601
|
-
|
|
602
|
-
### Intent delivery, station by station
|
|
603
|
-
|
|
604
|
-
How one intent travels from boarding to Done, and what the lock, bridge, and gates do at
|
|
605
|
-
each station.
|
|
606
|
-
|
|
607
|
-
| Station | Delivered artifact | Lock and bridge steps | Pre-stage gate | Post-stage record |
|
|
608
|
-
|---|---|---|---|---|
|
|
609
|
-
| Start (board) | none (a procedure, not a stage) | `plastic-lock fix` self-heals stale, corrupt, or legacy state; arm acquires `delivery.lock` (O_EXCL, session-keyed), provisions the code worktree, writes the bridge cache | lock-gate denies any write into an active intent dir without this intent's lock; every deny names the resolving command | savepoint confirms the boarding station |
|
|
610
|
-
| What (create) | `<id>--<slug>.md`, born complete | no lock yet; no bridge | create-gate validates the proposed intent content (Write, Edit, and MCP edits) | savepoint `What` line; intent listed in INDEX `## Active` |
|
|
611
|
-
| Why | `spec.md` | owner writes refresh the lease (lock file mtime heartbeat) | gate-check requires the intent file with `## Intent` before spec.md; lock-gate admits only the owner or a delegate | savepoint `Why started`, `Why spec.md created` |
|
|
612
|
-
| How | `plan.md`, `actions/ACTION_N.md` (at least one), `checklist.md` | heartbeat on writes; the code gate stays closed until plan.md, checklist.md, and a real action file all exist | gate-check requires spec.md before plan.md, and plan.md plus a real actions/ACTION_N.md before checklist.md | savepoint `How started`, `How plan.md created`, `How checklist.md created`, `Exec started` |
|
|
613
|
-
| Exec | code on the intent branch, checklist checked off | heartbeat; code edits confined to the provisioned worktree; delegates write under the owner's lock; bash, interpreter, and MCP writes gated the same way | code-gate, worktree-gate, bash-gate, lock-gate | checklist boxes; savepoint milestones |
|
|
614
|
-
| End (done) | mandatory `outcome.md` (`disposition: delivered\|abandoned`), INDEX moves to Completed or Abandoned | ordered End tail: verify, merge and remove worktrees, disarm clears `delivery.lock`, then the bridge is purge-eligible, and the QMD reindex runs LAST (after purge) | gate-check blocks outcome.md while checklist items are unchecked | savepoint `Done delivered` (or `abandoned`); takeover audits, if any, remain in savepoint.md |
|
|
615
|
-
| Maintenance (Future, Terminal, or Active-with-a-stale-or-no-lock) | `revisions.md` move-and-record entries | detects (never acquires) `delivery.lock`; defers and reports while the target's lock is FRESH (`Lock.fresh?`); a stale or absent lock is not-active, maintenance proceeds | none enforced by any gate; the maintenance tool or skill itself checks `Lock.fresh?` (see WORK vs MAINTENANCE below) | append-only, rule-tagged `revisions.md` entry written in the same operation as the change, or the change is refused; lands via a fresh branch off store main merged back as one closed op, never `git add -A` |
|
|
616
|
-
|
|
617
|
-
### What "intent done" means (intent 93)
|
|
618
|
-
|
|
619
|
-
Done is one law with three signals, and they must agree. INDEX `## Completed` /
|
|
620
|
-
`## Abandoned` is the single canonical terminal marker: it is the store-wide ledger a fresh
|
|
621
|
-
session reads first, so it wins on any conflict. `outcome.md` is the "deliverable exists"
|
|
622
|
-
signal, and the savepoint `Done delivered|abandoned` line is the audit echo. All three must
|
|
623
|
-
agree; when they disagree, INDEX is authoritative and `doctor` flags the mismatch (the
|
|
624
|
-
`done_signals` check: `outcome.md` real but still under `## Active`, or terminal without a
|
|
625
|
-
real `outcome.md`, or a terminal intent whose savepoint carries no `Done` line).
|
|
626
|
-
|
|
627
|
-
`outcome.md` is mandatory at every terminal transition, delivered and abandoned alike. It
|
|
628
|
-
self-declares its disposition through a `disposition: delivered|abandoned` frontmatter
|
|
629
|
-
header. The delivered path authors it with the result; the abandoned path authors it with
|
|
630
|
-
the abandonment reason and no longer leaves the scaffolded placeholder sentinel in place.
|
|
631
|
-
|
|
632
|
-
The canonical End tail runs in this order, and the QMD reindex is always LAST, after the
|
|
633
|
-
purge: `outcome.md -> INDEX terminal -> savepoint Done -> commit -> disarm (Worktree.release
|
|
634
|
-
-> Lock.release -> purge) -> QMD reindex`. Running the reindex last keeps the index from
|
|
635
|
-
ever referencing a bridge or lock that disarm is about to remove.
|
|
636
|
-
|
|
637
|
-
`scripts/end-intent` performs this order's disarm step (verify the code worktree is clean,
|
|
638
|
-
then merge/remove worktrees, then clear the lock) as its own step 5, mechanically, since
|
|
639
|
-
intent 188: a session no longer needs a separate one-liner for it, and the script's own
|
|
640
|
-
exit code (0) is the single fact a caller needs that the intent is closed AND its delivery
|
|
641
|
-
lock is gone. A pre-flight lock guard runs before anything is written (refuses a live
|
|
642
|
-
foreign session, reclaims a stale one with an audit line), and a dirty code worktree
|
|
643
|
-
refuses before removal rather than force-discarding uncommitted changes.
|
|
644
|
-
|
|
645
|
-
The post-done access window is lock-bounded: `[INDEX terminal -> Lock.release]`. Through it
|
|
646
|
-
the completing session keeps full read and write access to the terminal directory and no
|
|
647
|
-
purge can fire (108's lock-held keep-guard keeps the bridge while `delivery.lock` exists).
|
|
648
|
-
Once the lock is released the window closes: the bridge becomes purge-eligible and the
|
|
649
|
-
directory is frozen. A crash mid-tail is recovered by stale-lock reclaim plus finishing the
|
|
650
|
-
tail; `doctor` surfaces this as a "stalled completion" (terminal in INDEX but the lock is
|
|
651
|
-
still present or stale). Finishing the tail is FINISHING a completion, never a reactivation:
|
|
652
|
-
a done intent is never moved back to `## Active`.
|
|
653
|
-
|
|
654
|
-
### WORK vs MAINTENANCE (intent 197)
|
|
655
|
-
|
|
656
|
-
Plastic separates two different things an earlier doctrine blurred under one word,
|
|
657
|
-
"immutable." WORK is the delivered CONTENT an intent produced: the code and project files a
|
|
658
|
-
delivery changed, the research it recorded, the outcome it wrote. Once the intent is terminal
|
|
659
|
-
(Completed or Abandoned), that content is immutable - the only way to change it is another
|
|
660
|
-
intent that continues or reverts it. Editing a Done intent's own artifacts so it looks like it
|
|
661
|
-
delivered something different, or that parts are missing, is forbidden (the book analogy:
|
|
662
|
-
never rewrite the text on the pages of an old, valuable book).
|
|
663
|
-
|
|
664
|
-
MAINTENANCE is everything else: structure, the sources/chain graph, a section that does not
|
|
665
|
-
belong in the file, formatting, and any store-wide operational change (a new Plastic version
|
|
666
|
-
adding or removing a frontmatter field across every intent). Maintenance is not immutable and
|
|
667
|
-
needs no owner gate to run, on the one condition below (recording is universal). The
|
|
668
|
-
decidable test is CONTENT vs METADATA, not "meaning vs structure": a graph edit is structure
|
|
669
|
-
even when it is also, in a loose sense, about lineage, because it does not change what the
|
|
670
|
-
intent delivered. Precedent: plastic intent 124's own `revisions.md` v1 dropped a dead chain
|
|
671
|
-
edge to a non-existent `124b` (`[rule: broken-chain]`), and v2 added a missing required
|
|
672
|
-
reciprocity edge to `131` (`[rule: misplaced-content]`), both ordinary maintenance, not
|
|
673
|
-
owner-gated exceptions. Allowed maintenance: (a) a frontmatter chain/sources edge that points
|
|
674
|
-
to a non-existent or wrong intent, or a missing required edge; (b) an extra non-convention
|
|
675
|
-
section in the intent file, removed and moved into `revisions.md`; (c) a store-wide
|
|
676
|
-
operational change from a new Plastic version, applied to every intent; (d) any other
|
|
677
|
-
structural or operational tidy. Forbidden: anything that alters what the work delivered.
|
|
678
|
-
|
|
679
|
-
The residual guard on every graph edit: it must move TOWARD ground truth (drop a dangling or
|
|
680
|
-
false edge, add a reciprocity-forced or documented-real one) and must never invent a
|
|
681
|
-
relationship - "might be related" is never a valid `[rule:]` reason. This is already implied
|
|
682
|
-
by the mandatory `[rule: tag]` on every `revisions.md` entry; no additional per-edit owner
|
|
683
|
-
gate is needed for an ordinary graph fix of this kind.
|
|
684
|
-
|
|
685
|
-
Maintenance normally needs no intent and no roadmap at all; it runs through the maintenance
|
|
686
|
-
tools and skills and records itself. The one exception: a batch touching more than about 5
|
|
687
|
-
different intents at once must stay rare, and is always an owner decision - the agent asks
|
|
688
|
-
first and shows the diff before proceeding. This exception governs rare cross-intent sweeps;
|
|
689
|
-
it does not apply to an ordinary single-intent graph repair.
|
|
690
|
-
|
|
691
|
-
Maintenance target-state eligibility, by the intent's own lifecycle state: a Future intent,
|
|
692
|
-
yes; a Terminal (Completed or Abandoned) intent, yes; an Active intent mid-delivery, WAIT. The
|
|
693
|
-
wait is keyed on whether the target currently holds a FRESH `delivery.lock` (`Lock.fresh?`),
|
|
694
|
-
never on INDEX `## Active` membership - `end-intent` releases the lock only after the INDEX
|
|
695
|
-
move and its commit tail finish, so keying on Active membership would miss that tail window
|
|
696
|
-
and let maintenance race a live completion. A STALE lock is not maintenance's problem to
|
|
697
|
-
resolve; it is treated as not-active, and maintenance proceeds rather than waiting
|
|
698
|
-
indefinitely behind a dead session.
|
|
699
|
-
|
|
700
|
-
There is exactly one lock in the system (see the two-lock correction above): `delivery.lock`,
|
|
701
|
-
meaning an active agent is delivering that intent. Maintenance DETECTS this lock and NEVER
|
|
702
|
-
ACQUIRES it, even transiently, because a maintenance-held lock could be mistaken by a resuming
|
|
703
|
-
or continuation session for an active delivery. Maintenance leaves no lock behind: there is
|
|
704
|
-
nothing to clean up afterward, and no ambiguity about who, if anyone, holds the one lock.
|
|
705
|
-
`bridge.rb:1195`'s `lock_gate_decision` already allows any write once an intent is not in
|
|
706
|
-
INDEX `## Active` - there is no enforced freeze gate in the codebase today, and there never
|
|
707
|
-
was one that shipped (see the corrected history below).
|
|
708
|
-
|
|
709
|
-
Stranding and clobbering are avoided by construction, not by a second lock: a maintenance
|
|
710
|
-
action creates a fresh branch from the CURRENT state of store main, applies only its own
|
|
711
|
-
scoped changes, and merges that branch back to main as part of the SAME closed operation.
|
|
712
|
-
Nothing strands on an unmerged branch; two concurrent maintenance runs reconcile as ordinary
|
|
713
|
-
merge conflicts on main, never silent loss. This is lighter than intent 178's full per-session
|
|
714
|
-
delivery worktrees (178 stays about the agent write paths for delivery); maintenance only
|
|
715
|
-
needs branch-from-main plus scoped merge-back (`scripts/lib/maintenance_git.rb`,
|
|
716
|
-
`scripts/maintenance-run`).
|
|
717
|
-
|
|
718
|
-
No commit anywhere, store or project repo, uses `git add -A`; every maintenance and delivery
|
|
719
|
-
commit stages only the paths it actually changed (`scripts/end-intent`'s `store_commit`,
|
|
720
|
-
`scripts/maintenance-run`).
|
|
721
|
-
|
|
722
|
-
The one condition on every maintenance action, with no exception, is that it is recorded.
|
|
723
|
-
Every maintenance action, whether run by a tool or made by hand, must leave an append-only
|
|
724
|
-
`revisions.md` entry on its target intent (`## Revision vN`, a `Why ... [rule: tag]` line, a
|
|
725
|
-
`Prior location`, and the change itself). If the file already exists, a new run appends
|
|
726
|
-
`vN+1`; it never overwrites an earlier entry (precedent: intent 124's `revisions.md` v3
|
|
727
|
-
corrects v2 by appending a correction entry and explicitly leaving v2 in place). This is
|
|
728
|
-
tool-enforced, not prose alone: `scripts/project-links`, `scripts/rebuild-graph`, and
|
|
729
|
-
`scripts/restore-intent-v1` each write this receipt in the SAME write as the structural
|
|
730
|
-
change, or refuse to proceed without one (`scripts/lib/revisions_writer.rb`); the intent
|
|
731
|
-
curator (`agents/plastic-intent-curator.md`) holds itself to the identical rule by hand.
|
|
732
|
-
|
|
733
|
-
Doctor stays a detector: core and full checks, every installed agent, both global and project
|
|
734
|
-
stores. It gains no write path of its own. The "Fix all" prompt
|
|
735
|
-
(`skills/doctor/SKILL.md`) is a ROUTER: for each fixable finding it dispatches to the tool
|
|
736
|
-
that already owns that class of repair (`project-links`, `rebuild-graph`,
|
|
737
|
-
`restore-intent-v1`, or the curator, via `scripts/maintenance-run` where applicable), and
|
|
738
|
-
those tools perform the mutation and write the `revisions.md` receipt - never doctor itself.
|
|
739
|
-
|
|
740
|
-
Corrected history (D18): an earlier version of this section described a terminal-immutability
|
|
741
|
-
gate "intent 112 enforces" and a two-lock model. Intent 112 built that gate in full and was
|
|
742
|
-
then ABANDONED before merge on a design pivot; nothing from it ever shipped. `bridge.rb:1195`
|
|
743
|
-
confirms no such gate runs today: a write to a terminal intent is allowed unconditionally once
|
|
744
|
-
the intent leaves INDEX `## Active`. The deadlock that stopped intents 189, 192, and 195 from
|
|
745
|
-
repairing three live `graph_links_projection` violations was self-imposed discipline (agents
|
|
746
|
-
and the owner both treating undocumented doctrine as a real gate), not a technical one. This
|
|
747
|
-
section is the corrected doctrine; intent 112's own history stays in INDEX as an abandoned,
|
|
748
|
-
superseded design.
|
|
749
|
-
|
|
750
|
-
Restore-to-v1 (the owner rule that a completed intent is immutable: a late ruling goes to a
|
|
751
|
-
new `--parent` branch intent, and the completed intent is restored to v1) is performed ONLY by
|
|
752
|
-
`scripts/restore-intent-v1`. Its prose (the intent narrative, `checklist.md`, `outcome.md`,
|
|
753
|
-
`spec.md`, `plan.md`) is immutable and reverts to v1; its frontmatter graph
|
|
754
|
-
(`sources`/`chain`) is metadata about OTHER intents, not content of this one, and is
|
|
755
|
-
APPEND-ONLY: preserved as the union of the v1 snapshot and the current snapshot, never
|
|
756
|
-
subtracted. It writes its own `revisions.md` receipt in the same run. A hand-run whole-file
|
|
757
|
-
`git checkout`/revert of a completed intent is FORBIDDEN, because it cannot distinguish prose
|
|
758
|
-
from graph metadata and silently destroys backlinks written after v1 (proven on intent 124: a
|
|
759
|
-
legitimately accrued chain edge was destroyed by a hand-run restore and went undetected for a
|
|
760
|
-
week).
|
|
761
|
-
|
|
762
|
-
Fail-safe lock doctrine (the contract intent 111 implements): the lock system never traps a
|
|
763
|
-
session or burns credits. When a gate cannot verify lock integrity it fails open, degrading
|
|
764
|
-
to advisory (warn) rather than hard-blocking. Repair is orchestrator-driven: on a lock-issue
|
|
765
|
-
signal the orchestrator inspects and repairs the lock automatically, and the human
|
|
766
|
-
`plastic-lock` command is a fallback path, not the trigger. Intent 93 states this doctrine;
|
|
767
|
-
intent 111 builds the fail-open behavior, the lock-liveness surface, the lock-issue message,
|
|
768
|
-
and the auto-repair.
|
|
769
|
-
|
|
770
|
-
Scope split. Intent 93 ships doctrine plus the low-risk reconciliation that needs no new
|
|
771
|
-
lock: the canonical done-marker and three-signal reconciliation, the mandatory `outcome.md`
|
|
772
|
-
plus `disposition` header at both terminals, the End tail with the reindex moved last, the
|
|
773
|
-
`done_signals` doctor check (three-signal agreement plus stalled-completion detection), and
|
|
774
|
-
the lock-bounded post-done window with its keep-guard test. Intent 111 owns the lock
|
|
775
|
-
liveness surface, the lock-issue message, orchestrator auto-repair, and the fail-open
|
|
776
|
-
behavior itself. Intent 112 attempted a maintenance lock and an immutability gate; it was
|
|
777
|
-
abandoned before merge and superseded by intent 197's WORK vs MAINTENANCE doctrine
|
|
778
|
-
(detect-only lock, branch-and-merge, tool-enforced `revisions.md`). Intent 4a1b1 owns deep
|
|
779
|
-
agent stuck-detection and is not superseded.
|
|
301
|
+
Exactly one session or agent develops an intent's delivery at a time. Ownership is a
|
|
302
|
+
session-keyed, durable `delivery.lock` (O_EXCL) in the intent directory; liveness is a lease
|
|
303
|
+
(the owner's hooks refresh the file mtime, stale means older than the TTL). The `/tmp` session
|
|
304
|
+
bridge is only a cache: the lock file wins on any disagreement. A per-artifact claim token
|
|
305
|
+
arbitrates the file grain underneath the lock. Every code-touching intent gets its own git
|
|
306
|
+
worktree (`<repo>/.claude/worktrees/{id}--{slug}`, branch `plastic/{id}--{slug}`); code edits
|
|
307
|
+
happen only inside it. On a confirmed solo delivery the lock and worktree gates relax to
|
|
308
|
+
advisory; any parallel or team activity restores strict enforcement.
|
|
309
|
+
|
|
310
|
+
"Intent done" is one law with three signals that must agree: INDEX `## Completed` /
|
|
311
|
+
`## Abandoned`, `outcome.md`, and the savepoint `Done` line; INDEX is authoritative on any
|
|
312
|
+
conflict.
|
|
313
|
+
|
|
314
|
+
Structural maintenance (WORK vs MAINTENANCE) never touches delivered content: it only records
|
|
315
|
+
itself in `revisions.md`, and only proceeds when the target's `delivery.lock` is not fresh.
|
|
316
|
+
|
|
317
|
+
See `plastic-conventions > references/locks-and-worktrees.md`,
|
|
318
|
+
`references/completion-and-done.md`, and `references/maintenance-and-revisions.md` for the full
|
|
319
|
+
doctrine.
|