@sabaiway/agent-workflow-kit 10.2.0 → 10.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -0
- package/README.md +7 -7
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/agents/executor.md +40 -0
- package/references/modes/agents.md +9 -4
- package/references/modes/procedures.md +17 -8
- package/references/modes/recipes.md +7 -4
- package/references/modes/recommendations.md +4 -1
- package/references/modes/set-recipe.md +22 -5
- package/references/modes/status.md +3 -3
- package/references/modes/upgrade.md +7 -5
- package/references/shared/composition-handoff.md +1 -1
- package/references/shared/deploy-tail.md +2 -2
- package/references/templates/agent_rules.md +3 -2
- package/references/templates/orchestration.json +1 -1
- package/tools/ack-store.mjs +57 -0
- package/tools/ack-write.mjs +1 -1
- package/tools/autonomy-config.mjs +1 -1
- package/tools/carriers.mjs +140 -0
- package/tools/cheap-agents-read.mjs +172 -0
- package/tools/cheap-agents.mjs +57 -105
- package/tools/commands.mjs +3 -3
- package/tools/direct-run.mjs +6 -0
- package/tools/doc-parity.mjs +8 -0
- package/tools/ensure-ops.mjs +18 -9
- package/tools/ensure-specs.mjs +3 -4
- package/tools/ensure-vocabulary.mjs +5 -2
- package/tools/family-registry.mjs +70 -21
- package/tools/flow-check.mjs +2 -7
- package/tools/inject-methodology.mjs +4 -0
- package/tools/lens-region.mjs +4 -1
- package/tools/node-evidence.mjs +77 -0
- package/tools/orchestration-config.mjs +34 -13
- package/tools/procedures.mjs +65 -52
- package/tools/recipes.mjs +156 -184
- package/tools/recommendations.mjs +145 -78
- package/tools/renderers.mjs +36 -7
- package/tools/review-state.mjs +10 -11
- package/tools/set-recipe.mjs +63 -24
- package/tools/spec-adoption.mjs +71 -0
- package/tools/spec-check.mjs +2 -2
- package/tools/upgrade-runlist.mjs +1 -1
- package/tools/view-model.mjs +19 -3
package/tools/cheap-agents.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// cheap-agents.mjs — the onboarding writer behind `/agent-workflow-kit agents`: places the
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// skeletons, gate triage) stops running on a frontier model by default
|
|
2
|
+
// cheap-agents.mjs — the onboarding writer behind `/agent-workflow-kit agents`: places the bundled
|
|
3
|
+
// subagent definitions (references/agents/*.md) into a project's .claude/agents/. FOUR vehicles
|
|
4
|
+
// grant NO shell — three ride the cheap lane (haiku/low-effort) so mechanical work (sweeps,
|
|
5
|
+
// changelog skeletons, gate triage) stops running on a frontier model by default, and review-lens
|
|
6
|
+
// is the read-only review opinion. The fifth, `executor`, is the ONE full-tool vehicle: dispatched
|
|
7
|
+
// only for a bounded execution, authoring, or write-capable routine slice the orchestrator verifies, never for read-only work, and it
|
|
8
|
+
// never commits. `surveyExecutorVehicle` is that vehicle's readiness, for the subagent carrier.
|
|
6
9
|
//
|
|
7
10
|
// The family's second `.claude/` writer, the velocity-profile.mjs writer discipline verbatim:
|
|
8
11
|
// • preview-then-mutate — `--dry-run` is the DEFAULT and writes nothing; `--apply` writes;
|
|
@@ -25,29 +28,48 @@
|
|
|
25
28
|
// state, not an error); 1 precondition STOP (stamp, symlink, missing bundle); 2 usage.
|
|
26
29
|
// Dependency-free, Node >= 22. No side effects on import.
|
|
27
30
|
|
|
28
|
-
import {
|
|
29
|
-
import { join
|
|
31
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
32
|
+
import { join } from 'node:path';
|
|
30
33
|
import { fileURLToPath } from 'node:url';
|
|
31
34
|
import { isDirectRun } from './direct-run.mjs';
|
|
32
35
|
import { shellQuoteArg } from './repo-lex.mjs';
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
// The READ core, never a second copy: the bundle, the placement plan and the executor survey live
|
|
37
|
+
// there so the read-only advisor graph can reach them without reaching this writer.
|
|
38
|
+
import {
|
|
39
|
+
AGENTS_DIR,
|
|
40
|
+
CLAUDE_DIR,
|
|
41
|
+
WORKFLOW_STAMP,
|
|
42
|
+
EXPECTED_WORKFLOW_VERSION,
|
|
43
|
+
UTF8,
|
|
44
|
+
CHEAP_AGENTS_STAMP,
|
|
45
|
+
makeCheapAgentsError,
|
|
46
|
+
readFsDeps,
|
|
47
|
+
readBundledAgents,
|
|
48
|
+
readStamp,
|
|
49
|
+
assertDirSafe,
|
|
50
|
+
planPlacement,
|
|
51
|
+
} from './cheap-agents-read.mjs';
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
AGENTS_DIR,
|
|
55
|
+
CLAUDE_DIR,
|
|
56
|
+
WORKFLOW_STAMP,
|
|
57
|
+
EXPECTED_WORKFLOW_VERSION,
|
|
58
|
+
BUNDLED_AGENTS_DIR,
|
|
59
|
+
CHEAP_AGENTS_STAMP,
|
|
60
|
+
CHEAP_AGENTS_SYMLINK,
|
|
61
|
+
CHEAP_AGENTS_BUNDLE,
|
|
62
|
+
makeCheapAgentsError,
|
|
63
|
+
readBundledAgents,
|
|
64
|
+
planPlacement,
|
|
65
|
+
surveyExecutorVehicle,
|
|
66
|
+
EXECUTOR_VEHICLE,
|
|
67
|
+
EXECUTOR_VEHICLE_REL,
|
|
68
|
+
} from './cheap-agents-read.mjs';
|
|
41
69
|
|
|
42
70
|
const EXIT_OK = 0;
|
|
43
71
|
const EXIT_PRECONDITION = 1;
|
|
44
72
|
const EXIT_USAGE = 2;
|
|
45
|
-
const UTF8 = 'utf8';
|
|
46
|
-
const ERROR_PREFIX = '[agent-workflow-kit]';
|
|
47
|
-
|
|
48
|
-
export const CHEAP_AGENTS_STAMP = 'CHEAP_AGENTS_STAMP';
|
|
49
|
-
export const CHEAP_AGENTS_SYMLINK = 'CHEAP_AGENTS_SYMLINK';
|
|
50
|
-
export const CHEAP_AGENTS_BUNDLE = 'CHEAP_AGENTS_BUNDLE';
|
|
51
73
|
|
|
52
74
|
// The fallback-lens contract, formalized where it lives (flow-orchestration #15/#3, Phase 4.3):
|
|
53
75
|
// the internal-attestation evaluation consumes this sentence — a lens set claiming a configured
|
|
@@ -57,95 +79,24 @@ export const FALLBACK_LENS_ADDITIONAL_ONLY = 'review-lens is an ADDITIONAL read-
|
|
|
57
79
|
|
|
58
80
|
const USAGE = `usage: cheap-agents [--dry-run | --apply] [--cwd <dir>] [--help]
|
|
59
81
|
|
|
60
|
-
Places the bundled
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
82
|
+
Places the bundled subagent definitions into the project's ${AGENTS_DIR}/. Four vehicles grant NO
|
|
83
|
+
shell: three ride a cheap model (haiku/low) for mechanical work — extraction sweeps, changelog
|
|
84
|
+
fact-skeletons, gate triage — and review-lens is a read-only REVIEW vehicle on a review-capable
|
|
85
|
+
model. The fifth, executor, is the ONE full-tool vehicle: dispatched only for a bounded execution,
|
|
86
|
+
authoring, or write-capable routine slice the orchestrator verifies, never for read-only work, and
|
|
87
|
+
it never commits.
|
|
88
|
+
Default is --dry-run (a preview; writes nothing). --apply writes.
|
|
64
89
|
An existing file with DIFFERENT content is preserved and reported, never overwritten.`;
|
|
65
90
|
|
|
66
91
|
export const fail = (exitCode, message) => Object.assign(new Error(message), { exitCode });
|
|
67
92
|
|
|
68
|
-
|
|
69
|
-
Object.assign(new Error(`${ERROR_PREFIX} ${message}`), { name: 'CheapAgentsError', code, exitCode: EXIT_PRECONDITION });
|
|
70
|
-
|
|
71
|
-
const fsDeps = (deps = {}) => ({
|
|
72
|
-
exists: deps.exists ?? existsSync,
|
|
73
|
-
lstat: deps.lstat ?? lstatSync,
|
|
93
|
+
const writeFsDeps = (deps = {}) => ({
|
|
74
94
|
mkdir: deps.mkdir ?? mkdirSync,
|
|
75
|
-
readFile: deps.readFile ?? readFileSync,
|
|
76
95
|
writeFile: deps.writeFile ?? writeFileSync,
|
|
77
|
-
readdir: deps.readdir ?? readdirSync,
|
|
78
96
|
});
|
|
79
97
|
|
|
80
|
-
const lstatNoFollow = (absPath, fs) => {
|
|
81
|
-
try {
|
|
82
|
-
return fs.lstat(absPath);
|
|
83
|
-
} catch (err) {
|
|
84
|
-
if (err && err.code === 'ENOENT') return null;
|
|
85
|
-
throw err;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// ── the bundle (the kit's own references/agents/) ─────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
export const readBundledAgents = (deps = {}) => {
|
|
92
|
-
const fs = fsDeps(deps);
|
|
93
|
-
const bundleDir = deps.bundleDir ?? BUNDLED_AGENTS_DIR;
|
|
94
|
-
let names;
|
|
95
|
-
try {
|
|
96
|
-
names = fs.readdir(bundleDir);
|
|
97
|
-
} catch (err) {
|
|
98
|
-
throw makeCheapAgentsError(CHEAP_AGENTS_BUNDLE, `bundled agents dir unreadable (${err.code ?? err.message}): ${bundleDir}`);
|
|
99
|
-
}
|
|
100
|
-
const templates = names
|
|
101
|
-
.filter((name) => name.endsWith('.md'))
|
|
102
|
-
.sort()
|
|
103
|
-
.map((name) => ({ name, content: fs.readFile(join(bundleDir, name), UTF8) }));
|
|
104
|
-
if (templates.length === 0) {
|
|
105
|
-
throw makeCheapAgentsError(CHEAP_AGENTS_BUNDLE, `no bundled agent templates found in ${bundleDir} — the kit install is incomplete`);
|
|
106
|
-
}
|
|
107
|
-
return templates;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// ── preflight (velocity discipline: symlink-safe, stamp read, no writes) ──────────────
|
|
111
|
-
|
|
112
|
-
const readStamp = (absPath, fs) => {
|
|
113
|
-
try {
|
|
114
|
-
if (!fs.exists(absPath)) return null;
|
|
115
|
-
const stamp = String(fs.readFile(absPath, UTF8)).trim();
|
|
116
|
-
return stamp.length ? stamp : null;
|
|
117
|
-
} catch {
|
|
118
|
-
return null; // unreadable stamp == not a valid deployment stamp (apply STOPs; dry-run reports)
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const assertDirSafe = (absPath, relPath, fs) => {
|
|
123
|
-
const stat = lstatNoFollow(absPath, fs);
|
|
124
|
-
if (stat === null) return { absent: true };
|
|
125
|
-
if (stat.isSymbolicLink()) throw makeCheapAgentsError(CHEAP_AGENTS_SYMLINK, `${relPath} is a symlink — refusing to write through it`);
|
|
126
|
-
if (!stat.isDirectory()) throw makeCheapAgentsError(CHEAP_AGENTS_SYMLINK, `${relPath} exists but is not a directory — refusing to write through it`);
|
|
127
|
-
return { absent: false };
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// Per-template placement plan: place | already-current | customized-preserved (never clobbered).
|
|
131
|
-
export const planPlacement = (templates, projectDir, deps = {}) => {
|
|
132
|
-
const fs = fsDeps(deps);
|
|
133
|
-
return templates.map((template) => {
|
|
134
|
-
const rel = `${AGENTS_DIR}/${template.name}`;
|
|
135
|
-
const abs = join(projectDir, AGENTS_DIR, template.name);
|
|
136
|
-
const stat = lstatNoFollow(abs, fs);
|
|
137
|
-
if (stat === null) return { ...template, rel, abs, action: 'place' };
|
|
138
|
-
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
139
|
-
throw makeCheapAgentsError(CHEAP_AGENTS_SYMLINK, `${rel} exists but is not a regular file — refusing to touch it`);
|
|
140
|
-
}
|
|
141
|
-
const existing = fs.readFile(abs, UTF8);
|
|
142
|
-
if (existing === template.content) return { ...template, rel, abs, action: 'already-current' };
|
|
143
|
-
return { ...template, rel, abs, action: 'customized-preserved' };
|
|
144
|
-
});
|
|
145
|
-
};
|
|
146
|
-
|
|
147
98
|
export const preflightCheapAgents = ({ cwd }, deps = {}) => {
|
|
148
|
-
const fs =
|
|
99
|
+
const fs = readFsDeps(deps);
|
|
149
100
|
const projectDir = cwd ?? process.cwd();
|
|
150
101
|
const templates = readBundledAgents(deps);
|
|
151
102
|
const stamp = readStamp(join(projectDir, WORKFLOW_STAMP), fs);
|
|
@@ -159,7 +110,7 @@ export const preflightCheapAgents = ({ cwd }, deps = {}) => {
|
|
|
159
110
|
// ── the writer ────────────────────────────────────────────────────────────────────────
|
|
160
111
|
|
|
161
112
|
export const writeCheapAgents = ({ cwd, dryRun = true } = {}, deps = {}) => {
|
|
162
|
-
const fs =
|
|
113
|
+
const fs = writeFsDeps(deps);
|
|
163
114
|
const preflight = preflightCheapAgents({ cwd }, deps);
|
|
164
115
|
if (dryRun) return { wrote: false, dryRun: true, ...preflight };
|
|
165
116
|
|
|
@@ -186,8 +137,8 @@ const ACTION_LABEL = {
|
|
|
186
137
|
export const formatResult = (result) => {
|
|
187
138
|
const lines = [
|
|
188
139
|
result.dryRun
|
|
189
|
-
? 'agent-workflow
|
|
190
|
-
: 'agent-workflow
|
|
140
|
+
? 'agent-workflow subagent vehicles — DRY RUN (no changes)'
|
|
141
|
+
: 'agent-workflow subagent vehicles — APPLY',
|
|
191
142
|
];
|
|
192
143
|
for (const item of result.plan) {
|
|
193
144
|
const verb = result.dryRun && item.action === 'place' ? 'would place' : ACTION_LABEL[item.action];
|
|
@@ -197,8 +148,9 @@ export const formatResult = (result) => {
|
|
|
197
148
|
lines.push(`note: no current deployment stamp found (${result.stamp ?? 'none'}) — --apply will refuse until init/upgrade runs.`);
|
|
198
149
|
}
|
|
199
150
|
lines.push(
|
|
200
|
-
'
|
|
201
|
-
`three ride the cheap lane (model: haiku, effort: low) for mechanical work; ${FALLBACK_LENS_ADDITIONAL_ONLY}
|
|
151
|
+
'four vehicles are Claude Code subagents with READ-ONLY tools and NO shell — so a fan-out can never turn into a wave of approval prompts.',
|
|
152
|
+
`three of those ride the cheap lane (model: haiku, effort: low) for mechanical work; ${FALLBACK_LENS_ADDITIONAL_ONLY}`,
|
|
153
|
+
'executor is the one FULL-TOOL vehicle: dispatched only for a bounded execution, authoring, or write-capable routine slice the orchestrator verifies, never for read-only work, and it never commits.',
|
|
202
154
|
);
|
|
203
155
|
// A preview must print the EXACT command that applies it. The advisor renders this dry-run as an
|
|
204
156
|
// item's one-liner, and that flow's contract is "run the printed command, no improvisation" — a
|
package/tools/commands.mjs
CHANGED
|
@@ -131,7 +131,7 @@ const CATALOG = [
|
|
|
131
131
|
invocation: invocationOf('agents'),
|
|
132
132
|
group: 'Configure',
|
|
133
133
|
kind: WRITER,
|
|
134
|
-
oneLine: 'Place bundled
|
|
134
|
+
oneLine: 'Place the bundled subagent vehicles: four read-only ones, none granted a shell — three cheap-model ones for mechanical work (sweeps, changelog skeletons, gate triage) and a review lens — plus the one full-tool executor that carries bounded slices you verify (Claude Code; opt-in; preview first).',
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
key: 'hook',
|
|
@@ -180,7 +180,7 @@ const CATALOG = [
|
|
|
180
180
|
invocation: invocationOf('recipes'),
|
|
181
181
|
group: 'Orchestrate',
|
|
182
182
|
kind: READ_ONLY,
|
|
183
|
-
oneLine: 'See the orchestration recipes (Solo / Reviewed / Council / Delegated), which one fits this environment, and the configured per-activity line to paste at session start.',
|
|
183
|
+
oneLine: 'See the orchestration recipes (Solo / Reviewed / Council / Delegated / Subagent), which one fits this environment, and the configured per-activity line to paste at session start.',
|
|
184
184
|
},
|
|
185
185
|
{
|
|
186
186
|
key: 'procedures',
|
|
@@ -341,7 +341,7 @@ const TUNE_TAIL = Object.freeze([
|
|
|
341
341
|
'',
|
|
342
342
|
'Tune — opt-in accelerators (consent-first: every writer previews before writing; nothing runs without your yes)',
|
|
343
343
|
` ${BARE_INVOCATION} velocity routine read-only commands stop prompting (incl. the --kit-tools tier for the kit's own read-only tools)`,
|
|
344
|
-
` ${BARE_INVOCATION} agents
|
|
344
|
+
` ${BARE_INVOCATION} agents subagent vehicles: four read-only (cheap ones take the mechanical work, a review lens gives another opinion) + the one full-tool executor that carries bounded slices you verify`,
|
|
345
345
|
` ${BARE_INVOCATION} gates run your declared gates (docs/ai/gates.json) as one batch; its guide also offers the consent-gated seeding preview — writes only on your yes`,
|
|
346
346
|
` ${BARE_INVOCATION} hook auto-approve exactly your declared gate commands (byte-exact matches only)`,
|
|
347
347
|
` ${BARE_INVOCATION} set-recipe put a ready review backend to work on plans and diffs`,
|
package/tools/direct-run.mjs
CHANGED
|
@@ -56,6 +56,12 @@ export const LIBRARY_ONLY_MODULES = Object.freeze({
|
|
|
56
56
|
// Named by references/modes/mcp.md as the read half the advisor and uninstall ask. It only ever
|
|
57
57
|
// REPORTS; the command that acts on what it reports is the mode itself.
|
|
58
58
|
'mcp-registration.mjs': '/agent-workflow-kit mcp',
|
|
59
|
+
// Named by references/modes/set-recipe.md as the activity/slot registry; the command that shows
|
|
60
|
+
// the recipes it defines, resolved for this environment, is the recipes advisor.
|
|
61
|
+
'carriers.mjs': '/agent-workflow-kit recipes',
|
|
62
|
+
// The READ core of the subagent-vehicle surface: it sits one name away from the writer
|
|
63
|
+
// references/modes/agents.md DOES name, and reaching for it is reaching for the agents mode.
|
|
64
|
+
'cheap-agents-read.mjs': '/agent-workflow-kit agents',
|
|
59
65
|
});
|
|
60
66
|
|
|
61
67
|
// The frozen refusal line. One line, names the module, names the command.
|
package/tools/doc-parity.mjs
CHANGED
|
@@ -72,6 +72,8 @@ import { RELAYED_ENSURE_TOKENS, RELAYED_FAILURE_CAUSES } from './ensure-vocabula
|
|
|
72
72
|
// The MCP registration's four public strings. Imported from the READ-ONLY leaf, never from the
|
|
73
73
|
// writer: a read-only lint must not pull the atomic-write core into its import graph.
|
|
74
74
|
import { ENABLED_KEY as MCP_ENABLED_KEY, MCP_JSON_REL, SERVER_NAME as MCP_SERVER_NAME, allowRulesFor } from './mcp-registration.mjs';
|
|
75
|
+
// The spec-adoption state tokens the status mode doc must name (contract: kit/spec-adoption).
|
|
76
|
+
import { ADOPTION_STATES, SPEC_ADOPTION_LANE } from './spec-adoption.mjs';
|
|
75
77
|
|
|
76
78
|
const AUTONOMY_DOCTOR_DOC = 'references/modes/autonomy-doctor.md';
|
|
77
79
|
const RECOMMENDATIONS_DOC = 'references/modes/recommendations.md';
|
|
@@ -86,6 +88,7 @@ const RECEIPT_DEADLINE_DOC = 'references/modes/receipt-deadline.md';
|
|
|
86
88
|
const GATES_DOC = 'references/modes/gates.md';
|
|
87
89
|
const MCP_DOC = 'references/modes/mcp.md';
|
|
88
90
|
const UNINSTALL_DOC = 'references/modes/uninstall.md';
|
|
91
|
+
const STATUS_DOC = 'references/modes/status.md';
|
|
89
92
|
// One literal for the dispatch mode doc: the structure leaf already names it as the file it anchors
|
|
90
93
|
// its table in, and a second copy here is exactly the drift this lint exists to catch.
|
|
91
94
|
const DISPATCH_DOC = ADVISOR_MATRIX_DOC;
|
|
@@ -236,6 +239,11 @@ export const BINDINGS = Object.freeze([
|
|
|
236
239
|
valueBinding('mcp-enabled-key', MCP_ENABLED_KEY, `\`${MCP_ENABLED_KEY}\``, [MCP_DOC, UNINSTALL_DOC]),
|
|
237
240
|
valueBinding('mcp-server-name', MCP_SERVER_NAME, `\`"${MCP_SERVER_NAME}"\``, [MCP_DOC, UNINSTALL_DOC]),
|
|
238
241
|
...allowRulesFor().map((rule) => valueBinding(`mcp-allow-rule:${rule}`, rule, `\`${rule}\``, [MCP_DOC, UNINSTALL_DOC])),
|
|
242
|
+
// The spec-adoption state tokens: status.md renders a plain phrase per token, so the doc must name
|
|
243
|
+
// every token the survey can answer — a fifth state added to the leaf with no phrase fails here.
|
|
244
|
+
// The decline lane rides both the status line and the advisor item, so both docs name it.
|
|
245
|
+
...ADOPTION_STATES.map((state) => valueBinding(`spec-adoption:${state}`, state, `\`${state}\``, [STATUS_DOC])),
|
|
246
|
+
valueBinding('spec-adoption-lane', SPEC_ADOPTION_LANE, `--lane ${SPEC_ADOPTION_LANE}`, [RECOMMENDATIONS_DOC, UPGRADE_DOC]),
|
|
239
247
|
].map((b) => Object.freeze(b)));
|
|
240
248
|
|
|
241
249
|
// ── the pure checker (readText is injectable for hermetic tests) ────────────────────────
|
package/tools/ensure-ops.mjs
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
// • The DECISION lives where it already lived. The orchestration `_README` refresh asks
|
|
17
17
|
// orchestration-config.mjs (refreshReadme / the known-prior canonical set) and writes through
|
|
18
18
|
// orchestration-write.mjs — the file's one writer. Nothing here re-derives either.
|
|
19
|
-
// • Every token names a state this run PROVED. `already-present` follows a probe; `skipped-no-node`
|
|
20
|
-
// names
|
|
19
|
+
// • Every token names a state this run PROVED. `already-present` follows a probe; `skipped-no-node-evidence`
|
|
20
|
+
// names every Node probe that answered absent; an ADR-layout read that fails is `adr-layout-unverifiable` and
|
|
21
21
|
// writes NOTHING (the STRICT survey, fail-closed — the lenient status wrapper reads an unreadable
|
|
22
22
|
// tree as `none`, which here would mean seeding a rotator beside a store nobody could inspect).
|
|
23
23
|
// • A failed op is a non-zero signal, never a line that reads like success.
|
|
@@ -34,6 +34,7 @@ import { GATES_REL } from './gates-declaration.mjs';
|
|
|
34
34
|
import { AUTONOMY_REL } from './autonomy-config.mjs';
|
|
35
35
|
import { surveyAdrLayoutStrict } from './family-registry.mjs';
|
|
36
36
|
import { ENSURE_TOKENS, FAILURE_CAUSES, SEED_SCRIPTS } from './ensure-vocabulary.mjs';
|
|
37
|
+
import { NODE_EVIDENCE, describeNodeProbes, probeNodeEvidence } from './node-evidence.mjs';
|
|
37
38
|
|
|
38
39
|
// The closed vocabulary lives in its own PURE leaf so the read-only doc-parity lint can bind the
|
|
39
40
|
// relayed token set without importing this module's writer graph. Re-exported here because every
|
|
@@ -49,7 +50,6 @@ export {
|
|
|
49
50
|
WRITE_TOKENS,
|
|
50
51
|
} from './ensure-vocabulary.mjs';
|
|
51
52
|
|
|
52
|
-
const PACKAGE_JSON = 'package.json';
|
|
53
53
|
const SCRIPTS_DIR = 'scripts';
|
|
54
54
|
|
|
55
55
|
const outcome = (op, token, lines, failed = false) => {
|
|
@@ -204,9 +204,19 @@ export const ensureAutonomy = ({ cwd, kitRoot, dryRun = false, deps = {} }) =>
|
|
|
204
204
|
|
|
205
205
|
// ── 4. scripts/ — the ADR-cascade enforcement pairs, detect-first ──────────────────────────────────
|
|
206
206
|
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
|
|
207
|
+
// The Node-evidence refusal every ensure that places Node scripts shares (contract: kit/node-evidence):
|
|
208
|
+
// null when Node provably runs here, else the ONE outcome the caller returns — a stated skip naming the
|
|
209
|
+
// probes that answered absent, or a fail-closed failure when a probe could not be read.
|
|
210
|
+
export const nodeEvidenceRefusal = (op, cwd, lstat) => {
|
|
211
|
+
const evidence = probeNodeEvidence(cwd, lstat);
|
|
212
|
+
if (evidence.state === NODE_EVIDENCE.UNREADABLE) {
|
|
213
|
+
return loud(op, 'node-evidence-unverifiable', `${SCRIPTS_DIR}/: whether Node runs here could not be read (${evidence.error}), so nothing was written — resolve it by hand, then re-run`);
|
|
214
|
+
}
|
|
215
|
+
if (evidence.state === NODE_EVIDENCE.NONE) {
|
|
216
|
+
return ok(op, 'skipped-no-node-evidence', `${SCRIPTS_DIR}/: no Node evidence in this tree — probed ${describeNodeProbes(evidence)}, no regular file present; the seeded pairs are Node enforcement, so nothing written`);
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
};
|
|
210
220
|
|
|
211
221
|
const OLD_ADR_LAYOUTS = new Set(['old', 'old-unrotated']);
|
|
212
222
|
|
|
@@ -221,9 +231,8 @@ const partialNote = (lines) => {
|
|
|
221
231
|
export const ensureScripts = ({ cwd, kitRoot, dryRun = false, deps = {} }) => {
|
|
222
232
|
const lstat = deps.lstat ?? lstatSync;
|
|
223
233
|
const read = deps.readFile ?? readFileSync;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
234
|
+
const refusal = nodeEvidenceRefusal('scripts', cwd, lstat);
|
|
235
|
+
if (refusal) return refusal;
|
|
227
236
|
let layout;
|
|
228
237
|
try {
|
|
229
238
|
layout = surveyAdrLayoutStrict(cwd, deps);
|
package/tools/ensure-specs.mjs
CHANGED
|
@@ -26,7 +26,7 @@ import { readFileSync, lstatSync } from 'node:fs';
|
|
|
26
26
|
import { join } from 'node:path';
|
|
27
27
|
import { writeContainedFileAtomic, writeProjectFileCreateOnly } from './atomic-write.mjs';
|
|
28
28
|
import { classifyDeployedScript } from './script-priors.mjs';
|
|
29
|
-
import { composeFailure, composeOutcome,
|
|
29
|
+
import { composeFailure, composeOutcome, nodeEvidenceRefusal, probeSeedTarget, tmpNote } from './ensure-ops.mjs';
|
|
30
30
|
|
|
31
31
|
const OP = 'specs';
|
|
32
32
|
const SCRIPTS_DIR = 'scripts';
|
|
@@ -159,9 +159,8 @@ const renderStoreRoot = (kitRoot, read, today) => {
|
|
|
159
159
|
export const ensureSpecs = ({ cwd, kitRoot, dryRun = false, deps = {} }) => {
|
|
160
160
|
const lstat = deps.lstat ?? lstatSync;
|
|
161
161
|
const read = deps.readFile ?? readFileSync;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
162
|
+
const refusal = nodeEvidenceRefusal(OP, cwd, lstat);
|
|
163
|
+
if (refusal) return refusal;
|
|
165
164
|
const survey = (name) => surveyScript({ cwd, kitRoot, name, read, lstat });
|
|
166
165
|
const reader = READER_PAIR.map(survey);
|
|
167
166
|
const checker = CHECKER_PAIR.map(survey);
|
|
@@ -31,7 +31,7 @@ export const ENSURE_TOKENS = Object.freeze([
|
|
|
31
31
|
'already-present',
|
|
32
32
|
'customized-preserved',
|
|
33
33
|
'malformed-preserved',
|
|
34
|
-
'skipped-no-node',
|
|
34
|
+
'skipped-no-node-evidence',
|
|
35
35
|
'old-adr-layout-migration-instructed',
|
|
36
36
|
'failed',
|
|
37
37
|
]);
|
|
@@ -44,6 +44,9 @@ export const FAILURE_CAUSES = Object.freeze([
|
|
|
44
44
|
'template-unreadable',
|
|
45
45
|
'bundle-unreadable',
|
|
46
46
|
'adr-layout-unverifiable',
|
|
47
|
+
// A Node probe (package.json, a kit-seeded script) failed with anything but ENOENT: whether Node runs
|
|
48
|
+
// here is unproven, so the ensures that place Node scripts write nothing (contract: kit/node-evidence).
|
|
49
|
+
'node-evidence-unverifiable',
|
|
47
50
|
'wrong-node-kind',
|
|
48
51
|
'write-refused',
|
|
49
52
|
'unexpected-error',
|
|
@@ -75,7 +78,7 @@ export const RELAYED_ENSURE_TOKENS = Object.freeze([
|
|
|
75
78
|
'customized-preserved',
|
|
76
79
|
'malformed-preserved',
|
|
77
80
|
'already-present',
|
|
78
|
-
'skipped-no-node',
|
|
81
|
+
'skipped-no-node-evidence',
|
|
79
82
|
'old-adr-layout-migration-instructed',
|
|
80
83
|
'failed',
|
|
81
84
|
]);
|
|
@@ -28,7 +28,7 @@ import { parseSemver, compareSemver } from './semver-lite.mjs';
|
|
|
28
28
|
import { validateManifest, readAuthoritativeVersion, UNSUPPORTED, INVALID } from './manifest/validate.mjs';
|
|
29
29
|
import { START_MARKER, excludePath, inferVisibility } from './hide-footprint.mjs';
|
|
30
30
|
import { readEngineFragment, ORCHESTRATION_FRAGMENT_REL, PROCEDURES_FRAGMENT_REL, AUTONOMY_FRAGMENT_REL, LENS_FRAGMENT_REL, LENS_PRIORS_REL } from './engine-source.mjs';
|
|
31
|
-
import { ACTIVITIES, resolveActivityRecipe } from './recipes.mjs';
|
|
31
|
+
import { ACTIVITIES, resolveActivityRecipe, composeReadiness, safeLine } from './recipes.mjs';
|
|
32
32
|
// The config reader lives in orchestration-config.mjs (the single config contract). The read-only status
|
|
33
33
|
// settings-survey reuses THIS reader (one strict-JSON + loud-on-malformed contract), not a second copy.
|
|
34
34
|
import { loadConfig } from './orchestration-config.mjs';
|
|
@@ -52,9 +52,10 @@ import { HOOK_FILE_REL as GATE_HOOK_FILE_REL, isHookWired } from './gate-hook.mj
|
|
|
52
52
|
// writer, which pulls in the atomic-write core) so the status survey stays a pure reader.
|
|
53
53
|
import { settingsSnapshot } from './bridge-settings-read.mjs';
|
|
54
54
|
import { GATES_REL, loadDeclaration } from './run-gates.mjs';
|
|
55
|
-
// The cheap-agents
|
|
56
|
-
// (one implementation, never a drifting copy;
|
|
57
|
-
|
|
55
|
+
// The cheap-agents READ core's bundle reader, placement planner and executor-vehicle survey —
|
|
56
|
+
// reused by the settings survey (one implementation, never a drifting copy; the read core imports
|
|
57
|
+
// only node builtins, no cycle).
|
|
58
|
+
import { readBundledAgents, planPlacement, surveyExecutorVehicle, EXECUTOR_VEHICLE, assertDirSafe, readFsDeps, CLAUDE_DIR, AGENTS_DIR } from './cheap-agents-read.mjs';
|
|
58
59
|
// The status vocabulary (manifestState constants, internal→public maps, display names, the no-leak
|
|
59
60
|
// forbidden set) lives in the frozen labels.mjs LEAF (Plan §4.2 B1) so the import graph is acyclic —
|
|
60
61
|
// nothing imports family-registry for vocabulary. Imported here for internal use; the public subset is
|
|
@@ -82,6 +83,8 @@ import {
|
|
|
82
83
|
import { detectSurface } from './surface.mjs';
|
|
83
84
|
import { toViewModel } from './view-model.mjs';
|
|
84
85
|
import { render } from './renderers.mjs';
|
|
86
|
+
// The feature-spec adoption state (contract: kit/spec-adoption) — read-only leaves, no cycle.
|
|
87
|
+
import { ADOPTION, readDeclineAck, surveySpecAdoption } from './spec-adoption.mjs';
|
|
85
88
|
|
|
86
89
|
// ── manifestState values — re-export the EXACT public subset family-registry exported before B1 ─────
|
|
87
90
|
// (the 7 state constants + DISPLAY_NAMES) so every existing importer (uninstall.mjs, the test suites)
|
|
@@ -428,9 +431,33 @@ const surveyAdrLayout = (dir, deps) => {
|
|
|
428
431
|
}
|
|
429
432
|
};
|
|
430
433
|
|
|
434
|
+
// The spec-adoption survey, LENIENT for the read-only view — two facts, each failing on its own: a
|
|
435
|
+
// survey that throws becomes the `unreadable` state with its reason, and an ack read that throws
|
|
436
|
+
// keeps the store state and carries `declineError` beside `declined: false` — the status line still
|
|
437
|
+
// renders, never a crash, never a silent "not adopted" and never a decline it could not read.
|
|
438
|
+
const surveySpecs = (dir, deps) => {
|
|
439
|
+
const survey = (() => {
|
|
440
|
+
try {
|
|
441
|
+
return surveySpecAdoption(dir, deps);
|
|
442
|
+
} catch (err) {
|
|
443
|
+
return { state: ADOPTION.UNREADABLE, live: 0, draft: 0, reason: localizeError(err) };
|
|
444
|
+
}
|
|
445
|
+
})();
|
|
446
|
+
const decline = (() => {
|
|
447
|
+
if (survey.state === ADOPTION.ADOPTED) return { declined: false, declineError: null };
|
|
448
|
+
try {
|
|
449
|
+
return { declined: readDeclineAck(dir, deps), declineError: null };
|
|
450
|
+
} catch (err) {
|
|
451
|
+
return { declined: false, declineError: localizeError(err) };
|
|
452
|
+
}
|
|
453
|
+
})();
|
|
454
|
+
return { state: survey.state, live: survey.live, draft: survey.draft, reason: survey.reason, ...decline };
|
|
455
|
+
};
|
|
456
|
+
|
|
431
457
|
// surveyProject → the deploy axis for a target project dir: the per-member deployment stamps, whether
|
|
432
|
-
// docs/ai/ exists, the ADR-store layout, and whether the hidden-mode fence is
|
|
433
|
-
// only, all injectable), no git subprocess — the read-only `status` view must
|
|
458
|
+
// docs/ai/ exists, the ADR-store layout, the spec-adoption state, and whether the hidden-mode fence is
|
|
459
|
+
// present. Pure (fs reads only, all injectable), no git subprocess — the read-only `status` view must
|
|
460
|
+
// never mutate or spawn.
|
|
434
461
|
export const surveyProject = (projectDir, deps = {}) => {
|
|
435
462
|
const exists = deps.exists ?? existsSync;
|
|
436
463
|
const dir = resolve(projectDir);
|
|
@@ -445,7 +472,7 @@ export const surveyProject = (projectDir, deps = {}) => {
|
|
|
445
472
|
}
|
|
446
473
|
})();
|
|
447
474
|
const deployed = stamps.some((s) => s.version != null) || docsAiPresent;
|
|
448
|
-
return { dir, deployed, docsAiPresent, adrLayout: surveyAdrLayout(dir, deps), hiddenFence: hasHiddenFence(dir, deps), stamps };
|
|
475
|
+
return { dir, deployed, docsAiPresent, adrLayout: surveyAdrLayout(dir, deps), specs: surveySpecs(dir, deps), hiddenFence: hasHiddenFence(dir, deps), stamps };
|
|
449
476
|
};
|
|
450
477
|
|
|
451
478
|
// ── report ───────────────────────────────────────────────────────────────────────
|
|
@@ -460,9 +487,6 @@ export const surveyProject = (projectDir, deps = {}) => {
|
|
|
460
487
|
// consumes THIS, never the human table verbatim. An envelope-shape test pins its shape so later phases
|
|
461
488
|
// (the settings/visibility block) can't silently break the Phase-2 version consumer.
|
|
462
489
|
|
|
463
|
-
// STATE_PUBLIC (internal→public token map) + DISPLAY_NAMES + displayOf now live in labels.mjs (B1) —
|
|
464
|
-
// imported at the top of this file. They are used below exactly as before.
|
|
465
|
-
|
|
466
490
|
// ── the settings survey (Phase 3) — read-only, honest, localized-on-error ──────────
|
|
467
491
|
// Each sub-survey returns a small user-safe object OR a single `{ error }` field (a localized message,
|
|
468
492
|
// never a crash): a malformed/unreadable file in ONE area must not break the rest of `status`. The
|
|
@@ -501,19 +525,21 @@ export const surveyVisibility = (dir, deps = {}) => {
|
|
|
501
525
|
};
|
|
502
526
|
|
|
503
527
|
// orchestration recipes: the EFFECTIVE recipe per slot (config · default · effective), engine-free —
|
|
504
|
-
// shared loadConfig + resolveActivityRecipe + the
|
|
505
|
-
//
|
|
528
|
+
// shared loadConfig + resolveActivityRecipe + the ONE readiness composition (detected backends +
|
|
529
|
+
// the executor vehicle). A malformed config → a localized error field; a detection failure floors
|
|
530
|
+
// at solo (a corrupt bridge must not break the view) but is surfaced as `detectError`, so the
|
|
531
|
+
// render says "couldn't check backends" instead of letting a real solo-default look identical.
|
|
506
532
|
export const surveyRecipes = (dir, deps = {}) => {
|
|
507
|
-
// A detector failure floors recipes at solo (mirrors procedures) but is surfaced as `detectError`, so
|
|
508
|
-
// the render says "couldn't check backends" instead of letting a real solo-default look identical.
|
|
509
533
|
const { detection, error: detectError } = detectSafe(deps);
|
|
534
|
+
const projectDir = resolve(dir);
|
|
535
|
+
const readiness = composeReadiness(projectDir, { ...deps, detect: () => detection });
|
|
510
536
|
try {
|
|
511
|
-
const { config, source } = loadConfig(
|
|
537
|
+
const { config, source } = loadConfig(projectDir, deps.readFile ?? readFileSync, deps.lstat ?? lstatSync);
|
|
512
538
|
const activities = {};
|
|
513
539
|
for (const [activity, def] of Object.entries(ACTIVITIES)) {
|
|
514
540
|
activities[activity] = {};
|
|
515
541
|
for (const slot of Object.keys(def.slots)) {
|
|
516
|
-
const r = resolveActivityRecipe({ config: config ?? {}, readiness
|
|
542
|
+
const r = resolveActivityRecipe({ config: config ?? {}, readiness, activity, slot });
|
|
517
543
|
activities[activity][slot] = { recipe: r.recipe, source: r.source, degradedFrom: r.degradedFrom ?? null };
|
|
518
544
|
}
|
|
519
545
|
}
|
|
@@ -590,14 +616,34 @@ export const surveyGateHook = (dir, deps = {}) => {
|
|
|
590
616
|
};
|
|
591
617
|
|
|
592
618
|
// cheap agents: the kit-placed .claude/agents/ vehicles (Mode: agents) — how many of the bundled
|
|
593
|
-
//
|
|
594
|
-
// the writer preserves it) — the welcome-mat agents rung keys on
|
|
595
|
-
//
|
|
619
|
+
// definitions are present in the project, plus the ONE full-tool vehicle's own state. A customized
|
|
620
|
+
// copy counts as PLACED (it exists; the writer preserves it) — the welcome-mat agents rung keys on
|
|
621
|
+
// zero placed, so the counts span every vehicle, the executor included. The executor state comes
|
|
622
|
+
// from the read core's survey (the subagent carrier's one instrument — one implementation), which
|
|
623
|
+
// answers a state instead of throwing; its reason rides along when it carries one.
|
|
596
624
|
export const surveyCheapAgents = (dir, deps = {}) => {
|
|
597
625
|
try {
|
|
626
|
+
const projectDir = resolve(dir);
|
|
598
627
|
const templates = readBundledAgents(deps);
|
|
599
|
-
const
|
|
600
|
-
|
|
628
|
+
const vehicle = (deps.surveyVehicle ?? surveyExecutorVehicle)(projectDir, deps);
|
|
629
|
+
// The executor is judged by its own survey (which answers a state where the placement plan
|
|
630
|
+
// would throw); the plan covers the read-only vehicles, so a broken executor never hides them,
|
|
631
|
+
// and it never follows a symlinked ancestor — the writer's own guard refuses first.
|
|
632
|
+
const executor = { executor: vehicle.state, ...(vehicle.reason ? { executorReason: safeLine(vehicle.reason) } : {}) };
|
|
633
|
+
try {
|
|
634
|
+
const fs = readFsDeps(deps);
|
|
635
|
+
assertDirSafe(join(projectDir, CLAUDE_DIR), CLAUDE_DIR, fs);
|
|
636
|
+
assertDirSafe(join(projectDir, AGENTS_DIR), AGENTS_DIR, fs);
|
|
637
|
+
const plan = planPlacement(templates.filter((t) => t.name !== EXECUTOR_VEHICLE), projectDir, deps);
|
|
638
|
+
return {
|
|
639
|
+
bundled: templates.length,
|
|
640
|
+
readOnly: templates.filter((t) => t.name !== EXECUTOR_VEHICLE).length,
|
|
641
|
+
placed: plan.filter((p) => p.action !== 'place').length + (['placed', 'customized'].includes(vehicle.state) ? 1 : 0),
|
|
642
|
+
...executor,
|
|
643
|
+
};
|
|
644
|
+
} catch (err) {
|
|
645
|
+
return { error: localizeError(err), ...executor };
|
|
646
|
+
}
|
|
601
647
|
} catch (err) {
|
|
602
648
|
return { error: localizeError(err) };
|
|
603
649
|
}
|
|
@@ -683,6 +729,9 @@ export const buildEnvelope = (family, project = null, extras = {}) => {
|
|
|
683
729
|
deployed: project.deployed,
|
|
684
730
|
docsAi: project.docsAiPresent,
|
|
685
731
|
adrLayout: project.adrLayout, // 'old' | 'old-unrotated' | 'migrated' | 'none' — a user-safe token, never a raw path
|
|
732
|
+
// { state: not-adopted | adopting | adopted | unreadable, live, draft, reason, declined, declineError }
|
|
733
|
+
// — an envelope predating the field omits it (the view-model reads that as unknown, never as a state).
|
|
734
|
+
...(project.specs ? { specs: project.specs } : {}),
|
|
686
735
|
// member + display + version only — never the internal stamp FILENAME (s.file).
|
|
687
736
|
deployStamps: project.stamps.map((s) => ({ member: s.name, display: displayOf(s.name), version: s.version ?? null })),
|
|
688
737
|
};
|
package/tools/flow-check.mjs
CHANGED
|
@@ -29,8 +29,7 @@ import {
|
|
|
29
29
|
resolveReceiptsPath, readReceipts, computeTreeFingerprint,
|
|
30
30
|
} from './core-evidence.mjs';
|
|
31
31
|
import { loadConfig } from './orchestration-config.mjs';
|
|
32
|
-
import { requiredBackendsForConfiguredRecipe, DISPLAY_ALIASES } from './recipes.mjs';
|
|
33
|
-
import { detectBackends } from './detect-backends.mjs';
|
|
32
|
+
import { requiredBackendsForConfiguredRecipe, DISPLAY_ALIASES, composeReadiness } from './recipes.mjs';
|
|
34
33
|
import { decideFlowCheck } from './flow-check-cores.mjs';
|
|
35
34
|
import { short } from './flow-check-rungs.mjs';
|
|
36
35
|
import {
|
|
@@ -116,11 +115,7 @@ export const computeFlowDecision = ({ cwd = process.cwd(), consumer = 'gate', pr
|
|
|
116
115
|
let readiness = [];
|
|
117
116
|
let detectionFailed = false;
|
|
118
117
|
if (configFailure == null && config?.['plan-execution']?.review == null) {
|
|
119
|
-
|
|
120
|
-
readiness = detectBackends();
|
|
121
|
-
} catch {
|
|
122
|
-
detectionFailed = true;
|
|
123
|
-
}
|
|
118
|
+
readiness = composeReadiness(top, { ...probes, onDetectError: () => { detectionFailed = true; } });
|
|
124
119
|
}
|
|
125
120
|
const obligations = configFailure == null
|
|
126
121
|
? requiredBackendsForConfiguredRecipe({ config, readiness, detectionFailed })
|
|
@@ -86,10 +86,14 @@ export const KNOWN_PRIOR_METHODOLOGY_SLOT = [
|
|
|
86
86
|
'> **Workflow methodology** — plan → execute → review. Plans are ephemeral `docs/plans/*.md` (gitignored, **never committed**); every Plan ends with a mandatory **Phase: Cleanup**; series order lives in `docs/plans/queue.md`. Full vocabulary, lifecycle, and the plan-then-execute split live in the project\'s **planning skill** (it overrides the generic `writing-plans`); summary in `docs/ai/agent_rules.md` §5. Named activities (plan-authoring, plan-execution) have procedures — see `/agent-workflow-kit procedures <activity>` for the steps + resolved recipe.',
|
|
87
87
|
// engine 2.1.0 — the pre-canon-rewrite pointer (vocabulary + plan-then-execute wording, with the communication contract).
|
|
88
88
|
'> **Workflow methodology** — plan → execute → review. Plans are ephemeral `docs/plans/*.md` (gitignored, **never committed**); every Plan ends with a mandatory **Phase: Cleanup**; series order lives in `docs/plans/queue.md`. Full vocabulary, lifecycle, and the plan-then-execute split live in the project\'s **planning skill** (it overrides the generic `writing-plans`); summary in `docs/ai/agent_rules.md` §5. Named activities (plan-authoring, plan-execution) have procedures — see `/agent-workflow-kit procedures <activity>` for the steps + resolved recipe. **Communication:** user-facing messages deliver the artifact inline (paste the prompt / diff / command — never "see §X" as a substitute), lead with the result, show exactly what was asked, and never read as mockery (a large artifact: a real summary inline + a link).',
|
|
89
|
+
// engine 4.2.0 — the two-activity pointer, before `routine` joined the named activities.
|
|
90
|
+
'> **Workflow methodology** — plan → execute → review. Plans are ephemeral `docs/plans/*.md` (gitignored, **never committed**); every Plan ends with a mandatory **Phase: Cleanup**; series order lives in `docs/plans/queue.md`. The plan shape, its caps and lifecycle live in the project\'s **planning skill** (it overrides the generic `writing-plans`); summary in `docs/ai/agent_rules.md` §5. Named activities (plan-authoring, plan-execution) have procedures — see `/agent-workflow-kit procedures <activity>` for the steps + resolved recipe. **Communication:** user-facing messages deliver the artifact inline (paste the prompt / diff / command — never "see §X" as a substitute), lead with the result, show exactly what was asked, and never read as mockery (a large artifact: a real summary inline + a link).',
|
|
89
91
|
];
|
|
90
92
|
export const KNOWN_PRIOR_ORCH_SLOT = [
|
|
91
93
|
// v1.3.0 — pre-read-at-start orchestration pointer (recipes vocabulary, no orchestration.json clause).
|
|
92
94
|
'> **Orchestration recipes** — compose plan → execute → review with a named recipe: **Solo** (no backend), **Reviewed** (one backend reviews), **Council** (both review, you synthesize), **Delegated** (a backend executes a bounded sub-task); the orchestrator always commits, a backend is never autonomous. Pick + plan one for this environment with `/agent-workflow-kit recipes` (read-only); the deployed how/why lives in your `docs/ai/` workflow docs.',
|
|
95
|
+
// engine 4.2.0 — the four-recipe pointer with the read-at-start clause, before the Subagent carrier joined the list.
|
|
96
|
+
'> **Orchestration recipes** — compose plan → execute → review with a named recipe: **Solo** (no backend), **Reviewed** (one backend reviews), **Council** (both review, you synthesize), **Delegated** (a backend executes a bounded sub-task); the orchestrator always commits, a backend is never autonomous. Pick + plan one for this environment with `/agent-workflow-kit recipes` (read-only); the deployed how/why lives in your `docs/ai/` workflow docs. At the start of a planning/execution session, read your standing recipe preference in `docs/ai/orchestration.json` — set it in plain language with `/agent-workflow-kit set-recipe` (previews first; hand-edit stays supported).',
|
|
93
97
|
];
|
|
94
98
|
|
|
95
99
|
// A slot descriptor bundles everything the generic engine needs to operate on ONE marker pair.
|
package/tools/lens-region.mjs
CHANGED
|
@@ -62,7 +62,10 @@ Apply this as part of §2 before any user-facing summary:
|
|
|
62
62
|
- **No condescension, no filler.** Own a miss plainly and fix it in the same message.
|
|
63
63
|
- **Large artifact (≈>100 lines):** deliver a real summary or the key excerpt inline **and** link the file — never flood the reader with a 2000-line paste, never hide the answer behind a bare pointer.
|
|
64
64
|
- **Live host/session facts are tool-composed only.** Any claim about the current host or session state (prompts fired, sandbox scope, whether a bypass was needed, network reachability, approval counts) must trace to **live tool output** from **this session**; a memory/handover snapshot is **context, never report facts**, and a claim with no live signal is **omitted or explicitly marked unverified** — never asserted from recollection.`;
|
|
65
|
-
|
|
65
|
+
// The canon that shipped between the closing-state-block contract and the contradicted-skip bullet.
|
|
66
|
+
const COMMS_PRIOR_STATE_BLOCK = `${COMMS_PRIOR_PLAIN_LANGUAGE}
|
|
67
|
+
- **The closing state block answers three DIFFERENT questions.** Close a user-facing message with three labelled slots — *now* · *what I need from you* · *what's next*. The slot LABELS stay ENGLISH — an English label is what lets a state-block checker FIND the block and its slots at all; everything written INTO a slot is in the project's dialogue language; when that language is not English, the checker's English phrase sets do not judge those values. **Now** = the state at this instant: what is RUNNING, or what the work is stopped on. It is **never a report of finished work** — what you completed goes in the message BODY, above the block. **From you** = the real unblocker, named; a turn that is ENDING always has one. **Next** = what follows. A *now* slot that opens with what was completed buries the one fact the reader opened the message for, and the three slots collapse into one restatement.`;
|
|
68
|
+
export const COMMS_PRIORS = [COMMS_PRIOR_PRE_AD054, COMMS_PRIOR_AD054, COMMS_PRIOR_PLAIN_LANGUAGE, COMMS_PRIOR_STATE_BLOCK];
|
|
66
69
|
|
|
67
70
|
const stripCr = (line) => (line.endsWith('\r') ? line.slice(0, -1) : line);
|
|
68
71
|
const isBoundary = (bareLine) => bareLine === '---' || /^#{2,3} /.test(bareLine);
|