cc-codeconductor 1.4.1 → 1.4.2
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/README.md +24 -1
- package/dist/core/verification/rdd-receipt.d.ts +39 -0
- package/dist/core/verification/verification-runner.d.ts +8 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +996 -251
- package/dist/library.js +267 -26
- package/dist/validation/schemas.d.ts +521 -26
- package/docs/generated/cli.md +12 -0
- package/package.json +1 -1
- package/presets/agy/AGENTS.md +6 -0
- package/presets/agy/workflows/cc-handoff.md +2 -1
- package/presets/agy/workflows/cc-odd.md +15 -0
- package/presets/claude/CLAUDE.md +6 -0
- package/presets/claude/commands/cc/handoff.md +2 -1
- package/presets/claude/commands/cc/odd.md +15 -0
- package/presets/codex/AGENTS.md +6 -0
- package/presets/codex/skills/cc-handoff/SKILL.md +3 -1
- package/presets/codex/skills/cc-odd/SKILL.md +25 -0
- package/presets/codex/skills/cc-openspec/SKILL.md +4 -0
- package/presets/codex/skills/cc-spec-mutation/SKILL.md +5 -0
- package/presets/codex/skills/cc-tdd-cycle/SKILL.md +8 -0
- package/presets/cursor/AGENTS.md +6 -0
- package/presets/cursor/commands/cc/handoff.md +3 -1
- package/presets/cursor/commands/cc/odd.md +20 -0
- package/presets/cursor/commands/cc/openspec.md +4 -0
- package/presets/cursor/commands/cc/spec-mutation.md +5 -0
- package/presets/cursor/commands/cc/tdd-cycle.md +8 -0
- package/presets/gemini/GEMINI.md +6 -0
- package/presets/gemini/commands/cc/handoff.toml +3 -1
- package/presets/gemini/commands/cc/odd.toml +20 -0
- package/presets/gemini/commands/cc/openspec.toml +4 -0
- package/presets/gemini/commands/cc/spec-mutation.toml +5 -0
- package/presets/gemini/commands/cc/tdd-cycle.toml +8 -0
- package/presets/opencode/agents/implementer.md +2 -0
- package/presets/opencode/agents/reviewer.md +2 -0
- package/presets/opencode/agents/tester.md +2 -0
- package/presets/opencode/commands/cc-handoff.md +2 -1
- package/presets/opencode/commands/cc-odd.md +15 -0
- package/presets/pi/AGENTS.md +6 -0
- package/src/presets/targets/pi.yml +1 -0
package/dist/library.js
CHANGED
|
@@ -149,7 +149,7 @@ function validateProductEvent(data) {
|
|
|
149
149
|
function validateSkillsRegistry(data) {
|
|
150
150
|
return SkillsRegistrySchema.parse(data);
|
|
151
151
|
}
|
|
152
|
-
var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, SkillFrontmatterSchema, CommandFrontmatterSchema, TargetCapabilitiesSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema, ConfidenceLevelSchema, ProductNodeTypeSchema, GraphRelationSchema, KnowledgeEntitySchema, DecisionSchema, EvidenceSchema, CanonicalTaskCardStatusSchema, CanonicalTaskCardSchema, ProductGraphNodeSchema, ProductGraphEdgeSchema, ProductGraphSchema, ProductMetaSchema, ProductEventTypeSchema, ProductEventSchema, OperationalStateSchema, StrategicMemorySchema, ImpactReportSchema, BusinessReviewOutputSchema, VerificationReportSchema, BacklogStatusSchema, BacklogTypeSchema, BacklogPrioritySchema, BacklogGlobalSchema, BacklogItemSchema, BacklogDocumentSchema, OpenspecTaskCardPhaseSchema, OpenspecTaskCardStatusSchema, OpenspecTaskCardSchema, OpenspecStateSchema, SpecAnalyzeFindingSchema, SpecAnalyzeReportSchema, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema, SkillsRegistrySchema;
|
|
152
|
+
var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, SkillFrontmatterSchema, CommandFrontmatterSchema, TargetCapabilitiesSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema, ConfidenceLevelSchema, ProductNodeTypeSchema, GraphRelationSchema, KnowledgeEntitySchema, DecisionSchema, EvidenceSchema, CanonicalTaskCardStatusSchema, CanonicalTaskCardSchema, ProductGraphNodeSchema, ProductGraphEdgeSchema, ProductGraphSchema, ProductMetaSchema, ProductEventTypeSchema, ProductEventSchema, OperationalStateSchema, StrategicMemorySchema, ImpactReportSchema, BusinessReviewOutputSchema, VerificationReportSchema, BacklogStatusSchema, BacklogTypeSchema, BacklogPrioritySchema, BacklogGlobalSchema, BacklogItemSchema, BacklogDocumentSchema, OpenspecTaskCardPhaseSchema, OpenspecTaskCardStatusSchema, OpenspecTaskCardSchema, OpenspecStateSchema, SpecAnalyzeFindingSchema, SpecAnalyzeReportSchema, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, DeliveryRouteSchema, TokenUsageSchema, DeliveryTelemetrySchema, DeliveryLedgerTaskSchema, DeliveryLedgerSchema, DeliveryLedgerRequestSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema, SkillsRegistrySchema;
|
|
153
153
|
var init_schemas = __esm(() => {
|
|
154
154
|
init_runner_target();
|
|
155
155
|
CouncilAgentSpecSchema = z.object({
|
|
@@ -313,7 +313,8 @@ var init_schemas = __esm(() => {
|
|
|
313
313
|
complianceVeto: z.boolean().optional(),
|
|
314
314
|
confidence: z.number().min(0).max(1),
|
|
315
315
|
findings: z.array(CouncilFindingSchema),
|
|
316
|
-
summary: z.string()
|
|
316
|
+
summary: z.string(),
|
|
317
|
+
candidateHash: z.string().min(1).optional()
|
|
317
318
|
});
|
|
318
319
|
ConsensusConfigSchema = z.object({
|
|
319
320
|
algorithm: z.enum(["majority", "unanimous"]),
|
|
@@ -321,7 +322,8 @@ var init_schemas = __esm(() => {
|
|
|
321
322
|
allowComplianceVeto: z.boolean().optional(),
|
|
322
323
|
expectedAgentIds: z.array(z.string().min(1).refine((id) => id.trim().length > 0)).min(1).refine((ids) => new Set(ids.map((id) => id.trim().toLowerCase())).size === ids.length).optional(),
|
|
323
324
|
quorum: z.number().int().positive().optional(),
|
|
324
|
-
criticalFindingsPolicy: z.enum(["escalate", "reject", "ignore"]).optional()
|
|
325
|
+
criticalFindingsPolicy: z.enum(["escalate", "reject", "ignore"]).optional(),
|
|
326
|
+
candidateHash: z.string().min(1).optional()
|
|
325
327
|
}).refine((config) => config.algorithm !== "unanimous" || config.expectedAgentIds !== undefined, {
|
|
326
328
|
message: "unanimous consensus requires a non-empty expectedAgentIds roster",
|
|
327
329
|
path: ["expectedAgentIds"]
|
|
@@ -698,7 +700,8 @@ var init_schemas = __esm(() => {
|
|
|
698
700
|
"prototype",
|
|
699
701
|
"handoff",
|
|
700
702
|
"clarify",
|
|
701
|
-
"security"
|
|
703
|
+
"security",
|
|
704
|
+
"odd"
|
|
702
705
|
]);
|
|
703
706
|
CcepOutputFormatSchema = z.enum(["taskcard", "plan", "verdict"]);
|
|
704
707
|
CommandEnvelopeSchema = z.object({
|
|
@@ -914,6 +917,46 @@ var init_schemas = __esm(() => {
|
|
|
914
917
|
"revise",
|
|
915
918
|
"reject"
|
|
916
919
|
]);
|
|
920
|
+
DeliveryRouteSchema = z.enum(["read-only", "small", "tracked", "openspec"]);
|
|
921
|
+
TokenUsageSchema = z.union([
|
|
922
|
+
z.object({
|
|
923
|
+
status: z.literal("known"),
|
|
924
|
+
total: z.number().nonnegative()
|
|
925
|
+
}),
|
|
926
|
+
z.object({ status: z.literal("unknown") })
|
|
927
|
+
]);
|
|
928
|
+
DeliveryTelemetrySchema = z.object({
|
|
929
|
+
route: DeliveryRouteSchema,
|
|
930
|
+
contextBytes: z.number().int().nonnegative().optional(),
|
|
931
|
+
handoffs: z.number().int().nonnegative().optional(),
|
|
932
|
+
checks: z.array(z.string().min(1)).optional(),
|
|
933
|
+
tokenUsage: TokenUsageSchema
|
|
934
|
+
});
|
|
935
|
+
DeliveryLedgerTaskSchema = z.object({
|
|
936
|
+
id: z.string().min(1),
|
|
937
|
+
title: z.string().min(1),
|
|
938
|
+
status: z.enum(["pending", "done", "blocked"])
|
|
939
|
+
});
|
|
940
|
+
DeliveryLedgerSchema = z.object({
|
|
941
|
+
version: z.literal(1),
|
|
942
|
+
id: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
|
|
943
|
+
taskCard: CanonicalTaskCardSchema,
|
|
944
|
+
tasks: z.array(DeliveryLedgerTaskSchema).min(1),
|
|
945
|
+
evidence: z.array(z.string().min(1)),
|
|
946
|
+
nextStep: z.string().min(1),
|
|
947
|
+
memoryTopicKey: z.string().min(1),
|
|
948
|
+
workspace: z.record(z.string(), z.string()),
|
|
949
|
+
createdAt: z.string().datetime(),
|
|
950
|
+
updatedAt: z.string().datetime()
|
|
951
|
+
});
|
|
952
|
+
DeliveryLedgerRequestSchema = z.object({
|
|
953
|
+
id: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
|
|
954
|
+
authorized: z.boolean(),
|
|
955
|
+
route: DeliveryRouteSchema,
|
|
956
|
+
taskCard: CanonicalTaskCardSchema,
|
|
957
|
+
tasks: z.array(DeliveryLedgerTaskSchema).min(1),
|
|
958
|
+
nextStep: z.string().min(1)
|
|
959
|
+
});
|
|
917
960
|
TaskOutcomeSchema = z.object({
|
|
918
961
|
id: z.string(),
|
|
919
962
|
taskId: z.string(),
|
|
@@ -937,7 +980,8 @@ var init_schemas = __esm(() => {
|
|
|
937
980
|
variantId: z.string().optional(),
|
|
938
981
|
suiteTaskId: z.string().optional(),
|
|
939
982
|
harnessFingerprint: z.string().optional(),
|
|
940
|
-
disabledComponents: z.array(z.string()).optional()
|
|
983
|
+
disabledComponents: z.array(z.string()).optional(),
|
|
984
|
+
delivery: DeliveryTelemetrySchema.optional()
|
|
941
985
|
});
|
|
942
986
|
HarnessComponentIdSchema = z.enum([
|
|
943
987
|
"wayfinding",
|
|
@@ -1342,8 +1386,8 @@ async function clearActiveTask(projectRoot, taskId) {
|
|
|
1342
1386
|
// src/core/verification/verification-runner.ts
|
|
1343
1387
|
init_schemas();
|
|
1344
1388
|
import { existsSync } from "node:fs";
|
|
1345
|
-
import { readFile as
|
|
1346
|
-
import { dirname, join as
|
|
1389
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
1390
|
+
import { dirname, join as join3, resolve as resolve6 } from "node:path";
|
|
1347
1391
|
|
|
1348
1392
|
// src/core/config/config-loader.ts
|
|
1349
1393
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
@@ -1659,7 +1703,128 @@ async function runCompileCheck(options) {
|
|
|
1659
1703
|
}
|
|
1660
1704
|
}
|
|
1661
1705
|
// src/core/verification/verification-runner.ts
|
|
1662
|
-
import { mkdir as mkdir5, readdir, writeFile as writeFile4 } from "node:fs/promises";
|
|
1706
|
+
import { mkdir as mkdir5, readdir as readdir2, writeFile as writeFile4 } from "node:fs/promises";
|
|
1707
|
+
|
|
1708
|
+
// src/core/verification/rdd-receipt.ts
|
|
1709
|
+
import { createHash } from "node:crypto";
|
|
1710
|
+
import { readdir, readFile as readFile6 } from "node:fs/promises";
|
|
1711
|
+
import { join as join2, relative, resolve as resolve5, sep } from "node:path";
|
|
1712
|
+
var EXCLUDED_DIRECTORIES = new Set([
|
|
1713
|
+
".git",
|
|
1714
|
+
".codeconductor",
|
|
1715
|
+
"node_modules",
|
|
1716
|
+
"dist",
|
|
1717
|
+
"coverage",
|
|
1718
|
+
"graphify-out"
|
|
1719
|
+
]);
|
|
1720
|
+
function sha256(content) {
|
|
1721
|
+
return createHash("sha256").update(content).digest("hex");
|
|
1722
|
+
}
|
|
1723
|
+
function canonicalPath(projectRoot, input) {
|
|
1724
|
+
const root = resolve5(projectRoot);
|
|
1725
|
+
const absolute = resolve5(root, input);
|
|
1726
|
+
if (absolute !== root && !absolute.startsWith(`${root}${sep}`)) {
|
|
1727
|
+
throw new Error(`RDD receipt path "${input}" is outside the project root`);
|
|
1728
|
+
}
|
|
1729
|
+
const normalized = relative(root, absolute);
|
|
1730
|
+
if (!normalized || normalized === ".") {
|
|
1731
|
+
throw new Error("RDD receipts require file paths, not the project root");
|
|
1732
|
+
}
|
|
1733
|
+
return { absolute, relative: normalized.split(sep).join("/") };
|
|
1734
|
+
}
|
|
1735
|
+
async function fingerprint(projectRoot, paths) {
|
|
1736
|
+
const deduplicated = new Map;
|
|
1737
|
+
for (const path of paths) {
|
|
1738
|
+
const canonical = canonicalPath(projectRoot, path);
|
|
1739
|
+
deduplicated.set(canonical.relative, canonical.absolute);
|
|
1740
|
+
}
|
|
1741
|
+
const result = [];
|
|
1742
|
+
for (const [path, absolute] of [...deduplicated.entries()].sort(([left], [right]) => left.localeCompare(right))) {
|
|
1743
|
+
result.push({ path, hash: sha256(await readFile6(absolute)) });
|
|
1744
|
+
}
|
|
1745
|
+
return result;
|
|
1746
|
+
}
|
|
1747
|
+
function manifestHash(paths) {
|
|
1748
|
+
return sha256(paths.map((entry) => `${entry.path}\x00${entry.hash}`).join(`
|
|
1749
|
+
`));
|
|
1750
|
+
}
|
|
1751
|
+
function isRddReceipt(value) {
|
|
1752
|
+
if (!value || typeof value !== "object")
|
|
1753
|
+
return false;
|
|
1754
|
+
const receipt = value;
|
|
1755
|
+
return receipt.version === 1 && typeof receipt.taskId === "string" && typeof receipt.phase === "string" && Array.isArray(receipt.paths) && receipt.paths.every((entry) => Boolean(entry) && typeof entry.path === "string" && typeof entry.hash === "string") && (receipt.coverage === "paths" || receipt.coverage === "project") && typeof receipt.manifestHash === "string" && (receipt.outcome === "passed" || receipt.outcome === "failed") && typeof receipt.capturedAt === "string";
|
|
1756
|
+
}
|
|
1757
|
+
async function captureReceipt(projectRoot, input) {
|
|
1758
|
+
if (!input.taskId.trim())
|
|
1759
|
+
throw new Error("RDD receipts require a task id");
|
|
1760
|
+
if (input.paths.length === 0 && input.coverage !== "project") {
|
|
1761
|
+
throw new Error("RDD receipts require at least one protected path");
|
|
1762
|
+
}
|
|
1763
|
+
const paths = await fingerprint(projectRoot, input.paths);
|
|
1764
|
+
return {
|
|
1765
|
+
version: 1,
|
|
1766
|
+
taskId: input.taskId,
|
|
1767
|
+
phase: input.phase,
|
|
1768
|
+
paths,
|
|
1769
|
+
coverage: input.coverage ?? "paths",
|
|
1770
|
+
manifestHash: manifestHash(paths),
|
|
1771
|
+
command: input.command,
|
|
1772
|
+
outcome: input.outcome,
|
|
1773
|
+
capturedAt: new Date().toISOString()
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
async function collectReceiptPaths(projectRoot) {
|
|
1777
|
+
const root = resolve5(projectRoot);
|
|
1778
|
+
const paths = [];
|
|
1779
|
+
async function visit(current) {
|
|
1780
|
+
const entries = await readdir(current, { withFileTypes: true });
|
|
1781
|
+
for (const entry of entries) {
|
|
1782
|
+
if (entry.isDirectory()) {
|
|
1783
|
+
if (!EXCLUDED_DIRECTORIES.has(entry.name))
|
|
1784
|
+
await visit(join2(current, entry.name));
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
if (entry.isFile())
|
|
1788
|
+
paths.push(relative(root, join2(current, entry.name)).split(sep).join("/"));
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
await visit(root);
|
|
1792
|
+
return paths.sort();
|
|
1793
|
+
}
|
|
1794
|
+
async function verifyReceipt(projectRoot, receipt) {
|
|
1795
|
+
const expected = new Map(receipt.paths.map((entry) => [entry.path, entry.hash]));
|
|
1796
|
+
const changedPaths = [];
|
|
1797
|
+
const current = [];
|
|
1798
|
+
const pathsToCheck = receipt.coverage === "project" ? await collectReceiptPaths(projectRoot) : receipt.paths.map((entry) => entry.path);
|
|
1799
|
+
const currentByPath = new Map;
|
|
1800
|
+
for (const path of pathsToCheck) {
|
|
1801
|
+
try {
|
|
1802
|
+
const canonical = canonicalPath(projectRoot, path);
|
|
1803
|
+
const hash = sha256(await readFile6(canonical.absolute));
|
|
1804
|
+
const currentEntry = { path: canonical.relative, hash };
|
|
1805
|
+
current.push(currentEntry);
|
|
1806
|
+
currentByPath.set(currentEntry.path, currentEntry);
|
|
1807
|
+
if (hash !== expected.get(currentEntry.path))
|
|
1808
|
+
changedPaths.push(currentEntry.path);
|
|
1809
|
+
} catch {
|
|
1810
|
+
changedPaths.push(path);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
for (const path of expected.keys()) {
|
|
1814
|
+
if (!currentByPath.has(path) && !changedPaths.includes(path))
|
|
1815
|
+
changedPaths.push(path);
|
|
1816
|
+
}
|
|
1817
|
+
changedPaths.sort();
|
|
1818
|
+
const actualHash = manifestHash(current.sort((left, right) => left.path.localeCompare(right.path)));
|
|
1819
|
+
return {
|
|
1820
|
+
valid: changedPaths.length === 0 && actualHash === receipt.manifestHash,
|
|
1821
|
+
expectedHash: receipt.manifestHash,
|
|
1822
|
+
actualHash,
|
|
1823
|
+
changedPaths
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// src/core/verification/verification-runner.ts
|
|
1663
1828
|
function isPassingEvidence(ev) {
|
|
1664
1829
|
switch (ev.type) {
|
|
1665
1830
|
case "verification":
|
|
@@ -1676,9 +1841,17 @@ function isPassingEvidence(ev) {
|
|
|
1676
1841
|
return false;
|
|
1677
1842
|
}
|
|
1678
1843
|
}
|
|
1844
|
+
async function isCurrentPassingEvidence(projectRoot, ev) {
|
|
1845
|
+
if (!isPassingEvidence(ev))
|
|
1846
|
+
return false;
|
|
1847
|
+
const receipt = ev.data?.rddReceipt;
|
|
1848
|
+
if (!isRddReceipt(receipt))
|
|
1849
|
+
return false;
|
|
1850
|
+
return (await verifyReceipt(projectRoot, receipt)).valid;
|
|
1851
|
+
}
|
|
1679
1852
|
function evidenceFilePath(evDir, id) {
|
|
1680
|
-
const path =
|
|
1681
|
-
if (dirname(path) !==
|
|
1853
|
+
const path = resolve6(evDir, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
1854
|
+
if (dirname(path) !== resolve6(evDir)) {
|
|
1682
1855
|
return err(new Error(`Refusing to write evidence for "${id}" outside ${evDir}`));
|
|
1683
1856
|
}
|
|
1684
1857
|
return ok(path);
|
|
@@ -1704,7 +1877,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
1704
1877
|
return ok({ records, invalid });
|
|
1705
1878
|
let files;
|
|
1706
1879
|
try {
|
|
1707
|
-
files = await
|
|
1880
|
+
files = await readdir2(evDir);
|
|
1708
1881
|
} catch (e) {
|
|
1709
1882
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
1710
1883
|
}
|
|
@@ -1712,7 +1885,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
1712
1885
|
if (!file.endsWith(".json"))
|
|
1713
1886
|
continue;
|
|
1714
1887
|
try {
|
|
1715
|
-
const raw = await
|
|
1888
|
+
const raw = await readFile7(join3(evDir, file), "utf-8");
|
|
1716
1889
|
const ev = EvidenceSchema.parse(JSON.parse(raw));
|
|
1717
1890
|
if (ev.relatedTask === taskId)
|
|
1718
1891
|
records.push(ev);
|
|
@@ -1733,7 +1906,7 @@ async function validateEvidenceIds(projectRoot, taskId, evidenceIds) {
|
|
|
1733
1906
|
if (!record) {
|
|
1734
1907
|
return err(new Error(`Evidence "${id}" is not a record of task ${taskId}`));
|
|
1735
1908
|
}
|
|
1736
|
-
if (!
|
|
1909
|
+
if (!await isCurrentPassingEvidence(projectRoot, record)) {
|
|
1737
1910
|
return err(new Error(`Evidence "${id}" does not record a passing result`));
|
|
1738
1911
|
}
|
|
1739
1912
|
if (record.type === "verification")
|
|
@@ -1814,7 +1987,12 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
1814
1987
|
passed: collected.data.invalid === 0,
|
|
1815
1988
|
message: collected.data.invalid === 0 ? "All evidence records are readable" : `${collected.data.invalid} unreadable evidence record(s)`
|
|
1816
1989
|
});
|
|
1817
|
-
const supporting =
|
|
1990
|
+
const supporting = [];
|
|
1991
|
+
for (const candidate of collected.data.records) {
|
|
1992
|
+
if (candidate.type !== "verification" && await isCurrentPassingEvidence(projectRoot, candidate)) {
|
|
1993
|
+
supporting.push(candidate);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1818
1996
|
evidenceIds.push(...supporting.map((ev) => ev.id));
|
|
1819
1997
|
checks.push({
|
|
1820
1998
|
name: "evidence_present",
|
|
@@ -1822,6 +2000,13 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
1822
2000
|
message: supporting.length > 0 ? `${supporting.length} passing evidence record(s) for task` : task.risk === "low" ? "Low risk task — evidence optional" : "No passing evidence records found for task"
|
|
1823
2001
|
});
|
|
1824
2002
|
const passed = checks.every((c) => c.passed);
|
|
2003
|
+
const receipt = await captureReceipt(projectRoot, {
|
|
2004
|
+
taskId,
|
|
2005
|
+
phase: "verification",
|
|
2006
|
+
paths: await collectReceiptPaths(projectRoot),
|
|
2007
|
+
outcome: passed ? "passed" : "failed",
|
|
2008
|
+
coverage: "project"
|
|
2009
|
+
});
|
|
1825
2010
|
const evidence = {
|
|
1826
2011
|
id: `ev-verify-${taskId}-${Date.now()}`,
|
|
1827
2012
|
source: "cc verify",
|
|
@@ -1830,7 +2015,7 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
1830
2015
|
relatedTask: taskId,
|
|
1831
2016
|
confidence: passed ? 0.9 : 0.3,
|
|
1832
2017
|
summary: passed ? "Verification passed" : "Verification failed",
|
|
1833
|
-
data: { passed, checks }
|
|
2018
|
+
data: { passed, checks, rddReceipt: receipt }
|
|
1834
2019
|
};
|
|
1835
2020
|
const persisted = await persistEvidence(projectRoot, evidence);
|
|
1836
2021
|
if (!persisted.success)
|
|
@@ -1853,19 +2038,25 @@ async function gateTaskCompletion(projectRoot, taskId, evidenceRequired, evidenc
|
|
|
1853
2038
|
if (collected.data.invalid > 0)
|
|
1854
2039
|
return ok(false);
|
|
1855
2040
|
const cited = evidenceIds ? new Set(evidenceIds) : undefined;
|
|
1856
|
-
const hasPassing = (type) =>
|
|
2041
|
+
const hasPassing = async (type) => {
|
|
2042
|
+
for (const evidence of collected.data.records) {
|
|
2043
|
+
if ((!cited || cited.has(evidence.id)) && evidence.type === type && await isCurrentPassingEvidence(projectRoot, evidence))
|
|
2044
|
+
return true;
|
|
2045
|
+
}
|
|
2046
|
+
return false;
|
|
2047
|
+
};
|
|
1857
2048
|
for (const req of evidenceRequired) {
|
|
1858
2049
|
switch (req) {
|
|
1859
2050
|
case "acceptance_criteria_met":
|
|
1860
|
-
if (!hasPassing("verification"))
|
|
2051
|
+
if (!await hasPassing("verification"))
|
|
1861
2052
|
return ok(false);
|
|
1862
2053
|
break;
|
|
1863
2054
|
case "tests_passed":
|
|
1864
|
-
if (!hasPassing("test"))
|
|
2055
|
+
if (!await hasPassing("test"))
|
|
1865
2056
|
return ok(false);
|
|
1866
2057
|
break;
|
|
1867
2058
|
case "review_approved":
|
|
1868
|
-
if (!hasPassing("review"))
|
|
2059
|
+
if (!await hasPassing("review"))
|
|
1869
2060
|
return ok(false);
|
|
1870
2061
|
break;
|
|
1871
2062
|
default:
|
|
@@ -1880,12 +2071,26 @@ async function captureTddSuiteEvidence(projectRoot, taskId, options) {
|
|
|
1880
2071
|
if (!options.allowCompileCheck && !isAllowlistedTestCommand(options.command)) {
|
|
1881
2072
|
return err(new Error("TDD suite command is not on the test allowlist. Re-run with --allow-compile-check to trust it."));
|
|
1882
2073
|
}
|
|
2074
|
+
const paths = await collectReceiptPaths(projectRoot);
|
|
2075
|
+
const before = await captureReceipt(projectRoot, {
|
|
2076
|
+
taskId,
|
|
2077
|
+
phase: options.phase ?? "verification",
|
|
2078
|
+
paths,
|
|
2079
|
+
command: options.command,
|
|
2080
|
+
outcome: "failed",
|
|
2081
|
+
coverage: "project"
|
|
2082
|
+
});
|
|
1883
2083
|
const result = await runCompileCheck({
|
|
1884
2084
|
command: options.command,
|
|
1885
2085
|
cwd: projectRoot
|
|
1886
2086
|
});
|
|
1887
2087
|
const suitePassed = result.success && !result.timedOut && result.exitCode === 0;
|
|
1888
2088
|
const suiteFailed = !result.timedOut && result.exitCode !== 0;
|
|
2089
|
+
const receipt = { ...before, outcome: suitePassed ? "passed" : "failed" };
|
|
2090
|
+
const receiptResult = await verifyReceipt(projectRoot, receipt);
|
|
2091
|
+
if (!receiptResult.valid) {
|
|
2092
|
+
return err(new Error(`RDD candidate changed while the TDD suite ran: ${receiptResult.changedPaths.join(", ")}`));
|
|
2093
|
+
}
|
|
1889
2094
|
const evidence = {
|
|
1890
2095
|
id: `ev-tdd-${taskId}-${Date.now()}`,
|
|
1891
2096
|
source: TDD_EVIDENCE_SOURCE,
|
|
@@ -1900,7 +2105,8 @@ async function captureTddSuiteEvidence(projectRoot, taskId, options) {
|
|
|
1900
2105
|
suitePassed,
|
|
1901
2106
|
exitCode: result.exitCode,
|
|
1902
2107
|
timedOut: result.timedOut,
|
|
1903
|
-
command: options.command
|
|
2108
|
+
command: options.command,
|
|
2109
|
+
rddReceipt: receipt
|
|
1904
2110
|
}
|
|
1905
2111
|
};
|
|
1906
2112
|
const persisted = await persistEvidence(projectRoot, evidence);
|
|
@@ -1922,7 +2128,8 @@ async function captureTddSuiteEvidence(projectRoot, taskId, options) {
|
|
|
1922
2128
|
return ok({
|
|
1923
2129
|
evidenceId: evidence.id,
|
|
1924
2130
|
suiteFailed,
|
|
1925
|
-
suitePassed
|
|
2131
|
+
suitePassed,
|
|
2132
|
+
receipt
|
|
1926
2133
|
});
|
|
1927
2134
|
}
|
|
1928
2135
|
async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
|
|
@@ -1932,7 +2139,7 @@ async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
|
|
|
1932
2139
|
return path;
|
|
1933
2140
|
let raw;
|
|
1934
2141
|
try {
|
|
1935
|
-
raw = await
|
|
2142
|
+
raw = await readFile7(path.data, "utf-8");
|
|
1936
2143
|
} catch (e) {
|
|
1937
2144
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
1938
2145
|
}
|
|
@@ -1954,13 +2161,37 @@ async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
|
|
|
1954
2161
|
if (capturedBy !== TDD_CAPTURED_BY || typeof suiteFailed !== "boolean" || typeof suitePassed !== "boolean") {
|
|
1955
2162
|
return err(new Error(`Evidence "${evidenceId}" was not captured by the verification runner`));
|
|
1956
2163
|
}
|
|
1957
|
-
|
|
2164
|
+
const receipt = ev.data?.rddReceipt;
|
|
2165
|
+
if (!isRddReceipt(receipt)) {
|
|
2166
|
+
return err(new Error(`Evidence "${evidenceId}" has no valid RDD receipt`));
|
|
2167
|
+
}
|
|
2168
|
+
if (receipt.taskId !== taskId) {
|
|
2169
|
+
return err(new Error(`Evidence "${evidenceId}" receipt belongs to another task`));
|
|
2170
|
+
}
|
|
2171
|
+
const verified = await verifyReceipt(projectRoot, receipt);
|
|
2172
|
+
if (!verified.valid) {
|
|
2173
|
+
return err(new Error(`Evidence "${evidenceId}" RDD receipt is stale: ${verified.changedPaths.join(", ")}`));
|
|
2174
|
+
}
|
|
2175
|
+
return ok({ capturedBy, suiteFailed, suitePassed, receipt });
|
|
1958
2176
|
}
|
|
1959
|
-
async function hasTddRunnerEvidence(projectRoot, taskId) {
|
|
2177
|
+
async function hasTddRunnerEvidence(projectRoot, taskId, expectedPhase) {
|
|
1960
2178
|
const collected = await collectTaskEvidence(projectRoot, taskId);
|
|
1961
2179
|
if (!collected.success)
|
|
1962
2180
|
return false;
|
|
1963
|
-
|
|
2181
|
+
for (const ev of collected.data.records) {
|
|
2182
|
+
if (ev.type !== "tdd" || ev.source !== TDD_EVIDENCE_SOURCE || ev.data?.capturedBy !== TDD_CAPTURED_BY) {
|
|
2183
|
+
continue;
|
|
2184
|
+
}
|
|
2185
|
+
const loaded = await loadTddSuiteEvidence(projectRoot, taskId, ev.id);
|
|
2186
|
+
if (!loaded.success)
|
|
2187
|
+
continue;
|
|
2188
|
+
if (expectedPhase === "red" && (!loaded.data.suiteFailed || loaded.data.suitePassed))
|
|
2189
|
+
continue;
|
|
2190
|
+
if (expectedPhase === "green" && (!loaded.data.suitePassed || loaded.data.suiteFailed))
|
|
2191
|
+
continue;
|
|
2192
|
+
return true;
|
|
2193
|
+
}
|
|
2194
|
+
return false;
|
|
1964
2195
|
}
|
|
1965
2196
|
|
|
1966
2197
|
// src/core/orchestrator/runtime-orchestrator.ts
|
|
@@ -2729,6 +2960,11 @@ export {
|
|
|
2729
2960
|
CouncilVerdictInputSchema,
|
|
2730
2961
|
CouncilVerdictSchema,
|
|
2731
2962
|
DecisionSchema,
|
|
2963
|
+
DeliveryLedgerRequestSchema,
|
|
2964
|
+
DeliveryLedgerSchema,
|
|
2965
|
+
DeliveryLedgerTaskSchema,
|
|
2966
|
+
DeliveryRouteSchema,
|
|
2967
|
+
DeliveryTelemetrySchema,
|
|
2732
2968
|
EvaluationIndexSchema,
|
|
2733
2969
|
EvidenceSchema,
|
|
2734
2970
|
ExecutionContextSchema,
|
|
@@ -2794,6 +3030,7 @@ export {
|
|
|
2794
3030
|
TaskOutcomeSourceSchema,
|
|
2795
3031
|
TaskOutcomeStatusSchema,
|
|
2796
3032
|
TechnicalPlanOutputSchema,
|
|
3033
|
+
TokenUsageSchema,
|
|
2797
3034
|
ToolProviderNamesSchema,
|
|
2798
3035
|
VerificationReportSchema,
|
|
2799
3036
|
WorkflowCommandSchema,
|
|
@@ -2802,7 +3039,9 @@ export {
|
|
|
2802
3039
|
WorkflowRiskRuleSchema,
|
|
2803
3040
|
advanceTddPhase,
|
|
2804
3041
|
buildTaskEnvelope,
|
|
3042
|
+
captureReceipt,
|
|
2805
3043
|
captureTddSuiteEvidence,
|
|
3044
|
+
collectReceiptPaths,
|
|
2806
3045
|
completeTask,
|
|
2807
3046
|
createInitialState,
|
|
2808
3047
|
createInitialTddState,
|
|
@@ -2813,6 +3052,7 @@ export {
|
|
|
2813
3052
|
getReadyTasks,
|
|
2814
3053
|
goalTaskToCanonicalCard,
|
|
2815
3054
|
hasTddRunnerEvidence,
|
|
3055
|
+
isRddReceipt,
|
|
2816
3056
|
loadTddSuiteEvidence,
|
|
2817
3057
|
loopStateMachine,
|
|
2818
3058
|
rollbackTaskStatus,
|
|
@@ -2861,5 +3101,6 @@ export {
|
|
|
2861
3101
|
validateSkillsRegistry,
|
|
2862
3102
|
validateTargetCapabilities,
|
|
2863
3103
|
validateTaskOutcome,
|
|
2864
|
-
validateWorkflowProfile
|
|
3104
|
+
validateWorkflowProfile,
|
|
3105
|
+
verifyReceipt
|
|
2865
3106
|
};
|