@cleocode/core 2026.4.5 → 2026.4.6
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/discovery.d.ts +69 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1643 -2349
- package/dist/index.js.map +4 -4
- package/dist/init.d.ts +51 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/internal.d.ts +9 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/lifecycle/default-chain.d.ts +8 -2
- package/dist/lifecycle/default-chain.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/stage-guidance.d.ts +140 -0
- package/dist/lifecycle/stage-guidance.d.ts.map +1 -0
- package/dist/orchestration/protocol-validators.d.ts +122 -3
- package/dist/orchestration/protocol-validators.d.ts.map +1 -1
- package/dist/paths.d.ts +91 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/scaffold.d.ts +31 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/skills/dispatch.d.ts +1 -1
- package/dist/skills/skill-paths.d.ts +9 -6
- package/dist/skills/skill-paths.d.ts.map +1 -1
- package/dist/validation/protocols/_shared.d.ts +40 -0
- package/dist/validation/protocols/_shared.d.ts.map +1 -0
- package/dist/validation/protocols/architecture-decision.d.ts +23 -0
- package/dist/validation/protocols/architecture-decision.d.ts.map +1 -0
- package/dist/validation/protocols/artifact-publish.d.ts +22 -0
- package/dist/validation/protocols/artifact-publish.d.ts.map +1 -0
- package/dist/validation/protocols/consensus.d.ts +11 -17
- package/dist/validation/protocols/consensus.d.ts.map +1 -1
- package/dist/validation/protocols/contribution.d.ts +12 -17
- package/dist/validation/protocols/contribution.d.ts.map +1 -1
- package/dist/validation/protocols/decomposition.d.ts +18 -21
- package/dist/validation/protocols/decomposition.d.ts.map +1 -1
- package/dist/validation/protocols/implementation.d.ts +9 -17
- package/dist/validation/protocols/implementation.d.ts.map +1 -1
- package/dist/validation/protocols/provenance.d.ts +23 -0
- package/dist/validation/protocols/provenance.d.ts.map +1 -0
- package/dist/validation/protocols/release.d.ts +25 -0
- package/dist/validation/protocols/release.d.ts.map +1 -0
- package/dist/validation/protocols/research.d.ts +9 -17
- package/dist/validation/protocols/research.d.ts.map +1 -1
- package/dist/validation/protocols/specification.d.ts +7 -17
- package/dist/validation/protocols/specification.d.ts.map +1 -1
- package/dist/validation/protocols/testing.d.ts +22 -0
- package/dist/validation/protocols/testing.d.ts.map +1 -0
- package/dist/validation/protocols/validation.d.ts +22 -0
- package/dist/validation/protocols/validation.d.ts.map +1 -0
- package/package.json +7 -7
- package/src/__tests__/injection-mvi-tiers.test.js +54 -90
- package/src/__tests__/injection-mvi-tiers.test.js.map +1 -1
- package/src/discovery.ts +235 -0
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js +3 -1
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js.map +1 -1
- package/src/index.ts +16 -0
- package/src/init.ts +196 -0
- package/src/internal.ts +31 -1
- package/src/lifecycle/default-chain.ts +11 -2
- package/src/lifecycle/index.ts +10 -0
- package/src/lifecycle/stage-guidance.ts +282 -0
- package/src/metrics/__tests__/provider-detection.test.js +19 -7
- package/src/metrics/__tests__/provider-detection.test.js.map +1 -1
- package/src/orchestration/__tests__/protocol-validators.test.js +228 -8
- package/src/orchestration/__tests__/protocol-validators.test.js.map +1 -1
- package/src/orchestration/__tests__/protocol-validators.test.ts +259 -7
- package/src/orchestration/protocol-validators.ts +419 -4
- package/src/paths.ts +110 -0
- package/src/scaffold.ts +240 -4
- package/src/skills/dispatch.ts +6 -6
- package/src/skills/skill-paths.ts +27 -23
- package/src/validation/protocols/_shared.ts +88 -0
- package/src/validation/protocols/architecture-decision.ts +52 -0
- package/src/validation/protocols/artifact-publish.ts +49 -0
- package/src/validation/protocols/consensus.ts +44 -74
- package/src/validation/protocols/contribution.ts +28 -65
- package/src/validation/protocols/decomposition.ts +37 -64
- package/src/validation/protocols/implementation.ts +25 -65
- package/src/validation/protocols/protocols-markdown/architecture-decision.md +303 -0
- package/src/validation/protocols/protocols-markdown/artifact-publish.md +600 -0
- package/src/validation/protocols/protocols-markdown/consensus.md +322 -0
- package/src/validation/protocols/protocols-markdown/contribution.md +388 -0
- package/src/validation/protocols/protocols-markdown/decomposition.md +421 -0
- package/src/validation/protocols/protocols-markdown/implementation.md +357 -0
- package/src/validation/protocols/protocols-markdown/provenance.md +613 -0
- package/src/validation/protocols/protocols-markdown/release.md +783 -0
- package/src/validation/protocols/protocols-markdown/research.md +261 -0
- package/src/validation/protocols/protocols-markdown/specification.md +300 -0
- package/src/validation/protocols/protocols-markdown/testing.md +287 -0
- package/src/validation/protocols/protocols-markdown/validation.md +242 -0
- package/src/validation/protocols/provenance.ts +50 -0
- package/src/validation/protocols/release.ts +44 -0
- package/src/validation/protocols/research.ts +25 -87
- package/src/validation/protocols/specification.ts +27 -89
- package/src/validation/protocols/testing.ts +46 -0
- package/src/validation/protocols/validation.ts +46 -0
- package/dist/validation/protocols/release-protocol.d.ts +0 -27
- package/dist/validation/protocols/release-protocol.d.ts.map +0 -1
- package/dist/validation/protocols/testing-protocol.d.ts +0 -27
- package/dist/validation/protocols/testing-protocol.d.ts.map +0 -1
- package/dist/validation/protocols/validation-protocol.d.ts +0 -27
- package/dist/validation/protocols/validation-protocol.d.ts.map +0 -1
- package/schemas/agent-configs.schema.json +0 -120
- package/schemas/agent-registry.schema.json +0 -132
- package/schemas/archive.schema.json +0 -450
- package/schemas/brain-decision.schema.json +0 -69
- package/schemas/brain-learning.schema.json +0 -57
- package/schemas/brain-pattern.schema.json +0 -72
- package/schemas/critical-path.schema.json +0 -246
- package/schemas/deps-cache.schema.json +0 -97
- package/schemas/doctor-output.schema.json +0 -283
- package/schemas/error.schema.json +0 -161
- package/schemas/global-config.schema.json +0 -219
- package/schemas/grade.schema.json +0 -49
- package/schemas/log.schema.json +0 -250
- package/schemas/metrics.schema.json +0 -328
- package/schemas/migrations.schema.json +0 -150
- package/schemas/nexus-registry.schema.json +0 -90
- package/schemas/operation-constitution.schema.json +0 -438
- package/schemas/output.schema.json +0 -164
- package/schemas/projects-registry.schema.json +0 -107
- package/schemas/protocol-frontmatter.schema.json +0 -72
- package/schemas/rcasd-consensus-report.schema.json +0 -10
- package/schemas/rcasd-evidence.schema.json +0 -42
- package/schemas/rcasd-gate-result.schema.json +0 -46
- package/schemas/rcasd-hitl-resolution.schema.json +0 -10
- package/schemas/rcasd-index.schema.json +0 -10
- package/schemas/rcasd-manifest.schema.json +0 -10
- package/schemas/rcasd-research-output.schema.json +0 -10
- package/schemas/rcasd-spec-frontmatter.schema.json +0 -10
- package/schemas/rcasd-stage-transition.schema.json +0 -38
- package/schemas/releases.schema.json +0 -267
- package/schemas/skills-manifest.schema.json +0 -91
- package/schemas/spec-index.schema.json +0 -196
- package/schemas/system-flow-atlas.schema.json +0 -125
- package/src/conduit/__tests__/dual-api-e2e.test.d.ts.map +0 -1
- package/src/conduit/__tests__/dual-api-e2e.test.js +0 -178
- package/src/conduit/__tests__/dual-api-e2e.test.js.map +0 -1
- package/src/conduit/__tests__/dual-api-e2e.test.ts +0 -212
- package/src/validation/protocols/release-protocol.ts +0 -80
- package/src/validation/protocols/testing-protocol.ts +0 -93
- package/src/validation/protocols/validation-protocol.ts +0 -93
|
@@ -41,14 +41,29 @@ export interface ManifestEntryInput {
|
|
|
41
41
|
sources?: string[];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* All supported protocol types.
|
|
46
|
+
*
|
|
47
|
+
* The canonical set covers all 9 RCASD-IVTR pipeline stages plus the 3
|
|
48
|
+
* cross-cutting protocols that compose with specific stages:
|
|
49
|
+
*
|
|
50
|
+
* - Pipeline stages: research, consensus, architecture-decision,
|
|
51
|
+
* specification, decomposition, implementation, validation, testing, release
|
|
52
|
+
* - Cross-cutting: contribution (at implementation),
|
|
53
|
+
* artifact-publish (at release), provenance (at release)
|
|
54
|
+
*
|
|
55
|
+
* @task T260 — unify pipeline stages and cross-cutting protocols
|
|
56
|
+
*/
|
|
45
57
|
export const PROTOCOL_TYPES = [
|
|
46
58
|
'research',
|
|
47
59
|
'consensus',
|
|
60
|
+
'architecture-decision',
|
|
48
61
|
'specification',
|
|
49
62
|
'decomposition',
|
|
50
63
|
'implementation',
|
|
51
64
|
'contribution',
|
|
65
|
+
'validation',
|
|
66
|
+
'testing',
|
|
52
67
|
'release',
|
|
53
68
|
'artifact-publish',
|
|
54
69
|
'provenance',
|
|
@@ -56,7 +71,17 @@ export const PROTOCOL_TYPES = [
|
|
|
56
71
|
|
|
57
72
|
export type ProtocolType = (typeof PROTOCOL_TYPES)[number];
|
|
58
73
|
|
|
59
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Map protocol types to exit codes.
|
|
76
|
+
*
|
|
77
|
+
* Pipeline protocols use the 60-67 orchestrator range. Cross-cutting
|
|
78
|
+
* protocols with dedicated ranges (artifact-publish 85-89, provenance 90-94)
|
|
79
|
+
* use their own codes. Architecture-decision uses 84 PROVENANCE_REQUIRED
|
|
80
|
+
* because every ADR MUST be generated from an accepted Consensus verdict
|
|
81
|
+
* (ADR-001) — the provenance chain is the whole point.
|
|
82
|
+
*
|
|
83
|
+
* @task T260
|
|
84
|
+
*/
|
|
60
85
|
export const PROTOCOL_EXIT_CODES: Record<ProtocolType, ExitCode> = {
|
|
61
86
|
research: ExitCode.PROTOCOL_MISSING, // 60
|
|
62
87
|
consensus: ExitCode.INVALID_RETURN_MESSAGE, // 61
|
|
@@ -65,8 +90,11 @@ export const PROTOCOL_EXIT_CODES: Record<ProtocolType, ExitCode> = {
|
|
|
65
90
|
implementation: ExitCode.AUTONOMOUS_BOUNDARY, // 64
|
|
66
91
|
contribution: ExitCode.HANDOFF_REQUIRED, // 65
|
|
67
92
|
release: ExitCode.RESUME_FAILED, // 66
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
testing: ExitCode.CONCURRENT_SESSION, // 67 (shared: both testing and validation are lifecycle gates)
|
|
94
|
+
validation: ExitCode.LIFECYCLE_GATE_FAILED, // 80
|
|
95
|
+
'architecture-decision': ExitCode.PROVENANCE_REQUIRED, // 84 (ADR-001: must link to consensus)
|
|
96
|
+
'artifact-publish': ExitCode.ARTIFACT_PUBLISH_FAILED, // 88 (dedicated)
|
|
97
|
+
provenance: ExitCode.ATTESTATION_INVALID, // 94 (dedicated)
|
|
70
98
|
};
|
|
71
99
|
|
|
72
100
|
// ============================================================
|
|
@@ -615,6 +643,381 @@ export function validateProvenanceProtocol(
|
|
|
615
643
|
return { valid: !hasErrors, protocol: 'provenance', violations, score: Math.max(0, score) };
|
|
616
644
|
}
|
|
617
645
|
|
|
646
|
+
// ============================================================
|
|
647
|
+
// Architecture Decision Record Protocol (ADR-*)
|
|
648
|
+
// ============================================================
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* ADR lifecycle status values.
|
|
652
|
+
* @task T260
|
|
653
|
+
*/
|
|
654
|
+
export type AdrStatus = 'proposed' | 'accepted' | 'superseded' | 'deprecated';
|
|
655
|
+
|
|
656
|
+
/** Architecture decision options for validator. */
|
|
657
|
+
export interface ArchitectureDecisionOptions {
|
|
658
|
+
/** Content of the ADR markdown document, used to verify required sections. */
|
|
659
|
+
adrContent?: string;
|
|
660
|
+
/** Current status of the decision record. */
|
|
661
|
+
status?: AdrStatus;
|
|
662
|
+
/** Whether a human-in-the-loop review has been completed (ADR-003). */
|
|
663
|
+
hitlReviewed?: boolean;
|
|
664
|
+
/** Whether downstream artifacts are flagged for review after supersession. */
|
|
665
|
+
downstreamFlagged?: boolean;
|
|
666
|
+
/** Whether the record is persisted in the canonical SQLite decisions table. */
|
|
667
|
+
persistedInDb?: boolean;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Validate an Architecture Decision Record manifest entry.
|
|
672
|
+
*
|
|
673
|
+
* Enforces the 8 MUST requirements from `architecture-decision.md`:
|
|
674
|
+
* ADR-001 (consensus provenance), ADR-002 (manifest link), ADR-003 (HITL),
|
|
675
|
+
* ADR-004 (required sections), ADR-005 (cascade on supersession),
|
|
676
|
+
* ADR-006 (SQLite persistence), ADR-007 (agent_type), ADR-008 (spec block).
|
|
677
|
+
*
|
|
678
|
+
* ADR-005, ADR-006, and ADR-008 require runtime state the caller must
|
|
679
|
+
* provide via options — this validator checks the options and never
|
|
680
|
+
* performs side-effectful I/O.
|
|
681
|
+
*
|
|
682
|
+
* @task T260
|
|
683
|
+
*/
|
|
684
|
+
export function validateArchitectureDecisionProtocol(
|
|
685
|
+
entry: ManifestEntryInput & { consensus_manifest_id?: string },
|
|
686
|
+
options: ArchitectureDecisionOptions = {},
|
|
687
|
+
): ProtocolValidationResult {
|
|
688
|
+
const violations: ProtocolViolation[] = [];
|
|
689
|
+
let score = 100;
|
|
690
|
+
|
|
691
|
+
// ADR-001 / ADR-002: MUST be generated from accepted Consensus and
|
|
692
|
+
// include a `consensus_manifest_id` link.
|
|
693
|
+
if (!entry.consensus_manifest_id || entry.consensus_manifest_id.trim().length === 0) {
|
|
694
|
+
violations.push({
|
|
695
|
+
requirement: 'ADR-001',
|
|
696
|
+
severity: 'error',
|
|
697
|
+
message: 'ADR must link to the originating consensus manifest',
|
|
698
|
+
fix: 'Add consensus_manifest_id to manifest entry referencing the accepted consensus',
|
|
699
|
+
});
|
|
700
|
+
score -= 25;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// ADR-003: Transition from proposed→accepted requires explicit HITL review.
|
|
704
|
+
if (options.status === 'accepted' && options.hitlReviewed === false) {
|
|
705
|
+
violations.push({
|
|
706
|
+
requirement: 'ADR-003',
|
|
707
|
+
severity: 'error',
|
|
708
|
+
message: 'ADR cannot be accepted without HITL (human-in-the-loop) review',
|
|
709
|
+
fix: 'Have a human review and approve the ADR before promoting to accepted',
|
|
710
|
+
});
|
|
711
|
+
score -= 30;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// ADR-004: MUST include Context, Options Evaluated, Decision, Rationale, Consequences.
|
|
715
|
+
if (options.adrContent) {
|
|
716
|
+
const requiredSections = [
|
|
717
|
+
/##\s+.*Context/i,
|
|
718
|
+
/##\s+.*Option/i,
|
|
719
|
+
/##\s+.*Decision/i,
|
|
720
|
+
/##\s+.*Rationale/i,
|
|
721
|
+
/##\s+.*Consequences/i,
|
|
722
|
+
];
|
|
723
|
+
const missing = requiredSections.filter((re) => !re.test(options.adrContent!)).length;
|
|
724
|
+
if (missing > 0) {
|
|
725
|
+
violations.push({
|
|
726
|
+
requirement: 'ADR-004',
|
|
727
|
+
severity: 'error',
|
|
728
|
+
message: `ADR missing ${missing} of 5 required sections (Context, Options, Decision, Rationale, Consequences)`,
|
|
729
|
+
fix: 'Add all five canonical sections to the ADR body',
|
|
730
|
+
});
|
|
731
|
+
score -= 20;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// ADR-005: If the record is superseded, downstream artifacts MUST be flagged.
|
|
736
|
+
if (options.status === 'superseded' && options.downstreamFlagged === false) {
|
|
737
|
+
violations.push({
|
|
738
|
+
requirement: 'ADR-005',
|
|
739
|
+
severity: 'error',
|
|
740
|
+
message: 'Superseded ADR has not triggered downstream invalidation cascade',
|
|
741
|
+
fix: 'Flag linked specifications, decomposition, and implementations for review',
|
|
742
|
+
});
|
|
743
|
+
score -= 20;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// ADR-006: MUST be persisted in the canonical SQLite decisions table.
|
|
747
|
+
if (options.persistedInDb === false) {
|
|
748
|
+
violations.push({
|
|
749
|
+
requirement: 'ADR-006',
|
|
750
|
+
severity: 'error',
|
|
751
|
+
message: 'ADR not persisted in canonical decisions SQLite table',
|
|
752
|
+
fix: 'Insert the decision via the Drizzle ORM architectureDecisions table',
|
|
753
|
+
});
|
|
754
|
+
score -= 15;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// ADR-007: MUST set agent_type: decision
|
|
758
|
+
if (!checkAgentType(entry, 'decision')) {
|
|
759
|
+
violations.push({
|
|
760
|
+
requirement: 'ADR-007',
|
|
761
|
+
severity: 'error',
|
|
762
|
+
message: `agent_type must be decision, got ${entry.agent_type ?? 'undefined'}`,
|
|
763
|
+
fix: 'Update manifest entry agent_type field to "decision"',
|
|
764
|
+
});
|
|
765
|
+
score -= 15;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// ADR (output file): MUST produce an ADR markdown document.
|
|
769
|
+
if (!checkRequiredField(entry, 'file')) {
|
|
770
|
+
violations.push({
|
|
771
|
+
requirement: 'ADR-004',
|
|
772
|
+
severity: 'error',
|
|
773
|
+
message: 'ADR must produce an output markdown file',
|
|
774
|
+
fix: 'Write the ADR to disk and reference it in the manifest entry',
|
|
775
|
+
});
|
|
776
|
+
score -= 10;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const hasErrors = violations.some((v) => v.severity === 'error');
|
|
780
|
+
return {
|
|
781
|
+
valid: !hasErrors,
|
|
782
|
+
protocol: 'architecture-decision',
|
|
783
|
+
violations,
|
|
784
|
+
score: Math.max(0, score),
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// ============================================================
|
|
789
|
+
// Validation Protocol (VALID-*)
|
|
790
|
+
// ============================================================
|
|
791
|
+
|
|
792
|
+
/** Validation-stage options for validator. */
|
|
793
|
+
export interface ValidationStageOptions {
|
|
794
|
+
/** Whether static analysis / type check passed (VALID-001). */
|
|
795
|
+
specMatchConfirmed?: boolean;
|
|
796
|
+
/** Whether the existing test suite ran successfully (VALID-002). */
|
|
797
|
+
testSuitePassed?: boolean;
|
|
798
|
+
/** Whether upstream protocol compliance checks passed (VALID-003). */
|
|
799
|
+
protocolComplianceChecked?: boolean;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Validate a manifest entry against the validation stage protocol.
|
|
804
|
+
*
|
|
805
|
+
* Enforces VALID-001..007 from `validation.md`. The validation stage runs
|
|
806
|
+
* static analysis, type checking, and pre-test quality gates. This validator
|
|
807
|
+
* verifies the manifest entry captures a real validation run; runtime gate
|
|
808
|
+
* enforcement happens in the lifecycle state machine, not here.
|
|
809
|
+
*
|
|
810
|
+
* @task T260
|
|
811
|
+
*/
|
|
812
|
+
export function validateValidationProtocol(
|
|
813
|
+
entry: ManifestEntryInput,
|
|
814
|
+
options: ValidationStageOptions = {},
|
|
815
|
+
): ProtocolValidationResult {
|
|
816
|
+
const violations: ProtocolViolation[] = [];
|
|
817
|
+
let score = 100;
|
|
818
|
+
|
|
819
|
+
// VALID-001: MUST verify implementation matches specification
|
|
820
|
+
if (options.specMatchConfirmed === false) {
|
|
821
|
+
violations.push({
|
|
822
|
+
requirement: 'VALID-001',
|
|
823
|
+
severity: 'error',
|
|
824
|
+
message: 'Validation must confirm implementation matches specification',
|
|
825
|
+
fix: 'Run spec-match validation before reporting completion',
|
|
826
|
+
});
|
|
827
|
+
score -= 25;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// VALID-002: MUST run existing test suite and report results
|
|
831
|
+
if (options.testSuitePassed === false) {
|
|
832
|
+
violations.push({
|
|
833
|
+
requirement: 'VALID-002',
|
|
834
|
+
severity: 'error',
|
|
835
|
+
message: 'Existing test suite failed during validation',
|
|
836
|
+
fix: 'Fix failing tests before completing the validation stage',
|
|
837
|
+
});
|
|
838
|
+
score -= 25;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// VALID-003: MUST check protocol compliance
|
|
842
|
+
if (options.protocolComplianceChecked === false) {
|
|
843
|
+
violations.push({
|
|
844
|
+
requirement: 'VALID-003',
|
|
845
|
+
severity: 'error',
|
|
846
|
+
message: 'Upstream protocol compliance not checked',
|
|
847
|
+
fix: 'Run cleo check protocol for every upstream protocol before validation exits',
|
|
848
|
+
});
|
|
849
|
+
score -= 20;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// VALID-005: MUST write validation summary (key_findings) to manifest
|
|
853
|
+
if (!checkArrayMinLength(entry, 'key_findings', 1)) {
|
|
854
|
+
violations.push({
|
|
855
|
+
requirement: 'VALID-005',
|
|
856
|
+
severity: 'error',
|
|
857
|
+
message: 'Validation must record summary findings in manifest entry',
|
|
858
|
+
fix: 'Populate key_findings with pass/fail counts and coverage',
|
|
859
|
+
});
|
|
860
|
+
score -= 15;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// VALID-006: MUST set agent_type: validation
|
|
864
|
+
if (!checkAgentType(entry, 'validation')) {
|
|
865
|
+
violations.push({
|
|
866
|
+
requirement: 'VALID-006',
|
|
867
|
+
severity: 'error',
|
|
868
|
+
message: `agent_type must be validation, got ${entry.agent_type ?? 'undefined'}`,
|
|
869
|
+
fix: 'Update manifest entry agent_type field to "validation"',
|
|
870
|
+
});
|
|
871
|
+
score -= 15;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const hasErrors = violations.some((v) => v.severity === 'error');
|
|
875
|
+
return { valid: !hasErrors, protocol: 'validation', violations, score: Math.max(0, score) };
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// ============================================================
|
|
879
|
+
// Testing Protocol (TEST-*)
|
|
880
|
+
// ============================================================
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Project-agnostic test framework identifiers.
|
|
884
|
+
*
|
|
885
|
+
* The testing protocol is deliberately framework-neutral. Whichever
|
|
886
|
+
* framework the project uses, the protocol only cares that tests run
|
|
887
|
+
* autonomously via a framework adapter and loop until the spec is met.
|
|
888
|
+
*
|
|
889
|
+
* @task T260
|
|
890
|
+
*/
|
|
891
|
+
export type TestFramework =
|
|
892
|
+
| 'vitest'
|
|
893
|
+
| 'jest'
|
|
894
|
+
| 'mocha'
|
|
895
|
+
| 'pytest'
|
|
896
|
+
| 'unittest'
|
|
897
|
+
| 'go-test'
|
|
898
|
+
| 'cargo-test'
|
|
899
|
+
| 'rspec'
|
|
900
|
+
| 'phpunit'
|
|
901
|
+
| 'bats'
|
|
902
|
+
| 'other';
|
|
903
|
+
|
|
904
|
+
/** Testing-stage options for validator. */
|
|
905
|
+
export interface TestingOptions {
|
|
906
|
+
/** Detected or declared test framework for the current worktree. */
|
|
907
|
+
framework?: TestFramework;
|
|
908
|
+
/** Total number of tests executed. */
|
|
909
|
+
testsRun?: number;
|
|
910
|
+
/** Number of tests that passed. */
|
|
911
|
+
testsPassed?: number;
|
|
912
|
+
/** Number of tests that failed. */
|
|
913
|
+
testsFailed?: number;
|
|
914
|
+
/** Coverage percentage achieved (0-100). */
|
|
915
|
+
coveragePercent?: number;
|
|
916
|
+
/** Minimum coverage threshold from project config. */
|
|
917
|
+
coverageThreshold?: number;
|
|
918
|
+
/** Whether the implementation→validate→test loop converged (spec met). */
|
|
919
|
+
ivtLoopConverged?: boolean;
|
|
920
|
+
/** Number of IVT loop iterations until convergence. */
|
|
921
|
+
ivtLoopIterations?: number;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Validate a manifest entry against the testing protocol.
|
|
926
|
+
*
|
|
927
|
+
* This validator is **project-agnostic**: it makes no assumption about the
|
|
928
|
+
* underlying test framework. It enforces the invariant that tests ran via
|
|
929
|
+
* a detected framework, achieved 100% pass rate, and (if an IVT loop was
|
|
930
|
+
* used) converged before the stage completes.
|
|
931
|
+
*
|
|
932
|
+
* Enforces TEST-001..007 from the post-2026-04 rewrite of `testing.md`.
|
|
933
|
+
*
|
|
934
|
+
* @task T260
|
|
935
|
+
*/
|
|
936
|
+
export function validateTestingProtocol(
|
|
937
|
+
entry: ManifestEntryInput,
|
|
938
|
+
options: TestingOptions = {},
|
|
939
|
+
): ProtocolValidationResult {
|
|
940
|
+
const violations: ProtocolViolation[] = [];
|
|
941
|
+
let score = 100;
|
|
942
|
+
|
|
943
|
+
// TEST-001: MUST identify the detected test framework (project-agnostic).
|
|
944
|
+
if (!options.framework) {
|
|
945
|
+
violations.push({
|
|
946
|
+
requirement: 'TEST-001',
|
|
947
|
+
severity: 'error',
|
|
948
|
+
message: 'Test framework not identified for the current worktree',
|
|
949
|
+
fix: 'Detect or declare the project test framework before running tests',
|
|
950
|
+
});
|
|
951
|
+
score -= 20;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// TEST-004: MUST achieve 100% pass rate before release.
|
|
955
|
+
if (
|
|
956
|
+
options.testsRun !== undefined &&
|
|
957
|
+
options.testsFailed !== undefined &&
|
|
958
|
+
options.testsFailed > 0
|
|
959
|
+
) {
|
|
960
|
+
violations.push({
|
|
961
|
+
requirement: 'TEST-004',
|
|
962
|
+
severity: 'error',
|
|
963
|
+
message: `${options.testsFailed} of ${options.testsRun} tests failed`,
|
|
964
|
+
fix: 'Fix failing tests; re-enter the IVT loop until all pass',
|
|
965
|
+
});
|
|
966
|
+
score -= 30;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// TEST-005: IVT loop MUST converge (spec satisfied) before testing exits.
|
|
970
|
+
if (options.ivtLoopConverged === false) {
|
|
971
|
+
violations.push({
|
|
972
|
+
requirement: 'TEST-005',
|
|
973
|
+
severity: 'error',
|
|
974
|
+
message: 'Implement→Validate→Test loop has not converged on specification',
|
|
975
|
+
fix: 'Continue IVT iterations until implementation satisfies the spec',
|
|
976
|
+
});
|
|
977
|
+
score -= 25;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// TEST-006: MUST include test summary (key_findings) in manifest.
|
|
981
|
+
if (!checkArrayMinLength(entry, 'key_findings', 1)) {
|
|
982
|
+
violations.push({
|
|
983
|
+
requirement: 'TEST-006',
|
|
984
|
+
severity: 'error',
|
|
985
|
+
message: 'Testing output must record pass/fail summary in key_findings',
|
|
986
|
+
fix: 'Populate key_findings with framework, pass count, fail count, coverage',
|
|
987
|
+
});
|
|
988
|
+
score -= 10;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// TEST-007: MUST set agent_type: testing
|
|
992
|
+
if (!checkAgentType(entry, 'testing')) {
|
|
993
|
+
violations.push({
|
|
994
|
+
requirement: 'TEST-007',
|
|
995
|
+
severity: 'error',
|
|
996
|
+
message: `agent_type must be testing, got ${entry.agent_type ?? 'undefined'}`,
|
|
997
|
+
fix: 'Update manifest entry agent_type field to "testing"',
|
|
998
|
+
});
|
|
999
|
+
score -= 15;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// Coverage threshold (advisory, non-blocking unless explicit threshold given).
|
|
1003
|
+
if (
|
|
1004
|
+
options.coveragePercent !== undefined &&
|
|
1005
|
+
options.coverageThreshold !== undefined &&
|
|
1006
|
+
options.coveragePercent < options.coverageThreshold
|
|
1007
|
+
) {
|
|
1008
|
+
violations.push({
|
|
1009
|
+
requirement: 'TEST-004',
|
|
1010
|
+
severity: 'warning',
|
|
1011
|
+
message: `Coverage ${options.coveragePercent}% below threshold ${options.coverageThreshold}%`,
|
|
1012
|
+
fix: 'Add tests for uncovered code paths',
|
|
1013
|
+
});
|
|
1014
|
+
score -= 5;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const hasErrors = violations.some((v) => v.severity === 'error');
|
|
1018
|
+
return { valid: !hasErrors, protocol: 'testing', violations, score: Math.max(0, score) };
|
|
1019
|
+
}
|
|
1020
|
+
|
|
618
1021
|
// ============================================================
|
|
619
1022
|
// Unified Dispatcher
|
|
620
1023
|
// ============================================================
|
|
@@ -680,6 +1083,18 @@ export function validateProtocol(
|
|
|
680
1083
|
options as { hasAttestation?: boolean; hasSbom?: boolean },
|
|
681
1084
|
);
|
|
682
1085
|
break;
|
|
1086
|
+
case 'architecture-decision':
|
|
1087
|
+
result = validateArchitectureDecisionProtocol(
|
|
1088
|
+
entry as ManifestEntryInput & { consensus_manifest_id?: string },
|
|
1089
|
+
options as ArchitectureDecisionOptions,
|
|
1090
|
+
);
|
|
1091
|
+
break;
|
|
1092
|
+
case 'validation':
|
|
1093
|
+
result = validateValidationProtocol(entry, options as ValidationStageOptions);
|
|
1094
|
+
break;
|
|
1095
|
+
case 'testing':
|
|
1096
|
+
result = validateTestingProtocol(entry, options as TestingOptions);
|
|
1097
|
+
break;
|
|
683
1098
|
default:
|
|
684
1099
|
throw new CleoError(ExitCode.CONCURRENT_SESSION, `Unknown protocol: ${protocol as string}`);
|
|
685
1100
|
}
|
package/src/paths.ts
CHANGED
|
@@ -354,6 +354,116 @@ export function getGlobalConfigPath(): string {
|
|
|
354
354
|
return join(getCleoHome(), 'config.json');
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
+
// ============================================================================
|
|
358
|
+
// CleoOS Hub Paths (Phase 1)
|
|
359
|
+
// ============================================================================
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Get the Global Justfile Hub directory.
|
|
363
|
+
*
|
|
364
|
+
* The hub stores cross-project recipe libraries agents can run in ANY project
|
|
365
|
+
* (cleo-bootstrap, rcasd-init, schema-validate, lint-standard). Both humans
|
|
366
|
+
* (via editor) and the meta Cleo Chef Agent write recipes here.
|
|
367
|
+
*
|
|
368
|
+
* @returns Absolute path to the global-recipes directory under CLEO home
|
|
369
|
+
*
|
|
370
|
+
* @remarks
|
|
371
|
+
* Returns `{cleoHome}/global-recipes`. Created by `ensureGlobalHome()`.
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```typescript
|
|
375
|
+
* const dir = getCleoGlobalRecipesDir();
|
|
376
|
+
* // Linux: "/home/user/.local/share/cleo/global-recipes"
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
export function getCleoGlobalRecipesDir(): string {
|
|
380
|
+
return join(getCleoHome(), 'global-recipes');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Get the absolute path to the primary global justfile.
|
|
385
|
+
*
|
|
386
|
+
* @returns Absolute path to `{cleoHome}/global-recipes/justfile`
|
|
387
|
+
*
|
|
388
|
+
* @remarks
|
|
389
|
+
* This is the single-file entry point for the Justfile Hub. Additional
|
|
390
|
+
* domain-specific justfiles live alongside it in the same directory.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* const path = getCleoGlobalJustfilePath();
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
export function getCleoGlobalJustfilePath(): string {
|
|
398
|
+
return join(getCleoGlobalRecipesDir(), 'justfile');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get the Global Pi Extensions Hub directory.
|
|
403
|
+
*
|
|
404
|
+
* Houses the Pi extensions that drive the CleoOS UI and tools:
|
|
405
|
+
* orchestrator.ts (Conductor Loop), project-manager.ts (TUI dashboard),
|
|
406
|
+
* tilldone.ts (work visualization), cant-bridge.ts (CANT runtime),
|
|
407
|
+
* stage-guide.ts (before_agent_start hook).
|
|
408
|
+
*
|
|
409
|
+
* @returns Absolute path to the pi-extensions directory under CLEO home
|
|
410
|
+
*
|
|
411
|
+
* @remarks
|
|
412
|
+
* Returns `{cleoHome}/pi-extensions`. Pi is configured to load extensions
|
|
413
|
+
* from this directory via settings.json or the PI extension path setting.
|
|
414
|
+
*
|
|
415
|
+
* @example
|
|
416
|
+
* ```typescript
|
|
417
|
+
* const dir = getCleoPiExtensionsDir();
|
|
418
|
+
* // Linux: "/home/user/.local/share/cleo/pi-extensions"
|
|
419
|
+
* ```
|
|
420
|
+
*/
|
|
421
|
+
export function getCleoPiExtensionsDir(): string {
|
|
422
|
+
return join(getCleoHome(), 'pi-extensions');
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Get the Global CANT Workflows Hub directory.
|
|
427
|
+
*
|
|
428
|
+
* Stores compiled and parsed `.cant` workflows that agents can invoke
|
|
429
|
+
* globally across projects. Project-local agents still live in `.cleo/agents/`.
|
|
430
|
+
*
|
|
431
|
+
* @returns Absolute path to the cant-workflows directory under CLEO home
|
|
432
|
+
*
|
|
433
|
+
* @remarks
|
|
434
|
+
* Returns `{cleoHome}/cant-workflows`. Used by the CANT runtime bridge
|
|
435
|
+
* to resolve globally-available workflow definitions.
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* ```typescript
|
|
439
|
+
* const dir = getCleoCantWorkflowsDir();
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
export function getCleoCantWorkflowsDir(): string {
|
|
443
|
+
return join(getCleoHome(), 'cant-workflows');
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Get the Global CLEO Agents directory.
|
|
448
|
+
*
|
|
449
|
+
* Holds globally-available CANT agent definitions (`.cant` files).
|
|
450
|
+
* Project-local agents still live in `{projectRoot}/.cleo/agents/`.
|
|
451
|
+
*
|
|
452
|
+
* @returns Absolute path to the agents directory under CLEO home
|
|
453
|
+
*
|
|
454
|
+
* @remarks
|
|
455
|
+
* Returns `{cleoHome}/agents`. Loaded when `cleo agent start <id>` resolves
|
|
456
|
+
* agent IDs that aren't found in the project-local registry.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* const dir = getCleoGlobalAgentsDir();
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
export function getCleoGlobalAgentsDir(): string {
|
|
464
|
+
return join(getCleoHome(), 'agents');
|
|
465
|
+
}
|
|
466
|
+
|
|
357
467
|
// ============================================================================
|
|
358
468
|
// Agent Outputs
|
|
359
469
|
// ============================================================================
|