cc-codeconductor 1.4.0 → 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 +1108 -319
- package/dist/library.js +296 -27
- package/dist/utils/cli-bin.d.ts +16 -0
- package/dist/validation/schemas.d.ts +521 -26
- package/docs/generated/cli.md +37 -25
- 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",
|
|
@@ -1134,6 +1178,34 @@ var init_goal_state = __esm(() => {
|
|
|
1134
1178
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1135
1179
|
init_schemas();
|
|
1136
1180
|
|
|
1181
|
+
// src/utils/cli-bin.ts
|
|
1182
|
+
import { basename } from "node:path";
|
|
1183
|
+
function resolveCliBin(env = process.env, argv = process.argv) {
|
|
1184
|
+
if (env.CC_CLI_BIN) {
|
|
1185
|
+
return env.CC_CLI_BIN;
|
|
1186
|
+
}
|
|
1187
|
+
if (env.npm_lifecycle_event === "npx" || env.npm_command === "exec" || env._?.endsWith("/npx") || env._ === "npx") {
|
|
1188
|
+
return "npx cc-codeconductor";
|
|
1189
|
+
}
|
|
1190
|
+
if (env._?.endsWith("/bunx") || env._ === "bunx") {
|
|
1191
|
+
return "bunx cc-codeconductor";
|
|
1192
|
+
}
|
|
1193
|
+
if (env.npm_lifecycle_event === "dev") {
|
|
1194
|
+
return env.npm_lifecycle_script?.startsWith("bun") ? "bun run dev" : "npm run dev";
|
|
1195
|
+
}
|
|
1196
|
+
if (argv && argv.length > 1 && argv[1]) {
|
|
1197
|
+
const script = argv[1];
|
|
1198
|
+
const binName = basename(script);
|
|
1199
|
+
if (binName === "main.ts") {
|
|
1200
|
+
return "bun run dev";
|
|
1201
|
+
}
|
|
1202
|
+
if (binName === "cc-codeconductor" || binName === "codeconductor") {
|
|
1203
|
+
return binName;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return "cc-codeconductor";
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1137
1209
|
// src/core/product-graph/paths.ts
|
|
1138
1210
|
import { join } from "node:path";
|
|
1139
1211
|
var PRODUCT_DIR = ".codeconductor";
|
|
@@ -1161,7 +1233,7 @@ async function loadGraph(projectRoot) {
|
|
|
1161
1233
|
return ok(ProductGraphSchema.parse(JSON.parse(raw)));
|
|
1162
1234
|
} catch (e) {
|
|
1163
1235
|
if (e.code === "ENOENT") {
|
|
1164
|
-
return err(new Error(
|
|
1236
|
+
return err(new Error(`Product graph not found. Run \`${resolveCliBin()} ingest\` first.`));
|
|
1165
1237
|
}
|
|
1166
1238
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
1167
1239
|
}
|
|
@@ -1314,8 +1386,8 @@ async function clearActiveTask(projectRoot, taskId) {
|
|
|
1314
1386
|
// src/core/verification/verification-runner.ts
|
|
1315
1387
|
init_schemas();
|
|
1316
1388
|
import { existsSync } from "node:fs";
|
|
1317
|
-
import { readFile as
|
|
1318
|
-
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";
|
|
1319
1391
|
|
|
1320
1392
|
// src/core/config/config-loader.ts
|
|
1321
1393
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
@@ -1631,7 +1703,128 @@ async function runCompileCheck(options) {
|
|
|
1631
1703
|
}
|
|
1632
1704
|
}
|
|
1633
1705
|
// src/core/verification/verification-runner.ts
|
|
1634
|
-
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
|
|
1635
1828
|
function isPassingEvidence(ev) {
|
|
1636
1829
|
switch (ev.type) {
|
|
1637
1830
|
case "verification":
|
|
@@ -1648,9 +1841,17 @@ function isPassingEvidence(ev) {
|
|
|
1648
1841
|
return false;
|
|
1649
1842
|
}
|
|
1650
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
|
+
}
|
|
1651
1852
|
function evidenceFilePath(evDir, id) {
|
|
1652
|
-
const path =
|
|
1653
|
-
if (dirname(path) !==
|
|
1853
|
+
const path = resolve6(evDir, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
1854
|
+
if (dirname(path) !== resolve6(evDir)) {
|
|
1654
1855
|
return err(new Error(`Refusing to write evidence for "${id}" outside ${evDir}`));
|
|
1655
1856
|
}
|
|
1656
1857
|
return ok(path);
|
|
@@ -1676,7 +1877,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
1676
1877
|
return ok({ records, invalid });
|
|
1677
1878
|
let files;
|
|
1678
1879
|
try {
|
|
1679
|
-
files = await
|
|
1880
|
+
files = await readdir2(evDir);
|
|
1680
1881
|
} catch (e) {
|
|
1681
1882
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
1682
1883
|
}
|
|
@@ -1684,7 +1885,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
1684
1885
|
if (!file.endsWith(".json"))
|
|
1685
1886
|
continue;
|
|
1686
1887
|
try {
|
|
1687
|
-
const raw = await
|
|
1888
|
+
const raw = await readFile7(join3(evDir, file), "utf-8");
|
|
1688
1889
|
const ev = EvidenceSchema.parse(JSON.parse(raw));
|
|
1689
1890
|
if (ev.relatedTask === taskId)
|
|
1690
1891
|
records.push(ev);
|
|
@@ -1705,7 +1906,7 @@ async function validateEvidenceIds(projectRoot, taskId, evidenceIds) {
|
|
|
1705
1906
|
if (!record) {
|
|
1706
1907
|
return err(new Error(`Evidence "${id}" is not a record of task ${taskId}`));
|
|
1707
1908
|
}
|
|
1708
|
-
if (!
|
|
1909
|
+
if (!await isCurrentPassingEvidence(projectRoot, record)) {
|
|
1709
1910
|
return err(new Error(`Evidence "${id}" does not record a passing result`));
|
|
1710
1911
|
}
|
|
1711
1912
|
if (record.type === "verification")
|
|
@@ -1786,7 +1987,12 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
1786
1987
|
passed: collected.data.invalid === 0,
|
|
1787
1988
|
message: collected.data.invalid === 0 ? "All evidence records are readable" : `${collected.data.invalid} unreadable evidence record(s)`
|
|
1788
1989
|
});
|
|
1789
|
-
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
|
+
}
|
|
1790
1996
|
evidenceIds.push(...supporting.map((ev) => ev.id));
|
|
1791
1997
|
checks.push({
|
|
1792
1998
|
name: "evidence_present",
|
|
@@ -1794,6 +2000,13 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
1794
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"
|
|
1795
2001
|
});
|
|
1796
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
|
+
});
|
|
1797
2010
|
const evidence = {
|
|
1798
2011
|
id: `ev-verify-${taskId}-${Date.now()}`,
|
|
1799
2012
|
source: "cc verify",
|
|
@@ -1802,7 +2015,7 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
1802
2015
|
relatedTask: taskId,
|
|
1803
2016
|
confidence: passed ? 0.9 : 0.3,
|
|
1804
2017
|
summary: passed ? "Verification passed" : "Verification failed",
|
|
1805
|
-
data: { passed, checks }
|
|
2018
|
+
data: { passed, checks, rddReceipt: receipt }
|
|
1806
2019
|
};
|
|
1807
2020
|
const persisted = await persistEvidence(projectRoot, evidence);
|
|
1808
2021
|
if (!persisted.success)
|
|
@@ -1825,19 +2038,25 @@ async function gateTaskCompletion(projectRoot, taskId, evidenceRequired, evidenc
|
|
|
1825
2038
|
if (collected.data.invalid > 0)
|
|
1826
2039
|
return ok(false);
|
|
1827
2040
|
const cited = evidenceIds ? new Set(evidenceIds) : undefined;
|
|
1828
|
-
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
|
+
};
|
|
1829
2048
|
for (const req of evidenceRequired) {
|
|
1830
2049
|
switch (req) {
|
|
1831
2050
|
case "acceptance_criteria_met":
|
|
1832
|
-
if (!hasPassing("verification"))
|
|
2051
|
+
if (!await hasPassing("verification"))
|
|
1833
2052
|
return ok(false);
|
|
1834
2053
|
break;
|
|
1835
2054
|
case "tests_passed":
|
|
1836
|
-
if (!hasPassing("test"))
|
|
2055
|
+
if (!await hasPassing("test"))
|
|
1837
2056
|
return ok(false);
|
|
1838
2057
|
break;
|
|
1839
2058
|
case "review_approved":
|
|
1840
|
-
if (!hasPassing("review"))
|
|
2059
|
+
if (!await hasPassing("review"))
|
|
1841
2060
|
return ok(false);
|
|
1842
2061
|
break;
|
|
1843
2062
|
default:
|
|
@@ -1852,12 +2071,26 @@ async function captureTddSuiteEvidence(projectRoot, taskId, options) {
|
|
|
1852
2071
|
if (!options.allowCompileCheck && !isAllowlistedTestCommand(options.command)) {
|
|
1853
2072
|
return err(new Error("TDD suite command is not on the test allowlist. Re-run with --allow-compile-check to trust it."));
|
|
1854
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
|
+
});
|
|
1855
2083
|
const result = await runCompileCheck({
|
|
1856
2084
|
command: options.command,
|
|
1857
2085
|
cwd: projectRoot
|
|
1858
2086
|
});
|
|
1859
2087
|
const suitePassed = result.success && !result.timedOut && result.exitCode === 0;
|
|
1860
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
|
+
}
|
|
1861
2094
|
const evidence = {
|
|
1862
2095
|
id: `ev-tdd-${taskId}-${Date.now()}`,
|
|
1863
2096
|
source: TDD_EVIDENCE_SOURCE,
|
|
@@ -1872,7 +2105,8 @@ async function captureTddSuiteEvidence(projectRoot, taskId, options) {
|
|
|
1872
2105
|
suitePassed,
|
|
1873
2106
|
exitCode: result.exitCode,
|
|
1874
2107
|
timedOut: result.timedOut,
|
|
1875
|
-
command: options.command
|
|
2108
|
+
command: options.command,
|
|
2109
|
+
rddReceipt: receipt
|
|
1876
2110
|
}
|
|
1877
2111
|
};
|
|
1878
2112
|
const persisted = await persistEvidence(projectRoot, evidence);
|
|
@@ -1894,7 +2128,8 @@ async function captureTddSuiteEvidence(projectRoot, taskId, options) {
|
|
|
1894
2128
|
return ok({
|
|
1895
2129
|
evidenceId: evidence.id,
|
|
1896
2130
|
suiteFailed,
|
|
1897
|
-
suitePassed
|
|
2131
|
+
suitePassed,
|
|
2132
|
+
receipt
|
|
1898
2133
|
});
|
|
1899
2134
|
}
|
|
1900
2135
|
async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
|
|
@@ -1904,7 +2139,7 @@ async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
|
|
|
1904
2139
|
return path;
|
|
1905
2140
|
let raw;
|
|
1906
2141
|
try {
|
|
1907
|
-
raw = await
|
|
2142
|
+
raw = await readFile7(path.data, "utf-8");
|
|
1908
2143
|
} catch (e) {
|
|
1909
2144
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
1910
2145
|
}
|
|
@@ -1926,13 +2161,37 @@ async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
|
|
|
1926
2161
|
if (capturedBy !== TDD_CAPTURED_BY || typeof suiteFailed !== "boolean" || typeof suitePassed !== "boolean") {
|
|
1927
2162
|
return err(new Error(`Evidence "${evidenceId}" was not captured by the verification runner`));
|
|
1928
2163
|
}
|
|
1929
|
-
|
|
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 });
|
|
1930
2176
|
}
|
|
1931
|
-
async function hasTddRunnerEvidence(projectRoot, taskId) {
|
|
2177
|
+
async function hasTddRunnerEvidence(projectRoot, taskId, expectedPhase) {
|
|
1932
2178
|
const collected = await collectTaskEvidence(projectRoot, taskId);
|
|
1933
2179
|
if (!collected.success)
|
|
1934
2180
|
return false;
|
|
1935
|
-
|
|
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;
|
|
1936
2195
|
}
|
|
1937
2196
|
|
|
1938
2197
|
// src/core/orchestrator/runtime-orchestrator.ts
|
|
@@ -2701,6 +2960,11 @@ export {
|
|
|
2701
2960
|
CouncilVerdictInputSchema,
|
|
2702
2961
|
CouncilVerdictSchema,
|
|
2703
2962
|
DecisionSchema,
|
|
2963
|
+
DeliveryLedgerRequestSchema,
|
|
2964
|
+
DeliveryLedgerSchema,
|
|
2965
|
+
DeliveryLedgerTaskSchema,
|
|
2966
|
+
DeliveryRouteSchema,
|
|
2967
|
+
DeliveryTelemetrySchema,
|
|
2704
2968
|
EvaluationIndexSchema,
|
|
2705
2969
|
EvidenceSchema,
|
|
2706
2970
|
ExecutionContextSchema,
|
|
@@ -2766,6 +3030,7 @@ export {
|
|
|
2766
3030
|
TaskOutcomeSourceSchema,
|
|
2767
3031
|
TaskOutcomeStatusSchema,
|
|
2768
3032
|
TechnicalPlanOutputSchema,
|
|
3033
|
+
TokenUsageSchema,
|
|
2769
3034
|
ToolProviderNamesSchema,
|
|
2770
3035
|
VerificationReportSchema,
|
|
2771
3036
|
WorkflowCommandSchema,
|
|
@@ -2774,7 +3039,9 @@ export {
|
|
|
2774
3039
|
WorkflowRiskRuleSchema,
|
|
2775
3040
|
advanceTddPhase,
|
|
2776
3041
|
buildTaskEnvelope,
|
|
3042
|
+
captureReceipt,
|
|
2777
3043
|
captureTddSuiteEvidence,
|
|
3044
|
+
collectReceiptPaths,
|
|
2778
3045
|
completeTask,
|
|
2779
3046
|
createInitialState,
|
|
2780
3047
|
createInitialTddState,
|
|
@@ -2785,6 +3052,7 @@ export {
|
|
|
2785
3052
|
getReadyTasks,
|
|
2786
3053
|
goalTaskToCanonicalCard,
|
|
2787
3054
|
hasTddRunnerEvidence,
|
|
3055
|
+
isRddReceipt,
|
|
2788
3056
|
loadTddSuiteEvidence,
|
|
2789
3057
|
loopStateMachine,
|
|
2790
3058
|
rollbackTaskStatus,
|
|
@@ -2833,5 +3101,6 @@ export {
|
|
|
2833
3101
|
validateSkillsRegistry,
|
|
2834
3102
|
validateTargetCapabilities,
|
|
2835
3103
|
validateTaskOutcome,
|
|
2836
|
-
validateWorkflowProfile
|
|
3104
|
+
validateWorkflowProfile,
|
|
3105
|
+
verifyReceipt
|
|
2837
3106
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the CLI command prefix based on execution context.
|
|
3
|
+
*
|
|
4
|
+
* - When invoked via npx (e.g. `npx cc-codeconductor ...`), returns `npx cc-codeconductor`.
|
|
5
|
+
* - When invoked via bunx, returns `bunx cc-codeconductor`.
|
|
6
|
+
* - When invoked during local development (`bun run dev` or executing `src/cli/main.ts`), returns `bun run dev`.
|
|
7
|
+
* - When invoked via an installed binary (`cc-codeconductor` or `codeconductor`), returns that binary name.
|
|
8
|
+
* - Respects the `CC_CLI_BIN` environment variable for testing or explicit overrides.
|
|
9
|
+
* - Defaults to `cc-codeconductor`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveCliBin(env?: NodeJS.ProcessEnv, argv?: readonly string[]): string;
|
|
12
|
+
/**
|
|
13
|
+
* Formats a command usage string replacing any legacy or alternative command prefix
|
|
14
|
+
* (`cc `, `cc-codeconductor `, or `codeconductor `) with the resolved bin prefix.
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatUsage(usage: string, bin?: string): string;
|