@jaimevalasek/aioson 1.30.2 → 1.36.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 +75 -0
- package/README.md +19 -6
- package/docs/en/1-understand/ecosystem-map.md +45 -29
- package/docs/en/1-understand/glossary.md +5 -5
- package/docs/en/1-understand/what-is-aioson.md +5 -5
- package/docs/en/2-start/existing-project.md +7 -7
- package/docs/en/2-start/first-project.md +32 -38
- package/docs/en/2-start/initial-decisions.md +18 -17
- package/docs/en/3-recipes/README.md +2 -2
- package/docs/en/3-recipes/continuity-between-sessions.md +2 -2
- package/docs/en/3-recipes/from-idea-to-prd-via-briefing.md +5 -2
- package/docs/en/3-recipes/full-feature-with-sheldon.md +327 -338
- package/docs/en/4-agents/README.md +28 -14
- package/docs/en/4-agents/discovery-design-doc.md +20 -8
- package/docs/en/4-agents/forge-run.md +3 -2
- package/docs/en/5-reference/cli-reference.md +51 -48
- package/docs/en/5-reference/executable-verification.md +10 -7
- package/docs/en/5-reference/parallel.md +2 -0
- package/docs/en/5-reference/qa-browser.md +2 -2
- package/docs/en/README.md +3 -3
- package/docs/pt/1-entender/glossario.md +4 -4
- package/docs/pt/1-entender/mapa-do-ecossistema.md +32 -21
- package/docs/pt/2-comecar/decisoes-iniciais.md +16 -16
- package/docs/pt/2-comecar/primeiro-projeto.md +1 -1
- package/docs/pt/3-receitas/README.md +1 -1
- package/docs/pt/3-receitas/app-saas-do-zero.md +35 -122
- package/docs/pt/3-receitas/feature-completa-com-sheldon.md +289 -338
- package/docs/pt/4-agentes/analyst.md +9 -5
- package/docs/pt/4-agentes/architect.md +9 -5
- package/docs/pt/4-agentes/dev.md +19 -7
- package/docs/pt/4-agentes/discovery-design-doc.md +25 -23
- package/docs/pt/4-agentes/orchestrator.md +164 -118
- package/docs/pt/4-agentes/pentester.md +5 -5
- package/docs/pt/4-agentes/pm.md +15 -7
- package/docs/pt/4-agentes/sheldon.md +166 -99
- package/docs/pt/4-agentes/ux-ui.md +7 -4
- package/docs/pt/5-referencia/agent-chain-continuity.md +14 -5
- package/docs/pt/5-referencia/comandos-cli.md +1 -1
- package/docs/pt/5-referencia/harness-retro.md +2 -1
- package/docs/pt/5-referencia/sdd-automation-scripts.md +6 -6
- package/docs/pt/5-referencia/sdd-framework.md +53 -16
- package/docs/pt/5-referencia/sdd-planos-e-estrutura.md +27 -15
- package/docs/pt/README.md +4 -4
- package/docs/pt/agentes.md +48 -50
- package/package.json +2 -2
- package/src/artifact-kinds.js +110 -0
- package/src/cli.js +82 -42
- package/src/commands/agent-epilogue.js +251 -186
- package/src/commands/agents.js +104 -48
- package/src/commands/audit-code.js +344 -0
- package/src/commands/classify.js +75 -13
- package/src/commands/feature-close.js +43 -18
- package/src/commands/harness-check.js +259 -175
- package/src/commands/harness-retro-promote.js +387 -0
- package/src/commands/live.js +24 -0
- package/src/commands/prototype-check.js +163 -0
- package/src/commands/review-feature.js +189 -0
- package/src/commands/runtime.js +81 -66
- package/src/commands/sync-agents-copy.js +115 -0
- package/src/commands/verification-plan.js +116 -0
- package/src/commands/verify-artifact.js +530 -0
- package/src/commands/verify-implementation.js +428 -0
- package/src/commands/workflow-execute.js +309 -309
- package/src/commands/workflow-next.js +361 -19
- package/src/commands/workflow-plan.js +5 -5
- package/src/constants.js +4 -0
- package/src/gateway-pointer-merge.js +7 -1
- package/src/handoff-contract.js +267 -172
- package/src/harness/contract-integrity-gate.js +172 -0
- package/src/harness/contract-integrity.js +111 -0
- package/src/harness/contract-schema.js +377 -332
- package/src/harness/detect-runtime-feature.js +90 -0
- package/src/harness/static-criteria.js +192 -0
- package/src/i18n/messages/en.js +8 -4
- package/src/i18n/messages/es.js +8 -4
- package/src/i18n/messages/fr.js +8 -4
- package/src/i18n/messages/pt-BR.js +8 -4
- package/src/install-wizard.js +8 -8
- package/src/installer.js +13 -6
- package/src/lib/retro/retro-render.js +10 -1
- package/src/lib/retro/retro-sources.js +45 -27
- package/src/lib/retro/verification-reports.js +230 -0
- package/src/parser.js +6 -0
- package/src/preflight-engine.js +12 -12
- package/src/runtime-store.js +13 -9
- package/src/verification/evidence-bundle.js +251 -0
- package/src/verification/ledger-store.js +221 -0
- package/src/verification/path-policy.js +74 -0
- package/src/verification/policy-engine.js +95 -0
- package/src/verification/prompt-package.js +314 -0
- package/src/verification/redaction.js +77 -0
- package/src/verification/report-parser.js +132 -0
- package/src/verification/report-store.js +97 -0
- package/src/verification/result.js +16 -0
- package/src/verification/runners/index.js +319 -0
- package/src/verification/runtime-telemetry.js +144 -0
- package/src/verification/schema.js +276 -0
- package/src/verification/source-discovery.js +153 -0
- package/src/verification-policy.js +398 -0
- package/src/version.js +52 -1
- package/template/.aioson/agents/analyst.md +9 -6
- package/template/.aioson/agents/architect.md +34 -5
- package/template/.aioson/agents/briefing-refiner.md +25 -0
- package/template/.aioson/agents/briefing.md +69 -12
- package/template/.aioson/agents/committer.md +2 -1
- package/template/.aioson/agents/copywriter.md +30 -21
- package/template/.aioson/agents/design-hybrid-forge.md +28 -15
- package/template/.aioson/agents/dev.md +28 -10
- package/template/.aioson/agents/deyvin.md +3 -2
- package/template/.aioson/agents/discover.md +12 -3
- package/template/.aioson/agents/discovery-design-doc.md +7 -2
- package/template/.aioson/agents/genome.md +19 -10
- package/template/.aioson/agents/neo.md +30 -30
- package/template/.aioson/agents/orache.md +20 -11
- package/template/.aioson/agents/orchestrator.md +84 -7
- package/template/.aioson/agents/pm.md +8 -8
- package/template/.aioson/agents/product.md +15 -11
- package/template/.aioson/agents/profiler-enricher.md +20 -11
- package/template/.aioson/agents/profiler-forge.md +20 -11
- package/template/.aioson/agents/profiler-researcher.md +20 -11
- package/template/.aioson/agents/qa.md +87 -17
- package/template/.aioson/agents/scope-check.md +19 -5
- package/template/.aioson/agents/setup.md +12 -1
- package/template/.aioson/agents/sheldon.md +92 -9
- package/template/.aioson/agents/site-forge.md +11 -2
- package/template/.aioson/agents/squad.md +20 -5
- package/template/.aioson/agents/ux-ui.md +4 -2
- package/template/.aioson/agents/validator.md +33 -1
- package/template/.aioson/config/verification.json +61 -0
- package/template/.aioson/config.md +13 -9
- package/template/.aioson/docs/LAYERS.md +2 -2
- package/template/.aioson/docs/autopilot-handoff.md +10 -10
- package/template/.aioson/docs/dev/execution-discipline.md +41 -0
- package/template/.aioson/docs/dev/phase-loop.md +47 -0
- package/template/.aioson/docs/feature-expansion-taxonomy.md +31 -3
- package/template/.aioson/docs/presets/workflow.config.full-merged.json +15 -0
- package/template/.aioson/docs/presets/workflow.config.lean.json +15 -0
- package/template/.aioson/docs/product/prd-contract.md +12 -12
- package/template/.aioson/docs/prototype-contract.md +98 -0
- package/template/.aioson/docs/reference-identity.md +94 -0
- package/template/.aioson/docs/sheldon/harness-contract.md +155 -28
- package/template/.aioson/docs/verification-config.md +82 -0
- package/template/.aioson/docs/verify-artifact-gates.md +91 -0
- package/template/.aioson/docs/workflow-lean-lane.md +129 -0
- package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +2 -0
- package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +16 -14
- package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +2 -2
- package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +4 -4
- package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +15 -15
- package/template/.aioson/skills/process/briefing-expansion-scout/SKILL.md +25 -3
- package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +5 -3
- package/template/.aioson/skills/process/design-hybrid-forge/references/external-source-ingestion.md +89 -0
- package/template/.aioson/skills/process/product-scope-expansion/SKILL.md +29 -2
- package/template/.aioson/skills/process/prototype-forge/SKILL.md +98 -0
- package/template/.aioson/skills/process/reference-identity-extract/SKILL.md +164 -0
- package/template/.aioson/skills/process/sheldon-expansion-audit/SKILL.md +23 -2
- package/template/.aioson/skills/static/multi-agent-patterns.md +5 -5
- package/template/CLAUDE.md +15 -11
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Verification sub-agent policy (.aioson/config/verification.json).
|
|
4
|
+
//
|
|
5
|
+
// Piece 1 of the "smart harness, fewer agents doing more" redesign. This is the
|
|
6
|
+
// reader the @dev phase-loop (auto-continue) and the post-dev review cycle
|
|
7
|
+
// consult to decide WHICH verification sub-agent runs, WHEN, and on WHICH model
|
|
8
|
+
// — resolved per host harness.
|
|
9
|
+
//
|
|
10
|
+
// Two dispatch modes per agent, keyed by the host harness (claude|codex|opencode):
|
|
11
|
+
// - native: in-harness sub-agent. On Claude Code the Task tool runs a Claude
|
|
12
|
+
// model tier (e.g. sonnet-4.6); codex/opencode run their own
|
|
13
|
+
// configured model. You CANNOT run a codex/gpt model as a native
|
|
14
|
+
// Claude Code sub-agent — that is what `external` is for.
|
|
15
|
+
// - external: spawn a different vendor CLI as a read-only auditor
|
|
16
|
+
// (`aioson verify:implementation --tool=...`). The only way to bring
|
|
17
|
+
// a cross-vendor model in while hosted elsewhere — see `cross_check`.
|
|
18
|
+
//
|
|
19
|
+
// The file is auto-generated from template/.aioson/config/verification.json by the
|
|
20
|
+
// installer (isConfigMergePath additive merge) and is hand-editable: user values
|
|
21
|
+
// survive `aioson update`, only `version` is template-owned. A missing or
|
|
22
|
+
// malformed file degrades to buildDefaultVerificationConfig() — never throws.
|
|
23
|
+
|
|
24
|
+
const fs = require('node:fs/promises');
|
|
25
|
+
const path = require('node:path');
|
|
26
|
+
const { exists } = require('./utils');
|
|
27
|
+
|
|
28
|
+
const VERIFICATION_CONFIG_RELATIVE_PATH = '.aioson/config/verification.json';
|
|
29
|
+
const KNOWN_HOSTS = ['claude', 'codex', 'opencode'];
|
|
30
|
+
const DISPATCH_MODES = ['native', 'external'];
|
|
31
|
+
const VERIFICATION_AGENTS = ['qa', 'tester', 'pentester', 'validator'];
|
|
32
|
+
const TRIGGERS = ['per-phase', 'end-of-feature', 'sensitive-surface'];
|
|
33
|
+
const DEFAULT_HOST = 'claude';
|
|
34
|
+
const DEFAULT_MODEL = 'configured-default';
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Defaults
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
function defaultTriggers(agentId) {
|
|
41
|
+
switch (agentId) {
|
|
42
|
+
case 'qa': return ['per-phase', 'end-of-feature'];
|
|
43
|
+
case 'pentester': return ['sensitive-surface', 'end-of-feature'];
|
|
44
|
+
default: return ['end-of-feature'];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function defaultReport(agentId) {
|
|
49
|
+
switch (agentId) {
|
|
50
|
+
case 'qa': return 'qa-report-{slug}.md';
|
|
51
|
+
case 'tester': return 'test-report-{slug}.md';
|
|
52
|
+
case 'pentester': return 'security-findings-{slug}.json';
|
|
53
|
+
case 'validator': return 'validator-report-{slug}.json';
|
|
54
|
+
default: return `${agentId}-report-{slug}.md`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildDefaultAgent(agentId) {
|
|
59
|
+
// pentester/validator default to the strongest tier; qa/tester to a cheaper
|
|
60
|
+
// tier — the per-phase verifier should be cheap, the security/contract
|
|
61
|
+
// verdicts should not be.
|
|
62
|
+
const claudeModel = (agentId === 'pentester' || agentId === 'validator') ? 'opus-4.8' : 'sonnet-4.6';
|
|
63
|
+
const agent = {
|
|
64
|
+
enabled: (agentId === 'qa' || agentId === 'validator') ? true : 'auto',
|
|
65
|
+
triggers: defaultTriggers(agentId),
|
|
66
|
+
dispatch: {
|
|
67
|
+
claude: { mode: 'native', model: claudeModel },
|
|
68
|
+
codex: { mode: 'native', model: DEFAULT_MODEL },
|
|
69
|
+
opencode: { mode: 'native', model: DEFAULT_MODEL }
|
|
70
|
+
},
|
|
71
|
+
report: defaultReport(agentId)
|
|
72
|
+
};
|
|
73
|
+
if (agentId === 'validator') {
|
|
74
|
+
agent.cross_check = { enabled: false, mode: 'external', tool: 'codex', model: DEFAULT_MODEL };
|
|
75
|
+
}
|
|
76
|
+
return agent;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function buildDefaultVerificationConfig() {
|
|
80
|
+
const agents = {};
|
|
81
|
+
for (const id of VERIFICATION_AGENTS) agents[id] = buildDefaultAgent(id);
|
|
82
|
+
return {
|
|
83
|
+
version: '1.0',
|
|
84
|
+
host: 'auto',
|
|
85
|
+
agents,
|
|
86
|
+
budget: {
|
|
87
|
+
max_subagents_per_phase: 1,
|
|
88
|
+
skip_on_micro: true,
|
|
89
|
+
full_smoke: 'end-of-feature-only'
|
|
90
|
+
},
|
|
91
|
+
phase_loop: {
|
|
92
|
+
auto_continue: true,
|
|
93
|
+
compact_between_phases: true,
|
|
94
|
+
max_fix_retries_per_phase: 2
|
|
95
|
+
},
|
|
96
|
+
// Deterministic build-free code-quality scan (`aioson audit:code`) wired into
|
|
97
|
+
// the tracked workflow:next dev/qa done-gate. `tracked_gate`: 'advisory'
|
|
98
|
+
// records + emits but never blocks (default — audit:code is a heuristic
|
|
99
|
+
// opinion, not the feature's declared contract); 'block' makes a HIGH finding
|
|
100
|
+
// in scope a hard gate; 'off' skips. `scope`: 'changed' = the git diff (fast),
|
|
101
|
+
// 'full' = the whole tree.
|
|
102
|
+
audit_code: {
|
|
103
|
+
tracked_gate: 'advisory',
|
|
104
|
+
scope: 'changed'
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Normalization
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
// Collapse an arbitrary value to a known host, falling back when unrecognized.
|
|
114
|
+
function normalizeHost(host, fallback = DEFAULT_HOST) {
|
|
115
|
+
const safe = String(host || '').trim().toLowerCase();
|
|
116
|
+
return KNOWN_HOSTS.includes(safe) ? safe : fallback;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// The top-level `host` setting additionally allows the literal 'auto'.
|
|
120
|
+
function normalizeHostSetting(host) {
|
|
121
|
+
const safe = String(host || 'auto').trim().toLowerCase();
|
|
122
|
+
if (safe === 'auto') return 'auto';
|
|
123
|
+
return KNOWN_HOSTS.includes(safe) ? safe : 'auto';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeEnabled(value, fallback = 'auto') {
|
|
127
|
+
if (value === true || value === false) return value;
|
|
128
|
+
if (String(value).trim().toLowerCase() === 'auto') return 'auto';
|
|
129
|
+
return fallback;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizeDispatchMode(mode, fallback = 'native') {
|
|
133
|
+
const safe = String(mode || '').trim().toLowerCase();
|
|
134
|
+
return DISPATCH_MODES.includes(safe) ? safe : fallback;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function normalizeModel(model, fallback = DEFAULT_MODEL) {
|
|
138
|
+
return typeof model === 'string' && model.trim() ? model.trim() : fallback;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizeDispatchEntry(entry, fallbackEntry) {
|
|
142
|
+
const safe = entry && typeof entry === 'object' ? entry : {};
|
|
143
|
+
return {
|
|
144
|
+
mode: normalizeDispatchMode(safe.mode, fallbackEntry.mode),
|
|
145
|
+
model: normalizeModel(safe.model, fallbackEntry.model)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeTriggers(triggers, fallback) {
|
|
150
|
+
if (!Array.isArray(triggers)) return [...fallback];
|
|
151
|
+
const cleaned = triggers
|
|
152
|
+
.map((t) => String(t || '').trim().toLowerCase())
|
|
153
|
+
.filter((t) => TRIGGERS.includes(t));
|
|
154
|
+
return cleaned.length ? Array.from(new Set(cleaned)) : [...fallback];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function normalizeAgent(parsed, agentId) {
|
|
158
|
+
const def = buildDefaultAgent(agentId);
|
|
159
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
160
|
+
const rawDispatch = raw.dispatch && typeof raw.dispatch === 'object' ? raw.dispatch : {};
|
|
161
|
+
const dispatch = {};
|
|
162
|
+
for (const host of KNOWN_HOSTS) {
|
|
163
|
+
dispatch[host] = normalizeDispatchEntry(rawDispatch[host], def.dispatch[host]);
|
|
164
|
+
}
|
|
165
|
+
const agent = {
|
|
166
|
+
enabled: normalizeEnabled(raw.enabled, def.enabled),
|
|
167
|
+
triggers: normalizeTriggers(raw.triggers, def.triggers),
|
|
168
|
+
dispatch,
|
|
169
|
+
report: normalizeModel(raw.report, def.report)
|
|
170
|
+
};
|
|
171
|
+
if (def.cross_check) {
|
|
172
|
+
const rawCc = raw.cross_check && typeof raw.cross_check === 'object' ? raw.cross_check : {};
|
|
173
|
+
agent.cross_check = {
|
|
174
|
+
enabled: typeof rawCc.enabled === 'boolean' ? rawCc.enabled : def.cross_check.enabled,
|
|
175
|
+
mode: normalizeDispatchMode(rawCc.mode, def.cross_check.mode),
|
|
176
|
+
tool: normalizeHost(rawCc.tool, def.cross_check.tool),
|
|
177
|
+
model: normalizeModel(rawCc.model, def.cross_check.model)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return agent;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeBudget(parsed) {
|
|
184
|
+
const def = buildDefaultVerificationConfig().budget;
|
|
185
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
186
|
+
return {
|
|
187
|
+
max_subagents_per_phase: Number.isInteger(raw.max_subagents_per_phase) && raw.max_subagents_per_phase >= 0
|
|
188
|
+
? raw.max_subagents_per_phase
|
|
189
|
+
: def.max_subagents_per_phase,
|
|
190
|
+
skip_on_micro: typeof raw.skip_on_micro === 'boolean' ? raw.skip_on_micro : def.skip_on_micro,
|
|
191
|
+
full_smoke: normalizeModel(raw.full_smoke, def.full_smoke)
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function normalizePhaseLoop(parsed) {
|
|
196
|
+
const def = buildDefaultVerificationConfig().phase_loop;
|
|
197
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
198
|
+
return {
|
|
199
|
+
auto_continue: typeof raw.auto_continue === 'boolean' ? raw.auto_continue : def.auto_continue,
|
|
200
|
+
compact_between_phases: typeof raw.compact_between_phases === 'boolean'
|
|
201
|
+
? raw.compact_between_phases
|
|
202
|
+
: def.compact_between_phases,
|
|
203
|
+
max_fix_retries_per_phase: Number.isInteger(raw.max_fix_retries_per_phase) && raw.max_fix_retries_per_phase >= 0
|
|
204
|
+
? raw.max_fix_retries_per_phase
|
|
205
|
+
: def.max_fix_retries_per_phase
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function normalizeAuditCode(parsed) {
|
|
210
|
+
const def = buildDefaultVerificationConfig().audit_code;
|
|
211
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
212
|
+
const gate = String(raw.tracked_gate || '').trim().toLowerCase();
|
|
213
|
+
const scope = String(raw.scope || '').trim().toLowerCase();
|
|
214
|
+
return {
|
|
215
|
+
tracked_gate: ['block', 'advisory', 'off'].includes(gate) ? gate : def.tracked_gate,
|
|
216
|
+
scope: ['changed', 'full'].includes(scope) ? scope : def.scope
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function normalizeVerificationConfig(parsed) {
|
|
221
|
+
const def = buildDefaultVerificationConfig();
|
|
222
|
+
const raw = parsed && typeof parsed === 'object' ? parsed : {};
|
|
223
|
+
const rawAgents = raw.agents && typeof raw.agents === 'object' ? raw.agents : {};
|
|
224
|
+
const agents = {};
|
|
225
|
+
// Always materialize the four known agents (defaults fill any gaps)...
|
|
226
|
+
for (const id of VERIFICATION_AGENTS) agents[id] = normalizeAgent(rawAgents[id], id);
|
|
227
|
+
// ...and preserve any user-defined extra agents (best-effort, generic shape).
|
|
228
|
+
for (const id of Object.keys(rawAgents)) {
|
|
229
|
+
if (!VERIFICATION_AGENTS.includes(id)) agents[id] = normalizeAgent(rawAgents[id], id);
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
version: typeof raw.version === 'string' ? raw.version : def.version,
|
|
233
|
+
host: normalizeHostSetting(raw.host),
|
|
234
|
+
agents,
|
|
235
|
+
budget: normalizeBudget(raw.budget),
|
|
236
|
+
phase_loop: normalizePhaseLoop(raw.phase_loop),
|
|
237
|
+
audit_code: normalizeAuditCode(raw.audit_code)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// Read
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
async function readVerificationConfig(targetDir) {
|
|
246
|
+
const configPath = path.join(targetDir, VERIFICATION_CONFIG_RELATIVE_PATH);
|
|
247
|
+
if (!(await exists(configPath))) return buildDefaultVerificationConfig();
|
|
248
|
+
try {
|
|
249
|
+
const rawText = await fs.readFile(configPath, 'utf8');
|
|
250
|
+
return normalizeVerificationConfig(JSON.parse(rawText));
|
|
251
|
+
} catch {
|
|
252
|
+
return buildDefaultVerificationConfig();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
// Resolution / getters
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
// Precedence: explicit arg > config.host (when not 'auto') > env > default.
|
|
261
|
+
function resolveHost(config, explicitHost = null, env = process.env) {
|
|
262
|
+
const explicit = String(explicitHost || '').trim().toLowerCase();
|
|
263
|
+
if (KNOWN_HOSTS.includes(explicit)) return explicit;
|
|
264
|
+
|
|
265
|
+
const configured = config && typeof config.host === 'string' ? config.host.trim().toLowerCase() : 'auto';
|
|
266
|
+
if (KNOWN_HOSTS.includes(configured)) return configured;
|
|
267
|
+
|
|
268
|
+
const envTool = String((env && (env.AIOSON_RUNNER_TOOL || env.AIOSON_TOOL)) || '').trim().toLowerCase();
|
|
269
|
+
if (KNOWN_HOSTS.includes(envTool)) return envTool;
|
|
270
|
+
|
|
271
|
+
return DEFAULT_HOST;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function getAgentConfig(config, agentId) {
|
|
275
|
+
const id = String(agentId || '').trim();
|
|
276
|
+
if (config && config.agents && typeof config.agents === 'object' && config.agents[id]) {
|
|
277
|
+
return config.agents[id];
|
|
278
|
+
}
|
|
279
|
+
if (VERIFICATION_AGENTS.includes(id)) return buildDefaultAgent(id);
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Resolve the {mode, model, host} a verification sub-agent should run on for the
|
|
284
|
+
// active host harness. Falls back to the default-host entry, then to built-ins.
|
|
285
|
+
function getAgentDispatch(config, agentId, host = null) {
|
|
286
|
+
const agent = getAgentConfig(config, agentId);
|
|
287
|
+
if (!agent) return null;
|
|
288
|
+
const resolvedHost = resolveHost(config, host);
|
|
289
|
+
const dispatch = agent.dispatch && typeof agent.dispatch === 'object' ? agent.dispatch : {};
|
|
290
|
+
const entry = dispatch[resolvedHost] || dispatch[DEFAULT_HOST];
|
|
291
|
+
if (entry && typeof entry === 'object') {
|
|
292
|
+
return { host: resolvedHost, mode: normalizeDispatchMode(entry.mode), model: normalizeModel(entry.model) };
|
|
293
|
+
}
|
|
294
|
+
const def = buildDefaultAgent(agentId);
|
|
295
|
+
const defEntry = def.dispatch[resolvedHost] || def.dispatch[DEFAULT_HOST];
|
|
296
|
+
return { host: resolvedHost, mode: defEntry.mode, model: defEntry.model };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Resolve enabled:true|false|'auto' to a concrete boolean given run context.
|
|
300
|
+
// 'auto' lets the framework decide: pentester only on a sensitive surface,
|
|
301
|
+
// tester on anything above MICRO, everything else on.
|
|
302
|
+
function resolveAgentEnabled(config, agentId, context = {}) {
|
|
303
|
+
const agent = getAgentConfig(config, agentId);
|
|
304
|
+
if (!agent) return false;
|
|
305
|
+
if (agent.enabled === true) return true;
|
|
306
|
+
if (agent.enabled === false) return false;
|
|
307
|
+
const classification = String(context.classification || '').trim().toUpperCase();
|
|
308
|
+
const sensitiveSurface = Boolean(context.sensitiveSurface);
|
|
309
|
+
switch (String(agentId).trim()) {
|
|
310
|
+
case 'pentester': return sensitiveSurface;
|
|
311
|
+
case 'tester': return classification !== 'MICRO';
|
|
312
|
+
default: return true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function getAgentTriggers(config, agentId) {
|
|
317
|
+
const agent = getAgentConfig(config, agentId);
|
|
318
|
+
return agent && Array.isArray(agent.triggers) ? [...agent.triggers] : [];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function agentHasTrigger(config, agentId, trigger) {
|
|
322
|
+
return getAgentTriggers(config, agentId).includes(String(trigger || '').trim().toLowerCase());
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function getAgentReportTemplate(config, agentId) {
|
|
326
|
+
const agent = getAgentConfig(config, agentId);
|
|
327
|
+
return agent && typeof agent.report === 'string' ? agent.report : '';
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function resolveAgentReportPath(config, agentId, slug) {
|
|
331
|
+
return getAgentReportTemplate(config, agentId).replace(/\{slug\}/g, String(slug || '').trim());
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function getCrossCheck(config, agentId) {
|
|
335
|
+
const agent = getAgentConfig(config, agentId);
|
|
336
|
+
if (!agent || !agent.cross_check || typeof agent.cross_check !== 'object') return null;
|
|
337
|
+
const cc = agent.cross_check;
|
|
338
|
+
return {
|
|
339
|
+
enabled: Boolean(cc.enabled),
|
|
340
|
+
mode: normalizeDispatchMode(cc.mode, 'external'),
|
|
341
|
+
tool: normalizeHost(cc.tool, 'codex'),
|
|
342
|
+
model: normalizeModel(cc.model)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function getBudget(config) {
|
|
347
|
+
return normalizeBudget(config && config.budget);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Phase-loop behavior: whether @dev auto-continues across phases, compacts
|
|
351
|
+
// between them, and how many in-phase fix retries are allowed before stopping.
|
|
352
|
+
function getPhaseLoop(config) {
|
|
353
|
+
return normalizePhaseLoop(config && config.phase_loop);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// audit:code tracked-gate policy: 'block' | 'advisory' | 'off' + 'changed' | 'full'.
|
|
357
|
+
function getAuditCodePolicy(config) {
|
|
358
|
+
return normalizeAuditCode(config && config.audit_code);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Composed decision: should `agentId` run for `context.trigger` right now?
|
|
362
|
+
// Combines trigger membership + enabled resolution + the skip-on-micro budget
|
|
363
|
+
// guard (per-phase verification is suppressed on MICRO to save tokens).
|
|
364
|
+
function shouldRunForTrigger(config, agentId, context = {}) {
|
|
365
|
+
const trigger = String(context.trigger || '').trim().toLowerCase();
|
|
366
|
+
if (!agentHasTrigger(config, agentId, trigger)) return false;
|
|
367
|
+
if (!resolveAgentEnabled(config, agentId, context)) return false;
|
|
368
|
+
const classification = String(context.classification || '').trim().toUpperCase();
|
|
369
|
+
if (getBudget(config).skip_on_micro && classification === 'MICRO' && trigger === 'per-phase') return false;
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
module.exports = {
|
|
374
|
+
VERIFICATION_CONFIG_RELATIVE_PATH,
|
|
375
|
+
KNOWN_HOSTS,
|
|
376
|
+
DISPATCH_MODES,
|
|
377
|
+
VERIFICATION_AGENTS,
|
|
378
|
+
TRIGGERS,
|
|
379
|
+
DEFAULT_HOST,
|
|
380
|
+
DEFAULT_MODEL,
|
|
381
|
+
buildDefaultVerificationConfig,
|
|
382
|
+
buildDefaultAgent,
|
|
383
|
+
normalizeVerificationConfig,
|
|
384
|
+
readVerificationConfig,
|
|
385
|
+
resolveHost,
|
|
386
|
+
getAgentConfig,
|
|
387
|
+
getAgentDispatch,
|
|
388
|
+
resolveAgentEnabled,
|
|
389
|
+
getAgentTriggers,
|
|
390
|
+
agentHasTrigger,
|
|
391
|
+
getAgentReportTemplate,
|
|
392
|
+
resolveAgentReportPath,
|
|
393
|
+
getCrossCheck,
|
|
394
|
+
getBudget,
|
|
395
|
+
getPhaseLoop,
|
|
396
|
+
getAuditCodePolicy,
|
|
397
|
+
shouldRunForTrigger
|
|
398
|
+
};
|
package/src/version.js
CHANGED
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const fsPromises = require('node:fs/promises');
|
|
5
5
|
const path = require('node:path');
|
|
6
|
+
const { execFileSync } = require('node:child_process');
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const ROOT_DIR = path.join(__dirname, '..');
|
|
9
|
+
const PACKAGE_JSON_PATH = path.join(ROOT_DIR, 'package.json');
|
|
8
10
|
const DEFAULT_VERSION = '0.0.0';
|
|
9
11
|
|
|
10
12
|
let cachedVersion = null;
|
|
13
|
+
let cachedGitInfo; // undefined = not computed yet; null = no git build info
|
|
11
14
|
|
|
12
15
|
function parseVersionFromPackageJson(text) {
|
|
13
16
|
try {
|
|
@@ -43,8 +46,56 @@ async function getCliVersion() {
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
function runGitSync(args) {
|
|
50
|
+
return execFileSync('git', ['-C', ROOT_DIR, ...args], {
|
|
51
|
+
encoding: 'utf8',
|
|
52
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
53
|
+
}).trim();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Short git build info for the package's OWN checkout (e.g. an `npm link`ed dev
|
|
58
|
+
* install). Lets `aioson --version` and the per-project install stamp report the
|
|
59
|
+
* exact commit a linked framework is running, so you don't need a semver bump
|
|
60
|
+
* per commit to know what's installed. Returns null when the package is not a
|
|
61
|
+
* git checkout (a normal npm install) or git is unavailable. Best-effort.
|
|
62
|
+
*/
|
|
63
|
+
function getGitBuildInfoSync() {
|
|
64
|
+
if (cachedGitInfo !== undefined) return cachedGitInfo;
|
|
65
|
+
try {
|
|
66
|
+
const sha = runGitSync(['rev-parse', '--short', 'HEAD']);
|
|
67
|
+
if (!sha) {
|
|
68
|
+
cachedGitInfo = null;
|
|
69
|
+
return cachedGitInfo;
|
|
70
|
+
}
|
|
71
|
+
let date = null;
|
|
72
|
+
try {
|
|
73
|
+
date = runGitSync(['show', '-s', '--format=%cs', 'HEAD']) || null;
|
|
74
|
+
} catch {
|
|
75
|
+
/* date is optional */
|
|
76
|
+
}
|
|
77
|
+
cachedGitInfo = { sha, date };
|
|
78
|
+
} catch {
|
|
79
|
+
cachedGitInfo = null;
|
|
80
|
+
}
|
|
81
|
+
return cachedGitInfo;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Display label: the semver, plus `(sha, date)` when running from a git checkout.
|
|
86
|
+
* e.g. "1.35.0 (<sha>, <date>)" linked, or "1.35.0" from an npm install.
|
|
87
|
+
*/
|
|
88
|
+
function getCliVersionLabelSync() {
|
|
89
|
+
const version = getCliVersionSync();
|
|
90
|
+
const git = getGitBuildInfoSync();
|
|
91
|
+
if (!git) return version;
|
|
92
|
+
return git.date ? `${version} (${git.sha}, ${git.date})` : `${version} (${git.sha})`;
|
|
93
|
+
}
|
|
94
|
+
|
|
46
95
|
module.exports = {
|
|
47
96
|
getCliVersion,
|
|
48
97
|
getCliVersionSync,
|
|
98
|
+
getGitBuildInfoSync,
|
|
99
|
+
getCliVersionLabelSync,
|
|
49
100
|
parseVersionFromPackageJson
|
|
50
101
|
};
|
|
@@ -9,7 +9,7 @@ Evaluate this immediately after reading this file and before loading any other c
|
|
|
9
9
|
If the user only activates `@analyst` without naming a feature, PRD, or concrete analysis task:
|
|
10
10
|
|
|
11
11
|
1. When the CLI is available, run `aioson workflow:status .` and `aioson context:select . --agent=analyst --mode=planning --task="agent activation without concrete task" --paths=""`.
|
|
12
|
-
2. Load only: `.aioson/context/project.context.md` and a filename listing of `.aioson/context/prd*.md` / `requirements-*.md` (names only — no contents).
|
|
12
|
+
2. Load only: `.aioson/context/project.context.md` and a filename listing of `.aioson/context/prd*.md` / `requirements-*.md` (names only — no contents).
|
|
13
13
|
3. Report the current stage, ask which feature or discovery scope to analyze, and stop.
|
|
14
14
|
|
|
15
15
|
Do NOT load on activation: PRD/requirements contents, `discovery.md`, `spec*.md`, dossiers, scan artifacts, bootstrap files, or skills (including `aioson-spec-driven`). Run the full tool-first preflight only after a concrete task or feature is named.
|
|
@@ -75,7 +75,7 @@ If the CLI is not available, compare modification dates manually:
|
|
|
75
75
|
|
|
76
76
|
## Mode detection
|
|
77
77
|
|
|
78
|
-
Resolve the active feature first: run `aioson feature:current . 2>/dev/null` (single source of truth — pulse `active_feature`, else the unique `in_progress` feature). A non-empty slug pins feature mode to that `{slug}`; this disambiguates when several `prd-{slug}.md` files coexist. If it returns `ambiguous: true` (`--json`), ask which feature before loading. Without the CLI, read `active_feature` from `.aioson/context/project-pulse.md`. Then check:
|
|
78
|
+
Resolve the active feature first: run `aioson feature:current . 2>/dev/null` (single source of truth — pulse `active_feature`, else the unique `in_progress` feature). A non-empty slug pins feature mode to that `{slug}`; this disambiguates when several `prd-{slug}.md` files coexist. If it returns `ambiguous: true` (`--json`), ask which feature before loading. Without the CLI, read `active_feature` from `.aioson/context/project-pulse.md`. Then check:
|
|
79
79
|
|
|
80
80
|
**Feature mode** — a `prd-{slug}.md` file exists in `.aioson/context/`:
|
|
81
81
|
- Read `prd-{slug}.md` to understand the feature scope.
|
|
@@ -144,7 +144,7 @@ Run after Sheldon enrichment context check. Check the frontmatter of the PRD bei
|
|
|
144
144
|
|
|
145
145
|
## Context integrity
|
|
146
146
|
|
|
147
|
-
Read `.aioson/context/project.context.md` before starting discovery.
|
|
147
|
+
Read `.aioson/context/project.context.md` before starting discovery.
|
|
148
148
|
|
|
149
149
|
Rules:
|
|
150
150
|
- If the file is inconsistent with the scope artifacts already present (`prd.md`, `prd-{slug}.md`, `discovery.md`, `spec.md`, `features.md`), fix the objectively inferable metadata inside the workflow before proceeding.
|
|
@@ -154,7 +154,7 @@ Rules:
|
|
|
154
154
|
|
|
155
155
|
## Brownfield pre-flight
|
|
156
156
|
|
|
157
|
-
Check `framework_installed` in `.aioson/context/project.context.md` before starting any phase.
|
|
157
|
+
Check `framework_installed` in `.aioson/context/project.context.md` before starting any phase.
|
|
158
158
|
|
|
159
159
|
**If `framework_installed=true` AND `.aioson/context/discovery.md` exists:**
|
|
160
160
|
- Skip Phases 1–3 below.
|
|
@@ -292,6 +292,9 @@ For each new or modified entity, produce field-level detail (same format as Phas
|
|
|
292
292
|
### Output contract — feature mode
|
|
293
293
|
|
|
294
294
|
**`requirements-{slug}.md`** — implementation spec for the feature:
|
|
295
|
+
|
|
296
|
+
> When `prd-{slug}.md` has a `## Prototype reference`, load `.aioson/docs/prototype-contract.md` and turn the prototype's Core screens and interactions into explicit acceptance criteria (e.g. "add card persists and re-renders", "board has a management surface"). This is how the prototype reaches @validator — as binary criteria, not a file it reads. After writing `requirements-{slug}.md`, run `aioson prototype:check . --feature={slug}` and resolve any `fail`/`warn` before handoff — it deterministically verifies every Core interaction in the prototype manifest is echoed by an acceptance criterion.
|
|
297
|
+
|
|
295
298
|
1. Feature summary (1–2 lines from prd-{slug}.md)
|
|
296
299
|
2. Requirement IDs (`REQ-{slug}-01...`) with source references
|
|
297
300
|
3. Acceptance criteria IDs (`AC-{slug}-01...`) mapped to requirement IDs
|
|
@@ -379,7 +382,7 @@ Generate `.aioson/context/discovery.md` with the following sections:
|
|
|
379
382
|
|
|
380
383
|
## Dev handoff producer
|
|
381
384
|
|
|
382
|
-
Before the final `agent:epilogue`/`agent:done` call, when the next agent in the workflow is `@dev`, produce `.aioson/context/dev-state.md` so the next `/aioson:agent:dev` session auto-resumes on cold start instead of pinging the user for context:
|
|
385
|
+
Before the final `agent:epilogue`/`agent:done` call, when the next agent in the workflow is `@dev`, produce `.aioson/context/dev-state.md` so the next `/aioson:agent:dev` session auto-resumes on cold start instead of pinging the user for context:
|
|
383
386
|
|
|
384
387
|
```bash
|
|
385
388
|
aioson dev:state:write . --feature={slug} --phase=1 \
|
|
@@ -389,7 +392,7 @@ aioson dev:state:write . --feature={slug} --phase=1 \
|
|
|
389
392
|
|
|
390
393
|
`--context` accepts canonical tokens (`prd`, `requirements`, `spec`, `architecture`, `impl-plan`, `sheldon`, `design-doc`, `readiness`, `ui-spec`, `dossier`, `simple-plan`), max 4 entries total; missing files emit a warning and are skipped. Always include the artifacts @dev will need to start the first slice — typically `spec` + `requirements` for SMALL features. Idempotent: re-running with the same args does not duplicate state.
|
|
391
394
|
|
|
392
|
-
If any workflow stage remains before `@dev` (`@scope-check`, `@architect`, `@discovery-design-doc`, or `@pm`), do not guess the final implementation package here. The last pre-dev stage writes the final `.aioson/context/dev-state.md`; `@analyst` only produces it for direct-to-dev shortcuts.
|
|
395
|
+
If any workflow stage remains before `@dev` (`@scope-check`, `@architect`, `@discovery-design-doc`, or `@pm`), do not guess the final implementation package here. The last pre-dev stage writes the final `.aioson/context/dev-state.md`; `@analyst` only produces it for direct-to-dev shortcuts.
|
|
393
396
|
|
|
394
397
|
**Handoff message:**
|
|
395
398
|
```
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
## Context loading modes
|
|
6
6
|
|
|
7
|
-
Before concrete `context:select`, run discovery: `aioson context:search . --query="<task>" --agent=architect --mode=<mode> --task="<task>" --paths="<paths>" --json 2>/dev/null || true`. Hits are hints only.
|
|
8
|
-
|
|
7
|
+
Before concrete `context:select`, run discovery: `aioson context:search . --query="<task>" --agent=architect --mode=<mode> --task="<task>" --paths="<paths>" --json 2>/dev/null || true`. Hits are hints only.
|
|
8
|
+
|
|
9
9
|
Use two explicit modes. Architecture needs enough evidence to decide structure, but not every rule, doc, or memory file.
|
|
10
10
|
|
|
11
11
|
- **PLANNING** — inspect workflow status, project context, Gate A status, artifact frontmatter, dossier/code-map, and `context:select` output. Do not load full `.aioson/rules/`, `.aioson/docs/`, `.aioson/design-docs/`, or bootstrap folders.
|
|
@@ -45,7 +45,7 @@ Full templates: `.aioson/docs/dossier/agent-templates.md`
|
|
|
45
45
|
|
|
46
46
|
## Activation guard
|
|
47
47
|
|
|
48
|
-
If activated without a feature slug or concrete task: read only `.aioson/context/project.context.md` + `.aioson/context/project-pulse.md` (or run `aioson context:select . --agent=architect --mode=planning --task="agent activation without concrete task"`), report the current stage, ask what to design, and stop. Do not load discovery, specs, or governance before that answer.
|
|
48
|
+
If activated without a feature slug or concrete task: read only `.aioson/context/project.context.md` + `.aioson/context/project-pulse.md` (or run `aioson context:select . --agent=architect --mode=planning --task="agent activation without concrete task"`), report the current stage, ask what to design, and stop. Do not load discovery, specs, or governance before that answer.
|
|
49
49
|
|
|
50
50
|
## Required input
|
|
51
51
|
|
|
@@ -131,10 +131,39 @@ aioson gate:approve . --feature={slug} --gate=B 2>/dev/null || true
|
|
|
131
131
|
```
|
|
132
132
|
Architecture defined: .aioson/context/architecture.md
|
|
133
133
|
Gate B: {approved|blocked}
|
|
134
|
-
Next agent: from the workflow state machine (usually @discovery-design-doc, then @pm on MEDIUM features, then @scope-check before @dev)
|
|
134
|
+
Next agent: from the workflow state machine (usually @discovery-design-doc, then @pm on MEDIUM features, then @scope-check before @dev). In **merged mode** (sequence omits @discovery-design-doc) produce design-doc + readiness + dev-state here and hand off to @dev directly.
|
|
135
135
|
Action: aioson workflow:next . --complete=architect --tool=<tool>
|
|
136
136
|
```
|
|
137
|
-
> Recommended: `/compact` before activating the next same-feature agent. Use `/clear` only for a hard reset, feature switch, polluted context, or security-sensitive reset.
|
|
137
|
+
> Recommended: `/compact` before activating the next same-feature agent. Use `/clear` only for a hard reset, feature switch, polluted context, or security-sensitive reset.
|
|
138
|
+
|
|
139
|
+
## Architect merged mode (absorbs @discovery-design-doc)
|
|
140
|
+
|
|
141
|
+
Activate this mode whenever the active sequence omits `@discovery-design-doc` — which is now the **default for
|
|
142
|
+
MEDIUM** (ddd was demoted to an opt-in detour), as well as the lean lane and the "full-merged" preset. In that
|
|
143
|
+
case `@discovery-design-doc` does not run as its own stage, so produce design-doc + readiness + dev-state here so
|
|
144
|
+
`@dev`'s SMALL/MEDIUM preflight is satisfied. Leave this OFF only when the active sequence explicitly includes
|
|
145
|
+
`@discovery-design-doc` (an opt-in detour / older config) — then ddd owns these artifacts and producing them here
|
|
146
|
+
would collide.
|
|
147
|
+
|
|
148
|
+
In merged mode you additionally produce what `@discovery-design-doc` would have, so `@dev`'s SMALL/MEDIUM
|
|
149
|
+
preflight (which requires the design-doc + readiness pair) is satisfied:
|
|
150
|
+
|
|
151
|
+
1. **Design-doc** — `.aioson/context/design-doc-{slug}.md` (project mode: `design-doc.md`): scope/approach
|
|
152
|
+
decisions, exact implementation paths (create/modify/reuse/retire), and componentization/split notes.
|
|
153
|
+
2. **Readiness** — `.aioson/context/readiness-{slug}.md` (project mode: `readiness.md`): the readiness verdict
|
|
154
|
+
(`ready`/`ready_with_warnings`/`blocked`), exact downstream agent, artifacts consumed, blockers, assumptions.
|
|
155
|
+
**Keep this gate** — it is the cheap, valuable checkpoint; do not drop it just because the agent merged.
|
|
156
|
+
3. **Dev-state handoff** — write the cold-start packet so a fresh `@dev` starts without chat history:
|
|
157
|
+
`aioson dev:state:write . --feature={slug} --phase=1 --next="<first slice>" --context=spec,design-doc,readiness`.
|
|
158
|
+
|
|
159
|
+
Then hand off to `@dev` (not `@discovery-design-doc`). Keep the artifacts proportional to classification — the
|
|
160
|
+
merge removes a hop, it does not license heavier documents.
|
|
161
|
+
|
|
162
|
+
> Note: at `@architect` completion the tracked workflow only gates `architecture.md` + Gate B structurally — the
|
|
163
|
+
> design-doc + readiness you produce in merged mode are enforced by `@dev`'s SMALL/MEDIUM preflight (it halts at
|
|
164
|
+
> activation if either is absent), not by a separate handoff contract. Produce both, or `@dev` stops. (The
|
|
165
|
+
> full-merged preset keeps `@analyst`, so `spec-{slug}.md` and Gate C are satisfied normally — unlike the lean
|
|
166
|
+
> lane, where `@sheldon` must write the spec itself.)
|
|
138
167
|
|
|
139
168
|
## Autopilot handoff
|
|
140
169
|
|
|
@@ -73,6 +73,20 @@ If the user declines application, leave `briefings.md` unchanged and record skip
|
|
|
73
73
|
|
|
74
74
|
If feedback contains unresolved blocking items, do not hand off as ready for `@product`. The next action is to resolve blockers or rerun refinement.
|
|
75
75
|
|
|
76
|
+
### Generate prototype (optional visual refinement)
|
|
77
|
+
|
|
78
|
+
Use this when the user asks to see the solution visually, or when a rich-surface briefing (workspaces, boards, cards, pipelines, CRM/Kanban, dashboards, admin/management surfaces, repeated-use CRUD) would benefit from validating screens and interactions before the PRD. It is optional and user-invoked — never block the briefing path on it.
|
|
79
|
+
|
|
80
|
+
1. Resolve the briefing slug and read `briefings.md` plus the operational surface map (`solution-options.md` / `expansion-scout.md`, falling back to the surface map in `.aioson/docs/feature-expansion-taxonomy.md`).
|
|
81
|
+
2. Resolve the visual route from `design_skill` in `.aioson/context/project.context.md`:
|
|
82
|
+
- **interface-design + reference images (recommended for a specific, premium look):** ask the user to drop reference images into `.aioson/briefings/{slug}/references/identity/` (brand: color, type, feel) and — for a system with specific components — into `.aioson/briefings/{slug}/references/structure/` (a board, a table, a screen). Then load `.aioson/skills/process/reference-identity-extract/SKILL.md`, extract them once into `.aioson/briefings/{slug}/identity.md`, and self-gate: `aioson verify:artifact . --kind=identity --file=.aioson/briefings/{slug}/identity.md --advisory 2>/dev/null || true`. The prototype's look comes from `identity.md`; its `## Component structure notes` feed the surface map. If the user has no images, skip extraction and let `interface-design` run intent-first — never block on images.
|
|
83
|
+
- **a fixed preset:** if `design_skill` names an installed preset, use it. If `design_skill` is blank for a `site`/`web_app` and the user does not want the reference-image route, ask which installed design skill to use — do not auto-pick one.
|
|
84
|
+
3. Load `.aioson/skills/process/prototype-forge/SKILL.md` and follow its build contract.
|
|
85
|
+
4. Write `.aioson/briefings/{slug}/prototype.html` and `.aioson/briefings/{slug}/prototype-manifest.md`.
|
|
86
|
+
5. Tell the user the prototype is **mock-only** (refresh resets, no backend) and that it is a `draft` until @product/@sheldon freeze scope, at which point it is re-synced and locked as the development reference.
|
|
87
|
+
|
|
88
|
+
The prototype never edits `briefings.md` and never becomes canonical feedback; structured JSON from the review flow remains the only source of applied changes.
|
|
89
|
+
|
|
76
90
|
## Review HTML contract
|
|
77
91
|
|
|
78
92
|
`review.html` must be static, local, and self-contained:
|
|
@@ -94,6 +108,7 @@ If feedback contains unresolved blocking items, do not hand off as ready for `@p
|
|
|
94
108
|
- Never approve a briefing automatically.
|
|
95
109
|
- Never route directly to `@product` while blocking items remain unresolved.
|
|
96
110
|
- Never treat edited HTML or DOM state as canonical feedback.
|
|
111
|
+
- Never treat `prototype.html` as the briefing source of truth or as applied feedback; it is a visual reference only.
|
|
97
112
|
- Never write refinement JSON into `.aioson/context/`.
|
|
98
113
|
- Never refine a briefing with `prd_generated` set unless the user explicitly chooses a new PRD/enrichment route outside this agent.
|
|
99
114
|
- Never drop mandatory briefing sections.
|
|
@@ -110,6 +125,14 @@ Review generation writes:
|
|
|
110
125
|
.aioson/briefings/{slug}/expansion-scout.md # only when expansion scout is triggered
|
|
111
126
|
```
|
|
112
127
|
|
|
128
|
+
Prototype generation (optional) writes:
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
.aioson/briefings/{slug}/identity.md # only when reference images were extracted
|
|
132
|
+
.aioson/briefings/{slug}/prototype.html
|
|
133
|
+
.aioson/briefings/{slug}/prototype-manifest.md
|
|
134
|
+
```
|
|
135
|
+
|
|
113
136
|
Confirmed application updates:
|
|
114
137
|
|
|
115
138
|
```text
|
|
@@ -123,6 +146,8 @@ Confirmed application updates:
|
|
|
123
146
|
- If review was generated: user opens `review.html`, saves/exports feedback, then reactivates `@briefing-refiner`.
|
|
124
147
|
- If changes were applied and no blockers remain: user runs `aioson briefing:approve . --slug={slug}`, then activates `@product`.
|
|
125
148
|
- If blockers remain: user resolves them in the review and reactivates `@briefing-refiner`.
|
|
149
|
+
- If a prototype was generated: user opens `prototype.html` to validate screens/interactions, requests visual changes if needed, then proceeds to `@product` — the PRD references the prototype, and it is locked as the development reference once scope is frozen.
|
|
150
|
+
- **Rich-surface recommendation (non-blocking):** if the briefing has a rich operational surface (workspaces, boards, cards, pipelines, CRM/Kanban, dashboards, admin/management, repeated-use CRUD) and no prototype exists yet, recommend running `@briefing-refiner` prototype mode before `@product` — it surfaces missing management screens and broken interactions before the PRD. The deterministic trigger is `aioson classify . --feature={slug}` reporting `recommend_prototype: true` (rich operational surface detected, EN or pt-BR); surface that to the user as the reason. Recommend only; never block the route to `@product`.
|
|
126
151
|
|
|
127
152
|
## Observability
|
|
128
153
|
|