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

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.
@@ -70,12 +70,12 @@ After all tasks complete, dispatch a final reviewer for the entire implementatio
70
70
  ### Step 4: Update Intent and Complete
71
71
  Capture observations in `## Insights`. When ALL checklist items are checked:
72
72
 
73
- 1. Write `outcome.md` with detailed results in the intent directory, using the `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md` form
73
+ 1. Write `outcome.md` with detailed results in the intent directory, using the `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md` form. Set the frontmatter `disposition: delivered` (the delivered terminal). `outcome.md` is mandatory at every terminal and self-declares its disposition (canonical done-marker in PLASTIC.md).
74
74
  2. Write `## Outcome` summary in the intent file (1-2 sentences)
75
75
  3. Move intent from `## Active` to `## Completed` in INDEX.md (with today's date)
76
76
  4. Update cluster entries to show `_(completed)_`
77
77
  5. Auto-commit: `cd <store-root> && git add . && git commit -m "feat: complete intent <ID> — <name>"`
78
- 6. On completion, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent), running in the background so it never blocks the turn: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`. Completion is the lifecycle event that keeps the search index fresh.
78
+ 6. QMD reindex LAST (canonical End tail). As the final End-tail step, after the terminal move and any disarm, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent), running in the background so it never blocks the turn: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`. Completion is the lifecycle event that keeps the search index fresh, and the reindex runs last so the index never references a bridge or lock that is about to disappear (see PLASTIC.md `## Delivery Isolation and the Single-Owner Lock`).
79
79
 
80
80
  **This is NOT optional.** An intent with all checklist items done but no Outcome is a broken state. Complete the intent immediately — do not leave it for later.
81
81
 
@@ -96,12 +96,12 @@ For each task:
96
96
  ### Step 3: Update Intent and Complete
97
97
  Capture observations in `## Insights`. When ALL checklist items are checked:
98
98
 
99
- 1. Write `outcome.md` with detailed results in the intent directory, using the `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md` form
99
+ 1. Write `outcome.md` with detailed results in the intent directory, using the `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md` form. Set the frontmatter `disposition: delivered` (the delivered terminal). `outcome.md` is mandatory at every terminal and self-declares its disposition (canonical done-marker in PLASTIC.md).
100
100
  2. Write `## Outcome` summary in the intent file (1-2 sentences)
101
101
  3. Move intent from `## Active` to `## Completed` in INDEX.md (with today's date)
102
102
  4. Update cluster entries to show `_(completed)_`
103
103
  5. Auto-commit: `cd <store-root> && git add . && git commit -m "feat: complete intent <ID> — <name>"`
104
- 6. On completion, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent), running in the background so it never blocks the turn: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`. Completion is the lifecycle event that keeps the search index fresh.
104
+ 6. QMD reindex LAST (canonical End tail). As the final End-tail step, after the terminal move and any disarm, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent), running in the background so it never blocks the turn: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`. Completion is the lifecycle event that keeps the search index fresh, and the reindex runs last so the index never references a bridge or lock that is about to disappear (see PLASTIC.md `## Delivery Isolation and the Single-Owner Lock`).
105
105
 
106
106
  **This is NOT optional.** Complete the intent immediately when work is done.
107
107
 
@@ -38,9 +38,10 @@ The agent handles:
38
38
  - Cluster management (create, merge, rename)
39
39
  - Orphan detection
40
40
 
41
- When an intent reaches a terminal state — moved to Completed OR Abandoned — do two things as the closing act of the transfer:
41
+ When an intent reaches a terminal state — moved to Completed OR Abandoned — do these things as the closing act of the transfer, in the canonical End-tail order (see PLASTIC.md `## Delivery Isolation and the Single-Owner Lock`):
42
42
 
43
- 1. Stamp the terminal savepoint bookend (intent 81), so the ledger's last line records the disposition: `ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.append_terminal_savepoint("<intent_dir>", "delivered")'` (use `"abandoned"` for an abandoned intent). Idempotent.
44
- 2. Refresh the QMD index for the affected store (no-op when QMD absent), running in the background so it never blocks: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`.
43
+ 1. Author a real `outcome.md` in the intent directory from `~/.plastic/templates/outcome.md`, with the frontmatter `disposition: delivered` for a completed intent or `disposition: abandoned` for an abandoned one. `outcome.md` is MANDATORY at every terminal, delivered and abandoned alike: on abandon it records the abandonment reason and replaces the scaffolded placeholder sentinel (never leave `outcome.md` a placeholder at a terminal).
44
+ 2. Stamp the terminal savepoint bookend (intent 81), so the ledger's last line records the disposition: `ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.append_terminal_savepoint("<intent_dir>", "delivered")'` (use `"abandoned"` for an abandoned intent). Idempotent.
45
+ 3. Refresh the QMD index for the affected store LAST, after the terminal move and savepoint (no-op when QMD absent), running in the background so it never blocks: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`.
45
46
 
46
47
  After the agent completes, report what changed.
@@ -34,7 +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
- 2. **Arm the bridge.** Which arm is chosen by the mode answer (below), but the lock itself is
37
+ 2. **Self-heal the lock state first.** Run:
38
+ `ruby ~/.plastic/scripts/plastic-lock fix --intent-dir <STORE>/<dir>`
39
+ This is the one repair function (same one /plastic-lock exposes): it removes
40
+ corrupt or legacy lock state and rebuilds the lock and bridge from disk for
41
+ this session. If it reports `held`, another session owns the intent: STOP
42
+ and tell the user who holds it. If it reports `stale`, ask the user before
43
+ running `plastic-lock reclaim` (takeover is audited).
44
+ 3. **Arm the bridge.** Which arm is chosen by the mode answer (below), but the lock itself is
38
45
  taken first. Reuse the arm one-liner shape from `plastic-auto`:
39
46
  ```bash
40
47
  # guided (lock only):
@@ -55,8 +62,14 @@ deterministic derived key (a hash of the store and intent id). It never returns
55
62
  lock is taken even when every session env var is empty; arming prints a one-line stderr
56
63
  notice when it falls through to the derived key.
57
64
 
58
- Idempotent re-arm: arming again with the same owner just refreshes the lock (re-derives and
59
- rewrites the bridge); it is not an error to re-board an intent this session already owns.
65
+ **What the lock IS.** Ownership is session-keyed and lease-based: arming writes a durable
66
+ `delivery.lock` file in the intent dir naming this session as owner, and the owner's hooks
67
+ refresh the file mtime on tool activity (the lease heartbeat). The /tmp bridge is only a
68
+ cache of that file; on any disagreement the lock file wins, so a wiped /tmp never strands
69
+ the owner. Idempotent re-arm: arming again with the same owner just refreshes the lock; it
70
+ is not an error to re-board an intent this session already owns. A failed arm raises with
71
+ a message naming the resolving `plastic-lock` verb (`status`, `reclaim`, or `fix`): follow
72
+ that message, never delete a lock file by hand.
60
73
 
61
74
  ## Confirm delivery state
62
75
 
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: lock
3
+ description: Inspect, repair, release, or reclaim an intent's delivery lock. Use when a lock-gate deny names /plastic-lock, when resuming interrupted work after a crash, reboot, or /tmp wipe, when a lock reads held or stale, or when the user says "fix the lock", "who holds the lock", or "reclaim the lock".
4
+ ---
5
+
6
+ # Plastic Lock
7
+
8
+ Command-only wrapper around `~/.plastic/scripts/plastic-lock`. The durable
9
+ delivery lock is a `delivery.lock` file in the intent directory: ownership is
10
+ session-keyed, liveness is a lease (the owner's hooks refresh the file mtime;
11
+ stale means the heartbeat is older than the TTL). The /tmp bridge is only a
12
+ cache; the lock file wins every disagreement.
13
+
14
+ ## Verbs
15
+
16
+ Run from the project (the intent resolves from this session's bridge), or pass
17
+ `--intent-dir` explicitly:
18
+
19
+ | Verb | What it does | When |
20
+ |---|---|---|
21
+ | `status` | Report the lock file, bridge cache, freshness, agreement | Always safe; run first |
22
+ | `fix` | Idempotent repair: rebuild lock + bridge from disk truth for THIS session. Never touches a fresh foreign lock | Interrupted work, corrupted state, /tmp wiped, legacy pid locks |
23
+ | `release` | Owner clears the lock | Ending or abandoning a boarding |
24
+ | `reclaim` | Explicit takeover of a STALE lock; appends an audit line to savepoint.md | The owner is gone and the lease expired |
25
+ | `delegate` | Owner registers a subagent session under the lock (`--delegate <session-id>`) | Auto-mode orchestration |
26
+
27
+ ```
28
+ ruby ~/.plastic/scripts/plastic-lock status
29
+ ruby ~/.plastic/scripts/plastic-lock fix --intent-dir <store>/<id>--<slug>
30
+ ruby ~/.plastic/scripts/plastic-lock reclaim --intent-dir <store>/<id>--<slug>
31
+ ruby ~/.plastic/scripts/plastic-lock delegate --delegate <subagent-session-id>
32
+ ```
33
+
34
+ ## Rules
35
+
36
+ - `fix` exits non-zero when another session holds a FRESH lock: back off, do
37
+ not retry in a loop. `status` shows the owner.
38
+ - `reclaim` refuses a fresh lock. There is no silent reclaim anywhere; every
39
+ takeover is audited in the intent's savepoint.md.
40
+ - Acquiring a lock for new work is NOT this skill's job: board through
41
+ `/plastic-intent-starting`, which calls the same repair internally.
@@ -41,7 +41,11 @@ Topic-based groupings. Manually curated. Create a new cluster when 3+ intents sh
41
41
  ### Completed
42
42
  All completed intents with dates. Links preserved, never deleted.
43
43
 
44
- When you move an intent INTO Completed or Abandoned, stamp the terminal savepoint bookend as the closing act of the transfer (intent 81), so the ledger's last line records the disposition: `ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.append_terminal_savepoint("<intent_dir>", "delivered")'` (use `"abandoned"` for an abandoned intent). Idempotent.
44
+ When you move an intent INTO Completed or Abandoned, run the closing acts of the transfer in the canonical End-tail order (see PLASTIC.md `## Delivery Isolation and the Single-Owner Lock`):
45
+
46
+ 1. Author a real `outcome.md` in the intent directory from `~/.plastic/templates/outcome.md`, with the frontmatter `disposition: delivered` for a completed intent or `disposition: abandoned` for an abandoned one. `outcome.md` is MANDATORY at every terminal, delivered and abandoned alike: on abandon it records the abandonment reason and replaces the scaffolded placeholder sentinel (never leave `outcome.md` a placeholder at a terminal).
47
+ 2. Stamp the terminal savepoint bookend (intent 81), so the ledger's last line records the disposition: `ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.append_terminal_savepoint("<intent_dir>", "delivered")'` (use `"abandoned"` for an abandoned intent). Idempotent.
48
+ 3. Refresh the QMD index for that store LAST, after the terminal move and savepoint (no-op when QMD is absent), in the background so it never blocks: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`.
45
49
 
46
50
  ## Workflow
47
51
 
@@ -1,3 +1,6 @@
1
+ ---
2
+ disposition: delivered|abandoned
3
+ ---
1
4
  # Outcome: <intent name>
2
5
 
3
6
  ## Summary