popeye-cli 2.2.0 → 2.7.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/dist/adapters/gemini.d.ts +14 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +41 -6
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.d.ts +14 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +42 -6
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.d.ts +10 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +44 -5
- package/dist/adapters/openai.js.map +1 -1
- package/dist/cli/commands/create.js +1 -1
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +324 -20
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +3 -2
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/doc-parser.d.ts +21 -6
- package/dist/generators/doc-parser.d.ts.map +1 -1
- package/dist/generators/doc-parser.js +55 -4
- package/dist/generators/doc-parser.js.map +1 -1
- package/dist/generators/templates/fullstack.js +1 -1
- package/dist/generators/templates/website-components.js +1 -1
- package/dist/generators/templates/website-components.js.map +1 -1
- package/dist/generators/templates/website-config.d.ts +4 -1
- package/dist/generators/templates/website-config.d.ts.map +1 -1
- package/dist/generators/templates/website-config.js +17 -11
- package/dist/generators/templates/website-config.js.map +1 -1
- package/dist/generators/templates/website-conversion.js +1 -1
- package/dist/generators/templates/website-conversion.js.map +1 -1
- package/dist/generators/templates/website-landing.js +1 -1
- package/dist/generators/templates/website-landing.js.map +1 -1
- package/dist/generators/templates/website-layout.d.ts +36 -4
- package/dist/generators/templates/website-layout.d.ts.map +1 -1
- package/dist/generators/templates/website-layout.js +466 -23
- package/dist/generators/templates/website-layout.js.map +1 -1
- package/dist/generators/templates/website-pricing.js +1 -1
- package/dist/generators/templates/website-pricing.js.map +1 -1
- package/dist/generators/templates/website-sections.js +1 -1
- package/dist/generators/templates/website-sections.js.map +1 -1
- package/dist/generators/templates/website-seo.d.ts.map +1 -1
- package/dist/generators/templates/website-seo.js +4 -1
- package/dist/generators/templates/website-seo.js.map +1 -1
- package/dist/generators/templates/website.d.ts +1 -1
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +1 -1
- package/dist/generators/templates/website.js.map +1 -1
- package/dist/generators/website-content-ai.d.ts +52 -0
- package/dist/generators/website-content-ai.d.ts.map +1 -0
- package/dist/generators/website-content-ai.js +141 -0
- package/dist/generators/website-content-ai.js.map +1 -0
- package/dist/generators/website-content-scanner.d.ts +1 -1
- package/dist/generators/website-content-scanner.d.ts.map +1 -1
- package/dist/generators/website-content-scanner.js +98 -1
- package/dist/generators/website-content-scanner.js.map +1 -1
- package/dist/generators/website-context.d.ts +34 -1
- package/dist/generators/website-context.d.ts.map +1 -1
- package/dist/generators/website-context.js +131 -9
- package/dist/generators/website-context.js.map +1 -1
- package/dist/generators/website-debug.d.ts +12 -0
- package/dist/generators/website-debug.d.ts.map +1 -1
- package/dist/generators/website-debug.js +16 -0
- package/dist/generators/website-debug.js.map +1 -1
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +26 -4
- package/dist/generators/website.js.map +1 -1
- package/dist/pipeline/auto-recovery.d.ts +56 -0
- package/dist/pipeline/auto-recovery.d.ts.map +1 -0
- package/dist/pipeline/auto-recovery.js +185 -0
- package/dist/pipeline/auto-recovery.js.map +1 -0
- package/dist/pipeline/change-request.d.ts +39 -0
- package/dist/pipeline/change-request.d.ts.map +1 -1
- package/dist/pipeline/change-request.js +40 -1
- package/dist/pipeline/change-request.js.map +1 -1
- package/dist/pipeline/check-runner.d.ts +30 -1
- package/dist/pipeline/check-runner.d.ts.map +1 -1
- package/dist/pipeline/check-runner.js +122 -1
- package/dist/pipeline/check-runner.js.map +1 -1
- package/dist/pipeline/command-resolver.d.ts.map +1 -1
- package/dist/pipeline/command-resolver.js +33 -2
- package/dist/pipeline/command-resolver.js.map +1 -1
- package/dist/pipeline/consensus/arbitrator-query.d.ts +22 -0
- package/dist/pipeline/consensus/arbitrator-query.d.ts.map +1 -0
- package/dist/pipeline/consensus/arbitrator-query.js +70 -0
- package/dist/pipeline/consensus/arbitrator-query.js.map +1 -0
- package/dist/pipeline/consensus/consensus-runner.d.ts +131 -7
- package/dist/pipeline/consensus/consensus-runner.d.ts.map +1 -1
- package/dist/pipeline/consensus/consensus-runner.js +809 -35
- package/dist/pipeline/consensus/consensus-runner.js.map +1 -1
- package/dist/pipeline/cr-lifecycle.d.ts +42 -0
- package/dist/pipeline/cr-lifecycle.d.ts.map +1 -0
- package/dist/pipeline/cr-lifecycle.js +89 -0
- package/dist/pipeline/cr-lifecycle.js.map +1 -0
- package/dist/pipeline/gate-engine.d.ts +1 -0
- package/dist/pipeline/gate-engine.d.ts.map +1 -1
- package/dist/pipeline/gate-engine.js +26 -7
- package/dist/pipeline/gate-engine.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +306 -16
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/packets/consensus-packet-builder.d.ts +15 -4
- package/dist/pipeline/packets/consensus-packet-builder.d.ts.map +1 -1
- package/dist/pipeline/packets/consensus-packet-builder.js +29 -17
- package/dist/pipeline/packets/consensus-packet-builder.js.map +1 -1
- package/dist/pipeline/phases/architecture.d.ts.map +1 -1
- package/dist/pipeline/phases/architecture.js +5 -3
- package/dist/pipeline/phases/architecture.js.map +1 -1
- package/dist/pipeline/phases/audit.d.ts.map +1 -1
- package/dist/pipeline/phases/audit.js +5 -3
- package/dist/pipeline/phases/audit.js.map +1 -1
- package/dist/pipeline/phases/consensus-architecture.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-architecture.js +10 -1
- package/dist/pipeline/phases/consensus-architecture.js.map +1 -1
- package/dist/pipeline/phases/consensus-master-plan.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-master-plan.js +10 -3
- package/dist/pipeline/phases/consensus-master-plan.js.map +1 -1
- package/dist/pipeline/phases/consensus-role-plans.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-role-plans.js +10 -1
- package/dist/pipeline/phases/consensus-role-plans.js.map +1 -1
- package/dist/pipeline/phases/done.d.ts.map +1 -1
- package/dist/pipeline/phases/done.js +9 -4
- package/dist/pipeline/phases/done.js.map +1 -1
- package/dist/pipeline/phases/intake.d.ts.map +1 -1
- package/dist/pipeline/phases/intake.js +7 -3
- package/dist/pipeline/phases/intake.js.map +1 -1
- package/dist/pipeline/phases/phase-context.d.ts +2 -0
- package/dist/pipeline/phases/phase-context.d.ts.map +1 -1
- package/dist/pipeline/phases/phase-context.js +3 -1
- package/dist/pipeline/phases/phase-context.js.map +1 -1
- package/dist/pipeline/phases/production-gate.d.ts.map +1 -1
- package/dist/pipeline/phases/production-gate.js +28 -3
- package/dist/pipeline/phases/production-gate.js.map +1 -1
- package/dist/pipeline/phases/qa-validation.d.ts.map +1 -1
- package/dist/pipeline/phases/qa-validation.js +38 -5
- package/dist/pipeline/phases/qa-validation.js.map +1 -1
- package/dist/pipeline/phases/recovery-loop.d.ts +2 -0
- package/dist/pipeline/phases/recovery-loop.d.ts.map +1 -1
- package/dist/pipeline/phases/recovery-loop.js +200 -6
- package/dist/pipeline/phases/recovery-loop.js.map +1 -1
- package/dist/pipeline/phases/review.d.ts.map +1 -1
- package/dist/pipeline/phases/review.js +58 -28
- package/dist/pipeline/phases/review.js.map +1 -1
- package/dist/pipeline/phases/role-planning.d.ts.map +1 -1
- package/dist/pipeline/phases/role-planning.js +18 -2
- package/dist/pipeline/phases/role-planning.js.map +1 -1
- package/dist/pipeline/phases/stuck.d.ts.map +1 -1
- package/dist/pipeline/phases/stuck.js +10 -0
- package/dist/pipeline/phases/stuck.js.map +1 -1
- package/dist/pipeline/repo-snapshot.d.ts.map +1 -1
- package/dist/pipeline/repo-snapshot.js +3 -0
- package/dist/pipeline/repo-snapshot.js.map +1 -1
- package/dist/pipeline/role-execution-adapter.d.ts +2 -1
- package/dist/pipeline/role-execution-adapter.d.ts.map +1 -1
- package/dist/pipeline/role-execution-adapter.js +22 -7
- package/dist/pipeline/role-execution-adapter.js.map +1 -1
- package/dist/pipeline/skill-loader.d.ts +19 -0
- package/dist/pipeline/skill-loader.d.ts.map +1 -1
- package/dist/pipeline/skill-loader.js +22 -0
- package/dist/pipeline/skill-loader.js.map +1 -1
- package/dist/pipeline/skills/coverage-gate.d.ts +44 -0
- package/dist/pipeline/skills/coverage-gate.d.ts.map +1 -0
- package/dist/pipeline/skills/coverage-gate.js +143 -0
- package/dist/pipeline/skills/coverage-gate.js.map +1 -0
- package/dist/pipeline/skills/usage-registry.d.ts +48 -0
- package/dist/pipeline/skills/usage-registry.d.ts.map +1 -0
- package/dist/pipeline/skills/usage-registry.js +55 -0
- package/dist/pipeline/skills/usage-registry.js.map +1 -0
- package/dist/pipeline/strategy-context.d.ts +20 -0
- package/dist/pipeline/strategy-context.d.ts.map +1 -0
- package/dist/pipeline/strategy-context.js +55 -0
- package/dist/pipeline/strategy-context.js.map +1 -0
- package/dist/pipeline/type-defs/artifacts.d.ts +25 -5
- package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -1
- package/dist/pipeline/type-defs/artifacts.js +4 -0
- package/dist/pipeline/type-defs/artifacts.js.map +1 -1
- package/dist/pipeline/type-defs/audit.d.ts +25 -13
- package/dist/pipeline/type-defs/audit.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.d.ts +18 -8
- package/dist/pipeline/type-defs/checks.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.js +4 -0
- package/dist/pipeline/type-defs/checks.js.map +1 -1
- package/dist/pipeline/type-defs/packets.d.ts +104 -18
- package/dist/pipeline/type-defs/packets.d.ts.map +1 -1
- package/dist/pipeline/type-defs/packets.js +17 -1
- package/dist/pipeline/type-defs/packets.js.map +1 -1
- package/dist/pipeline/type-defs/state.d.ts +160 -16
- package/dist/pipeline/type-defs/state.d.ts.map +1 -1
- package/dist/pipeline/type-defs/state.js +26 -1
- package/dist/pipeline/type-defs/state.js.map +1 -1
- package/dist/shared/text-utils.d.ts +23 -0
- package/dist/shared/text-utils.d.ts.map +1 -0
- package/dist/shared/text-utils.js +66 -0
- package/dist/shared/text-utils.js.map +1 -0
- package/dist/shared/website-strategy-format.d.ts +18 -0
- package/dist/shared/website-strategy-format.d.ts.map +1 -0
- package/dist/shared/website-strategy-format.js +47 -0
- package/dist/shared/website-strategy-format.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +57 -8
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +1 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/website-strategy.d.ts +1 -1
- package/dist/types/workflow.d.ts +447 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +3 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/handlers.d.ts.map +1 -1
- package/dist/upgrade/handlers.js +6 -3
- package/dist/upgrade/handlers.js.map +1 -1
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +1 -0
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/website-strategy.d.ts.map +1 -1
- package/dist/workflow/website-strategy.js +2 -29
- package/dist/workflow/website-strategy.js.map +1 -1
- package/dist/workflow/website-updater.d.ts.map +1 -1
- package/dist/workflow/website-updater.js +3 -2
- package/dist/workflow/website-updater.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/gemini.ts +51 -6
- package/src/adapters/grok.ts +51 -6
- package/src/adapters/openai.ts +53 -5
- package/src/cli/commands/create.ts +1 -1
- package/src/cli/interactive.ts +333 -19
- package/src/generators/all.ts +3 -2
- package/src/generators/doc-parser.ts +75 -15
- package/src/generators/templates/fullstack.ts +1 -1
- package/src/generators/templates/website-components.ts +1 -1
- package/src/generators/templates/website-config.ts +23 -11
- package/src/generators/templates/website-conversion.ts +1 -1
- package/src/generators/templates/website-landing.ts +1 -1
- package/src/generators/templates/website-layout.ts +491 -23
- package/src/generators/templates/website-pricing.ts +1 -1
- package/src/generators/templates/website-sections.ts +1 -1
- package/src/generators/templates/website-seo.ts +4 -1
- package/src/generators/templates/website.ts +3 -0
- package/src/generators/website-content-ai.ts +186 -0
- package/src/generators/website-content-scanner.ts +113 -1
- package/src/generators/website-context.ts +151 -12
- package/src/generators/website-debug.ts +26 -0
- package/src/generators/website.ts +28 -3
- package/src/pipeline/auto-recovery.ts +283 -0
- package/src/pipeline/change-request.ts +63 -1
- package/src/pipeline/check-runner.ts +141 -2
- package/src/pipeline/command-resolver.ts +34 -2
- package/src/pipeline/consensus/arbitrator-query.ts +101 -0
- package/src/pipeline/consensus/consensus-runner.ts +1099 -42
- package/src/pipeline/cr-lifecycle.ts +103 -0
- package/src/pipeline/gate-engine.ts +35 -7
- package/src/pipeline/orchestrator.ts +361 -16
- package/src/pipeline/packets/consensus-packet-builder.ts +44 -18
- package/src/pipeline/phases/architecture.ts +6 -3
- package/src/pipeline/phases/audit.ts +6 -3
- package/src/pipeline/phases/consensus-architecture.ts +10 -1
- package/src/pipeline/phases/consensus-master-plan.ts +10 -3
- package/src/pipeline/phases/consensus-role-plans.ts +10 -1
- package/src/pipeline/phases/done.ts +15 -4
- package/src/pipeline/phases/intake.ts +7 -3
- package/src/pipeline/phases/phase-context.ts +6 -1
- package/src/pipeline/phases/production-gate.ts +41 -3
- package/src/pipeline/phases/qa-validation.ts +51 -5
- package/src/pipeline/phases/recovery-loop.ts +229 -7
- package/src/pipeline/phases/review.ts +73 -30
- package/src/pipeline/phases/role-planning.ts +21 -2
- package/src/pipeline/phases/stuck.ts +10 -0
- package/src/pipeline/repo-snapshot.ts +3 -0
- package/src/pipeline/role-execution-adapter.ts +30 -4
- package/src/pipeline/skill-loader.ts +33 -0
- package/src/pipeline/skills/coverage-gate.ts +199 -0
- package/src/pipeline/skills/usage-registry.ts +87 -0
- package/src/pipeline/strategy-context.ts +60 -0
- package/src/pipeline/type-defs/artifacts.ts +4 -0
- package/src/pipeline/type-defs/checks.ts +4 -0
- package/src/pipeline/type-defs/packets.ts +18 -1
- package/src/pipeline/type-defs/state.ts +26 -1
- package/src/shared/text-utils.ts +70 -0
- package/src/shared/website-strategy-format.ts +56 -0
- package/src/state/index.ts +60 -8
- package/src/types/consensus.ts +1 -0
- package/src/types/workflow.ts +6 -0
- package/src/upgrade/handlers.ts +9 -3
- package/src/workflow/consensus.ts +1 -0
- package/src/workflow/website-strategy.ts +2 -36
- package/src/workflow/website-updater.ts +4 -2
- package/tests/adapters/gemini.test.ts +165 -0
- package/tests/adapters/grok.test.ts +137 -0
- package/tests/adapters/openai.test.ts +128 -0
- package/tests/generators/doc-parser.test.ts +88 -9
- package/tests/generators/quality-gate.test.ts +19 -3
- package/tests/generators/website-components.test.ts +34 -0
- package/tests/generators/website-content-ai.test.ts +308 -0
- package/tests/generators/website-content-scanner.test.ts +86 -0
- package/tests/generators/website-context.test.ts +3 -2
- package/tests/integration/smokestack-scaffold.test.ts +385 -0
- package/tests/pipeline/auto-recovery.test.ts +337 -0
- package/tests/pipeline/change-request.test.ts +70 -0
- package/tests/pipeline/command-resolver.test.ts +42 -0
- package/tests/pipeline/consensus/arbitrator-query.test.ts +107 -0
- package/tests/pipeline/consensus-runner.test.ts +1333 -10
- package/tests/pipeline/consensus-scoring.test.ts +602 -18
- package/tests/pipeline/gate-engine.test.ts +34 -0
- package/tests/pipeline/install-check.test.ts +261 -0
- package/tests/pipeline/orchestrator.test.ts +1506 -15
- package/tests/pipeline/packets/builders.test.ts +29 -6
- package/tests/pipeline/phases/role-planning.strategy.test.ts +204 -0
- package/tests/pipeline/pipeline-persistence.test.ts +230 -0
- package/tests/pipeline/recovery-loop-guidance.test.ts +280 -0
- package/tests/pipeline/role-execution-adapter.test.ts +88 -0
- package/tests/pipeline/skills/coverage-gate.test.ts +370 -0
- package/tests/pipeline/skills/usage-registry.test.ts +114 -0
- package/tests/pipeline/strategy-context.test.ts +148 -0
- package/tests/shared/text-utils.test.ts +155 -0
- package/tests/state/progress-analysis.test.ts +375 -0
- package/tests/upgrade/handlers.test.ts +33 -2
- package/tests/workflow/consensus.test.ts +6 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Coverage Gate — deterministic assertion that every active role
|
|
3
|
+
* has recorded usage (or is explicitly exempt).
|
|
4
|
+
*
|
|
5
|
+
* Called at CONSENSUS_ROLE_PLANS and PRODUCTION_GATE gates.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { PipelineRole, PipelinePhase, PipelineState } from '../types.js';
|
|
9
|
+
import type { SkillUsageEvent } from './usage-registry.js';
|
|
10
|
+
|
|
11
|
+
// ─── Phase Order (must match orchestrator/gate phase sequence) ────
|
|
12
|
+
|
|
13
|
+
/** Canonical phase order — used for phase-aware deferral in coverage checks. */
|
|
14
|
+
export const PHASE_ORDER: PipelinePhase[] = [
|
|
15
|
+
'INTAKE', 'CONSENSUS_MASTER_PLAN', 'ARCHITECTURE', 'CONSENSUS_ARCHITECTURE',
|
|
16
|
+
'ROLE_PLANNING', 'CONSENSUS_ROLE_PLANS', 'IMPLEMENTATION', 'QA_VALIDATION',
|
|
17
|
+
'REVIEW', 'AUDIT', 'PRODUCTION_GATE', 'RECOVERY_LOOP', 'DONE', 'STUCK',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
// ─── Required Usage Configuration ────────────────────────
|
|
21
|
+
|
|
22
|
+
export interface RoleUsageRequirement {
|
|
23
|
+
phases: PipelinePhase[];
|
|
24
|
+
minEvents: number;
|
|
25
|
+
conditional?: 'always' | 'if_recovery' | 'if_arbitrated' | 'if_journal_triggered';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const ROLE_REQUIRED_USAGE: Record<PipelineRole, RoleUsageRequirement> = {
|
|
29
|
+
DISPATCHER: { phases: [], minEvents: 0 },
|
|
30
|
+
ARCHITECT: { phases: ['ARCHITECTURE'], minEvents: 1 },
|
|
31
|
+
DB_EXPERT: { phases: ['ROLE_PLANNING'], minEvents: 1 },
|
|
32
|
+
BACKEND_PROGRAMMER: { phases: ['ROLE_PLANNING'], minEvents: 1 },
|
|
33
|
+
FRONTEND_PROGRAMMER: { phases: ['ROLE_PLANNING'], minEvents: 1 },
|
|
34
|
+
WEBSITE_PROGRAMMER: { phases: ['ROLE_PLANNING'], minEvents: 1 },
|
|
35
|
+
UI_UX_SPECIALIST: { phases: ['ROLE_PLANNING'], minEvents: 1 },
|
|
36
|
+
MARKETING_EXPERT: { phases: ['ROLE_PLANNING'], minEvents: 1 },
|
|
37
|
+
SOCIAL_EXPERT: { phases: ['ROLE_PLANNING'], minEvents: 1 },
|
|
38
|
+
QA_TESTER: { phases: ['ROLE_PLANNING', 'QA_VALIDATION'], minEvents: 1 },
|
|
39
|
+
REVIEWER: { phases: ['REVIEW'], minEvents: 1 },
|
|
40
|
+
ARBITRATOR: { phases: [], minEvents: 0, conditional: 'if_arbitrated' },
|
|
41
|
+
DEBUGGER: { phases: ['RECOVERY_LOOP'], minEvents: 0, conditional: 'if_recovery' },
|
|
42
|
+
AUDITOR: { phases: ['AUDIT'], minEvents: 1 },
|
|
43
|
+
JOURNALIST: {
|
|
44
|
+
phases: [
|
|
45
|
+
'CONSENSUS_MASTER_PLAN', 'CONSENSUS_ARCHITECTURE', 'CONSENSUS_ROLE_PLANS',
|
|
46
|
+
'AUDIT', 'PRODUCTION_GATE', 'RECOVERY_LOOP', 'DONE',
|
|
47
|
+
],
|
|
48
|
+
minEvents: 1,
|
|
49
|
+
conditional: 'if_journal_triggered',
|
|
50
|
+
},
|
|
51
|
+
RELEASE_MANAGER: { phases: ['DONE'], minEvents: 1 },
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ─── Coverage Result ─────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
export interface CoverageMissing {
|
|
57
|
+
role: PipelineRole;
|
|
58
|
+
expectedPhases: PipelinePhase[];
|
|
59
|
+
reason: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface CoverageResult {
|
|
63
|
+
pass: boolean;
|
|
64
|
+
missing: CoverageMissing[];
|
|
65
|
+
covered: PipelineRole[];
|
|
66
|
+
/** Roles skipped because their required phases haven't been reached yet (v2.4.5) */
|
|
67
|
+
deferred: PipelineRole[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── Assertion Logic ─────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Assert that all active roles have recorded skill usage
|
|
74
|
+
* according to their requirements.
|
|
75
|
+
*
|
|
76
|
+
* Args:
|
|
77
|
+
* activeRoles: Roles currently active in the pipeline.
|
|
78
|
+
* events: All recorded skill usage events.
|
|
79
|
+
* pipeline: Full pipeline state for conditional checks.
|
|
80
|
+
* currentPhase: Current pipeline phase for phase-aware deferral (v2.4.5).
|
|
81
|
+
* Omit for strict mode (checks all roles regardless of phase).
|
|
82
|
+
*
|
|
83
|
+
* Returns:
|
|
84
|
+
* CoverageResult with pass/fail and details.
|
|
85
|
+
*/
|
|
86
|
+
export function assertSkillCoverage(
|
|
87
|
+
activeRoles: PipelineRole[],
|
|
88
|
+
events: SkillUsageEvent[],
|
|
89
|
+
pipeline: PipelineState,
|
|
90
|
+
currentPhase?: PipelinePhase,
|
|
91
|
+
): CoverageResult {
|
|
92
|
+
const missing: CoverageMissing[] = [];
|
|
93
|
+
const covered: PipelineRole[] = [];
|
|
94
|
+
const deferred: PipelineRole[] = [];
|
|
95
|
+
|
|
96
|
+
// Resolve phase index; -1 means unknown/omitted -> strict mode (check all)
|
|
97
|
+
const currentIdx = currentPhase ? PHASE_ORDER.indexOf(currentPhase) : -1;
|
|
98
|
+
|
|
99
|
+
for (const role of activeRoles) {
|
|
100
|
+
const requirement = ROLE_REQUIRED_USAGE[role];
|
|
101
|
+
if (!requirement) {
|
|
102
|
+
// Reason: Unknown role — skip rather than crash
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Meta-only roles (DISPATCHER) with minEvents 0 and no conditional
|
|
107
|
+
if (requirement.minEvents === 0 && !requirement.conditional) {
|
|
108
|
+
covered.push(role);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Conditional roles — check if their condition is met
|
|
113
|
+
if (requirement.conditional) {
|
|
114
|
+
const isRequired = isConditionalRequired(requirement.conditional, pipeline);
|
|
115
|
+
if (!isRequired) {
|
|
116
|
+
// Condition not met — not required, count as covered
|
|
117
|
+
covered.push(role);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// v2.4.5: Phase-aware deferral — skip roles whose phases are all after currentPhase.
|
|
123
|
+
// Only applies when currentIdx >= 0 (known phase). Unknown phase = strict mode.
|
|
124
|
+
if (currentIdx >= 0 && requirement.phases.length > 0) {
|
|
125
|
+
const anyPhaseReached = requirement.phases.some(
|
|
126
|
+
(p) => PHASE_ORDER.indexOf(p) <= currentIdx,
|
|
127
|
+
);
|
|
128
|
+
if (!anyPhaseReached) {
|
|
129
|
+
// Reason: Role's required phases are all after currentPhase — defer check
|
|
130
|
+
deferred.push(role);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check if role has at least minEvents usage events
|
|
136
|
+
const roleEvents = events.filter((e) => e.role === role);
|
|
137
|
+
const effectiveMin = Math.max(requirement.minEvents, 1);
|
|
138
|
+
|
|
139
|
+
if (roleEvents.length >= effectiveMin) {
|
|
140
|
+
covered.push(role);
|
|
141
|
+
} else {
|
|
142
|
+
missing.push({
|
|
143
|
+
role,
|
|
144
|
+
expectedPhases: requirement.phases,
|
|
145
|
+
reason: roleEvents.length === 0
|
|
146
|
+
? `No skill usage recorded for ${role}`
|
|
147
|
+
: `Only ${roleEvents.length}/${effectiveMin} usage events for ${role}`,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
pass: missing.length === 0,
|
|
154
|
+
missing,
|
|
155
|
+
covered,
|
|
156
|
+
deferred,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Conditional Helpers ─────────────────────────────────
|
|
161
|
+
|
|
162
|
+
function isConditionalRequired(
|
|
163
|
+
conditional: NonNullable<RoleUsageRequirement['conditional']>,
|
|
164
|
+
pipeline: PipelineState,
|
|
165
|
+
): boolean {
|
|
166
|
+
switch (conditional) {
|
|
167
|
+
case 'always':
|
|
168
|
+
return true;
|
|
169
|
+
|
|
170
|
+
case 'if_recovery':
|
|
171
|
+
return pipeline.recoveryCount > 0;
|
|
172
|
+
|
|
173
|
+
case 'if_arbitrated':
|
|
174
|
+
return hasArbitratedConsensus(pipeline);
|
|
175
|
+
|
|
176
|
+
case 'if_journal_triggered':
|
|
177
|
+
return hasJournalTriggered(pipeline);
|
|
178
|
+
|
|
179
|
+
default:
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Check if any arbitration occurred during consensus phases. */
|
|
185
|
+
function hasArbitratedConsensus(pipeline: PipelineState): boolean {
|
|
186
|
+
// Reason: Arbitration artifacts are created when consensus requires arbitrator intervention
|
|
187
|
+
const arbitrationArtifacts = pipeline.artifacts.filter((a) => a.type === 'arbitration');
|
|
188
|
+
return arbitrationArtifacts.length > 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Check if any journalist-triggering phases have completed. */
|
|
192
|
+
function hasJournalTriggered(pipeline: PipelineState): boolean {
|
|
193
|
+
const journalPhases: PipelinePhase[] = [
|
|
194
|
+
'CONSENSUS_MASTER_PLAN', 'CONSENSUS_ARCHITECTURE', 'CONSENSUS_ROLE_PLANS',
|
|
195
|
+
'AUDIT', 'PRODUCTION_GATE', 'RECOVERY_LOOP', 'DONE',
|
|
196
|
+
];
|
|
197
|
+
const completedPhases = new Set(Object.keys(pipeline.gateResults));
|
|
198
|
+
return journalPhases.some((phase) => completedPhases.has(phase));
|
|
199
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Usage Registry — in-memory helper wrapping the persistent
|
|
3
|
+
* SkillUsageEvent[] array from PipelineState.
|
|
4
|
+
*
|
|
5
|
+
* Records when a skill is actually injected into an LLM prompt
|
|
6
|
+
* or execution context (not on mere load).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { PipelineRole, PipelinePhase } from '../types.js';
|
|
10
|
+
|
|
11
|
+
// ─── Types ───────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export type SkillUsedAs =
|
|
14
|
+
| 'system_prompt'
|
|
15
|
+
| 'review_prompt'
|
|
16
|
+
| 'arbitration_prompt'
|
|
17
|
+
| 'role_context'
|
|
18
|
+
| 'planning_prompt'
|
|
19
|
+
| 'strategy_context'
|
|
20
|
+
| 'other';
|
|
21
|
+
|
|
22
|
+
export type SkillSource = 'project_override' | 'defaults' | 'disk';
|
|
23
|
+
|
|
24
|
+
export interface SkillUsageEvent {
|
|
25
|
+
role: PipelineRole;
|
|
26
|
+
phase: PipelinePhase;
|
|
27
|
+
used_as: SkillUsedAs;
|
|
28
|
+
skill_source: SkillSource;
|
|
29
|
+
skill_version?: string;
|
|
30
|
+
timestamp: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ─── Registry ────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Wraps the persistent events array from PipelineState.
|
|
37
|
+
* Pushes directly into the shared array reference so state
|
|
38
|
+
* serialization captures all recorded events.
|
|
39
|
+
*/
|
|
40
|
+
export class SkillUsageRegistry {
|
|
41
|
+
constructor(private readonly events: SkillUsageEvent[]) {}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Record a usage event.
|
|
45
|
+
*
|
|
46
|
+
* Call ONLY when skill is injected into an LLM prompt
|
|
47
|
+
* or execution context, not on mere load.
|
|
48
|
+
*
|
|
49
|
+
* Args:
|
|
50
|
+
* role: The pipeline role whose skill was used.
|
|
51
|
+
* phase: The phase during which usage occurred.
|
|
52
|
+
* usedAs: How the skill was consumed.
|
|
53
|
+
* skillSource: Whether from project override or defaults.
|
|
54
|
+
* version: Optional skill version string.
|
|
55
|
+
*/
|
|
56
|
+
record(
|
|
57
|
+
role: PipelineRole,
|
|
58
|
+
phase: PipelinePhase,
|
|
59
|
+
usedAs: SkillUsedAs,
|
|
60
|
+
skillSource: SkillSource,
|
|
61
|
+
version?: string,
|
|
62
|
+
): void {
|
|
63
|
+
this.events.push({
|
|
64
|
+
role,
|
|
65
|
+
phase,
|
|
66
|
+
used_as: usedAs,
|
|
67
|
+
skill_source: skillSource,
|
|
68
|
+
skill_version: version,
|
|
69
|
+
timestamp: new Date().toISOString(),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Get a copy of all recorded events. */
|
|
74
|
+
getEvents(): SkillUsageEvent[] {
|
|
75
|
+
return [...this.events];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Get events for a specific role. */
|
|
79
|
+
getEventsForRole(role: PipelineRole): SkillUsageEvent[] {
|
|
80
|
+
return this.events.filter((e) => e.role === role);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Check if any usage has been recorded for a role. */
|
|
84
|
+
hasUsage(role: PipelineRole): boolean {
|
|
85
|
+
return this.events.some((e) => e.role === role);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strategy context loader for pipeline roles.
|
|
3
|
+
* Loads website strategy from disk and formats it for injection
|
|
4
|
+
* into role planning and execution prompts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
import type { PipelineRole } from './types.js';
|
|
11
|
+
import { WebsiteStrategySchema } from '../types/website-strategy.js';
|
|
12
|
+
import { formatWebsiteStrategy } from '../shared/website-strategy-format.js';
|
|
13
|
+
|
|
14
|
+
/** Roles that should receive website strategy context */
|
|
15
|
+
export const STRATEGY_ROLES: readonly PipelineRole[] = [
|
|
16
|
+
'WEBSITE_PROGRAMMER',
|
|
17
|
+
'MARKETING_EXPERT',
|
|
18
|
+
'SOCIAL_EXPERT',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/** Known strategy file locations, checked in order */
|
|
22
|
+
const STRATEGY_PATHS = [
|
|
23
|
+
'.popeye/website-strategy.json',
|
|
24
|
+
'.popeye/website-strategy.md',
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Load website strategy from disk and format for prompt injection.
|
|
29
|
+
* Checks known paths in order. Returns undefined if no valid strategy found.
|
|
30
|
+
*
|
|
31
|
+
* Args:
|
|
32
|
+
* projectDir: Root project directory containing .popeye/.
|
|
33
|
+
*
|
|
34
|
+
* Returns:
|
|
35
|
+
* string | undefined: Formatted strategy text, or undefined if not found/invalid.
|
|
36
|
+
*/
|
|
37
|
+
export function loadStrategyForRole(projectDir: string): string | undefined {
|
|
38
|
+
for (const relPath of STRATEGY_PATHS) {
|
|
39
|
+
const fullPath = join(projectDir, relPath);
|
|
40
|
+
if (!existsSync(fullPath)) continue;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const raw = readFileSync(fullPath, 'utf-8');
|
|
44
|
+
|
|
45
|
+
// .md files: return raw content directly
|
|
46
|
+
if (relPath.endsWith('.md')) return raw;
|
|
47
|
+
|
|
48
|
+
// .json files: parse, validate, format
|
|
49
|
+
const parsed = JSON.parse(raw);
|
|
50
|
+
const strategyData = parsed.strategy ?? parsed;
|
|
51
|
+
const result = WebsiteStrategySchema.safeParse(strategyData);
|
|
52
|
+
if (!result.success) continue;
|
|
53
|
+
|
|
54
|
+
return formatWebsiteStrategy(result.data);
|
|
55
|
+
} catch {
|
|
56
|
+
continue; // Malformed file, try next path
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
@@ -32,8 +32,12 @@ export const ArtifactTypeSchema = z.enum([
|
|
|
32
32
|
'resolved_commands',
|
|
33
33
|
'constitution',
|
|
34
34
|
'change_request',
|
|
35
|
+
'recovery_fix_plan',
|
|
36
|
+
'auto_recovery_guidance',
|
|
35
37
|
'additional_context',
|
|
36
38
|
'skill_generation_log',
|
|
39
|
+
'skill_usage_log',
|
|
40
|
+
'install_check',
|
|
37
41
|
]);
|
|
38
42
|
export type ArtifactType = z.infer<typeof ArtifactTypeSchema>;
|
|
39
43
|
|
|
@@ -16,6 +16,8 @@ export const GateCheckTypeSchema = z.enum([
|
|
|
16
16
|
'placeholder_scan',
|
|
17
17
|
'start',
|
|
18
18
|
'env_check',
|
|
19
|
+
'skill_coverage',
|
|
20
|
+
'install',
|
|
19
21
|
]);
|
|
20
22
|
export type GateCheckType = z.infer<typeof GateCheckTypeSchema>;
|
|
21
23
|
|
|
@@ -42,6 +44,8 @@ export const ResolvedCommandsSchema = z.object({
|
|
|
42
44
|
typecheck: z.string().optional(),
|
|
43
45
|
migrations: z.string().optional(),
|
|
44
46
|
start: z.string().optional(),
|
|
47
|
+
install: z.string().optional(),
|
|
48
|
+
install_cwd: z.string().optional(),
|
|
45
49
|
resolved_from: z.string(),
|
|
46
50
|
});
|
|
47
51
|
export type ResolvedCommands = z.infer<typeof ResolvedCommandsSchema>;
|
|
@@ -51,8 +51,10 @@ export const ReviewerVoteSchema = z.object({
|
|
|
51
51
|
vote: z.enum(['APPROVE', 'REJECT', 'CONDITIONAL']),
|
|
52
52
|
confidence: z.number().min(0).max(1),
|
|
53
53
|
blocking_issues: z.array(z.string()),
|
|
54
|
+
required_changes: z.array(z.string()).optional(),
|
|
54
55
|
suggestions: z.array(z.string()),
|
|
55
56
|
evidence_refs: z.array(ArtifactRefSchema),
|
|
57
|
+
reviewer_inconsistency: z.boolean().optional(),
|
|
56
58
|
});
|
|
57
59
|
export type ReviewerVote = z.infer<typeof ReviewerVoteSchema>;
|
|
58
60
|
|
|
@@ -69,6 +71,7 @@ export const ConsensusResultSchema = z.object({
|
|
|
69
71
|
score: z.number().min(0).max(1),
|
|
70
72
|
weighted_score: z.number().min(0).max(1),
|
|
71
73
|
participating_reviewers: z.number().int(),
|
|
74
|
+
has_true_blockers: z.boolean().default(false),
|
|
72
75
|
});
|
|
73
76
|
|
|
74
77
|
export const ArbitratorResultSchema = z.object({
|
|
@@ -77,6 +80,13 @@ export const ArbitratorResultSchema = z.object({
|
|
|
77
80
|
artifact_ref: ArtifactRefSchema.optional(),
|
|
78
81
|
});
|
|
79
82
|
|
|
83
|
+
export const NormalizationMovesSchema = z.object({
|
|
84
|
+
tagged_blockers_demoted_to_suggestions: z.number(),
|
|
85
|
+
tagged_blockers_demoted_to_required: z.number(),
|
|
86
|
+
untagged_from_blocking_routed_to_required: z.number(),
|
|
87
|
+
forced_rejects: z.number(),
|
|
88
|
+
}).optional();
|
|
89
|
+
|
|
80
90
|
export const ConsensusPacketSchema = z.object({
|
|
81
91
|
metadata: z.object({
|
|
82
92
|
packet_id: z.string(),
|
|
@@ -89,6 +99,7 @@ export const ConsensusPacketSchema = z.object({
|
|
|
89
99
|
consensus_result: ConsensusResultSchema,
|
|
90
100
|
arbitrator_result: ArbitratorResultSchema.optional(),
|
|
91
101
|
final_status: z.enum(['APPROVED', 'REJECTED', 'ARBITRATED']),
|
|
102
|
+
normalization_moves: NormalizationMovesSchema,
|
|
92
103
|
});
|
|
93
104
|
export type ConsensusPacket = z.infer<typeof ConsensusPacketSchema>;
|
|
94
105
|
|
|
@@ -125,7 +136,13 @@ export const ChangeRequestSchema = z.object({
|
|
|
125
136
|
affected_phases: z.array(PipelinePhaseSchema),
|
|
126
137
|
risk_level: z.enum(['low', 'medium', 'high']),
|
|
127
138
|
}),
|
|
128
|
-
status: z.enum(['proposed', 'approved', 'rejected']),
|
|
139
|
+
status: z.enum(['proposed', 'approved', 'rejected', 'resolved']),
|
|
129
140
|
approval_artifact: ArtifactRefSchema.optional(),
|
|
141
|
+
/** Deterministic drift fingerprint for CR deduplication (v2.4.9) */
|
|
142
|
+
drift_key: z.string().optional(),
|
|
143
|
+
/** ISO timestamp when the CR was resolved (v2.4.9) */
|
|
144
|
+
resolved_at: z.string().optional(),
|
|
145
|
+
/** How the CR was resolved (v2.4.9) */
|
|
146
|
+
resolution: z.enum(['accepted_baseline', 'reverted', 'manual_override']).optional(),
|
|
130
147
|
});
|
|
131
148
|
export type ChangeRequest = z.infer<typeof ChangeRequestSchema>;
|
|
@@ -9,6 +9,7 @@ import { GateCheckTypeSchema, GateCheckResultSchema, ResolvedCommandsSchema, typ
|
|
|
9
9
|
import type { ArtifactType } from './artifacts.js';
|
|
10
10
|
import type { ConsensusPacket } from './packets.js';
|
|
11
11
|
import type { RCAPacket } from './packets.js';
|
|
12
|
+
// Reason: SkillUsageEvent type is defined inline in the Zod schema to keep state serialization self-contained
|
|
12
13
|
|
|
13
14
|
// ─── Gate Definition ─────────────────────────────────────
|
|
14
15
|
|
|
@@ -88,6 +89,7 @@ export const PipelineStateSchema = z.object({
|
|
|
88
89
|
missingArtifacts: z.array(ArtifactTypeSchema),
|
|
89
90
|
failedChecks: z.array(GateCheckTypeSchema),
|
|
90
91
|
consensusScore: z.number().optional(),
|
|
92
|
+
finalStatus: z.string().optional(), // v2.4.3: 'APPROVED' | 'REJECTED' | 'ARBITRATED'
|
|
91
93
|
timestamp: z.string(),
|
|
92
94
|
})),
|
|
93
95
|
gateChecks: z.record(z.string(), z.array(GateCheckResultSchema)),
|
|
@@ -97,6 +99,12 @@ export const PipelineStateSchema = z.object({
|
|
|
97
99
|
resolvedCommands: ResolvedCommandsSchema.optional(),
|
|
98
100
|
/** Tracks which phase failed, for recovery routing */
|
|
99
101
|
failedPhase: PipelinePhaseSchema.optional(),
|
|
102
|
+
/** Last rewind target from recovery — detects repeated same-target rewinds (v2.4.6) */
|
|
103
|
+
lastRewindTarget: PipelinePhaseSchema.optional(),
|
|
104
|
+
/** v2.6.0: Auto-recovery result — tracks whether arbitrator guidance was attempted before STUCK */
|
|
105
|
+
autoRecoveryResult: z.enum(['success', 'timeout', 'invalid', 'error']).optional(),
|
|
106
|
+
/** v2.7.0: Baseline failure count before recovery — for regression detection */
|
|
107
|
+
recoveryBaselineFailedCheckCount: z.number().int().min(0).optional(),
|
|
100
108
|
/** Session guidance: user steering, upgrade context, or resume instructions */
|
|
101
109
|
sessionGuidance: z.string().optional(),
|
|
102
110
|
/** Pending change requests that force re-routing to consensus phases (v1.1) */
|
|
@@ -104,7 +112,23 @@ export const PipelineStateSchema = z.object({
|
|
|
104
112
|
cr_id: z.string(),
|
|
105
113
|
change_type: z.enum(['scope', 'architecture', 'dependency', 'config', 'requirement']),
|
|
106
114
|
target_phase: PipelinePhaseSchema,
|
|
107
|
-
status: z.enum(['proposed', 'approved', 'rejected']),
|
|
115
|
+
status: z.enum(['proposed', 'approved', 'rejected', 'resolved']),
|
|
116
|
+
drift_key: z.string().optional(),
|
|
117
|
+
})).optional(),
|
|
118
|
+
/** ID of the CR currently being processed by the routed phase (v2.4.9) */
|
|
119
|
+
activeChangeRequestId: z.string().optional(),
|
|
120
|
+
/** Snapshot override: after config CR resolved, REVIEW uses this instead of CONSENSUS_ROLE_PLANS baseline (v2.4.9) */
|
|
121
|
+
baselineSnapshotOverride: ArtifactRefSchema.optional(),
|
|
122
|
+
/** Rolling loop signatures for stagnation detection (v2.4.9) */
|
|
123
|
+
lastSignatures: z.array(z.string()).optional(),
|
|
124
|
+
/** Skill usage events for coverage enforcement (v2.2.1) */
|
|
125
|
+
skillUsageEvents: z.array(z.object({
|
|
126
|
+
role: PipelineRoleSchema,
|
|
127
|
+
phase: PipelinePhaseSchema,
|
|
128
|
+
used_as: z.enum(['system_prompt', 'review_prompt', 'arbitration_prompt', 'role_context', 'planning_prompt', 'other']),
|
|
129
|
+
skill_source: z.enum(['project_override', 'defaults']),
|
|
130
|
+
skill_version: z.string().optional(),
|
|
131
|
+
timestamp: z.string(),
|
|
108
132
|
})).optional(),
|
|
109
133
|
});
|
|
110
134
|
export type PipelineState = z.infer<typeof PipelineStateSchema>;
|
|
@@ -163,5 +187,6 @@ export function createDefaultPipelineState(): PipelineState {
|
|
|
163
187
|
gateChecks: {},
|
|
164
188
|
activeRoles: [],
|
|
165
189
|
constitutionHash: '',
|
|
190
|
+
skillUsageEvents: [],
|
|
166
191
|
};
|
|
167
192
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text utilities for normalizing LLM-generated issue lists.
|
|
3
|
+
* Detects false-positive "no issues" responses that LLMs produce in
|
|
4
|
+
* BLOCKING_ISSUES fields (e.g. "No blocking issues found", "N/A", "None identified").
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Exact-match tokens that mean "nothing" after trim+lowercase */
|
|
8
|
+
const NONE_EXACT = new Set(['none', 'n/a', 'na', 'nil', 'nothing']);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Anchored phrase patterns that indicate "no issues".
|
|
12
|
+
* Tested against the cleaned text (bullet-stripped, trimmed, lowercased,
|
|
13
|
+
* trailing punctuation removed).
|
|
14
|
+
*/
|
|
15
|
+
const NONE_PHRASES: RegExp[] = [
|
|
16
|
+
/^no\s+(?:(?:blocking|critical|significant|major)\s+)*(?:issues?|concerns?|problems?|blockers?|showstoppers?)\b/,
|
|
17
|
+
/^none\s+(?:identified|found|detected|noted|observed|reported|applicable|at this time)\b/,
|
|
18
|
+
/^there\s+are\s+no\s+(?:(?:significant|major|critical|blocking)\s+)*(?:issues|concerns|problems|blockers)\b/,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Determine whether a text string is a "none-variant" — an LLM's way of
|
|
23
|
+
* saying "no blocking issues" rather than an actual issue description.
|
|
24
|
+
*
|
|
25
|
+
* @param text - Raw text from a blocking_issues list item
|
|
26
|
+
* @returns true if the text is a none-variant (should be filtered out)
|
|
27
|
+
*/
|
|
28
|
+
export function isNoneVariant(text: string): boolean {
|
|
29
|
+
// Empty / whitespace-only
|
|
30
|
+
const trimmed = text.trim();
|
|
31
|
+
if (trimmed.length === 0) return true;
|
|
32
|
+
|
|
33
|
+
// Strip leading bullet prefixes: "- ", "* ", "+ ", "1) ", "1. ", etc.
|
|
34
|
+
const stripped = trimmed.replace(/^[-*+\d.)\s]+/, '').trim();
|
|
35
|
+
if (stripped.length === 0) return true;
|
|
36
|
+
|
|
37
|
+
// Strip trailing punctuation
|
|
38
|
+
const cleaned = stripped.replace(/[.,;!]+$/, '').trim();
|
|
39
|
+
if (cleaned.length === 0) return true;
|
|
40
|
+
|
|
41
|
+
const lower = cleaned.toLowerCase();
|
|
42
|
+
|
|
43
|
+
// Exact match check
|
|
44
|
+
if (NONE_EXACT.has(lower)) return true;
|
|
45
|
+
|
|
46
|
+
// Anchored phrase check
|
|
47
|
+
for (const pattern of NONE_PHRASES) {
|
|
48
|
+
if (pattern.test(lower)) return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Length guard: >80 chars and no phrase match means it is likely a real issue
|
|
52
|
+
if (cleaned.length > 80) return false;
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Normalize an issue list by filtering out empty strings and none-variants.
|
|
59
|
+
* Intended as a drop-in replacement for the weak `.filter(i => i.toLowerCase() !== 'none')`
|
|
60
|
+
* used across adapters.
|
|
61
|
+
*
|
|
62
|
+
* @param items - Raw list items from parseList()
|
|
63
|
+
* @returns Filtered list with only genuine issues
|
|
64
|
+
*/
|
|
65
|
+
export function normalizeIssueList(items: string[]): string[] {
|
|
66
|
+
return items
|
|
67
|
+
.map((i) => i.trim())
|
|
68
|
+
.filter((i) => i.length > 0)
|
|
69
|
+
.filter((i) => !isNoneVariant(i));
|
|
70
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared website strategy formatter.
|
|
3
|
+
* Formats a WebsiteStrategyDocument as structured text for prompt injection.
|
|
4
|
+
* Used by both workflow (plan-mode consensus) and pipeline (role planning/execution).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { WebsiteStrategyDocument } from '../types/website-strategy.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format a WebsiteStrategyDocument as structured text for prompt injection.
|
|
11
|
+
*
|
|
12
|
+
* Args:
|
|
13
|
+
* strategy: The validated strategy document to format.
|
|
14
|
+
*
|
|
15
|
+
* Returns:
|
|
16
|
+
* string: Multi-line formatted text with ICP, Positioning, Messaging,
|
|
17
|
+
* SEO Keywords, Site Architecture, and Conversion Strategy sections.
|
|
18
|
+
*/
|
|
19
|
+
export function formatWebsiteStrategy(strategy: WebsiteStrategyDocument): string {
|
|
20
|
+
const lines: string[] = [];
|
|
21
|
+
|
|
22
|
+
lines.push(`### Target Customer`);
|
|
23
|
+
lines.push(`- Persona: ${strategy.icp.primaryPersona}`);
|
|
24
|
+
lines.push(`- Pain points: ${strategy.icp.painPoints.join(', ')}`);
|
|
25
|
+
lines.push('');
|
|
26
|
+
|
|
27
|
+
lines.push(`### Positioning`);
|
|
28
|
+
lines.push(`- Category: ${strategy.positioning.category}`);
|
|
29
|
+
lines.push(`- Value proposition: ${strategy.positioning.valueProposition}`);
|
|
30
|
+
lines.push(`- Differentiators: ${strategy.positioning.differentiators.join(', ')}`);
|
|
31
|
+
lines.push('');
|
|
32
|
+
|
|
33
|
+
lines.push(`### Messaging`);
|
|
34
|
+
lines.push(`- Headline: ${strategy.messaging.headline}`);
|
|
35
|
+
lines.push(`- Subheadline: ${strategy.messaging.subheadline}`);
|
|
36
|
+
lines.push('');
|
|
37
|
+
|
|
38
|
+
lines.push(`### SEO Keywords`);
|
|
39
|
+
lines.push(`- Primary: ${strategy.seoStrategy.primaryKeywords.join(', ')}`);
|
|
40
|
+
lines.push(`- Secondary: ${strategy.seoStrategy.secondaryKeywords.join(', ')}`);
|
|
41
|
+
lines.push('');
|
|
42
|
+
|
|
43
|
+
lines.push(`### Site Architecture`);
|
|
44
|
+
for (const page of strategy.siteArchitecture.pages) {
|
|
45
|
+
lines.push(`- ${page.path} (${page.pageType}): ${page.purpose}`);
|
|
46
|
+
}
|
|
47
|
+
lines.push('');
|
|
48
|
+
|
|
49
|
+
lines.push(`### Conversion Strategy`);
|
|
50
|
+
lines.push(`- Primary CTA: "${strategy.conversionStrategy.primaryCta.text}" -> ${strategy.conversionStrategy.primaryCta.href}`);
|
|
51
|
+
lines.push(`- Secondary CTA: "${strategy.conversionStrategy.secondaryCta.text}" -> ${strategy.conversionStrategy.secondaryCta.href}`);
|
|
52
|
+
lines.push(`- Trust signals: ${strategy.conversionStrategy.trustSignals.join(', ')}`);
|
|
53
|
+
lines.push(`- Lead capture: ${strategy.conversionStrategy.leadCapture}`);
|
|
54
|
+
|
|
55
|
+
return lines.join('\n');
|
|
56
|
+
}
|