@aperant/framework 0.7.0 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +180 -0
- package/agents/apt-planner.md +12 -0
- package/agents/apt-pr-review-fixer.md +13 -9
- package/bin/apt-tools.mjs +7 -0
- package/dist/cli/ci-watch/stop-matrix.d.mts.map +1 -1
- package/dist/cli/ci-watch/stop-matrix.mjs +16 -0
- package/dist/cli/ci-watch/stop-matrix.mjs.map +1 -1
- package/dist/cli/commands/ci-watch.d.mts +11 -0
- package/dist/cli/commands/ci-watch.d.mts.map +1 -1
- package/dist/cli/commands/ci-watch.mjs +108 -2
- package/dist/cli/commands/ci-watch.mjs.map +1 -1
- package/dist/cli/commands/features-audit.d.mts +24 -0
- package/dist/cli/commands/features-audit.d.mts.map +1 -1
- package/dist/cli/commands/features-audit.mjs +159 -5
- package/dist/cli/commands/features-audit.mjs.map +1 -1
- package/dist/cli/commands/health-check.d.mts +16 -0
- package/dist/cli/commands/health-check.d.mts.map +1 -1
- package/dist/cli/commands/health-check.mjs +118 -2
- package/dist/cli/commands/health-check.mjs.map +1 -1
- package/dist/cli/commands/init.d.mts +9 -0
- package/dist/cli/commands/init.d.mts.map +1 -1
- package/dist/cli/commands/init.mjs +49 -4
- package/dist/cli/commands/init.mjs.map +1 -1
- package/dist/cli/commands/modes.d.mts.map +1 -1
- package/dist/cli/commands/modes.mjs +11 -0
- package/dist/cli/commands/modes.mjs.map +1 -1
- package/dist/cli/commands/pr-review-audit-fixer.d.mts +13 -0
- package/dist/cli/commands/pr-review-audit-fixer.d.mts.map +1 -1
- package/dist/cli/commands/pr-review-audit-fixer.mjs +18 -5
- package/dist/cli/commands/pr-review-audit-fixer.mjs.map +1 -1
- package/dist/cli/commands/task.d.mts.map +1 -1
- package/dist/cli/commands/task.mjs +84 -5
- package/dist/cli/commands/task.mjs.map +1 -1
- package/dist/cli/commands/validate-evidence.d.mts +24 -2
- package/dist/cli/commands/validate-evidence.d.mts.map +1 -1
- package/dist/cli/commands/validate-evidence.mjs +154 -17
- package/dist/cli/commands/validate-evidence.mjs.map +1 -1
- package/dist/cli/commands/vitest-doctor.d.mts +2 -0
- package/dist/cli/commands/vitest-doctor.d.mts.map +1 -0
- package/dist/cli/commands/vitest-doctor.mjs +168 -0
- package/dist/cli/commands/vitest-doctor.mjs.map +1 -0
- package/dist/cli/config/gitignore-drift.d.mts +23 -1
- package/dist/cli/config/gitignore-drift.d.mts.map +1 -1
- package/dist/cli/config/gitignore-drift.mjs +81 -3
- package/dist/cli/config/gitignore-drift.mjs.map +1 -1
- package/dist/cli/config/load.d.mts +56 -2
- package/dist/cli/config/load.d.mts.map +1 -1
- package/dist/cli/config/load.mjs +192 -2
- package/dist/cli/config/load.mjs.map +1 -1
- package/dist/cli/consistency/parse-review.mjs +7 -0
- package/dist/cli/consistency/parse-review.mjs.map +1 -1
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +24 -2
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/gate/gates/gitignore-in-sync.d.mts +1 -1
- package/dist/cli/gate/gates/gitignore-in-sync.d.mts.map +1 -1
- package/dist/cli/gate/gates/gitignore-in-sync.mjs +5 -2
- package/dist/cli/gate/gates/gitignore-in-sync.mjs.map +1 -1
- package/dist/cli/gate/gates/review-clean.d.mts +5 -1
- package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
- package/dist/cli/gate/gates/review-clean.mjs +23 -18
- package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
- package/dist/cli/gate/gates/verify-approved.d.mts +49 -1
- package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
- package/dist/cli/gate/gates/verify-approved.mjs +93 -14
- package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
- package/dist/cli/help.d.mts.map +1 -1
- package/dist/cli/help.mjs +7 -2
- package/dist/cli/help.mjs.map +1 -1
- package/dist/cli/install/runtime-detect.d.mts +13 -0
- package/dist/cli/install/runtime-detect.d.mts.map +1 -1
- package/dist/cli/install/runtime-detect.mjs +9 -0
- package/dist/cli/install/runtime-detect.mjs.map +1 -1
- package/dist/cli/task/index-md.d.mts.map +1 -1
- package/dist/cli/task/index-md.mjs +14 -2
- package/dist/cli/task/index-md.mjs.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +11 -2
- package/dist/plugin/agents/apt-improver.md +99 -0
- package/dist/plugin/agents/apt-planner.md +127 -10
- package/dist/plugin/agents/apt-pr-review-fixer.md +13 -9
- package/dist/plugin/skills/apt/SKILL.md +1 -0
- package/dist/plugin/skills/apt-close-task/SKILL.md +63 -1
- package/dist/plugin/skills/apt-debug/SKILL.md +39 -6
- package/dist/plugin/skills/apt-debug/appendices/diagnose-discipline.md +119 -0
- package/dist/plugin/skills/apt-diagram/SKILL.md +378 -0
- package/dist/plugin/skills/apt-diagram/appendices/design-discipline.md +97 -0
- package/dist/plugin/skills/apt-discuss/SKILL.md +72 -5
- package/dist/plugin/skills/apt-discuss/appendices/grill-discipline.md +104 -0
- package/dist/plugin/skills/apt-discuss/appendices/zoom-out-helper.md +79 -0
- package/dist/plugin/skills/apt-execute/SKILL.md +57 -5
- package/dist/plugin/skills/apt-execute/appendices/tdd-mode.md +107 -0
- package/dist/plugin/skills/apt-improve/DEEPENING.md +84 -0
- package/dist/plugin/skills/apt-improve/INTERFACE-DESIGN.md +97 -0
- package/dist/plugin/skills/apt-improve/LANGUAGE.md +104 -0
- package/dist/plugin/skills/apt-improve/SKILL.md +141 -0
- package/dist/plugin/skills/apt-plan/SKILL.md +171 -4
- package/dist/plugin/skills/apt-plan/adapters/conductor.md +98 -0
- package/dist/plugin/skills/apt-pr-review/SKILL.md +57 -18
- package/dist/plugin/skills/apt-prototype/LOGIC.md +109 -0
- package/dist/plugin/skills/apt-prototype/SKILL.md +143 -0
- package/dist/plugin/skills/apt-prototype/UI.md +90 -0
- package/dist/plugin/skills/apt-quick/SKILL.md +49 -8
- package/dist/plugin/skills/apt-release-notes/SKILL.md +193 -0
- package/dist/plugin/skills/apt-release-notes/appendices/persona-voice.md +59 -0
- package/dist/plugin/skills/apt-review/SKILL.md +2 -0
- package/dist/plugin/skills/apt-run/SKILL.md +32 -4
- package/dist/plugin/skills/apt-setup/SKILL.md +308 -6
- package/dist/plugin/skills/apt-ship/SKILL.md +122 -1
- package/dist/plugin/skills/apt-spar/SKILL.md +315 -0
- package/dist/plugin/skills/apt-triage/AGENT-BRIEF.md +84 -0
- package/dist/plugin/skills/apt-triage/OUT-OF-SCOPE.md +75 -0
- package/dist/plugin/skills/apt-triage/SKILL.md +169 -0
- package/dist/plugin/skills/apt-update/SKILL.md +77 -10
- package/dist/plugin/skills/apt-verify/SKILL.md +3 -0
- package/dist/plugin/skills/apt-verify-proof/SKILL.md +10 -5
- package/dist/plugin/skills/apt-watch-ci/SKILL.md +166 -0
- package/dist/plugin/skills/apt-zoom-out/SKILL.md +130 -0
- package/package.json +133 -133
- package/prompts/conductor-framework-context.md +63 -0
- package/prompts/conductor-system.md +11 -0
- package/skills/apt-close-task/SKILL.md +1 -0
- package/skills/apt-discuss/SKILL.md +47 -5
- package/skills/apt-execute/SKILL.md +9 -0
- package/skills/apt-plan/SKILL.md +12 -0
- package/skills/apt-pr-review/SKILL.md +11 -2
- package/skills/apt-quick/SKILL.md +19 -8
- package/skills/apt-researcher.md +1 -0
- package/skills/apt-setup/SKILL.md +33 -2
- package/skills/apt-verify-proof/SKILL.md +7 -5
- package/src/cli/ci-watch/stop-matrix.mjs +17 -0
- package/src/cli/commands/ci-watch.mjs +113 -2
- package/src/cli/commands/features-audit.mjs +164 -5
- package/src/cli/commands/health-check.mjs +115 -2
- package/src/cli/commands/init.mjs +52 -4
- package/src/cli/commands/modes.mjs +11 -0
- package/src/cli/commands/pr-review-audit-fixer.mjs +18 -5
- package/src/cli/commands/task.mjs +83 -5
- package/src/cli/commands/validate-evidence.mjs +158 -17
- package/src/cli/commands/vitest-doctor.mjs +173 -0
- package/src/cli/config/gitignore-drift.mjs +74 -3
- package/src/cli/config/load.mjs +188 -2
- package/src/cli/consistency/parse-review.mjs +6 -0
- package/src/cli/dispatch.mjs +23 -2
- package/src/cli/gate/gates/gitignore-in-sync.mjs +5 -2
- package/src/cli/gate/gates/review-clean.mjs +24 -19
- package/src/cli/gate/gates/verify-approved.mjs +97 -14
- package/src/cli/help.mjs +7 -2
- package/src/cli/install/runtime-detect.mjs +9 -0
- package/src/cli/task/index-md.mjs +15 -2
- package/templates/config.json +2 -7
- package/workflows/verify-proof.md +8 -3
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: apt:diagram
|
|
3
|
+
description: "Generate .excalidraw or MCP-canvas diagrams"
|
|
4
|
+
apt-skill-version: {{APT_VERSION}}
|
|
5
|
+
stage: design
|
|
6
|
+
intent: design
|
|
7
|
+
when_to_use: "The user wants to generate a diagram from a prompt — system architecture sketch, flowchart, data-flow picture. Produces either a local .excalidraw file (default) or a live Excalidraw MCP canvas."
|
|
8
|
+
user_invocable: true
|
|
9
|
+
internal: false
|
|
10
|
+
spawns_agent: false
|
|
11
|
+
agent_name: null
|
|
12
|
+
task_context: self-managed
|
|
13
|
+
# default_track omitted — self-managed skill, router does not consult this field
|
|
14
|
+
default_execution_mode: auto
|
|
15
|
+
execution_modes:
|
|
16
|
+
- auto
|
|
17
|
+
- step
|
|
18
|
+
allowed-tools: "Read, Write, Edit, Bash, Grep, Glob"
|
|
19
|
+
argument-hint: "apt:diagram [prompt] [--mode=file|--mode=mcp] [--depth=default|comprehensive]"
|
|
20
|
+
gates: []
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<objective>
|
|
24
|
+
Turn a natural-language prompt into a diagram. The skill splits the
|
|
25
|
+
work into two phases: (1) the agent itself produces a `DiagramScene`
|
|
26
|
+
JSON (a minimal graph of labelled `nodes` and directed `edges`) from
|
|
27
|
+
the user prompt, and (2) a deterministic renderer turns that scene
|
|
28
|
+
into Excalidraw geometry — either a local `.excalidraw` file under
|
|
29
|
+
`docs/diagrams/<slug>.excalidraw` or a live canvas via the Excalidraw
|
|
30
|
+
MCP server.
|
|
31
|
+
|
|
32
|
+
There are exactly two render modes — `file` (offline, default) and
|
|
33
|
+
`mcp` (live canvas). There is no `auto` mode and no automatic
|
|
34
|
+
degradation between them: if the chosen renderer fails, the skill
|
|
35
|
+
hard-errors with a message telling the user to re-run with the other
|
|
36
|
+
mode. This is intentional in v1 — diagnostics over magic.
|
|
37
|
+
</objective>
|
|
38
|
+
|
|
39
|
+
<process>
|
|
40
|
+
|
|
41
|
+
## 1. Parse arguments
|
|
42
|
+
|
|
43
|
+
`$ARGUMENTS` may contain free-text prompt material plus at most one
|
|
44
|
+
`--mode=<value>` flag and at most one `--depth=<value>` flag. Extract the
|
|
45
|
+
prompt by removing any flags (if present) and trimming. Extract each
|
|
46
|
+
flag value if any.
|
|
47
|
+
|
|
48
|
+
- If a `--mode=...` flag is present and its value is anything other
|
|
49
|
+
than `"file"` or `"mcp"`, emit a hard error of the exact shape:
|
|
50
|
+
`Unknown --mode value: <value>. Expected file or mcp.` and exit.
|
|
51
|
+
- Empty prompt → emit `apt:diagram requires a prompt describing what to draw.` and exit.
|
|
52
|
+
- If a `--depth=...` flag is present and its value is anything other
|
|
53
|
+
than `"default"` or `"comprehensive"`, emit a hard error of the exact
|
|
54
|
+
shape: `Unknown --depth value: <value>. Expected default or comprehensive.`
|
|
55
|
+
and exit. The default value when absent is `default`. When the value
|
|
56
|
+
is `comprehensive`, before generating the scene in Step 3 load
|
|
57
|
+
`appendices/design-discipline.md` into reasoning context (mirrors
|
|
58
|
+
apt:discuss's `appendices/grill-discipline.md` load pattern). The flag
|
|
59
|
+
is per-call only — it is NOT persisted to `.aperant/config.json`.
|
|
60
|
+
|
|
61
|
+
## 2. Resolve mode
|
|
62
|
+
|
|
63
|
+
Resolution order: --mode flag > .aperant/config.json#diagram.mode > "file" default
|
|
64
|
+
|
|
65
|
+
Read `.aperant/config.json` and look for `diagram.mode`. The lookup
|
|
66
|
+
runs through the same in-skill Node one-liner pattern apt:quick uses
|
|
67
|
+
for `preferences.quick_task_post_verify` (see apt-quick/SKILL.md
|
|
68
|
+
Step 8):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
node -e '
|
|
72
|
+
const fs = require("fs");
|
|
73
|
+
const path = require("path");
|
|
74
|
+
const cfgPath = path.join(process.cwd(), ".aperant/config.json");
|
|
75
|
+
let mode = "file"; // default
|
|
76
|
+
try {
|
|
77
|
+
const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
78
|
+
if (cfg && cfg.diagram && (cfg.diagram.mode === "file" || cfg.diagram.mode === "mcp")) {
|
|
79
|
+
mode = cfg.diagram.mode;
|
|
80
|
+
}
|
|
81
|
+
} catch {}
|
|
82
|
+
// The flag override (if present) is layered on top in the agent step,
|
|
83
|
+
// not in this snippet — this only reads the persisted default.
|
|
84
|
+
process.stdout.write(mode);
|
|
85
|
+
'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If the user passed `--mode=file` or `--mode=mcp`, that wins
|
|
89
|
+
unconditionally over whatever the file says — the flag is a one-call
|
|
90
|
+
override and is not persisted to config.
|
|
91
|
+
|
|
92
|
+
## 3. Generate a `DiagramScene`
|
|
93
|
+
|
|
94
|
+
The agent itself produces a JSON object of shape:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"nodes": [{ "id": "<stable-id>", "label": "<short-label>", "x": <number>, "y": <number> }, ...],
|
|
99
|
+
"edges": [{ "from": "<id>", "to": "<id>", "label": "<optional>" }, ...]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Constraints the agent MUST honour while producing the scene:
|
|
104
|
+
|
|
105
|
+
- **Stable string ids.** Every node has a short, stable `id` (lowercase
|
|
106
|
+
kebab works well — `auth-service`, `db`, `cdn`). Edges reference
|
|
107
|
+
these ids in `from`/`to`. NEVER use positional ids like `n0`/`n1`.
|
|
108
|
+
- **Endpoint validity.** Every `edge.from` and `edge.to` MUST appear in
|
|
109
|
+
the `nodes` array. The renderer hard-errors on a missing endpoint
|
|
110
|
+
with `DiagramScene edge references unknown node id: <id>` — that is
|
|
111
|
+
a failure of scene generation, not a renderer bug, so the agent
|
|
112
|
+
MUST validate this before dispatching to the renderer.
|
|
113
|
+
- **Coordinates.** Use a rough grid to avoid overlap — e.g. 200px
|
|
114
|
+
horizontal spacing between sibling nodes, 150px vertical between
|
|
115
|
+
layers. Nodes default to ~180x70 px in the renderer; keep ≥200px
|
|
116
|
+
centre-to-centre.
|
|
117
|
+
- **Scope.** v1 has no groups, containers, or layout constraints.
|
|
118
|
+
Don't invent extra fields — the renderer ignores them at best,
|
|
119
|
+
rejects the scene at worst.
|
|
120
|
+
|
|
121
|
+
## 4. File mode — dispatch to `renderToFile`
|
|
122
|
+
|
|
123
|
+
When the resolved mode is `file`:
|
|
124
|
+
|
|
125
|
+
1. Derive a slug from the prompt by calling `deriveSlug(prompt)` from
|
|
126
|
+
`@aperant/core/workflows/diagram`. The slug becomes the filename
|
|
127
|
+
under `docs/diagrams/<slug>.excalidraw`.
|
|
128
|
+
2. Invoke `renderToFile({ scene, slug, projectDir })` via a small Node
|
|
129
|
+
invocation — same precedent as apt:design Step 3 ("invoke the
|
|
130
|
+
library entry point via a small Node invocation"). The agent
|
|
131
|
+
serialises the scene into the command via a temp file or
|
|
132
|
+
`JSON.stringify`-ed env var:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
node -e '
|
|
136
|
+
import("@aperant/core/workflows/diagram").then(async (m) => {
|
|
137
|
+
const scene = JSON.parse(process.env.APT_DIAGRAM_SCENE);
|
|
138
|
+
const slug = m.deriveSlug(process.env.APT_DIAGRAM_PROMPT || "diagram");
|
|
139
|
+
const result = await m.renderToFile({
|
|
140
|
+
scene,
|
|
141
|
+
slug,
|
|
142
|
+
projectDir: process.cwd(),
|
|
143
|
+
});
|
|
144
|
+
process.stdout.write(result.absolutePath);
|
|
145
|
+
}).catch((err) => {
|
|
146
|
+
process.stderr.write("renderToFile failed: " + err.message + "\n");
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|
|
149
|
+
'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Pass the scene as `APT_DIAGRAM_SCENE` env (JSON-encoded) and the raw
|
|
153
|
+
prompt as `APT_DIAGRAM_PROMPT`. The wrapper prints the absolute path
|
|
154
|
+
to stdout on success.
|
|
155
|
+
|
|
156
|
+
3. On success, print to the user:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
Diagram written to <absolutePath>. Open it in Excalidraw (drag-drop the file into excalidraw.com or open via Aperant's Excalidraw integration).
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Relative path under the project: `docs/diagrams/<slug>.excalidraw`.
|
|
163
|
+
|
|
164
|
+
4. On failure (any `Error` thrown from `renderToFile`, including the
|
|
165
|
+
`unknown node id` validation case), surface the message to the user
|
|
166
|
+
verbatim with no retry and no fallback. Keep the generated scene
|
|
167
|
+
JSON visible in the chat so the user can inspect what was
|
|
168
|
+
produced — that is the diagnostic.
|
|
169
|
+
|
|
170
|
+
## 5. MCP mode — consent gate + renderer dispatch
|
|
171
|
+
|
|
172
|
+
When the resolved mode is `mcp`, the skill runs three sub-steps in
|
|
173
|
+
order: (a) consent gate, (b) renderer dispatch, (c) failure handling.
|
|
174
|
+
|
|
175
|
+
### 5a. Consent gate
|
|
176
|
+
|
|
177
|
+
Per FRAMEWORK-AUDIT-001, `mcpConsent` is per-user state and lives in
|
|
178
|
+
`.aperant/config.local.json` (gitignored), NOT the shared
|
|
179
|
+
`.aperant/config.json`. The `diagram.mode` field stays in `config.json` as
|
|
180
|
+
team policy. Read the MERGED config (config.json deep-merged with
|
|
181
|
+
config.local.json — local wins on collision) and look at
|
|
182
|
+
`diagram.mcpConsent` via the same in-skill `node -e` pattern Step 2 uses:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
node -e '
|
|
186
|
+
const fs = require("fs");
|
|
187
|
+
const path = require("path");
|
|
188
|
+
const aperantDir = path.join(process.cwd(), ".aperant");
|
|
189
|
+
function readJson(p) {
|
|
190
|
+
try { return JSON.parse(fs.readFileSync(p, "utf-8")); } catch { return null; }
|
|
191
|
+
}
|
|
192
|
+
const shared = readJson(path.join(aperantDir, "config.json")) || {};
|
|
193
|
+
const local = readJson(path.join(aperantDir, "config.local.json")) || {};
|
|
194
|
+
// Deep-merge — local wins on collision; matches loadMergedProjectConfig semantics.
|
|
195
|
+
function merge(a, b) {
|
|
196
|
+
if (a === null || typeof a !== "object" || Array.isArray(a)) return b;
|
|
197
|
+
if (b === null || typeof b !== "object" || Array.isArray(b)) return b;
|
|
198
|
+
const out = { ...a };
|
|
199
|
+
for (const k of Object.keys(b)) out[k] = (k in a) ? merge(a[k], b[k]) : b[k];
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
const cfg = merge(shared, local);
|
|
203
|
+
const consent = cfg && cfg.diagram ? cfg.diagram.mcpConsent : null;
|
|
204
|
+
process.stdout.write(JSON.stringify(consent || null));
|
|
205
|
+
'
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The shape is:
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{ "host": "<host string>", "decision": "granted" | "denied", "grantedAt": "<ISO 8601 timestamp>" }
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
or `null` if no decision has ever been recorded.
|
|
215
|
+
|
|
216
|
+
Determine the "configured MCP host" from the connected Excalidraw MCP
|
|
217
|
+
server. The skill does NOT manage MCP server installation — it reads
|
|
218
|
+
the host name from whatever `mcp__excalidraw__*` namespace is already
|
|
219
|
+
present on the agent's tool surface. (For most setups the host is
|
|
220
|
+
something like `mcp.excalidraw.com` or a self-hosted URL — use
|
|
221
|
+
whatever the surface reports.)
|
|
222
|
+
|
|
223
|
+
Decide whether to prompt:
|
|
224
|
+
|
|
225
|
+
- If `diagram.mcpConsent` is `null` → prompt.
|
|
226
|
+
- If `diagram.mcpConsent.host !== <configured MCP host>` → prompt.
|
|
227
|
+
- If `diagram.mcpConsent.host === <configured MCP host>` AND `diagram.mcpConsent.decision === "denied"` → hard-error immediately with the exact denied text (no re-prompt):
|
|
228
|
+
```
|
|
229
|
+
diagram MCP consent denied; re-run with `--mode=file` or change diagram.mcpConsent in .aperant/config.local.json
|
|
230
|
+
```
|
|
231
|
+
- Otherwise (host matches AND decision is `"granted"`) → the existing consent applies; proceed to 5b.
|
|
232
|
+
|
|
233
|
+
When prompting, print a single line naming the host and ask `granted / denied`:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
apt:diagram is about to send diagram data to the Excalidraw MCP server at <host>. Type `granted` to allow, `denied` to refuse.
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Read one line of input. Persist the decision to
|
|
240
|
+
`.aperant/config.local.json` (NOT `config.json` — `mcpConsent` is per-user
|
|
241
|
+
state per FRAMEWORK-AUDIT-001; the local file is gitignored). The
|
|
242
|
+
read-mutate-write JSON pattern targets the local file only:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
node -e '
|
|
246
|
+
const fs = require("fs");
|
|
247
|
+
const path = require("path");
|
|
248
|
+
const cfgPath = path.join(process.cwd(), ".aperant/config.local.json");
|
|
249
|
+
const decision = process.env.APT_DIAGRAM_DECISION; // "granted" | "denied"
|
|
250
|
+
if (decision !== "granted" && decision !== "denied") {
|
|
251
|
+
process.stderr.write("invalid decision\n");
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
const host = process.env.APT_DIAGRAM_HOST;
|
|
255
|
+
let cfg = { diagram: { mcpConsent: null } };
|
|
256
|
+
try { cfg = JSON.parse(fs.readFileSync(cfgPath, "utf-8")); } catch {}
|
|
257
|
+
cfg.diagram = cfg.diagram || { mcpConsent: null };
|
|
258
|
+
cfg.diagram.mcpConsent = {
|
|
259
|
+
host,
|
|
260
|
+
decision,
|
|
261
|
+
grantedAt: new Date().toISOString(),
|
|
262
|
+
};
|
|
263
|
+
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
|
|
264
|
+
'
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Note: the write target above is `config.local.json` (gitignored, per-user).
|
|
268
|
+
The shared `config.json` is not touched — `diagram.mode` lives there and
|
|
269
|
+
stays team-shared policy.
|
|
270
|
+
|
|
271
|
+
If the decision is `"denied"`, exit with EXACTLY this text (verbatim,
|
|
272
|
+
no rewording, no extra punctuation):
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
diagram MCP consent denied; re-run with `--mode=file` or change diagram.mcpConsent in .aperant/config.local.json
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
If the decision is `"granted"`, proceed to 5b.
|
|
279
|
+
|
|
280
|
+
### 5b. Renderer dispatch
|
|
281
|
+
|
|
282
|
+
The skill IS the MCP renderer — there is no shared TS module here. The
|
|
283
|
+
agent dispatches MCP tool calls directly:
|
|
284
|
+
|
|
285
|
+
1. Iterate `scene.nodes` FIRST. For each node, call the Excalidraw MCP
|
|
286
|
+
`create_element` tool (expected name `mcp__excalidraw__create_element`;
|
|
287
|
+
if the connected server exposes a different equivalent, use that
|
|
288
|
+
discovered name) with rectangle parameters: position from
|
|
289
|
+
`node.x`/`node.y`, default size 180x70, and the `node.label` as
|
|
290
|
+
text content. Record the element id the MCP server returns.
|
|
291
|
+
2. Then iterate `scene.edges`. For each edge, call the same tool with
|
|
292
|
+
arrow parameters, setting `startBinding.elementId` to the rectangle
|
|
293
|
+
id created for `edge.from` and `endBinding.elementId` to the
|
|
294
|
+
rectangle id created for `edge.to`. If `edge.label` is set, pass it
|
|
295
|
+
as the arrow's label.
|
|
296
|
+
|
|
297
|
+
Nodes MUST be created before edges so the edge bindings have valid
|
|
298
|
+
element ids on both ends.
|
|
299
|
+
|
|
300
|
+
On success, surface whatever stable identifier the MCP server returned
|
|
301
|
+
(session id, canvas URL, or scene id) so the user can open the live
|
|
302
|
+
canvas — e.g. `Live canvas: <url>`.
|
|
303
|
+
|
|
304
|
+
### 5c. Failure handling
|
|
305
|
+
|
|
306
|
+
If ANY `mcp__excalidraw__*` tool call fails — tool error, transport
|
|
307
|
+
error, or the expected tool is missing from the surface — emit EXACTLY
|
|
308
|
+
this message (verbatim) and exit:
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
Excalidraw MCP renderer failed: <reason>. Re-run with `--mode=file` to generate an offline .excalidraw file.
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
No retries. No automatic switch to file mode. No partial cleanup of
|
|
315
|
+
already-created MCP elements (v1 leaves them as a debugging artifact;
|
|
316
|
+
v2 may add cleanup).
|
|
317
|
+
|
|
318
|
+
## 7. Quality pass loop (file mode only)
|
|
319
|
+
|
|
320
|
+
(Step 6 is intentionally reserved for future MCP-side quality work; v2
|
|
321
|
+
ships Step 7 for file mode only.)
|
|
322
|
+
|
|
323
|
+
After Step 4 (file written), if Playwright is installed, run a visual
|
|
324
|
+
quality-pass loop. The loop is capped at **3 iterations**. If
|
|
325
|
+
`validateScene(scene).score >= 70` AND the vision review reports no
|
|
326
|
+
critical issues, exit early.
|
|
327
|
+
|
|
328
|
+
### 7a. Detect Playwright
|
|
329
|
+
|
|
330
|
+
Probe with:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
node -e 'import("playwright").then(() => process.stdout.write("yes"), () => process.stdout.write("no"))'
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
If the result is `no`, skip Step 7 silently and append the following
|
|
337
|
+
one-line tip to the success message printed in Step 4:
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
Tip: install playwright to enable visual quality-pass on future runs (pnpm add -D playwright && npx playwright install chromium).
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### 7b. Loop body (max 3 iterations)
|
|
344
|
+
|
|
345
|
+
For each iteration `i` in `0..2`:
|
|
346
|
+
|
|
347
|
+
1. Call `renderToPng({ excalidrawPath })` from `@aperant/core/workflows/diagram`
|
|
348
|
+
via the same Node-invocation pattern Step 4 uses. Capture the
|
|
349
|
+
absolute PNG path.
|
|
350
|
+
2. Call `validateScene(scene)` from the same module. Capture
|
|
351
|
+
`{warnings, score}`.
|
|
352
|
+
3. Read the PNG via the `Read` tool (multimodal vision review).
|
|
353
|
+
4. Decide:
|
|
354
|
+
- If `score >= 70` AND the vision review reports no critical issues
|
|
355
|
+
(overlaps, off-canvas elements, illegible text), exit the loop
|
|
356
|
+
and return success.
|
|
357
|
+
- Else: emit IR patches as a JSON delta. Mutable fields are
|
|
358
|
+
exactly `DiagramNode.x`, `DiagramNode.y`, and `DiagramNode.label`.
|
|
359
|
+
Apply the patches to `scene`, re-call `renderToFile` (re-using
|
|
360
|
+
the slug from Step 4), and continue to the next iteration.
|
|
361
|
+
5. After 3 iterations: return final state. If unresolved warnings
|
|
362
|
+
remain, print exactly:
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
Quality pass: <N> warning(s) remain after 3 iterations. See <png-path> to inspect.
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 7c. Hard rules
|
|
369
|
+
|
|
370
|
+
- Iteration cap is exactly **3** (hard-coded, not configurable in v2).
|
|
371
|
+
- Score threshold is exactly **70** (hard-coded, not configurable in v2).
|
|
372
|
+
- Patch grammar is `{x, y, label}` — no other `DiagramNode`/
|
|
373
|
+
`DiagramEdge` fields are mutable.
|
|
374
|
+
- If `renderToPng` throws `DiagramMissingDep`, treat the same as Step
|
|
375
|
+
7a's `no` branch (silent skip + tip). Any OTHER `renderToPng` error
|
|
376
|
+
fails the loop and surfaces verbatim to the user.
|
|
377
|
+
|
|
378
|
+
</process>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# apt:diagram — design-discipline appendix
|
|
2
|
+
|
|
3
|
+
Loaded into `apt:diagram`'s reasoning context when the skill is invoked
|
|
4
|
+
with `--depth=comprehensive`. Aperant-authored — not a verbatim port. The
|
|
5
|
+
goal: emit a diagram that earns its space on the page, not a free-floating
|
|
6
|
+
box graph that "looks like a diagram."
|
|
7
|
+
|
|
8
|
+
This appendix is a posture loader. It does not change the IR. It changes
|
|
9
|
+
what the agent decides to put *in* the IR.
|
|
10
|
+
|
|
11
|
+
## Visual pattern library
|
|
12
|
+
|
|
13
|
+
Excalidraw exposes rectangles, ellipses, diamonds, arrows, text, and
|
|
14
|
+
free-form shapes. When you have a choice, the shape should encode
|
|
15
|
+
information the label can't:
|
|
16
|
+
|
|
17
|
+
- **Rectangle** — a unit of work, a service, a process, a system
|
|
18
|
+
component. Default for "this is a thing that exists." The boring,
|
|
19
|
+
honest shape.
|
|
20
|
+
- **Ellipse** — a state, a phase, a "moment in time." Use when the noun
|
|
21
|
+
is more like a place a flow visits than a thing that owns state.
|
|
22
|
+
- **Diamond** — a decision point, a branching condition. If the
|
|
23
|
+
outgoing arrows have different semantics ("yes" / "no" / "retry"),
|
|
24
|
+
the source should be a diamond.
|
|
25
|
+
|
|
26
|
+
v2 only emits rectangles. The IR has no `shape` field on `DiagramNode`,
|
|
27
|
+
and `renderToFile` hard-codes rectangle elements. The choice above is
|
|
28
|
+
informational — it tells you *what richer shapes would mean* so you can
|
|
29
|
+
note the missing semantics in the diagram's accompanying prose if a
|
|
30
|
+
node really wants to be a diamond. Richer shapes are scheduled for v3.
|
|
31
|
+
|
|
32
|
+
## Isomorphism test
|
|
33
|
+
|
|
34
|
+
The diagram should be *the same shape* as the thing it depicts.
|
|
35
|
+
|
|
36
|
+
If the system has three layers (edge / app / data), the diagram has
|
|
37
|
+
three visually-distinct horizontal zones. If the system has a single
|
|
38
|
+
hub that talks to four leaves, the diagram has a single central node
|
|
39
|
+
with four spokes — not five nodes in a line.
|
|
40
|
+
|
|
41
|
+
Concretely:
|
|
42
|
+
|
|
43
|
+
- Count the "structural axes" in the thing being depicted. Layers?
|
|
44
|
+
Phases? Owners? Each axis should map to a visual axis (vertical /
|
|
45
|
+
horizontal / radial) in the diagram.
|
|
46
|
+
- If you cannot draw the structural axis, the diagram is lying. Either
|
|
47
|
+
add the missing axis or admit you're depicting a different thing.
|
|
48
|
+
- Symmetry in the system should appear as symmetry in the layout.
|
|
49
|
+
Asymmetry is a flag — either it's load-bearing (good — show it) or
|
|
50
|
+
it's noise (bad — fix it).
|
|
51
|
+
|
|
52
|
+
The `validateStructure` validator codifies one slice of this: it checks
|
|
53
|
+
that nodes occupy the top / middle / bottom thirds of the canvas in
|
|
54
|
+
roughly the expected counts. That's a weak proxy — the real check is in
|
|
55
|
+
your eyes.
|
|
56
|
+
|
|
57
|
+
## Depth assessment
|
|
58
|
+
|
|
59
|
+
Every diagram answers one of two questions: **what** or **what + how**.
|
|
60
|
+
|
|
61
|
+
- A **what** diagram shows the inventory of things and the connections
|
|
62
|
+
between them. "These are the services. They talk to each other."
|
|
63
|
+
v2 targets this depth.
|
|
64
|
+
- A **what + how** diagram additionally encodes the protocol on each
|
|
65
|
+
connection — request shape, retry policy, idempotency key, transport.
|
|
66
|
+
This often requires labels on every edge, sub-groups around clusters
|
|
67
|
+
of services, and sometimes a second pass at a different zoom level.
|
|
68
|
+
v2 does not target this. The IR has no edge-protocol field.
|
|
69
|
+
|
|
70
|
+
Before generating the scene, decide which question the diagram answers.
|
|
71
|
+
If the user prompt implies "what + how" and you can only render "what,"
|
|
72
|
+
say so in the chat so the user knows they're getting the shallower
|
|
73
|
+
picture.
|
|
74
|
+
|
|
75
|
+
## Multi-zoom architecture
|
|
76
|
+
|
|
77
|
+
A real system has multiple zoom levels:
|
|
78
|
+
|
|
79
|
+
- **Far zoom** — one diagram showing the 4-7 top-level components and
|
|
80
|
+
their primary connections. Fits on a slide.
|
|
81
|
+
- **Mid zoom** — one diagram per top-level component, showing its
|
|
82
|
+
internal modules. 4-7 boxes each, again.
|
|
83
|
+
- **Near zoom** — one diagram per critical interface, showing the
|
|
84
|
+
protocol/sequence.
|
|
85
|
+
|
|
86
|
+
v2 generates one diagram per `apt:diagram` invocation. If a user prompt
|
|
87
|
+
implies a multi-zoom set ("system architecture for the auth flow,"
|
|
88
|
+
where "the auth flow" is itself worth a deeper drawing), the agent
|
|
89
|
+
should:
|
|
90
|
+
|
|
91
|
+
1. Generate the far-zoom diagram for v2.
|
|
92
|
+
2. State in the chat that a mid-zoom diagram for the named subsystem
|
|
93
|
+
would be a natural follow-up.
|
|
94
|
+
3. NOT chain a second `renderToFile` call automatically.
|
|
95
|
+
|
|
96
|
+
The default is one diagram per prompt. Multi-zoom orchestration is a
|
|
97
|
+
v3 problem.
|
|
@@ -17,7 +17,7 @@ execution_modes:
|
|
|
17
17
|
- step
|
|
18
18
|
- research
|
|
19
19
|
allowed-tools: "Bash, Read, Grep, Glob, Write"
|
|
20
|
-
argument-hint: "apt:discuss [--brainstorm] [topic]"
|
|
20
|
+
argument-hint: "apt:discuss [--brainstorm] [--grill] [topic]"
|
|
21
21
|
gates: []
|
|
22
22
|
---
|
|
23
23
|
<objective>
|
|
@@ -46,6 +46,31 @@ never by asking:
|
|
|
46
46
|
These are two postures of the same skill, not two different skills. The
|
|
47
47
|
intent-detection step below decides which one to enter — same file, same
|
|
48
48
|
artifact layout, different section populated.
|
|
49
|
+
|
|
50
|
+
**Decision-lock posture loader (Pocock adoption ID-02 + ID-04 — AC11).**
|
|
51
|
+
When entering decision-lock posture, you MUST load
|
|
52
|
+
`appendices/grill-discipline.md` into reasoning context. It carries the
|
|
53
|
+
validated 100-token reframe sentence ("Your job is not to emit a plan;
|
|
54
|
+
your job is to resolve the design tree until planning is mechanical.")
|
|
55
|
+
and the autonomy-aware degradation table (Autonomy 1 grills everything;
|
|
56
|
+
Autonomy 2 batches recommendations; Autonomy 3 auto-locks with three
|
|
57
|
+
escalation triggers — conflict with prior locked decision, critical-path
|
|
58
|
+
file touched, or no defensible default). This appendix is the canonical
|
|
59
|
+
source for the autonomy-aware rule; downstream skills (`apt:improve`,
|
|
60
|
+
`apt:triage`) reference it rather than re-stating the policy. Brainstorm
|
|
61
|
+
posture does NOT load this appendix.
|
|
62
|
+
|
|
63
|
+
**Brainstorm posture zoom-out helper (Pocock adoption AC6 + AC11).**
|
|
64
|
+
When in brainstorm posture and the user's turn contains a zoom-out
|
|
65
|
+
trigger phrase ("give me a broader picture", "I'm lost in this area",
|
|
66
|
+
"explain how this fits", "what's the surrounding architecture", "zoom
|
|
67
|
+
out from X"), load `appendices/zoom-out-helper.md` and follow its
|
|
68
|
+
3-6-line compressed orientation pattern. The brainstorm posture stays
|
|
69
|
+
active — this is an augmentation, not a pivot. The full router-invocable
|
|
70
|
+
`/apt:zoom-out` skill is the right answer when the user invokes it
|
|
71
|
+
explicitly; this helper is the right answer when the request surfaces
|
|
72
|
+
inside an active brainstorm discussion. Decision-lock posture does NOT
|
|
73
|
+
load this helper (orientation is descriptive, not normative).
|
|
49
74
|
</objective>
|
|
50
75
|
|
|
51
76
|
<your_environment>
|
|
@@ -158,10 +183,11 @@ If any file is missing, proceed with what's available. The framework works with
|
|
|
158
183
|
Extract the task description from `$ARGUMENTS`:
|
|
159
184
|
|
|
160
185
|
1. Strip mode flags (`--assumptions`, `--power`, `--auto`, `--review-auto`, `--continue`) from arguments
|
|
161
|
-
2.
|
|
162
|
-
3.
|
|
163
|
-
4. If
|
|
164
|
-
5.
|
|
186
|
+
2. Strip the **`--grill`** overlay flag from arguments (orthogonal to the mode flags — see Section 6.5). `--grill` may combine with any mode; standalone `--grill` defaults to Interactive.
|
|
187
|
+
3. The remaining text is the task description
|
|
188
|
+
4. If task description is empty but `state.json` has an `active_task`, use that task's description
|
|
189
|
+
5. If still empty, ask: "What are you working on?"
|
|
190
|
+
6. Generate a `task-id` slug from the description: lowercase, hyphens, max 40 characters
|
|
165
191
|
- Example: "Add OAuth login with Google" -> `add-oauth-login-with-google`
|
|
166
192
|
|
|
167
193
|
## 3.5. Brainstorm (skip if you picked decision-lock in Section 1)
|
|
@@ -539,6 +565,47 @@ Review-Auto is Auto Mode **plus one human checkpoint at the batch end**. It does
|
|
|
539
565
|
|
|
540
566
|
Do NOT introduce per-decision staging ("show me D-01, wait, show me D-02, wait...") — that collapses review-auto into spaced interactive mode and defeats the point of joint reasoning up front. Do NOT skip the pause ("autonomy is high, let's just write it") — that collapses review-auto into auto. The one pause at the end is the whole identity of this mode.
|
|
541
567
|
|
|
568
|
+
## 6.5. Grill Mode (`--grill` flag — Pocock `grill-with-docs` overlay)
|
|
569
|
+
|
|
570
|
+
**Activate when:** the user invoked the skill with `--grill`. This is an **overlay**, not a mode — it stacks on top of whichever Section 6 mode is active (Interactive by default; combine with `--auto` etc. as desired). The overlay does two things: (1) it applies Pocock's 4 grill moves during decision resolution; (2) it fires inline writes to CONTEXT.md and ADRs as terms resolve and decisions pass the Nygard 3-gate.
|
|
571
|
+
|
|
572
|
+
The discipline this overlay loads is already in [`appendices/grill-discipline.md`](./appendices/grill-discipline.md) — re-read the reframe sentence and the autonomy degradation table before each grill turn. **Do NOT duplicate that content here.** What follows is only the moves + writer-call instructions specific to the overlay.
|
|
573
|
+
|
|
574
|
+
### The 4 grill moves (apply during gray-area resolution)
|
|
575
|
+
|
|
576
|
+
1. **Challenge against the glossary.** When the user uses a term that conflicts with an existing CONTEXT.md term-block, call it out immediately: *"Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?"* Read `CONTEXT.md` (or per-context CONTEXT files via `CONTEXT-MAP.md`) when the overlay activates; skip silently if absent.
|
|
577
|
+
2. **Sharpen fuzzy language.** When the user uses vague or overloaded terms, propose a precise canonical term: *"You're saying 'account' — do you mean Customer or User? Those are different things."*
|
|
578
|
+
3. **Stress-test with concrete scenarios.** When domain relationships are being discussed, invent specific scenarios that probe edge cases and force precision about the boundaries between concepts.
|
|
579
|
+
4. **Cross-reference with code.** When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: *"Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?"*
|
|
580
|
+
|
|
581
|
+
### Inline writer calls (NOT batched)
|
|
582
|
+
|
|
583
|
+
When a term resolves with confidence during the grill, invoke `apt-tools context write` to upsert the term-block into CONTEXT.md **right there** — do NOT batch:
|
|
584
|
+
|
|
585
|
+
```bash
|
|
586
|
+
node .aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs context write . \
|
|
587
|
+
--term "Plan thread" \
|
|
588
|
+
--definition "<one-line definition>" \
|
|
589
|
+
--aliases-to-avoid "Plan session,plan chat" \
|
|
590
|
+
--cardinality "one-to-many"
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
**Lazy creation:** if no `CONTEXT.md` exists at the project root (or in the relevant per-context directory when `CONTEXT-MAP.md` is present), the first resolved term creates the file. Do NOT create CONTEXT.md upfront with placeholder content.
|
|
594
|
+
|
|
595
|
+
**Glossary-only rule:** CONTEXT.md is a glossary of domain terms — NO implementation details, NO spec content, NO scratch-pad notes. Implementation lives in `spec.md` / `implementation_plan.json`; scratch lives in `.aperant/context/notes/{task-id}.md`. Term-blocks are user-facing concepts (e.g. "Plan thread", "Sparring round"), not code identifiers (e.g. "ChatSession", "useTaskStore").
|
|
596
|
+
|
|
597
|
+
When a decision passes the **Nygard 3-gate**, offer to write an ADR via `apt-tools adr write .`. The 3-gate (ALL three must hold — miss one and skip the ADR):
|
|
598
|
+
|
|
599
|
+
1. **Hard to reverse** — the cost of changing your mind later is meaningful.
|
|
600
|
+
2. **Surprising without context** — a future reader will wonder *"why did they do it this way?"*.
|
|
601
|
+
3. **Result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons.
|
|
602
|
+
|
|
603
|
+
If only one or two hold, the decision belongs in `.aperant/context/notes/{task-id}.md` (locked-decisions list), not in an ADR.
|
|
604
|
+
|
|
605
|
+
### Autonomy interaction
|
|
606
|
+
|
|
607
|
+
`--grill` does not change the autonomy degradation already specified in [`appendices/grill-discipline.md`](./appendices/grill-discipline.md). Autonomy 1 grills every term/decision interactively; Autonomy 2 batches recommendations; Autonomy 3 auto-locks with the three escalation triggers (conflict-with-locked, critical-path file, no-defensible-default). The overlay simply ensures that whichever autonomy path runs, the writer CLIs fire as side-effects of the resolution work.
|
|
608
|
+
|
|
542
609
|
## 7. Advanced Elicitation (DEEP Tasks Only)
|
|
543
610
|
|
|
544
611
|
After all decisions are locked, check the task track. If the track is DEEP (from state.json or router context), behavior depends on the resolved mode from Section 6's precedence chain (D-07):
|