@jaimevalasek/aioson 1.33.1 → 1.37.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 +76 -0
- package/docs/en/1-understand/ecosystem-map.md +45 -29
- package/docs/en/1-understand/glossary.md +6 -6
- 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 +33 -39
- package/docs/en/2-start/initial-decisions.md +22 -19
- 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 +13 -3
- package/docs/en/3-recipes/full-feature-with-sheldon.md +329 -338
- package/docs/en/4-agents/README.md +33 -16
- package/docs/en/4-agents/briefing-refiner.md +146 -0
- 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/README.md +1 -0
- package/docs/en/5-reference/autopilot-handoff.md +286 -0
- package/docs/en/5-reference/cli-reference.md +57 -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 +5 -5
- package/docs/pt/1-entender/mapa-do-ecossistema.md +33 -22
- package/docs/pt/2-comecar/decisoes-iniciais.md +20 -18
- package/docs/pt/2-comecar/primeiro-projeto.md +2 -2
- 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/da-ideia-ao-prd-via-briefing.md +8 -1
- package/docs/pt/3-receitas/feature-completa-com-sheldon.md +291 -338
- package/docs/pt/4-agentes/README.md +13 -11
- package/docs/pt/4-agentes/analyst.md +9 -5
- package/docs/pt/4-agentes/architect.md +9 -5
- package/docs/pt/4-agentes/briefing-refiner.md +64 -40
- package/docs/pt/4-agentes/briefing.md +6 -1
- package/docs/pt/4-agentes/dev.md +38 -8
- package/docs/pt/4-agentes/deyvin.md +4 -0
- package/docs/pt/4-agentes/discover.md +4 -0
- package/docs/pt/4-agentes/discovery-design-doc.md +25 -23
- package/docs/pt/4-agentes/neo.md +4 -0
- package/docs/pt/4-agentes/orache.md +6 -0
- package/docs/pt/4-agentes/orchestrator.md +176 -118
- package/docs/pt/4-agentes/pentester.md +11 -5
- package/docs/pt/4-agentes/pm.md +15 -7
- package/docs/pt/4-agentes/product.md +19 -1
- package/docs/pt/4-agentes/qa.md +10 -2
- package/docs/pt/4-agentes/setup.md +3 -1
- package/docs/pt/4-agentes/sheldon.md +178 -99
- package/docs/pt/4-agentes/tester.md +6 -0
- package/docs/pt/4-agentes/ux-ui.md +9 -5
- package/docs/pt/5-referencia/README.md +1 -1
- package/docs/pt/5-referencia/agent-chain-continuity.md +14 -5
- package/docs/pt/5-referencia/autopilot-handoff.md +191 -74
- package/docs/pt/5-referencia/comandos-cli.md +16 -7
- 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/5-referencia/skills.md +2 -0
- package/docs/pt/README.md +4 -4
- package/docs/pt/agentes.md +50 -50
- package/package.json +2 -2
- package/src/agents.js +1 -1
- package/src/artifact-kinds.js +111 -0
- package/src/autopilot-signal.js +71 -0
- package/src/cli.js +112 -81
- package/src/commands/agent-epilogue.js +251 -186
- package/src/commands/agents.js +122 -50
- package/src/commands/audit-code.js +344 -0
- package/src/commands/briefing.js +337 -1
- package/src/commands/classify.js +389 -389
- package/src/commands/feature-close.js +122 -4
- package/src/commands/harness-check.js +259 -175
- package/src/commands/live.js +71 -11
- 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/update.js +5 -1
- package/src/commands/verification-plan.js +143 -0
- package/src/commands/verify-artifact.js +593 -0
- package/src/commands/workflow-execute.js +434 -316
- package/src/commands/workflow-next.js +193 -20
- package/src/commands/workflow-plan.js +5 -5
- package/src/constants.js +2 -0
- package/src/doctor.js +4 -2
- 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 +10 -5
- package/src/i18n/messages/es.js +10 -5
- package/src/i18n/messages/fr.js +10 -5
- package/src/i18n/messages/pt-BR.js +10 -5
- package/src/install-wizard.js +8 -8
- package/src/installer.js +13 -6
- package/src/lib/briefing-refiner/apply-feedback.js +18 -4
- package/src/lib/briefing-refiner/feedback-schema.js +73 -4
- package/src/lib/briefing-refiner/refinement-report.js +11 -0
- package/src/lib/briefing-refiner/review-html.js +388 -68
- package/src/parser.js +12 -0
- package/src/preflight-engine.js +12 -12
- package/src/verification/policy-engine.js +95 -95
- package/src/verification-policy.js +398 -0
- package/src/version.js +52 -1
- package/template/.aioson/agents/architect.md +34 -5
- package/template/.aioson/agents/briefing-refiner.md +91 -48
- package/template/.aioson/agents/briefing.md +4 -0
- package/template/.aioson/agents/committer.md +2 -1
- package/template/.aioson/agents/copywriter.md +30 -21
- package/template/.aioson/agents/design-hybrid-forge.md +10 -1
- package/template/.aioson/agents/dev.md +35 -23
- package/template/.aioson/agents/deyvin.md +4 -0
- package/template/.aioson/agents/discover.md +16 -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 +34 -30
- package/template/.aioson/agents/orache.md +24 -11
- package/template/.aioson/agents/orchestrator.md +100 -7
- package/template/.aioson/agents/pentester.md +4 -0
- package/template/.aioson/agents/pm.md +8 -8
- package/template/.aioson/agents/product.md +25 -1
- 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 +96 -31
- package/template/.aioson/agents/setup.md +12 -1
- package/template/.aioson/agents/sheldon.md +103 -13
- package/template/.aioson/agents/site-forge.md +11 -2
- package/template/.aioson/agents/squad.md +20 -5
- package/template/.aioson/agents/tester.md +4 -0
- package/template/.aioson/agents/ux-ui.md +2 -1
- 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/agent-help.md +126 -0
- package/template/.aioson/docs/autopilot-handoff.md +30 -20
- package/template/.aioson/docs/dev/execution-discipline.md +41 -0
- package/template/.aioson/docs/dev/phase-loop.md +50 -0
- package/template/.aioson/docs/play/llm-data-and-bindings.md +70 -7
- 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 +21 -4
- 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/SKILL.md +17 -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/prototype-forge/SKILL.md +6 -0
- package/template/.aioson/skills/process/reference-identity-extract/SKILL.md +164 -0
- package/template/.aioson/skills/static/multi-agent-patterns.md +5 -5
- package/template/AGENTS.md +36 -36
- package/template/CLAUDE.md +15 -11
|
@@ -16,6 +16,7 @@ const { logError, buildHealingPrompt } = require('../self-healing');
|
|
|
16
16
|
const { validateHandoffProtocol } = require('../handoff-validator');
|
|
17
17
|
const { readAutonomyProtocol, resolveEffectiveMode } = require('../autonomy-policy');
|
|
18
18
|
const { readAgentManifest, buildAgentCapabilitySummary } = require('../agent-manifests');
|
|
19
|
+
const { resolveAutopilotSignal } = require('../autopilot-signal');
|
|
19
20
|
const { runMemoryReflectPrepare } = require('./memory-reflect-prepare');
|
|
20
21
|
const { inspectStagedChanges } = require('../lib/git-commit-guard');
|
|
21
22
|
const { emitSecurityRuntimeEvent } = require('../lib/security/runtime-events');
|
|
@@ -26,6 +27,11 @@ const { emitDossierEvent } = require('../lib/dossier-telemetry');
|
|
|
26
27
|
const { parseVerificationReport } = require('../verification/report-parser');
|
|
27
28
|
const { applyPolicy } = require('../verification/policy-engine');
|
|
28
29
|
const { normalizePolicy } = require('../verification/result');
|
|
30
|
+
const {
|
|
31
|
+
evaluateContractIntegrityGate,
|
|
32
|
+
formatContractIntegrityGateError
|
|
33
|
+
} = require('../harness/contract-integrity-gate');
|
|
34
|
+
const { runSpecAnalyze } = require('./spec-analyze');
|
|
29
35
|
const {
|
|
30
36
|
validateFeatureSlug,
|
|
31
37
|
featureContextDir,
|
|
@@ -40,22 +46,34 @@ const SCOPE_CHECK_MODES = new Set(['pre-dev', 'post-dev', 'post-fix', 'final']);
|
|
|
40
46
|
|
|
41
47
|
const DEFAULT_FEATURE_WORKFLOW_BY_CLASSIFICATION = {
|
|
42
48
|
MICRO: ['product', 'dev', 'qa'],
|
|
43
|
-
SMALL:
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
MEDIUM
|
|
49
|
+
// SMALL defaults to the lean lane: @sheldon is the single spec authority
|
|
50
|
+
// (requirements + spec + design-doc + readiness + plan + harness-contract in
|
|
51
|
+
// one pass), replacing analyst/scope-check/architect/discovery-design-doc.
|
|
52
|
+
// Those agents remain available as opt-in detours (allowDetours: true).
|
|
53
|
+
SMALL: ['product', 'sheldon', 'dev', 'qa'],
|
|
54
|
+
// MEDIUM collapses the spec phase into @orchestrator — the maestro that fans out
|
|
55
|
+
// to @analyst + @architect + @pm (+ @ux-ui when UI-heavy) as sub-agents, then
|
|
56
|
+
// consolidates/verifies/redoes their output into the gated spec package
|
|
57
|
+
// (requirements + spec[A/B/C approved] + design-doc + readiness + implementation-
|
|
58
|
+
// plan + harness-contract) and hands to @dev. SMALL = @sheldon solo (vertical),
|
|
59
|
+
// MEDIUM = @orchestrator fan-out (horizontal). The scope drift check is enforced at
|
|
60
|
+
// the dev/qa done gate (finalizeCurrentStage). @pentester stays as the post-dev
|
|
61
|
+
// security stage. analyst/architect/pm/ddd/scope-check/ux-ui remain opt-in detours.
|
|
62
|
+
MEDIUM: ['product', 'orchestrator', 'dev', 'pentester', 'qa']
|
|
49
63
|
};
|
|
50
64
|
|
|
51
|
-
// Stages eligible for autopilot handoff
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
65
|
+
// Stages eligible for autopilot handoff — the FULL feature chain (see
|
|
66
|
+
// .aioson/docs/autopilot-handoff.md). Activation = auto_handoff: true in
|
|
67
|
+
// project.context.md OR the seeded scheme (resolveAutopilotSignal). Two segments:
|
|
68
|
+
// 1. spec → dev chain: @product seeds the agentic scheme and invokes the spec
|
|
69
|
+
// authority (@sheldon lean / @orchestrator maestro), which crosses into
|
|
70
|
+
// @dev via the dev-state.md cold-start packet once its own gates/decisions
|
|
71
|
+
// are settled. Detour agents (analyst/architect/pm/...) chain only when an
|
|
72
|
+
// opt-in detour adds them to the active sequence.
|
|
73
|
+
// 2. post-dev review cycle: @dev → @qa → @tester/@pentester (when their @qa
|
|
74
|
+
// triggers fire) → @validator → STOPS before feature:close (human gate).
|
|
58
75
|
const AUTOPILOT_HANDOFF_STAGES = new Set([
|
|
76
|
+
'product', 'sheldon', 'orchestrator',
|
|
59
77
|
'analyst', 'scope-check', 'architect', 'discovery-design-doc', 'pm',
|
|
60
78
|
'dev', 'qa', 'tester', 'pentester', 'validator'
|
|
61
79
|
]);
|
|
@@ -73,13 +91,29 @@ function normalizeClassification(value, fallback = 'MICRO') {
|
|
|
73
91
|
return fallback;
|
|
74
92
|
}
|
|
75
93
|
|
|
94
|
+
// MEDIUM maestro lane: @orchestrator routes straight to @dev (it is the single
|
|
95
|
+
// spec authority that fans out to analyst/architect/pm sub-agents and consolidates).
|
|
96
|
+
function isMaestroOrchestratorState(state) {
|
|
97
|
+
const sequence = Array.isArray(state && state.sequence) ? state.sequence.map(normalizeAgentName) : [];
|
|
98
|
+
const idx = sequence.indexOf('orchestrator');
|
|
99
|
+
return idx !== -1 && sequence[idx + 1] === 'dev';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Lean lane (SMALL, sheldon → dev): @sheldon is the single spec authority.
|
|
103
|
+
// Mirrors isLeanSheldonState in handoff-contract.js.
|
|
104
|
+
function isLeanSheldonState(state) {
|
|
105
|
+
const sequence = Array.isArray(state && state.sequence) ? state.sequence.map(normalizeAgentName) : [];
|
|
106
|
+
const idx = sequence.indexOf('sheldon');
|
|
107
|
+
return idx !== -1 && sequence[idx + 1] === 'dev';
|
|
108
|
+
}
|
|
109
|
+
|
|
76
110
|
function buildDefaultWorkflowConfig() {
|
|
77
111
|
return {
|
|
78
112
|
version: 1,
|
|
79
113
|
project: {
|
|
80
114
|
MICRO: ['setup', 'dev'],
|
|
81
|
-
SMALL: ['setup', 'product', '
|
|
82
|
-
MEDIUM: ['setup', 'product', '
|
|
115
|
+
SMALL: ['setup', 'product', 'sheldon', 'dev', 'qa'],
|
|
116
|
+
MEDIUM: ['setup', 'product', 'orchestrator', 'dev', 'qa']
|
|
83
117
|
},
|
|
84
118
|
feature: DEFAULT_FEATURE_WORKFLOW_BY_CLASSIFICATION,
|
|
85
119
|
rules: {
|
|
@@ -350,7 +384,43 @@ async function validateStageArtifacts(targetDir, state, stage) {
|
|
|
350
384
|
return true;
|
|
351
385
|
}
|
|
352
386
|
|
|
387
|
+
if (stage === 'sheldon') {
|
|
388
|
+
// Lean lane (SMALL, sheldon → dev): @sheldon is the single spec authority —
|
|
389
|
+
// "done" once the collapsed spec package exists (mirrors the orchestrator
|
|
390
|
+
// maestro branch below; the handoff contract re-checks Gates A/B/C). Without
|
|
391
|
+
// this branch nothing ever marks the sheldon stage resolved, and a later
|
|
392
|
+
// `--complete=dev` computes `next: sheldon` — the state machine walking
|
|
393
|
+
// BACKWARDS into the spec agent after implementation.
|
|
394
|
+
if (state.mode === 'feature' && slug && isLeanSheldonState(state)) {
|
|
395
|
+
const designDoc = [path.join(base, `design-doc-${slug}.md`), path.join(base, 'design-doc.md')];
|
|
396
|
+
const readiness = [path.join(base, `readiness-${slug}.md`), path.join(base, 'readiness.md')];
|
|
397
|
+
return (await exists(path.join(base, `sheldon-enrichment-${slug}.md`)))
|
|
398
|
+
&& (await exists(path.join(base, `requirements-${slug}.md`)))
|
|
399
|
+
&& (await exists(path.join(base, `spec-${slug}.md`)))
|
|
400
|
+
&& (await exists(path.join(base, `implementation-plan-${slug}.md`)))
|
|
401
|
+
&& (await anyExists(designDoc))
|
|
402
|
+
&& (await anyExists(readiness));
|
|
403
|
+
}
|
|
404
|
+
if (state.mode === 'feature' && slug) {
|
|
405
|
+
return await exists(path.join(base, `sheldon-enrichment-${slug}.md`));
|
|
406
|
+
}
|
|
407
|
+
return await exists(path.join(base, 'sheldon-enrichment.md'));
|
|
408
|
+
}
|
|
409
|
+
|
|
353
410
|
if (stage === 'orchestrator') {
|
|
411
|
+
// Maestro lane (MEDIUM, orchestrator → dev): the orchestrator is the single
|
|
412
|
+
// spec authority — "done" once the gated spec package exists (mirrors how the
|
|
413
|
+
// per-hop stages were inferred). Otherwise it is the parallel-impl coordinator
|
|
414
|
+
// and owns the lane workspace.
|
|
415
|
+
if (state.mode === 'feature' && slug && isMaestroOrchestratorState(state)) {
|
|
416
|
+
const designDoc = [path.join(base, `design-doc-${slug}.md`), path.join(base, 'design-doc.md')];
|
|
417
|
+
const readiness = [path.join(base, `readiness-${slug}.md`), path.join(base, 'readiness.md')];
|
|
418
|
+
return (await exists(path.join(base, `requirements-${slug}.md`)))
|
|
419
|
+
&& (await exists(path.join(base, `spec-${slug}.md`)))
|
|
420
|
+
&& (await exists(path.join(base, `implementation-plan-${slug}.md`)))
|
|
421
|
+
&& (await anyExists(designDoc))
|
|
422
|
+
&& (await anyExists(readiness));
|
|
423
|
+
}
|
|
354
424
|
return await exists(path.join(base, 'parallel'));
|
|
355
425
|
}
|
|
356
426
|
|
|
@@ -480,7 +550,10 @@ function isInferableStage(stage) {
|
|
|
480
550
|
// could never infer scope-check as completed during stale-state recovery.
|
|
481
551
|
// pm is inferable from implementation-plan-{slug}.md for the same reason:
|
|
482
552
|
// it sits before scope-check in the MEDIUM feature sequence.
|
|
483
|
-
|
|
553
|
+
// sheldon (lean lane) and orchestrator (maestro lane) are the single spec
|
|
554
|
+
// authorities — both inferable from their collapsed spec package so stale-state
|
|
555
|
+
// recovery and mid-lane seeding never re-activate a finished spec stage.
|
|
556
|
+
return ['setup', 'product', 'analyst', 'scope-check', 'architect', 'discovery-design-doc', 'ux-ui', 'pm', 'sheldon', 'orchestrator'].includes(
|
|
484
557
|
normalizeAgentName(stage)
|
|
485
558
|
);
|
|
486
559
|
}
|
|
@@ -767,9 +840,94 @@ async function finalizeCurrentStage(targetDir, config, state, stageName) {
|
|
|
767
840
|
if (!normalizedStage) {
|
|
768
841
|
throw new Error('No stage is active to complete.');
|
|
769
842
|
}
|
|
843
|
+
let auditCodeSummary = null;
|
|
770
844
|
|
|
771
845
|
// ── Harness Done Gate ───────────────────────────────────────────────────
|
|
772
846
|
if (state.mode === 'feature' && state.featureSlug) {
|
|
847
|
+
if (normalizedStage === 'dev' || normalizedStage === 'qa') {
|
|
848
|
+
const integrityGate = await evaluateContractIntegrityGate(targetDir, state.featureSlug, {
|
|
849
|
+
runChecks: true
|
|
850
|
+
});
|
|
851
|
+
if (!integrityGate.ok) {
|
|
852
|
+
const errMsg = formatContractIntegrityGateError(integrityGate, normalizedStage);
|
|
853
|
+
await logError(targetDir, normalizedStage, errMsg, 'harness-contract');
|
|
854
|
+
throw new Error(errMsg);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// ── Scope drift gate (absorbs @scope-check's deterministic spec:analyze) ──
|
|
858
|
+
// scope-check is no longer a default stage; preserve its drift check so a
|
|
859
|
+
// dev/qa completion still blocks on real drift — a design-doc/readiness that
|
|
860
|
+
// declares `readiness: blocked`, or a malformed/invalid harness-contract.json.
|
|
861
|
+
// Non-strict spec:analyze only raises error-severity findings for those
|
|
862
|
+
// genuine problems (never for merely-absent artifacts), so this never
|
|
863
|
+
// false-blocks an artifact-light feature. Defensive: never crashes the run.
|
|
864
|
+
try {
|
|
865
|
+
const drift = await runSpecAnalyze({
|
|
866
|
+
args: [targetDir],
|
|
867
|
+
options: { feature: state.featureSlug },
|
|
868
|
+
logger: { log() {}, error() {} }
|
|
869
|
+
});
|
|
870
|
+
if (drift && Array.isArray(drift.findings) && drift.summary && drift.summary.errors > 0) {
|
|
871
|
+
const errs = drift.findings
|
|
872
|
+
.filter((f) => f.severity === 'error')
|
|
873
|
+
.map((f) => ` - ${f.check}: ${f.message}`)
|
|
874
|
+
.join('\n');
|
|
875
|
+
const driftMsg = `[Scope Drift Gate] @${normalizedStage} blocked — spec:analyze found ${drift.summary.errors} drift error(s):\n${errs}\nResolve the drift (or run @scope-check) before completing this stage.`;
|
|
876
|
+
await logError(targetDir, normalizedStage, driftMsg, 'scope-drift');
|
|
877
|
+
throw new Error(driftMsg);
|
|
878
|
+
}
|
|
879
|
+
} catch (driftErr) {
|
|
880
|
+
if (driftErr && driftErr.message && driftErr.message.includes('[Scope Drift Gate]')) throw driftErr;
|
|
881
|
+
// spec:analyze unavailable or non-analyzable — non-blocking
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// ── Code-quality gate (deterministic audit:code) ───────────────────────
|
|
885
|
+
// Build-free scan of the changed files for the non-security categories
|
|
886
|
+
// (anti-patterns / TODOs / dead code / duplication). Unlike the integrity
|
|
887
|
+
// and drift gates above — which enforce the feature's DECLARED contract /
|
|
888
|
+
// spec — audit:code is a heuristic opinion, so it defaults to ADVISORY: the
|
|
889
|
+
// report is persisted (.aioson/context/audit-code.json), a guard event is
|
|
890
|
+
// emitted, and a summary rides the finalize result, but the stage is NOT
|
|
891
|
+
// blocked. Set verification.json `audit_code.tracked_gate: "block"` to make a
|
|
892
|
+
// HIGH finding in scope a hard gate, or "off" to skip. Defensive: only a
|
|
893
|
+
// deliberate block-mode throw escapes; any other error is swallowed.
|
|
894
|
+
try {
|
|
895
|
+
const { readVerificationConfig, getAuditCodePolicy } = require('../verification-policy');
|
|
896
|
+
const auditPolicy = getAuditCodePolicy(await readVerificationConfig(targetDir));
|
|
897
|
+
if (auditPolicy.tracked_gate !== 'off') {
|
|
898
|
+
const { runAuditCode } = require('./audit-code');
|
|
899
|
+
const codeAudit = await runAuditCode({
|
|
900
|
+
args: [targetDir],
|
|
901
|
+
options: { changed: auditPolicy.scope !== 'full', json: true, suppressExitCode: true },
|
|
902
|
+
logger: { log() {}, error() {} }
|
|
903
|
+
});
|
|
904
|
+
const high = codeAudit && codeAudit.by_severity ? (codeAudit.by_severity.HIGH || 0) : 0;
|
|
905
|
+
const med = codeAudit && codeAudit.by_severity ? (codeAudit.by_severity.MED || 0) : 0;
|
|
906
|
+
const categories = Object.keys((codeAudit && codeAudit.by_category) || {});
|
|
907
|
+
auditCodeSummary = { gate: auditPolicy.tracked_gate, scope: auditPolicy.scope, high, med, categories };
|
|
908
|
+
if (high > 0 && auditPolicy.tracked_gate === 'block') {
|
|
909
|
+
const msg = `[Code-Quality Gate] @${normalizedStage} blocked — audit:code found ${high} HIGH finding(s) in the ${auditPolicy.scope} files (${categories.join(', ')}). Fix them, or relax verification.json audit_code.tracked_gate to "advisory"/"off". See .aioson/context/audit-code.json`;
|
|
910
|
+
await logError(targetDir, normalizedStage, msg, 'audit-code');
|
|
911
|
+
throw new Error(msg);
|
|
912
|
+
}
|
|
913
|
+
if (high > 0) {
|
|
914
|
+
try {
|
|
915
|
+
const { emitGuardEvent } = require('../harness/guard-events');
|
|
916
|
+
await emitGuardEvent(targetDir, {
|
|
917
|
+
eventType: 'audit_code_findings',
|
|
918
|
+
agent: normalizedStage,
|
|
919
|
+
message: `${high} HIGH / ${med} MED in ${auditPolicy.scope} files (${categories.join(', ')}) — advisory`,
|
|
920
|
+
payload: { slug: state.featureSlug, high, med, scope: auditPolicy.scope }
|
|
921
|
+
});
|
|
922
|
+
} catch { /* telemetry best-effort */ }
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
} catch (auditErr) {
|
|
926
|
+
if (auditErr && auditErr.message && auditErr.message.includes('[Code-Quality Gate]')) throw auditErr;
|
|
927
|
+
// audit:code unavailable / non-git — non-blocking
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
773
931
|
const contractPath = path.join(targetDir, '.aioson', 'plans', state.featureSlug, 'harness-contract.json');
|
|
774
932
|
const progressPath = path.join(targetDir, '.aioson', 'plans', state.featureSlug, 'progress.json');
|
|
775
933
|
|
|
@@ -869,7 +1027,18 @@ async function finalizeCurrentStage(targetDir, config, state, stageName) {
|
|
|
869
1027
|
detour: null
|
|
870
1028
|
});
|
|
871
1029
|
|
|
872
|
-
|
|
1030
|
+
// Reconcile eagerly: completing a later stage must never leave `next` pointing
|
|
1031
|
+
// at an earlier unresolved stage (e.g. lean-lane sheldon that only chained via
|
|
1032
|
+
// prompt). Without this, `--complete=dev` re-activated the spec agent and the
|
|
1033
|
+
// reconcile only healed on the NEXT load — after the backwards activation had
|
|
1034
|
+
// already been printed and persisted.
|
|
1035
|
+
const reconciled = reconcileWorkflowState(nextState);
|
|
1036
|
+
|
|
1037
|
+
return {
|
|
1038
|
+
state: reconciled.changed ? reconciled.state : nextState,
|
|
1039
|
+
completedStage: normalizedStage,
|
|
1040
|
+
...(auditCodeSummary ? { auditCode: auditCodeSummary } : {})
|
|
1041
|
+
};
|
|
873
1042
|
}
|
|
874
1043
|
|
|
875
1044
|
/**
|
|
@@ -1432,9 +1601,12 @@ async function activateStage(
|
|
|
1432
1601
|
state.mode === 'feature' &&
|
|
1433
1602
|
(state.classification === 'SMALL' || state.classification === 'MEDIUM')
|
|
1434
1603
|
) {
|
|
1604
|
+
// Frontmatter flag OR the seeded scheme (slug-scoped) — the per-feature
|
|
1605
|
+
// "Autopilot" choice only seeds workflow-execute.json and never writes
|
|
1606
|
+
// auto_handoff, so reading the frontmatter alone silently disabled it.
|
|
1435
1607
|
try {
|
|
1436
|
-
const
|
|
1437
|
-
autoHandoff =
|
|
1608
|
+
const signal = await resolveAutopilotSignal(targetDir, { slug: state.featureSlug });
|
|
1609
|
+
autoHandoff = signal.enabled;
|
|
1438
1610
|
} catch {
|
|
1439
1611
|
autoHandoff = false;
|
|
1440
1612
|
}
|
|
@@ -1608,7 +1780,8 @@ async function runWorkflowNext({ args, options, logger, t }) {
|
|
|
1608
1780
|
const autoHeal = Boolean(options['auto-heal'] || options.autoHeal);
|
|
1609
1781
|
const isHealabled = autoHeal && (
|
|
1610
1782
|
err.message.includes('[Technical Gate BLOCKED]') ||
|
|
1611
|
-
err.message.includes('[Handoff Contract BLOCKED]')
|
|
1783
|
+
err.message.includes('[Handoff Contract BLOCKED]') ||
|
|
1784
|
+
err.message.includes('[Harness Contract Gate BLOCKED]')
|
|
1612
1785
|
);
|
|
1613
1786
|
if (isHealabled) {
|
|
1614
1787
|
const failedStage = normalizeAgentName(options.complete === true ? state.current || state.next : options.complete);
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const { validateProjectContextFile } = require('../context');
|
|
5
5
|
|
|
6
|
-
const WORKFLOW_BY_CLASSIFICATION = {
|
|
7
|
-
MICRO: ['setup', 'dev'],
|
|
8
|
-
SMALL: ['setup', 'product', '
|
|
9
|
-
MEDIUM: ['setup', 'product', '
|
|
10
|
-
};
|
|
6
|
+
const WORKFLOW_BY_CLASSIFICATION = {
|
|
7
|
+
MICRO: ['setup', 'dev'],
|
|
8
|
+
SMALL: ['setup', 'product', 'sheldon', 'dev', 'qa'],
|
|
9
|
+
MEDIUM: ['setup', 'product', 'orchestrator', 'dev', 'qa']
|
|
10
|
+
};
|
|
11
11
|
|
|
12
12
|
function normalizeClassification(value, fallback = 'MICRO') {
|
|
13
13
|
const text = String(value || '').trim().toUpperCase();
|
package/src/constants.js
CHANGED
|
@@ -47,6 +47,7 @@ const MANAGED_FILES = [
|
|
|
47
47
|
'.aioson/docs/squad/genome-bindings.md',
|
|
48
48
|
'.aioson/docs/feature-expansion-taxonomy.md',
|
|
49
49
|
'.aioson/docs/prototype-contract.md',
|
|
50
|
+
'.aioson/docs/reference-identity.md',
|
|
50
51
|
'.aioson/docs/product/conversation-playbook.md',
|
|
51
52
|
'.aioson/docs/product/research-loop.md',
|
|
52
53
|
'.aioson/docs/product/quality-lens.md',
|
|
@@ -83,6 +84,7 @@ const MANAGED_FILES = [
|
|
|
83
84
|
'.aioson/skills/process/product-scope-expansion/SKILL.md',
|
|
84
85
|
'.aioson/skills/process/sheldon-expansion-audit/SKILL.md',
|
|
85
86
|
'.aioson/skills/process/prototype-forge/SKILL.md',
|
|
87
|
+
'.aioson/skills/process/reference-identity-extract/SKILL.md',
|
|
86
88
|
'.aioson/skills/static/laravel-conventions.md',
|
|
87
89
|
'.aioson/skills/static/tall-stack-patterns.md',
|
|
88
90
|
'.aioson/skills/static/jetstream-setup.md',
|
package/src/doctor.js
CHANGED
|
@@ -228,8 +228,10 @@ async function runDoctor(targetDir) {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// Autopilot handoff: protocol doc installed but flag never declared in the
|
|
231
|
-
// context frontmatter
|
|
232
|
-
//
|
|
231
|
+
// context frontmatter. Absent is no longer "silently manual" — @product asks
|
|
232
|
+
// the run mode on screen at each feature kickoff. Declaring an explicit
|
|
233
|
+
// true/false sets a project default (skip the kickoff question) and passes;
|
|
234
|
+
// the advisory just surfaces that a default can be set.
|
|
233
235
|
const autopilotDocExists = await exists(path.join(targetDir, '.aioson/docs/autopilot-handoff.md'));
|
|
234
236
|
if (autopilotDocExists && contextValidation.exists && contextValidation.data) {
|
|
235
237
|
const autoHandoffDeclared = Object.prototype.hasOwnProperty.call(contextValidation.data, 'auto_handoff');
|
|
@@ -31,6 +31,12 @@ function buildBlock(templateContent) {
|
|
|
31
31
|
return `${MARKER_BEGIN}\n${BLOCK_NOTICE}\n\n${body}${MARKER_END}\n`;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function trimBlankLinesAfterBlock(content, rangeEnd) {
|
|
35
|
+
let end = rangeEnd;
|
|
36
|
+
while (content[end] === '\n' || content[end] === '\r') end += 1;
|
|
37
|
+
return end;
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
function findBlockRange(content) {
|
|
35
41
|
const start = content.indexOf(MARKER_BEGIN);
|
|
36
42
|
if (start === -1) return null;
|
|
@@ -71,7 +77,7 @@ async function mergeGatewayPointer({ templatePath, targetPath, backupRoot, targe
|
|
|
71
77
|
let action;
|
|
72
78
|
if (range) {
|
|
73
79
|
const before = stripLegacyUnmanagedGateway(existing.slice(0, range.start));
|
|
74
|
-
const after = existing.slice(range.end);
|
|
80
|
+
const after = existing.slice(trimBlankLinesAfterBlock(existing, range.end));
|
|
75
81
|
const cleanBefore = before.length === 0 || before.endsWith('\n') ? before : `${before}\n`;
|
|
76
82
|
next = `${cleanBefore}${block}${after}`;
|
|
77
83
|
action = 'block_updated';
|