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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/PLASTIC.md +293 -7
  2. package/README.md +10 -0
  3. package/agents/plastic-brainstorming.md +2 -2
  4. package/agents/plastic-enforcer.md +17 -2
  5. package/agents/plastic-executor.md +3 -3
  6. package/agents/plastic-future-intent-researcher.md +2 -1
  7. package/agents/plastic-intent-curator.md +15 -7
  8. package/agents/plastic-intent-discovery.md +37 -0
  9. package/agents/plastic-planner.md +2 -2
  10. package/agents/plastic-spec-specialist.md +2 -2
  11. package/hooks/code-gate +10 -1
  12. package/hooks/hooks.json +28 -3
  13. package/hooks/lock-gate +21 -0
  14. package/hooks/retrieval-gate +10 -0
  15. package/hooks/savepoint-pre +10 -0
  16. package/hooks/statusline +150 -41
  17. package/package.json +1 -1
  18. package/scripts/agent-report +21 -0
  19. package/scripts/doctor.rb +404 -5
  20. package/scripts/hook-auto-arm +1 -1
  21. package/scripts/hook-bash-gate +22 -4
  22. package/scripts/hook-code-gate +11 -6
  23. package/scripts/hook-create-gate +45 -9
  24. package/scripts/hook-gate-check +24 -23
  25. package/scripts/hook-lock-gate +83 -0
  26. package/scripts/hook-retrieval-gate +148 -0
  27. package/scripts/hook-savepoint-pre +32 -0
  28. package/scripts/hook-session-start +1 -1
  29. package/scripts/insight-append +51 -0
  30. package/scripts/lib/agent_models.rb +43 -0
  31. package/scripts/lib/bridge.rb +625 -47
  32. package/scripts/lib/frontmatter_writer.rb +130 -0
  33. package/scripts/lib/graph_rebuild.rb +328 -0
  34. package/scripts/lib/hook_registry.rb +95 -0
  35. package/scripts/lib/insights.rb +86 -0
  36. package/scripts/lib/installer_core.rb +64 -54
  37. package/scripts/lib/link_suggestions.rb +319 -0
  38. package/scripts/lib/links_projection.rb +160 -0
  39. package/scripts/lib/links_section.rb +207 -0
  40. package/scripts/lib/lock.rb +375 -0
  41. package/scripts/lib/power_tools.rb +75 -0
  42. package/scripts/lib/qmd_hook.rb +38 -25
  43. package/scripts/lib/qmd_sync.rb +36 -0
  44. package/scripts/lib/retrieval_gate.rb +211 -0
  45. package/scripts/lib/worktree.rb +384 -0
  46. package/scripts/link-suggest +213 -0
  47. package/scripts/new-intent +138 -29
  48. package/scripts/plastic-lock +164 -0
  49. package/scripts/project-links +287 -0
  50. package/scripts/qmd-sync +50 -3
  51. package/scripts/read-config +4 -0
  52. package/scripts/rebuild-graph +244 -0
  53. package/scripts/spawn-preamble +11 -3
  54. package/skills/auto/SKILL.md +98 -16
  55. package/skills/auto/evals/evals.json +48 -0
  56. package/skills/auto/references/agent-architecture.md +7 -4
  57. package/skills/auto/references/agent-report-contract.md +36 -1
  58. package/skills/brainstorming/SKILL.md +1 -0
  59. package/skills/brainstorming/evals/evals.json +22 -0
  60. package/skills/continuing/SKILL.md +30 -8
  61. package/skills/continuing/evals/evals.json +9 -0
  62. package/skills/creating-intent/SKILL.md +16 -2
  63. package/skills/creating-intent/evals/evals.json +16 -0
  64. package/skills/creating-intent/references/lifecycle.md +9 -4
  65. package/skills/creating-skills/SKILL.md +65 -0
  66. package/skills/creating-skills/evals/evals.json +108 -0
  67. package/skills/creating-skills/references/agents.md +168 -0
  68. package/skills/creating-skills/references/evals.md +41 -0
  69. package/skills/creating-skills/references/hooks.md +248 -0
  70. package/skills/creating-skills/references/progressive-disclosure.md +176 -0
  71. package/skills/creating-skills/references/scripts.md +166 -0
  72. package/skills/creating-skills/references/skills.md +165 -0
  73. package/skills/creating-skills/scripts/scaffold.rb +313 -0
  74. package/skills/dashboard/SKILL.md +5 -0
  75. package/skills/dashboard/evals/evals.json +22 -0
  76. package/skills/doctor/SKILL.md +4 -1
  77. package/skills/executing-plan/SKILL.md +4 -4
  78. package/skills/humanizer/SKILL.md +39 -0
  79. package/skills/humanizer/evals/evals.json +70 -0
  80. package/skills/humanizer/references/always-on-snippet.md +9 -0
  81. package/skills/humanizer/references/examples.md +48 -0
  82. package/skills/intent-curator/SKILL.md +7 -1
  83. package/skills/intent-curator/evals/evals.json +22 -0
  84. package/skills/intent-discovery/SKILL.md +46 -0
  85. package/skills/intent-starting/SKILL.md +127 -0
  86. package/skills/intent-starting/evals/evals.json +117 -0
  87. package/skills/intent-starting/references/boarding-matrix.md +35 -0
  88. package/skills/linking-intents/SKILL.md +54 -12
  89. package/skills/linking-intents/evals/evals.json +22 -0
  90. package/skills/linking-intents/references/zettelkasten.md +7 -0
  91. package/skills/lock/SKILL.md +41 -0
  92. package/skills/managing-index/SKILL.md +12 -0
  93. package/skills/managing-index/evals/evals.json +22 -0
  94. package/skills/managing-index/references/zettelkasten-linking.md +6 -1
  95. package/skills/releasing/SKILL.md +32 -0
  96. package/skills/research/SKILL.md +8 -0
  97. package/skills/research/evals/evals.json +22 -0
  98. package/templates/config.yml +8 -0
  99. package/templates/outcome.md +3 -0
  100. package/templates/revisions.md +58 -0
  101. package/skills/writing-instructions/SKILL.md +0 -159
  102. package/skills/writing-instructions/references/agentskills-spec.md +0 -135
@@ -0,0 +1,9 @@
1
+ # Always-on chat style (copy-paste)
2
+
3
+ Paste this into your global `CLAUDE.md` (applies to all projects) or a project `AGENTS.md` (one project). It keeps the chat style on at near-zero cost. The full cleanup for documents is the `plastic-humanizer` skill.
4
+
5
+ ```
6
+ Reframe your answers and any questions like you are speaking to a well-educated person who is not a native English speaker. Reduce slang and rarely used words. Cut fancy words from explanations. Technical terms and concepts are fine.
7
+
8
+ Answer like a busy bartender or a senior support engineer who has seen almost every ticket: help fast, give the fix first, earn the tip.
9
+ ```
@@ -0,0 +1,48 @@
1
+ # Examples - before and after
2
+
3
+ Each pair shows a tell or a slop pattern, then the fix.
4
+
5
+ ## 1. Em-dash
6
+ Before: The release is ready - we just need sign-off.
7
+ After: The release is ready. We just need sign-off.
8
+ Why: Use a full stop or comma, not an em-dash.
9
+
10
+ ## 2. "Not X but Y"
11
+ Before: This is not just a config change, it's a rethink of the flow.
12
+ After: This change reworks the flow.
13
+ Why: State the point directly.
14
+
15
+ ## 3. Rule of three
16
+ Before: The tool is fast, robust, and powerful.
17
+ After: The tool is fast.
18
+ Why: Drop the list-for-rhythm; keep the claim you can back.
19
+
20
+ ## 4. Hype / AI word
21
+ Before: We leverage a comprehensive caching layer to unlock performance.
22
+ After: We cache results to make it faster.
23
+ Why: Plain verbs and nouns.
24
+
25
+ ## 5. Filler opener / signposting
26
+ Before: It's worth noting that the cache expires after five minutes.
27
+ After: The cache expires after five minutes.
28
+ Why: Say the thing; skip the runway.
29
+
30
+ ## 6. Hedging pile-up
31
+ Before: This might possibly help reduce cost in some cases, generally.
32
+ After: This reduces cost.
33
+ Why: One clear claim. Add a caveat only if it is real.
34
+
35
+ ## 7. Sycophancy
36
+ Before: Great question! That's a really insightful point. Here's the answer.
37
+ After: Here's the answer.
38
+ Why: Drop the praise; answer.
39
+
40
+ ## 8. Structural fix (the mush)
41
+ Before: There are several important considerations to keep in mind when thinking about how best to approach the broad and multifaceted topic of caching, each of which carries its own trade-offs.
42
+ After: Cache reads, not writes. Reads repeat; writes do not.
43
+ Why: One load-bearing point. Cut the restatement and the abstract throat-clearing.
44
+
45
+ ## 9. Over-bolding
46
+ Before: **Note:** the cache **expires** after **five minutes**, so **always** check it.
47
+ After: The cache expires after five minutes, so check it first.
48
+ Why: Bold nothing here; the sentence carries itself.
@@ -33,9 +33,15 @@ Invoke the `plastic-intent-curator` agent via the Agent tool with `subagent_type
33
33
 
34
34
  The agent handles:
35
35
  - Intent lifecycle management (status transitions, Outcome sections)
36
- - INDEX.md maintenance (Active/Future/Clusters/Completed sections)
36
+ - INDEX.md maintenance (Active/Future/Clusters/Completed/Abandoned sections)
37
37
  - Link discovery between related intents
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 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
+
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`.
46
+
41
47
  After the agent completes, report what changed.
@@ -0,0 +1,22 @@
1
+ {
2
+ "skill_name": "plastic-intent-curator",
3
+ "notes": "Intent 66a. Spec for the QMD-first discovery step in agents/plastic-intent-curator.md (locate a specific intent before grep/find; distinct from the completion-time reindex step). Runner is intent 76; spec only.",
4
+ "evals": [
5
+ {
6
+ "id": 1,
7
+ "scope": "behavior",
8
+ "set": "validation",
9
+ "prompt": "QMD is present. The user says: find and reclassify the intent about orphan detection.",
10
+ "expected_output": "During discovery (How You Work), before scanning the store with grep/find to locate the intent, runs `ruby ~/.plastic/scripts/qmd-sync search \"orphan detection\"` to surface the candidate or related intents, then opens the authoritative intent file for the hit it acts on. This discovery step is distinct from the completion-time reindex step. No-op fallback to the full scan when QMD is absent.",
11
+ "files": [],
12
+ "assertions": [
13
+ {
14
+ "type": "human",
15
+ "check": "qmd-sync search is run before grep/find during discovery; authoritative file opened for the hit; reindex step stays separate",
16
+ "observed": "SKILL.md (or agent file) carries the QMD-first step: run qmd-sync search before grep/Read, then open the authoritative file; no-op fallback when QMD is absent",
17
+ "result": "pass"
18
+ }
19
+ ]
20
+ }
21
+ ]
22
+ }
@@ -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.
@@ -0,0 +1,127 @@
1
+ ---
2
+ name: plastic-intent-starting
3
+ description: >-
4
+ Board a session onto an intent: take the lock FIRST, confirm savepoint state, ask auto or
5
+ guided ONCE, then resume at the latest delivered station and run the cycle to Done. Use on
6
+ `continuing --intent {id}`, when a new intent is registered and the user asks to work it,
7
+ or when the user picks an intent to work. Requires the intent in INDEX `## Active`.
8
+ ---
9
+
10
+ # Intent Starting — board a session onto an intent
11
+
12
+ Announce: "Boarding intent [ID] — [name]."
13
+
14
+ `plastic-intent-starting` is the Start procedure. It boards a session onto one intent: take
15
+ the lock FIRST, confirm the delivery state, ask **auto or guided ONCE**, board at the latest
16
+ delivered station, then run the cycle to Done. The What → Why → How → Exec stations are the
17
+ train track; Start boards the train, the ending procedure (~93) exits it.
18
+
19
+ ## Precondition + trigger
20
+
21
+ Fires when the user picks an intent to work, when an agent is told to continue a SPECIFIC
22
+ intent, or on `continuing --intent {id}` (the `continuing` → `starting` router is 106's job;
23
+ this skill is invokable standalone now).
24
+
25
+ If the intent is **terminal** (Done / Abandoned in INDEX): report only. Take NO lock, run NO
26
+ resume, do NOT reopen it. Summarize the outcome and ask what is next. Stop here.
27
+
28
+ ## Lock FIRST (the spine)
29
+
30
+ The lock is non-negotiable and comes before any mutating work. The ACTION-3 lock-gate
31
+ enforces it: without a held lock, mutating writes to this active intent's dir are denied with
32
+ "run /plastic-intent-starting to lock and begin".
33
+
34
+ 1. **Ensure the intent is in INDEX `## Active`.** If it sits in `## Future`, activate it
35
+ (move it to `## Active`, auto-commit) before arming. Creation precedes activation, so a
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.
45
+ 2. **Self-heal the lock state first.** Run:
46
+ `ruby ~/.plastic/scripts/plastic-lock fix --intent-dir <STORE>/<dir>`
47
+ This is the one repair function (same one /plastic-lock exposes): it removes
48
+ corrupt or legacy lock state and rebuilds the lock and bridge from disk for
49
+ this session. If it reports `held`, another session owns the intent: STOP
50
+ and tell the user who holds it. If it reports `stale`, ask the user before
51
+ running `plastic-lock reclaim` (takeover is audited).
52
+ 3. **Arm the bridge.** Which arm is chosen by the mode answer (below), but the lock itself is
53
+ taken first. Reuse the arm one-liner shape from `plastic-auto`:
54
+ ```bash
55
+ # guided (lock only):
56
+ ruby -r ~/.plastic/scripts/lib/bridge -e \
57
+ 'Bridge.arm_guided(ENV["CLAUDE_CODE_SESSION_ID"], intent_id: "<ID>", intent_dir: "<STORE>/<dir>", store: "<STORE>", name: "<name>")'
58
+ # auto (lock + auto), then hand to plastic-auto:
59
+ ruby -r ~/.plastic/scripts/lib/bridge -e \
60
+ 'Bridge.arm_auto(ENV["CLAUDE_CODE_SESSION_ID"], intent_id: "<ID>", intent_dir: "<STORE>/<dir>", store: "<STORE>", name: "<name>")'
61
+ ```
62
+ Replace `<ID>`, `<STORE>` (`~/.plastic/projects/<slug>/store` or `~/.plastic/store`),
63
+ `<dir>` (the `ID--slug` directory), and `<name>`.
64
+
65
+ **Session id resolution (verbatim from `plastic-auto`).** The first argument is the session
66
+ id the bridge is keyed by: pass the hook stdin `session_id` when you have it, otherwise
67
+ `ENV["CLAUDE_CODE_SESSION_ID"]`, otherwise `nil`. Both arms call `resolve_session`, which
68
+ picks the first non-empty of: the explicit id you pass → `CLAUDE_CODE_SESSION_ID` → a
69
+ deterministic derived key (a hash of the store and intent id). It never returns nil, so the
70
+ lock is taken even when every session env var is empty; arming prints a one-line stderr
71
+ notice when it falls through to the derived key.
72
+
73
+ **What the lock IS.** Ownership is session-keyed and lease-based: arming writes a durable
74
+ `delivery.lock` file in the intent dir naming this session as owner, and the owner's hooks
75
+ refresh the file mtime on tool activity (the lease heartbeat). The /tmp bridge is only a
76
+ cache of that file; on any disagreement the lock file wins, so a wiped /tmp never strands
77
+ the owner. Idempotent re-arm: arming again with the same owner just refreshes the lock; it
78
+ is not an error to re-board an intent this session already owns. A failed arm raises with
79
+ a message naming the resolving `plastic-lock` verb (`status`, `reclaim`, or `fix`): follow
80
+ that message, never delete a lock file by hand.
81
+
82
+ ## Confirm delivery state
83
+
84
+ Read `savepoint.md` and classify from the **last line** alone, then verify ONLY that line's
85
+ artifact is real (sentinel-aware via `Bridge.stage_file_present?`). On drift (the last line
86
+ disagrees with files on disk), rebuild the ledger from disk and note the correction. Do not
87
+ inline the rebuild; the `plastic-savepoint` skill owns it:
88
+ ```bash
89
+ ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.rebuild_savepoint("<intent_dir>")'
90
+ ```
91
+
92
+ ## Report + ask "auto or guided?" ONCE
93
+
94
+ Report: the intent, the station it lands at (the matrix below), what is delivered, the next
95
+ step. Then ask the user **"auto or guided?"** — exactly ONCE, whatever station it lands at.
96
+ Never re-ask at a later station.
97
+
98
+ - **guided** → `arm_guided` (lock only); continue step by step with the user through the
99
+ station's work below.
100
+ - **auto** → `arm_auto` (lock + auto), then hand off to `plastic-auto`. The auto branch's
101
+ only remaining job is the handoff; `plastic-auto` runs the cycle from here.
102
+
103
+ ## Board at the latest delivered station
104
+
105
+ The station is derived from `savepoint.md` last line + real artifacts on disk. See
106
+ `references/boarding-matrix.md` for the full table (last line → latest delivered → boards at →
107
+ continue with) and the per-station notes. Summary of what "continue" means per station:
108
+
109
+ - **What** → do what What requires (106-expanded), then brainstorm → `spec.md`.
110
+ - **Why** → continue brainstorming → `spec.md`.
111
+ - **How** → continue `plan.md` + `actions/` + `checklist.md`.
112
+ - **Exec** → verify what has been delivered, then continue (or restart) the delivery /
113
+ research; tick the checklist.
114
+ - **ready to complete** (`Exec outcome.md created`) → exit at Done.
115
+ - **Done** → report only, ask what is next, never reopen.
116
+
117
+ ## Disarm / release on done
118
+
119
+ When delivery finishes, disarm and release per the `plastic-auto` disarm/release prose (do
120
+ not duplicate it here). The guided branch releases the lock via `disarm_auto`, which is
121
+ mode-agnostic (it sets `auto = false` and calls `Worktree.release`), so it releases a guided
122
+ lock too. When the work ships through a release, the release path merges the branch before the
123
+ worktree is removed; the plain disarm remove is only for the no-release case.
124
+
125
+ ## References
126
+
127
+ - `references/boarding-matrix.md` — the full boarding table and per-station behaviour.
@@ -0,0 +1,117 @@
1
+ {
2
+ "skill_name": "plastic-intent-starting",
3
+ "notes": "Intent 96. Scopes: description triggering (1-5) and behavior (6-10: lock-first spine, ask-mode-once, terminal report-only, mode->arm mapping, boarding matrix). The lock-first and gate behaviors are also proven by Ruby tests (test/bridge_guided_test.rb, test/lock_gate_test.rb, test/lock_gate_hook_test.rb).",
4
+ "evals": [
5
+ {
6
+ "id": 1,
7
+ "scope": "triggering",
8
+ "set": "train",
9
+ "prompt": "start work on intent 96",
10
+ "expected_output": "Activates plastic-intent-starting (board a session onto the named intent).",
11
+ "files": [],
12
+ "assertions": [
13
+ { "type": "code", "check": "router CHOICE == plastic-intent-starting", "result": "expect-pass" }
14
+ ]
15
+ },
16
+ {
17
+ "id": 2,
18
+ "scope": "triggering",
19
+ "set": "train",
20
+ "prompt": "continuing --intent 96",
21
+ "expected_output": "Activates plastic-intent-starting (the --intent form boards a specific intent).",
22
+ "files": [],
23
+ "assertions": [
24
+ { "type": "code", "check": "router CHOICE == plastic-intent-starting", "result": "expect-pass" }
25
+ ]
26
+ },
27
+ {
28
+ "id": 3,
29
+ "scope": "triggering",
30
+ "set": "validation",
31
+ "prompt": "work this intent",
32
+ "expected_output": "Activates plastic-intent-starting when an intent is in context to be worked.",
33
+ "files": [],
34
+ "assertions": [
35
+ { "type": "code", "check": "router CHOICE == plastic-intent-starting", "result": "expect-pass" }
36
+ ]
37
+ },
38
+ {
39
+ "id": 4,
40
+ "scope": "triggering",
41
+ "set": "validation",
42
+ "prompt": "continue",
43
+ "expected_output": "Does NOT activate plastic-intent-starting; bare 'continue' is plastic-continuing (no specific intent named).",
44
+ "files": [],
45
+ "assertions": [
46
+ { "type": "code", "check": "router CHOICE != plastic-intent-starting", "result": "expect-pass" }
47
+ ]
48
+ },
49
+ {
50
+ "id": 5,
51
+ "scope": "triggering",
52
+ "set": "validation",
53
+ "prompt": "create a new intent for the uploader retry policy",
54
+ "expected_output": "Does NOT activate plastic-intent-starting; activates plastic-creating-intent.",
55
+ "files": [],
56
+ "assertions": [
57
+ { "type": "code", "check": "router CHOICE != plastic-intent-starting", "result": "expect-pass" }
58
+ ]
59
+ },
60
+ {
61
+ "id": 6,
62
+ "scope": "behavior",
63
+ "set": "train",
64
+ "prompt": "Active intent X exists at the Why station. Start work on it (guided).",
65
+ "expected_output": "Takes the lock FIRST via Bridge.arm_guided (auto stays false) before any mutating work, confirms savepoint, then boards at Why and continues brainstorming toward spec.md.",
66
+ "files": [],
67
+ "assertions": [
68
+ { "type": "human", "check": "lock acquired before any mutating write; SKILL.md 'Lock FIRST' section precedes station work", "result": "expect-pass" },
69
+ { "type": "code", "check": "Bridge.arm_guided stamps the lock with auto=false (test/bridge_guided_test.rb green); lock-gate denies a no-lock write to the active intent dir (test/lock_gate_hook_test.rb green)", "result": "pass" }
70
+ ]
71
+ },
72
+ {
73
+ "id": 7,
74
+ "scope": "behavior",
75
+ "set": "train",
76
+ "prompt": "Start work on an active intent without saying auto or guided.",
77
+ "expected_output": "Asks 'auto or guided?' exactly once after reporting the station; never re-asks at later stations.",
78
+ "files": [],
79
+ "assertions": [
80
+ { "type": "human", "check": "SKILL.md asks the mode exactly once and states 'never re-asked at a later station'", "result": "expect-pass" }
81
+ ]
82
+ },
83
+ {
84
+ "id": 8,
85
+ "scope": "behavior",
86
+ "set": "train",
87
+ "prompt": "Start work on an intent that is Done in INDEX.",
88
+ "expected_output": "Reports only: no lock, no resume, no reopen. Summarizes the outcome and asks what is next.",
89
+ "files": [],
90
+ "assertions": [
91
+ { "type": "human", "check": "terminal intent -> report only, no arm call", "result": "expect-pass" }
92
+ ]
93
+ },
94
+ {
95
+ "id": 9,
96
+ "scope": "behavior",
97
+ "set": "validation",
98
+ "prompt": "Start work and choose auto.",
99
+ "expected_output": "guided -> Bridge.arm_guided; auto -> Bridge.arm_auto then hand off to plastic-auto (auto branch's only remaining job is the handoff).",
100
+ "files": [],
101
+ "assertions": [
102
+ { "type": "human", "check": "SKILL.md maps guided->arm_guided and auto->arm_auto+handoff to plastic-auto", "result": "expect-pass" }
103
+ ]
104
+ },
105
+ {
106
+ "id": 10,
107
+ "scope": "behavior",
108
+ "set": "validation",
109
+ "prompt": "Start an intent whose savepoint last line is 'How checklist.md created'.",
110
+ "expected_output": "Boards at Exec (verify plan + checklist real), per references/boarding-matrix.md, and continues delivery/ticks the checklist.",
111
+ "files": [],
112
+ "assertions": [
113
+ { "type": "human", "check": "boarding matrix lands 'How checklist.md created' / 'Exec started' at Exec", "result": "expect-pass" }
114
+ ]
115
+ }
116
+ ]
117
+ }
@@ -0,0 +1,35 @@
1
+ # Boarding matrix — which station Start drops you at
2
+
3
+ The station is derived from `savepoint.md`'s last line plus the real artifacts on disk.
4
+ Classify from the last line alone, then verify ONLY that line's artifact is real
5
+ (sentinel-aware). On drift, rebuild the ledger from disk and note it.
6
+
7
+ | savepoint last line | latest delivered | boards at | continue with |
8
+ |---|---|---|---|
9
+ | `What {id}--{slug}.md` (born) | What | **What / Why** | What work (106-expanded), then brainstorm → `spec.md` |
10
+ | `Why started` (spec still sentinel) | What | **Why** | continue brainstorming → `spec.md` |
11
+ | `Why spec.md created` | Why | **How** | `plan.md` + `actions/` + `checklist.md` |
12
+ | `How started` / `How plan.md created` | (How in progress) | **How** | finish `plan.md` → `checklist.md` |
13
+ | `How checklist.md created` / `Exec started` | How | **Exec** | implement, tick the checklist |
14
+ | `Exec outcome.md created` | Exec | **ready to complete** | exit at Done |
15
+ | `Done delivered` / `Done abandoned` | terminal | **report only** | immutable; ask what is next |
16
+
17
+ ## Per-station behaviour (what "continue" means)
18
+
19
+ - **What** → do what What requires (to be expanded in 106), then brainstorm → `spec.md`.
20
+ - **Why** → continue brainstorming; deliver `spec.md`.
21
+ - **How** → continue `plan.md` + `actions/` + `checklist.md`.
22
+ - **Exec** → verify what has been delivered, then continue (or restart) the delivery /
23
+ research. The first unchecked `checklist.md` item is the next step; the newest `## Insights`
24
+ entry supplies human-readable context.
25
+ - **ready to complete** → `outcome.md` is real; run the ending procedure (~93).
26
+ - **Done** → terminal. Report the outcome, ask what is next. Never reopen; INDEX is
27
+ authoritative.
28
+
29
+ ## Notes
30
+
31
+ - The mode (auto / guided) is asked exactly ONCE, whatever station Start lands at. It is never
32
+ re-asked at a later station. The lock is taken FIRST regardless of station (terminal intents
33
+ excepted: they get no lock and no resume).
34
+ - An `## Insights` entry marked `(autonomous)` means the intent was being delivered
35
+ autonomously; in guided mode, surface that and offer to hand back to `plastic-auto`.
@@ -6,34 +6,70 @@ description: Use when creating connections between intents, the user says "link"
6
6
  # Linking Intents
7
7
 
8
8
  ## When to Use
9
- - During intent creation (automatic ask about related intents)
9
+ - During intent creation (automatic: ask about related intents)
10
10
  - User says "link", "connect", "relates to"
11
11
  - Agent discovers a relationship between intents during work
12
12
 
13
- ## Connection Types (Ranked by Strength)
13
+ ## Discovery and ranking are separate
14
14
 
15
- ### 1. Direct Links (Strongest)
16
- Explicit wikilinks in the `## Links` section. Bidirectional — add to both intents.
15
+ Two distinct steps, do not conflate them:
17
16
 
18
- ```markdown
19
- ## Links
20
- - [[1a]] research this plan is based on
21
- ```
17
+ 1. **Discovery** (finding candidate related intents) may use any tool: grep, find, ripgrep,
18
+ or QMD/Serena when present (QMD-first per the project rule). Discovery casts a wide net.
19
+ 2. **Ranking** the candidates is a CONTEXT-INFLUENCE judgement: read each candidate's `## Intent`
20
+ and `## Context` and ask whether that context actually informed this intent. Ranking is NOT a
21
+ structural metric (no shared-file or shared-symbol grading: on intent 90, matching whole files
22
+ flagged 35 intents because ~20 touch `bridge.rb`). It is NOT a similarity score either (QMD
23
+ relevance measures topic proximity, not influence). A script cannot make this call; an agent
24
+ does.
25
+
26
+ ## The three tiers (by context influence)
27
+
28
+ - **sources:** the foundational context that shaped this intent's CREATION (a split, an idea born
29
+ during development, a merge). Earns an edge. Decided by origin, never inferred.
30
+ - **chain:** the context that materially helps DELIVER this intent. HIGH bar: only the genuinely
31
+ delivery-moving intents, not everything in the same area. Earns an edge, reflected in `## Links`.
32
+ Worked example (intent 90): 79 created it so 79 is a source; 80 deferred the exact fix 90 makes,
33
+ so its context directly helps delivery and 80 is chain; 49/66/73 are same-area background, so
34
+ they get a shared tag and no link.
35
+ - **tags:** loose theme grouping for search. NOT a link.
36
+
37
+ **Timing.** The influence judgement happens at What/Why (and during upkeep), guided by this rule.
38
+ It does not wait for code to exist; it is reasoning over the candidate's context, not over a diff.
39
+
40
+ **Record the call.** For every edge an agent adds, store a rating (high / medium / low) plus a
41
+ one-line reason as a dated line under the intent file's `## Insights` section (per 96 D3, link
42
+ rationale lives in the intent file, not a side file). It stays out of frontmatter (graph only) and
43
+ out of the projected `## Links` label, so the audit trail never breaks the projection identity.
44
+
45
+ ## `## Links` is derived (never author it by hand)
22
46
 
23
- ### 2. Sources (Backward)
47
+ `## Links` is a DERIVED view of `sources` then `chain`, not a place to write links. Never
48
+ hand-write a `## Links` line, and never auto-delete one. To add a link, add the frontmatter
49
+ edge (below), then let the projection regenerate the section (`scripts/project-links`).
50
+
51
+ Run `scripts/link-suggest <id>` to gather candidate intents WITH each one's Intent and Context (the
52
+ evidence you judge influence on) and to flag drift (a `## Links` line with no frontmatter edge
53
+ behind it). To record a confirmed edge plus its rating and reason, run it with
54
+ `--record <id> --edge <sources|chain> --rating <high|medium|low> --reason "..." --confirm`. It never
55
+ grades influence itself, never writes an edge without `--confirm`, and never deletes.
56
+
57
+ ## Connection Types (the frontmatter edges)
58
+
59
+ ### 1. Sources (Backward)
24
60
  The `sources` array in frontmatter. The direct ascendant(s) this intent was created from / emerged from the lifecycle of (formation, not topic similarity), backward links to the work it was built out of:
25
61
  ```yaml
26
62
  sources: ["1a", "1a2"]
27
63
  ```
28
64
 
29
- ### 3. Chain (Forward)
65
+ ### 2. Chain (Forward)
30
66
  The `chain` array in frontmatter. What this intent spawned AND related-but-not-spawned successors it leads to, forward links to children, follow-on, and related work:
31
67
  ```yaml
32
68
  chain: ["1b1", "1b2"]
33
69
  ```
34
70
 
35
- ### 4. Tags (Weakest)
36
- Shared tags in frontmatter enable filtered discovery. Use `project-<name>` tags for project membership.
71
+ ### 3. Tags (for discovery, not links)
72
+ Shared tags in frontmatter enable filtered discovery. Use `project-<name>` tags for project membership. A shared tag is a loose theme grouping: it earns NO edge.
37
73
  ```yaml
38
74
  tags: [plastic, project-reddit-kb]
39
75
  ```
@@ -41,6 +77,12 @@ tags: [plastic, project-reddit-kb]
41
77
  ## Workflow
42
78
 
43
79
  ### 1. Identify Intents to Connect
80
+
81
+ QMD-first (when available): before scanning the store with grep/Read, run
82
+ `ruby ~/.plastic/scripts/qmd-sync search "<terms>"` to surface candidate, prior, or related
83
+ intents to propose as sources/chain, then open the authoritative intent file for any hit you act
84
+ on. The command is a no-op when QMD is absent, so fall back to the directory scan below.
85
+
44
86
  Show existing intents by scanning the store's directory for intent files:
45
87
  ```bash
46
88
  for dir in $STORE_ROOT/store/*/; do
@@ -0,0 +1,22 @@
1
+ {
2
+ "skill_name": "plastic-linking-intents",
3
+ "notes": "Intent 66a. Spec for the QMD-first discovery step (surface candidate/related intents to propose as sources/chain before grep/Read). Runner is intent 76; this case is a spec, not executed.",
4
+ "evals": [
5
+ {
6
+ "id": 1,
7
+ "scope": "behavior",
8
+ "set": "validation",
9
+ "prompt": "QMD is present. The user says: link this intent to the related upload-pipeline work.",
10
+ "expected_output": "Before scanning the store directory with grep/Read to identify related intents, runs `ruby ~/.plastic/scripts/qmd-sync search \"upload pipeline\"` to surface candidate or related intents to propose as sources/chain, then opens the authoritative intent file for any hit it links. No-op fallback to the directory scan when QMD is absent.",
11
+ "files": [],
12
+ "assertions": [
13
+ {
14
+ "type": "human",
15
+ "check": "qmd-sync search is run before the directory scan; authoritative file opened for any hit before proposing sources/chain",
16
+ "observed": "SKILL.md (or agent file) carries the QMD-first step: run qmd-sync search before grep/Read, then open the authoritative file; no-op fallback when QMD is absent",
17
+ "result": "pass"
18
+ }
19
+ ]
20
+ }
21
+ ]
22
+ }
@@ -10,6 +10,13 @@ Plastic implements three Zettelkasten structures:
10
10
 
11
11
  INDEX.md is a structure note (hub), not a table of contents.
12
12
 
13
+ `## Links` mirrors the frontmatter graph exactly. Each entry is
14
+ `- [[id--slug|<target's full intent: text>]]` (cross-store: `- [[store:id--slug|...]]`),
15
+ a clickable `id--slug` target with the target's full `intent:` text as the label.
16
+ Ordering is mandatory: all `sources` first (top), then all `chain`, frontmatter order
17
+ preserved within each group. Sources never appear at the end. No source/chain tags, no
18
+ sub-grouping. An intent with empty `sources` and `chain` carries the empty-state comment.
19
+
13
20
  ## Folgezettel IDs
14
21
 
15
22
  IDs encode lineage using Luhmann's alternating convention:
@@ -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,8 +41,20 @@ 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, 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`.
49
+
44
50
  ## Workflow
45
51
 
52
+ QMD-first (when available): when you need to locate a specific intent (to reclassify, flag, or
53
+ cluster it) rather than rebuild every section, before scanning the store with grep/Read run
54
+ `ruby ~/.plastic/scripts/qmd-sync search "<terms>"` to surface candidate or related intents, then
55
+ open the authoritative intent file for any hit you act on. The command is a no-op when QMD is
56
+ absent, so fall back to the directory scan below.
57
+
46
58
  ### Rebuild Sections
47
59
  Scan the active store's `store/` directory for intent files and rebuild each section:
48
60