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/index.js
CHANGED
|
@@ -72,7 +72,7 @@ var package_default;
|
|
|
72
72
|
var init_package = __esm(() => {
|
|
73
73
|
package_default = {
|
|
74
74
|
name: "cc-codeconductor",
|
|
75
|
-
version: "1.4.
|
|
75
|
+
version: "1.4.2",
|
|
76
76
|
description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
|
|
77
77
|
keywords: [
|
|
78
78
|
"ai",
|
|
@@ -11206,6 +11206,11 @@ __export(exports_schemas, {
|
|
|
11206
11206
|
CouncilVerdictInputSchema: () => CouncilVerdictInputSchema,
|
|
11207
11207
|
CouncilVerdictSchema: () => CouncilVerdictSchema,
|
|
11208
11208
|
DecisionSchema: () => DecisionSchema,
|
|
11209
|
+
DeliveryLedgerRequestSchema: () => DeliveryLedgerRequestSchema,
|
|
11210
|
+
DeliveryLedgerSchema: () => DeliveryLedgerSchema,
|
|
11211
|
+
DeliveryLedgerTaskSchema: () => DeliveryLedgerTaskSchema,
|
|
11212
|
+
DeliveryRouteSchema: () => DeliveryRouteSchema,
|
|
11213
|
+
DeliveryTelemetrySchema: () => DeliveryTelemetrySchema,
|
|
11209
11214
|
EvaluationIndexSchema: () => EvaluationIndexSchema,
|
|
11210
11215
|
EvidenceSchema: () => EvidenceSchema,
|
|
11211
11216
|
ExecutionContextSchema: () => ExecutionContextSchema,
|
|
@@ -11270,6 +11275,7 @@ __export(exports_schemas, {
|
|
|
11270
11275
|
TaskOutcomeSourceSchema: () => TaskOutcomeSourceSchema,
|
|
11271
11276
|
TaskOutcomeStatusSchema: () => TaskOutcomeStatusSchema,
|
|
11272
11277
|
TechnicalPlanOutputSchema: () => TechnicalPlanOutputSchema,
|
|
11278
|
+
TokenUsageSchema: () => TokenUsageSchema,
|
|
11273
11279
|
ToolProviderNamesSchema: () => ToolProviderNamesSchema,
|
|
11274
11280
|
VerificationReportSchema: () => VerificationReportSchema,
|
|
11275
11281
|
WorkflowCommandSchema: () => WorkflowCommandSchema,
|
|
@@ -11433,7 +11439,7 @@ function validateProductEvent(data) {
|
|
|
11433
11439
|
function validateSkillsRegistry(data) {
|
|
11434
11440
|
return SkillsRegistrySchema.parse(data);
|
|
11435
11441
|
}
|
|
11436
|
-
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;
|
|
11442
|
+
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;
|
|
11437
11443
|
var init_schemas = __esm(() => {
|
|
11438
11444
|
init_zod();
|
|
11439
11445
|
init_runner_target();
|
|
@@ -11598,7 +11604,8 @@ var init_schemas = __esm(() => {
|
|
|
11598
11604
|
complianceVeto: exports_external.boolean().optional(),
|
|
11599
11605
|
confidence: exports_external.number().min(0).max(1),
|
|
11600
11606
|
findings: exports_external.array(CouncilFindingSchema),
|
|
11601
|
-
summary: exports_external.string()
|
|
11607
|
+
summary: exports_external.string(),
|
|
11608
|
+
candidateHash: exports_external.string().min(1).optional()
|
|
11602
11609
|
});
|
|
11603
11610
|
ConsensusConfigSchema = exports_external.object({
|
|
11604
11611
|
algorithm: exports_external.enum(["majority", "unanimous"]),
|
|
@@ -11606,7 +11613,8 @@ var init_schemas = __esm(() => {
|
|
|
11606
11613
|
allowComplianceVeto: exports_external.boolean().optional(),
|
|
11607
11614
|
expectedAgentIds: exports_external.array(exports_external.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(),
|
|
11608
11615
|
quorum: exports_external.number().int().positive().optional(),
|
|
11609
|
-
criticalFindingsPolicy: exports_external.enum(["escalate", "reject", "ignore"]).optional()
|
|
11616
|
+
criticalFindingsPolicy: exports_external.enum(["escalate", "reject", "ignore"]).optional(),
|
|
11617
|
+
candidateHash: exports_external.string().min(1).optional()
|
|
11610
11618
|
}).refine((config) => config.algorithm !== "unanimous" || config.expectedAgentIds !== undefined, {
|
|
11611
11619
|
message: "unanimous consensus requires a non-empty expectedAgentIds roster",
|
|
11612
11620
|
path: ["expectedAgentIds"]
|
|
@@ -11983,7 +11991,8 @@ var init_schemas = __esm(() => {
|
|
|
11983
11991
|
"prototype",
|
|
11984
11992
|
"handoff",
|
|
11985
11993
|
"clarify",
|
|
11986
|
-
"security"
|
|
11994
|
+
"security",
|
|
11995
|
+
"odd"
|
|
11987
11996
|
]);
|
|
11988
11997
|
CcepOutputFormatSchema = exports_external.enum(["taskcard", "plan", "verdict"]);
|
|
11989
11998
|
CommandEnvelopeSchema = exports_external.object({
|
|
@@ -12199,6 +12208,46 @@ var init_schemas = __esm(() => {
|
|
|
12199
12208
|
"revise",
|
|
12200
12209
|
"reject"
|
|
12201
12210
|
]);
|
|
12211
|
+
DeliveryRouteSchema = exports_external.enum(["read-only", "small", "tracked", "openspec"]);
|
|
12212
|
+
TokenUsageSchema = exports_external.union([
|
|
12213
|
+
exports_external.object({
|
|
12214
|
+
status: exports_external.literal("known"),
|
|
12215
|
+
total: exports_external.number().nonnegative()
|
|
12216
|
+
}),
|
|
12217
|
+
exports_external.object({ status: exports_external.literal("unknown") })
|
|
12218
|
+
]);
|
|
12219
|
+
DeliveryTelemetrySchema = exports_external.object({
|
|
12220
|
+
route: DeliveryRouteSchema,
|
|
12221
|
+
contextBytes: exports_external.number().int().nonnegative().optional(),
|
|
12222
|
+
handoffs: exports_external.number().int().nonnegative().optional(),
|
|
12223
|
+
checks: exports_external.array(exports_external.string().min(1)).optional(),
|
|
12224
|
+
tokenUsage: TokenUsageSchema
|
|
12225
|
+
});
|
|
12226
|
+
DeliveryLedgerTaskSchema = exports_external.object({
|
|
12227
|
+
id: exports_external.string().min(1),
|
|
12228
|
+
title: exports_external.string().min(1),
|
|
12229
|
+
status: exports_external.enum(["pending", "done", "blocked"])
|
|
12230
|
+
});
|
|
12231
|
+
DeliveryLedgerSchema = exports_external.object({
|
|
12232
|
+
version: exports_external.literal(1),
|
|
12233
|
+
id: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
|
|
12234
|
+
taskCard: CanonicalTaskCardSchema,
|
|
12235
|
+
tasks: exports_external.array(DeliveryLedgerTaskSchema).min(1),
|
|
12236
|
+
evidence: exports_external.array(exports_external.string().min(1)),
|
|
12237
|
+
nextStep: exports_external.string().min(1),
|
|
12238
|
+
memoryTopicKey: exports_external.string().min(1),
|
|
12239
|
+
workspace: exports_external.record(exports_external.string(), exports_external.string()),
|
|
12240
|
+
createdAt: exports_external.string().datetime(),
|
|
12241
|
+
updatedAt: exports_external.string().datetime()
|
|
12242
|
+
});
|
|
12243
|
+
DeliveryLedgerRequestSchema = exports_external.object({
|
|
12244
|
+
id: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
|
|
12245
|
+
authorized: exports_external.boolean(),
|
|
12246
|
+
route: DeliveryRouteSchema,
|
|
12247
|
+
taskCard: CanonicalTaskCardSchema,
|
|
12248
|
+
tasks: exports_external.array(DeliveryLedgerTaskSchema).min(1),
|
|
12249
|
+
nextStep: exports_external.string().min(1)
|
|
12250
|
+
});
|
|
12202
12251
|
TaskOutcomeSchema = exports_external.object({
|
|
12203
12252
|
id: exports_external.string(),
|
|
12204
12253
|
taskId: exports_external.string(),
|
|
@@ -12222,7 +12271,8 @@ var init_schemas = __esm(() => {
|
|
|
12222
12271
|
variantId: exports_external.string().optional(),
|
|
12223
12272
|
suiteTaskId: exports_external.string().optional(),
|
|
12224
12273
|
harnessFingerprint: exports_external.string().optional(),
|
|
12225
|
-
disabledComponents: exports_external.array(exports_external.string()).optional()
|
|
12274
|
+
disabledComponents: exports_external.array(exports_external.string()).optional(),
|
|
12275
|
+
delivery: DeliveryTelemetrySchema.optional()
|
|
12226
12276
|
});
|
|
12227
12277
|
HarnessComponentIdSchema = exports_external.enum([
|
|
12228
12278
|
"wayfinding",
|
|
@@ -15419,6 +15469,25 @@ var init_profiles = __esm(() => {
|
|
|
15419
15469
|
routing: { default: ["validate-backlog", "discover", "design", "analyze", "test", "implement", "review"] },
|
|
15420
15470
|
confirmationGate: baseGate
|
|
15421
15471
|
},
|
|
15472
|
+
odd: {
|
|
15473
|
+
id: "odd",
|
|
15474
|
+
version: 1,
|
|
15475
|
+
command: "odd",
|
|
15476
|
+
taskCard: {
|
|
15477
|
+
type: "feature",
|
|
15478
|
+
requiredFields: ["title", "type", "risk", "scope", "context", "acceptanceCriteria"]
|
|
15479
|
+
},
|
|
15480
|
+
phases: [
|
|
15481
|
+
{ id: "wayfinding", agent: "repo-explorer", outputSchema: "agent-output" },
|
|
15482
|
+
{ id: "intake", agent: "task-coach", outputSchema: "planner-output", stopGate: "confirmation" },
|
|
15483
|
+
{ id: "ledger", agent: "orchestrator", type: "cli-gate" },
|
|
15484
|
+
{ id: "test", agent: "tester", dependsOn: ["intake"] },
|
|
15485
|
+
{ id: "implement", agent: "implementer", dependsOn: ["test"] },
|
|
15486
|
+
{ id: "review", agent: "reviewer" }
|
|
15487
|
+
],
|
|
15488
|
+
routing: { default: ["wayfinding", "intake", "ledger", "test", "implement", "review"] },
|
|
15489
|
+
confirmationGate: baseGate
|
|
15490
|
+
},
|
|
15422
15491
|
backlog: {
|
|
15423
15492
|
id: "backlog",
|
|
15424
15493
|
version: 1,
|
|
@@ -21487,7 +21556,10 @@ async function listScorecards(projectRoot, filter = {}) {
|
|
|
21487
21556
|
}
|
|
21488
21557
|
async function hasPassingScorecard(projectRoot, backlogId) {
|
|
21489
21558
|
const listed = await listScorecards(projectRoot, { backlogId, verdict: "PASS" });
|
|
21490
|
-
|
|
21559
|
+
if (listed.success && listed.data.length > 0)
|
|
21560
|
+
return true;
|
|
21561
|
+
const outcomes = await listOutcomes(projectRoot, { backlogId, source: "review" });
|
|
21562
|
+
return outcomes.success && outcomes.data.some((outcome) => outcome.verdict === "PASS");
|
|
21491
21563
|
}
|
|
21492
21564
|
async function loadScorecard(projectRoot, id) {
|
|
21493
21565
|
try {
|
|
@@ -21519,7 +21591,7 @@ function aggregateOutcomes(outcomes) {
|
|
|
21519
21591
|
agentScoreCounts[o.agent] = (agentScoreCounts[o.agent] ?? 0) + 1;
|
|
21520
21592
|
}
|
|
21521
21593
|
if (!byModel[o.model])
|
|
21522
|
-
byModel[o.model] = { count: 0, avgScore: 0 };
|
|
21594
|
+
byModel[o.model] = { count: 0, avgScore: 0, knownTokenMeasurements: 0 };
|
|
21523
21595
|
byModel[o.model].count++;
|
|
21524
21596
|
if (o.weightedScore !== undefined) {
|
|
21525
21597
|
byModel[o.model].avgScore += o.weightedScore;
|
|
@@ -21529,10 +21601,11 @@ function aggregateOutcomes(outcomes) {
|
|
|
21529
21601
|
byModel[o.model].avgCost = (byModel[o.model].avgCost ?? 0) + o.costUsd;
|
|
21530
21602
|
modelCostCounts[o.model] = (modelCostCounts[o.model] ?? 0) + 1;
|
|
21531
21603
|
}
|
|
21532
|
-
|
|
21533
|
-
|
|
21534
|
-
byModel[o.model].avgTokens = (byModel[o.model].avgTokens ?? 0) +
|
|
21604
|
+
const reportedTokens = o.delivery?.tokenUsage.status === "known" ? o.delivery.tokenUsage.total : o.tokensIn !== undefined || o.tokensOut !== undefined ? (o.tokensIn ?? 0) + (o.tokensOut ?? 0) : undefined;
|
|
21605
|
+
if (reportedTokens !== undefined) {
|
|
21606
|
+
byModel[o.model].avgTokens = (byModel[o.model].avgTokens ?? 0) + reportedTokens;
|
|
21535
21607
|
modelTokenCounts[o.model] = (modelTokenCounts[o.model] ?? 0) + 1;
|
|
21608
|
+
byModel[o.model].knownTokenMeasurements++;
|
|
21536
21609
|
}
|
|
21537
21610
|
if (o.variantId) {
|
|
21538
21611
|
if (!byVariant[o.variantId])
|
|
@@ -22182,11 +22255,140 @@ var init_compile_checker = __esm(() => {
|
|
|
22182
22255
|
];
|
|
22183
22256
|
});
|
|
22184
22257
|
|
|
22258
|
+
// src/core/verification/rdd-receipt.ts
|
|
22259
|
+
var exports_rdd_receipt = {};
|
|
22260
|
+
__export(exports_rdd_receipt, {
|
|
22261
|
+
captureReceipt: () => captureReceipt,
|
|
22262
|
+
collectReceiptPaths: () => collectReceiptPaths,
|
|
22263
|
+
isRddReceipt: () => isRddReceipt,
|
|
22264
|
+
verifyReceipt: () => verifyReceipt
|
|
22265
|
+
});
|
|
22266
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
22267
|
+
import { readdir as readdir7, readFile as readFile27 } from "node:fs/promises";
|
|
22268
|
+
import { join as join18, relative as relative7, resolve as resolve26, sep as sep5 } from "node:path";
|
|
22269
|
+
function sha2562(content) {
|
|
22270
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
22271
|
+
}
|
|
22272
|
+
function canonicalPath(projectRoot, input) {
|
|
22273
|
+
const root = resolve26(projectRoot);
|
|
22274
|
+
const absolute = resolve26(root, input);
|
|
22275
|
+
if (absolute !== root && !absolute.startsWith(`${root}${sep5}`)) {
|
|
22276
|
+
throw new Error(`RDD receipt path "${input}" is outside the project root`);
|
|
22277
|
+
}
|
|
22278
|
+
const normalized = relative7(root, absolute);
|
|
22279
|
+
if (!normalized || normalized === ".") {
|
|
22280
|
+
throw new Error("RDD receipts require file paths, not the project root");
|
|
22281
|
+
}
|
|
22282
|
+
return { absolute, relative: normalized.split(sep5).join("/") };
|
|
22283
|
+
}
|
|
22284
|
+
async function fingerprint(projectRoot, paths) {
|
|
22285
|
+
const deduplicated = new Map;
|
|
22286
|
+
for (const path of paths) {
|
|
22287
|
+
const canonical = canonicalPath(projectRoot, path);
|
|
22288
|
+
deduplicated.set(canonical.relative, canonical.absolute);
|
|
22289
|
+
}
|
|
22290
|
+
const result = [];
|
|
22291
|
+
for (const [path, absolute] of [...deduplicated.entries()].sort(([left], [right]) => left.localeCompare(right))) {
|
|
22292
|
+
result.push({ path, hash: sha2562(await readFile27(absolute)) });
|
|
22293
|
+
}
|
|
22294
|
+
return result;
|
|
22295
|
+
}
|
|
22296
|
+
function manifestHash(paths) {
|
|
22297
|
+
return sha2562(paths.map((entry) => `${entry.path}\x00${entry.hash}`).join(`
|
|
22298
|
+
`));
|
|
22299
|
+
}
|
|
22300
|
+
function isRddReceipt(value) {
|
|
22301
|
+
if (!value || typeof value !== "object")
|
|
22302
|
+
return false;
|
|
22303
|
+
const receipt = value;
|
|
22304
|
+
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";
|
|
22305
|
+
}
|
|
22306
|
+
async function captureReceipt(projectRoot, input) {
|
|
22307
|
+
if (!input.taskId.trim())
|
|
22308
|
+
throw new Error("RDD receipts require a task id");
|
|
22309
|
+
if (input.paths.length === 0 && input.coverage !== "project") {
|
|
22310
|
+
throw new Error("RDD receipts require at least one protected path");
|
|
22311
|
+
}
|
|
22312
|
+
const paths = await fingerprint(projectRoot, input.paths);
|
|
22313
|
+
return {
|
|
22314
|
+
version: 1,
|
|
22315
|
+
taskId: input.taskId,
|
|
22316
|
+
phase: input.phase,
|
|
22317
|
+
paths,
|
|
22318
|
+
coverage: input.coverage ?? "paths",
|
|
22319
|
+
manifestHash: manifestHash(paths),
|
|
22320
|
+
command: input.command,
|
|
22321
|
+
outcome: input.outcome,
|
|
22322
|
+
capturedAt: new Date().toISOString()
|
|
22323
|
+
};
|
|
22324
|
+
}
|
|
22325
|
+
async function collectReceiptPaths(projectRoot) {
|
|
22326
|
+
const root = resolve26(projectRoot);
|
|
22327
|
+
const paths = [];
|
|
22328
|
+
async function visit2(current) {
|
|
22329
|
+
const entries = await readdir7(current, { withFileTypes: true });
|
|
22330
|
+
for (const entry of entries) {
|
|
22331
|
+
if (entry.isDirectory()) {
|
|
22332
|
+
if (!EXCLUDED_DIRECTORIES.has(entry.name))
|
|
22333
|
+
await visit2(join18(current, entry.name));
|
|
22334
|
+
continue;
|
|
22335
|
+
}
|
|
22336
|
+
if (entry.isFile())
|
|
22337
|
+
paths.push(relative7(root, join18(current, entry.name)).split(sep5).join("/"));
|
|
22338
|
+
}
|
|
22339
|
+
}
|
|
22340
|
+
await visit2(root);
|
|
22341
|
+
return paths.sort();
|
|
22342
|
+
}
|
|
22343
|
+
async function verifyReceipt(projectRoot, receipt) {
|
|
22344
|
+
const expected = new Map(receipt.paths.map((entry) => [entry.path, entry.hash]));
|
|
22345
|
+
const changedPaths = [];
|
|
22346
|
+
const current = [];
|
|
22347
|
+
const pathsToCheck = receipt.coverage === "project" ? await collectReceiptPaths(projectRoot) : receipt.paths.map((entry) => entry.path);
|
|
22348
|
+
const currentByPath = new Map;
|
|
22349
|
+
for (const path of pathsToCheck) {
|
|
22350
|
+
try {
|
|
22351
|
+
const canonical = canonicalPath(projectRoot, path);
|
|
22352
|
+
const hash = sha2562(await readFile27(canonical.absolute));
|
|
22353
|
+
const currentEntry = { path: canonical.relative, hash };
|
|
22354
|
+
current.push(currentEntry);
|
|
22355
|
+
currentByPath.set(currentEntry.path, currentEntry);
|
|
22356
|
+
if (hash !== expected.get(currentEntry.path))
|
|
22357
|
+
changedPaths.push(currentEntry.path);
|
|
22358
|
+
} catch {
|
|
22359
|
+
changedPaths.push(path);
|
|
22360
|
+
}
|
|
22361
|
+
}
|
|
22362
|
+
for (const path of expected.keys()) {
|
|
22363
|
+
if (!currentByPath.has(path) && !changedPaths.includes(path))
|
|
22364
|
+
changedPaths.push(path);
|
|
22365
|
+
}
|
|
22366
|
+
changedPaths.sort();
|
|
22367
|
+
const actualHash = manifestHash(current.sort((left, right) => left.path.localeCompare(right.path)));
|
|
22368
|
+
return {
|
|
22369
|
+
valid: changedPaths.length === 0 && actualHash === receipt.manifestHash,
|
|
22370
|
+
expectedHash: receipt.manifestHash,
|
|
22371
|
+
actualHash,
|
|
22372
|
+
changedPaths
|
|
22373
|
+
};
|
|
22374
|
+
}
|
|
22375
|
+
var EXCLUDED_DIRECTORIES;
|
|
22376
|
+
var init_rdd_receipt = __esm(() => {
|
|
22377
|
+
EXCLUDED_DIRECTORIES = new Set([
|
|
22378
|
+
".git",
|
|
22379
|
+
".codeconductor",
|
|
22380
|
+
"node_modules",
|
|
22381
|
+
"dist",
|
|
22382
|
+
"coverage",
|
|
22383
|
+
"graphify-out"
|
|
22384
|
+
]);
|
|
22385
|
+
});
|
|
22386
|
+
|
|
22185
22387
|
// src/core/verification/verification-runner.ts
|
|
22186
22388
|
import { existsSync as existsSync3 } from "node:fs";
|
|
22187
|
-
import { readFile as
|
|
22188
|
-
import { dirname as dirname10, join as
|
|
22189
|
-
import { mkdir as mkdir18, readdir as
|
|
22389
|
+
import { readFile as readFile28 } from "node:fs/promises";
|
|
22390
|
+
import { dirname as dirname10, join as join19, resolve as resolve27 } from "node:path";
|
|
22391
|
+
import { mkdir as mkdir18, readdir as readdir8, writeFile as writeFile18 } from "node:fs/promises";
|
|
22190
22392
|
function isPassingEvidence(ev) {
|
|
22191
22393
|
switch (ev.type) {
|
|
22192
22394
|
case "verification":
|
|
@@ -22203,9 +22405,17 @@ function isPassingEvidence(ev) {
|
|
|
22203
22405
|
return false;
|
|
22204
22406
|
}
|
|
22205
22407
|
}
|
|
22408
|
+
async function isCurrentPassingEvidence(projectRoot, ev) {
|
|
22409
|
+
if (!isPassingEvidence(ev))
|
|
22410
|
+
return false;
|
|
22411
|
+
const receipt = ev.data?.rddReceipt;
|
|
22412
|
+
if (!isRddReceipt(receipt))
|
|
22413
|
+
return false;
|
|
22414
|
+
return (await verifyReceipt(projectRoot, receipt)).valid;
|
|
22415
|
+
}
|
|
22206
22416
|
function evidenceFilePath(evDir, id) {
|
|
22207
|
-
const path =
|
|
22208
|
-
if (dirname10(path) !==
|
|
22417
|
+
const path = resolve27(evDir, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
22418
|
+
if (dirname10(path) !== resolve27(evDir)) {
|
|
22209
22419
|
return err(new Error(`Refusing to write evidence for "${id}" outside ${evDir}`));
|
|
22210
22420
|
}
|
|
22211
22421
|
return ok(path);
|
|
@@ -22231,7 +22441,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
22231
22441
|
return ok({ records, invalid });
|
|
22232
22442
|
let files;
|
|
22233
22443
|
try {
|
|
22234
|
-
files = await
|
|
22444
|
+
files = await readdir8(evDir);
|
|
22235
22445
|
} catch (e) {
|
|
22236
22446
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
22237
22447
|
}
|
|
@@ -22239,7 +22449,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
|
|
|
22239
22449
|
if (!file.endsWith(".json"))
|
|
22240
22450
|
continue;
|
|
22241
22451
|
try {
|
|
22242
|
-
const raw = await
|
|
22452
|
+
const raw = await readFile28(join19(evDir, file), "utf-8");
|
|
22243
22453
|
const ev = EvidenceSchema.parse(JSON.parse(raw));
|
|
22244
22454
|
if (ev.relatedTask === taskId)
|
|
22245
22455
|
records.push(ev);
|
|
@@ -22260,7 +22470,7 @@ async function validateEvidenceIds(projectRoot, taskId, evidenceIds) {
|
|
|
22260
22470
|
if (!record) {
|
|
22261
22471
|
return err(new Error(`Evidence "${id}" is not a record of task ${taskId}`));
|
|
22262
22472
|
}
|
|
22263
|
-
if (!
|
|
22473
|
+
if (!await isCurrentPassingEvidence(projectRoot, record)) {
|
|
22264
22474
|
return err(new Error(`Evidence "${id}" does not record a passing result`));
|
|
22265
22475
|
}
|
|
22266
22476
|
if (record.type === "verification")
|
|
@@ -22341,7 +22551,12 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
22341
22551
|
passed: collected.data.invalid === 0,
|
|
22342
22552
|
message: collected.data.invalid === 0 ? "All evidence records are readable" : `${collected.data.invalid} unreadable evidence record(s)`
|
|
22343
22553
|
});
|
|
22344
|
-
const supporting =
|
|
22554
|
+
const supporting = [];
|
|
22555
|
+
for (const candidate of collected.data.records) {
|
|
22556
|
+
if (candidate.type !== "verification" && await isCurrentPassingEvidence(projectRoot, candidate)) {
|
|
22557
|
+
supporting.push(candidate);
|
|
22558
|
+
}
|
|
22559
|
+
}
|
|
22345
22560
|
evidenceIds.push(...supporting.map((ev) => ev.id));
|
|
22346
22561
|
checks.push({
|
|
22347
22562
|
name: "evidence_present",
|
|
@@ -22349,6 +22564,13 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
22349
22564
|
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"
|
|
22350
22565
|
});
|
|
22351
22566
|
const passed = checks.every((c) => c.passed);
|
|
22567
|
+
const receipt = await captureReceipt(projectRoot, {
|
|
22568
|
+
taskId,
|
|
22569
|
+
phase: "verification",
|
|
22570
|
+
paths: await collectReceiptPaths(projectRoot),
|
|
22571
|
+
outcome: passed ? "passed" : "failed",
|
|
22572
|
+
coverage: "project"
|
|
22573
|
+
});
|
|
22352
22574
|
const evidence = {
|
|
22353
22575
|
id: `ev-verify-${taskId}-${Date.now()}`,
|
|
22354
22576
|
source: "cc verify",
|
|
@@ -22357,7 +22579,7 @@ async function runVerification(projectRoot, taskId, goal, options) {
|
|
|
22357
22579
|
relatedTask: taskId,
|
|
22358
22580
|
confidence: passed ? 0.9 : 0.3,
|
|
22359
22581
|
summary: passed ? "Verification passed" : "Verification failed",
|
|
22360
|
-
data: { passed, checks }
|
|
22582
|
+
data: { passed, checks, rddReceipt: receipt }
|
|
22361
22583
|
};
|
|
22362
22584
|
const persisted = await persistEvidence(projectRoot, evidence);
|
|
22363
22585
|
if (!persisted.success)
|
|
@@ -22380,19 +22602,25 @@ async function gateTaskCompletion(projectRoot, taskId, evidenceRequired, evidenc
|
|
|
22380
22602
|
if (collected.data.invalid > 0)
|
|
22381
22603
|
return ok(false);
|
|
22382
22604
|
const cited = evidenceIds ? new Set(evidenceIds) : undefined;
|
|
22383
|
-
const hasPassing = (type) =>
|
|
22605
|
+
const hasPassing = async (type) => {
|
|
22606
|
+
for (const evidence of collected.data.records) {
|
|
22607
|
+
if ((!cited || cited.has(evidence.id)) && evidence.type === type && await isCurrentPassingEvidence(projectRoot, evidence))
|
|
22608
|
+
return true;
|
|
22609
|
+
}
|
|
22610
|
+
return false;
|
|
22611
|
+
};
|
|
22384
22612
|
for (const req of evidenceRequired) {
|
|
22385
22613
|
switch (req) {
|
|
22386
22614
|
case "acceptance_criteria_met":
|
|
22387
|
-
if (!hasPassing("verification"))
|
|
22615
|
+
if (!await hasPassing("verification"))
|
|
22388
22616
|
return ok(false);
|
|
22389
22617
|
break;
|
|
22390
22618
|
case "tests_passed":
|
|
22391
|
-
if (!hasPassing("test"))
|
|
22619
|
+
if (!await hasPassing("test"))
|
|
22392
22620
|
return ok(false);
|
|
22393
22621
|
break;
|
|
22394
22622
|
case "review_approved":
|
|
22395
|
-
if (!hasPassing("review"))
|
|
22623
|
+
if (!await hasPassing("review"))
|
|
22396
22624
|
return ok(false);
|
|
22397
22625
|
break;
|
|
22398
22626
|
default:
|
|
@@ -22401,11 +22629,66 @@ async function gateTaskCompletion(projectRoot, taskId, evidenceRequired, evidenc
|
|
|
22401
22629
|
}
|
|
22402
22630
|
return ok(true);
|
|
22403
22631
|
}
|
|
22404
|
-
async function
|
|
22632
|
+
async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
|
|
22633
|
+
const evDir = evidenceDir(projectRoot);
|
|
22634
|
+
const path = evidenceFilePath(evDir, evidenceId);
|
|
22635
|
+
if (!path.success)
|
|
22636
|
+
return path;
|
|
22637
|
+
let raw;
|
|
22638
|
+
try {
|
|
22639
|
+
raw = await readFile28(path.data, "utf-8");
|
|
22640
|
+
} catch (e) {
|
|
22641
|
+
return err(e instanceof Error ? e : new Error(String(e)));
|
|
22642
|
+
}
|
|
22643
|
+
let ev;
|
|
22644
|
+
try {
|
|
22645
|
+
ev = EvidenceSchema.parse(JSON.parse(raw));
|
|
22646
|
+
} catch {
|
|
22647
|
+
return err(new Error(`Evidence "${evidenceId}" is not valid TDD evidence`));
|
|
22648
|
+
}
|
|
22649
|
+
if (ev.relatedTask !== taskId) {
|
|
22650
|
+
return err(new Error(`Evidence "${evidenceId}" is not a record of task ${taskId}`));
|
|
22651
|
+
}
|
|
22652
|
+
if (ev.type !== "tdd" || ev.source !== TDD_EVIDENCE_SOURCE) {
|
|
22653
|
+
return err(new Error(`Evidence "${evidenceId}" was not captured by the verification runner`));
|
|
22654
|
+
}
|
|
22655
|
+
const capturedBy = ev.data?.capturedBy;
|
|
22656
|
+
const suiteFailed = ev.data?.suiteFailed;
|
|
22657
|
+
const suitePassed = ev.data?.suitePassed;
|
|
22658
|
+
if (capturedBy !== TDD_CAPTURED_BY || typeof suiteFailed !== "boolean" || typeof suitePassed !== "boolean") {
|
|
22659
|
+
return err(new Error(`Evidence "${evidenceId}" was not captured by the verification runner`));
|
|
22660
|
+
}
|
|
22661
|
+
const receipt = ev.data?.rddReceipt;
|
|
22662
|
+
if (!isRddReceipt(receipt)) {
|
|
22663
|
+
return err(new Error(`Evidence "${evidenceId}" has no valid RDD receipt`));
|
|
22664
|
+
}
|
|
22665
|
+
if (receipt.taskId !== taskId) {
|
|
22666
|
+
return err(new Error(`Evidence "${evidenceId}" receipt belongs to another task`));
|
|
22667
|
+
}
|
|
22668
|
+
const verified = await verifyReceipt(projectRoot, receipt);
|
|
22669
|
+
if (!verified.valid) {
|
|
22670
|
+
return err(new Error(`Evidence "${evidenceId}" RDD receipt is stale: ${verified.changedPaths.join(", ")}`));
|
|
22671
|
+
}
|
|
22672
|
+
return ok({ capturedBy, suiteFailed, suitePassed, receipt });
|
|
22673
|
+
}
|
|
22674
|
+
async function hasTddRunnerEvidence(projectRoot, taskId, expectedPhase) {
|
|
22405
22675
|
const collected = await collectTaskEvidence(projectRoot, taskId);
|
|
22406
22676
|
if (!collected.success)
|
|
22407
22677
|
return false;
|
|
22408
|
-
|
|
22678
|
+
for (const ev of collected.data.records) {
|
|
22679
|
+
if (ev.type !== "tdd" || ev.source !== TDD_EVIDENCE_SOURCE || ev.data?.capturedBy !== TDD_CAPTURED_BY) {
|
|
22680
|
+
continue;
|
|
22681
|
+
}
|
|
22682
|
+
const loaded = await loadTddSuiteEvidence(projectRoot, taskId, ev.id);
|
|
22683
|
+
if (!loaded.success)
|
|
22684
|
+
continue;
|
|
22685
|
+
if (expectedPhase === "red" && (!loaded.data.suiteFailed || loaded.data.suitePassed))
|
|
22686
|
+
continue;
|
|
22687
|
+
if (expectedPhase === "green" && (!loaded.data.suitePassed || loaded.data.suiteFailed))
|
|
22688
|
+
continue;
|
|
22689
|
+
return true;
|
|
22690
|
+
}
|
|
22691
|
+
return false;
|
|
22409
22692
|
}
|
|
22410
22693
|
var TDD_EVIDENCE_SOURCE = "cc verify", TDD_CAPTURED_BY = "verification-runner";
|
|
22411
22694
|
var init_verification_runner = __esm(() => {
|
|
@@ -22414,6 +22697,7 @@ var init_verification_runner = __esm(() => {
|
|
|
22414
22697
|
init_compile_checker();
|
|
22415
22698
|
init_paths();
|
|
22416
22699
|
init_episodic_store();
|
|
22700
|
+
init_rdd_receipt();
|
|
22417
22701
|
});
|
|
22418
22702
|
|
|
22419
22703
|
// src/core/orchestrator/runtime-orchestrator.ts
|
|
@@ -23536,6 +23820,163 @@ var init_verify_command = __esm(() => {
|
|
|
23536
23820
|
init_verification_runner();
|
|
23537
23821
|
});
|
|
23538
23822
|
|
|
23823
|
+
// src/commands/rdd.command.ts
|
|
23824
|
+
var exports_rdd_command = {};
|
|
23825
|
+
__export(exports_rdd_command, {
|
|
23826
|
+
rddCommand: () => rddCommand,
|
|
23827
|
+
rddStatus: () => rddStatus
|
|
23828
|
+
});
|
|
23829
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
23830
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
23831
|
+
import { mkdir as mkdir19, readFile as readFile29, readdir as readdir9, rename as rename3, writeFile as writeFile19, chmod } from "node:fs/promises";
|
|
23832
|
+
import { dirname as dirname11, resolve as resolve28 } from "node:path";
|
|
23833
|
+
import { promisify as promisify3 } from "node:util";
|
|
23834
|
+
function receiptPath(projectRoot, id) {
|
|
23835
|
+
const directory = resolve28(evidenceDir(projectRoot));
|
|
23836
|
+
const path = resolve28(directory, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
23837
|
+
if (dirname11(path) !== directory)
|
|
23838
|
+
throw new Error(`Invalid RDD receipt id: ${id}`);
|
|
23839
|
+
return path;
|
|
23840
|
+
}
|
|
23841
|
+
async function loadReceipt(projectRoot, id) {
|
|
23842
|
+
const raw = await readFile29(receiptPath(projectRoot, id), "utf-8");
|
|
23843
|
+
const evidence = EvidenceSchema.parse(JSON.parse(raw));
|
|
23844
|
+
if (evidence.type !== "rdd")
|
|
23845
|
+
throw new Error(`Evidence "${id}" is not an RDD receipt`);
|
|
23846
|
+
return { evidence, receipt: assertReceipt(evidence.data?.rddReceipt) };
|
|
23847
|
+
}
|
|
23848
|
+
function assertReceipt(value) {
|
|
23849
|
+
if (!isRddReceipt(value))
|
|
23850
|
+
throw new Error("RDD receipt payload is invalid");
|
|
23851
|
+
return value;
|
|
23852
|
+
}
|
|
23853
|
+
async function rddStatus(projectRoot, taskId) {
|
|
23854
|
+
const directory = evidenceDir(projectRoot);
|
|
23855
|
+
if (!existsSync4(directory))
|
|
23856
|
+
return [];
|
|
23857
|
+
const receipts = [];
|
|
23858
|
+
for (const file of await readdir9(directory)) {
|
|
23859
|
+
if (!file.endsWith(".json"))
|
|
23860
|
+
continue;
|
|
23861
|
+
try {
|
|
23862
|
+
const id = file.slice(0, -".json".length);
|
|
23863
|
+
const { evidence, receipt } = await loadReceipt(projectRoot, id);
|
|
23864
|
+
if (taskId && evidence.relatedTask !== taskId)
|
|
23865
|
+
continue;
|
|
23866
|
+
const verification = await verifyReceipt(projectRoot, receipt);
|
|
23867
|
+
receipts.push({
|
|
23868
|
+
id: evidence.id,
|
|
23869
|
+
taskId: evidence.relatedTask,
|
|
23870
|
+
phase: receipt.phase,
|
|
23871
|
+
timestamp: evidence.timestamp,
|
|
23872
|
+
valid: verification.valid,
|
|
23873
|
+
changedPaths: verification.changedPaths
|
|
23874
|
+
});
|
|
23875
|
+
} catch {}
|
|
23876
|
+
}
|
|
23877
|
+
return receipts.sort((left, right) => right.timestamp.localeCompare(left.timestamp));
|
|
23878
|
+
}
|
|
23879
|
+
async function installHooks(projectRoot) {
|
|
23880
|
+
const { stdout } = await execFileAsync3("git", ["rev-parse", "--git-path", "hooks"], {
|
|
23881
|
+
cwd: projectRoot,
|
|
23882
|
+
encoding: "utf8"
|
|
23883
|
+
});
|
|
23884
|
+
const hooksDir = resolve28(projectRoot, stdout.trim());
|
|
23885
|
+
await mkdir19(hooksDir, { recursive: true });
|
|
23886
|
+
const installed = [];
|
|
23887
|
+
const skipped = [];
|
|
23888
|
+
for (const hook of ["pre-commit", "pre-push"]) {
|
|
23889
|
+
const path = resolve28(hooksDir, hook);
|
|
23890
|
+
const backup = resolve28(hooksDir, `${hook}.rdd-original`);
|
|
23891
|
+
const marker = "# codeconductor-rdd-hook";
|
|
23892
|
+
if (existsSync4(path)) {
|
|
23893
|
+
const existing = await readFile29(path, "utf8");
|
|
23894
|
+
if (existing.includes(marker)) {
|
|
23895
|
+
skipped.push(hook);
|
|
23896
|
+
continue;
|
|
23897
|
+
}
|
|
23898
|
+
if (existsSync4(backup)) {
|
|
23899
|
+
throw new Error(`Cannot preserve ${hook}: ${backup} already exists`);
|
|
23900
|
+
}
|
|
23901
|
+
await rename3(path, backup);
|
|
23902
|
+
}
|
|
23903
|
+
const script = `#!/bin/sh
|
|
23904
|
+
${marker}
|
|
23905
|
+
set -e
|
|
23906
|
+
HOOK_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
|
23907
|
+
if [ -x "$HOOK_DIR/${hook}.rdd-original" ]; then
|
|
23908
|
+
"$HOOK_DIR/${hook}.rdd-original" "$@"
|
|
23909
|
+
fi
|
|
23910
|
+
cc-codeconductor rdd git-check
|
|
23911
|
+
`;
|
|
23912
|
+
await writeFile19(path, script, "utf8");
|
|
23913
|
+
await chmod(path, 493);
|
|
23914
|
+
installed.push(hook);
|
|
23915
|
+
}
|
|
23916
|
+
return { installed, skipped };
|
|
23917
|
+
}
|
|
23918
|
+
async function rddCommand(options) {
|
|
23919
|
+
try {
|
|
23920
|
+
if (options.subcommand === "capture") {
|
|
23921
|
+
if (!options.taskId)
|
|
23922
|
+
throw new Error("Missing task id. Use --task <id>.");
|
|
23923
|
+
const protectedPaths = options.paths?.length ? options.paths : await collectReceiptPaths(options.projectRoot);
|
|
23924
|
+
const receipt = await captureReceipt(options.projectRoot, {
|
|
23925
|
+
taskId: options.taskId,
|
|
23926
|
+
phase: options.phase ?? "verification",
|
|
23927
|
+
paths: protectedPaths,
|
|
23928
|
+
outcome: options.outcome ?? "passed",
|
|
23929
|
+
command: options.command,
|
|
23930
|
+
coverage: options.paths?.length ? "paths" : "project"
|
|
23931
|
+
});
|
|
23932
|
+
const id = `ev-rdd-${options.taskId}-${Date.now()}`;
|
|
23933
|
+
const evidence = {
|
|
23934
|
+
id,
|
|
23935
|
+
source: "cc rdd",
|
|
23936
|
+
type: "rdd",
|
|
23937
|
+
timestamp: new Date().toISOString(),
|
|
23938
|
+
relatedTask: options.taskId,
|
|
23939
|
+
confidence: 0.9,
|
|
23940
|
+
summary: `RDD ${receipt.phase} receipt captured`,
|
|
23941
|
+
data: { rddReceipt: receipt }
|
|
23942
|
+
};
|
|
23943
|
+
const directory = evidenceDir(options.projectRoot);
|
|
23944
|
+
await mkdir19(directory, { recursive: true });
|
|
23945
|
+
await writeFile19(receiptPath(options.projectRoot, id), JSON.stringify(evidence, null, 2), "utf-8");
|
|
23946
|
+
return { code: 0, data: { success: true, receiptId: id, receipt } };
|
|
23947
|
+
}
|
|
23948
|
+
if (options.subcommand === "verify") {
|
|
23949
|
+
if (!options.receiptId)
|
|
23950
|
+
throw new Error("Missing receipt id. Use --receipt <id>.");
|
|
23951
|
+
const { receipt } = await loadReceipt(options.projectRoot, options.receiptId);
|
|
23952
|
+
const verification = await verifyReceipt(options.projectRoot, receipt);
|
|
23953
|
+
return {
|
|
23954
|
+
code: verification.valid ? 0 : 1,
|
|
23955
|
+
data: { success: verification.valid, receiptId: options.receiptId, ...verification }
|
|
23956
|
+
};
|
|
23957
|
+
}
|
|
23958
|
+
if (options.subcommand === "status") {
|
|
23959
|
+
return { code: 0, data: { success: true, receipts: await rddStatus(options.projectRoot, options.taskId) } };
|
|
23960
|
+
}
|
|
23961
|
+
if (options.subcommand === "git-check") {
|
|
23962
|
+
const current = (await rddStatus(options.projectRoot))[0];
|
|
23963
|
+
if (!current)
|
|
23964
|
+
return { code: 1, data: { success: false, errors: ["No RDD receipt exists for the Git candidate."] } };
|
|
23965
|
+
return current.valid ? { code: 0, data: { success: true, receiptId: current.id } } : { code: 1, data: { success: false, receiptId: current.id, changedPaths: current.changedPaths } };
|
|
23966
|
+
}
|
|
23967
|
+
return { code: 0, data: { success: true, ...await installHooks(options.projectRoot) } };
|
|
23968
|
+
} catch (error) {
|
|
23969
|
+
return { code: 1, data: { success: false, errors: [error instanceof Error ? error.message : String(error)] } };
|
|
23970
|
+
}
|
|
23971
|
+
}
|
|
23972
|
+
var execFileAsync3;
|
|
23973
|
+
var init_rdd_command = __esm(() => {
|
|
23974
|
+
init_schemas();
|
|
23975
|
+
init_paths();
|
|
23976
|
+
init_rdd_receipt();
|
|
23977
|
+
execFileAsync3 = promisify3(execFile3);
|
|
23978
|
+
});
|
|
23979
|
+
|
|
23539
23980
|
// src/core/ccep/confirmation-gate.ts
|
|
23540
23981
|
function matchesRiskThreshold(severity, threshold) {
|
|
23541
23982
|
const order = { low: 0, medium: 1, high: 2 };
|
|
@@ -23841,11 +24282,11 @@ var init_output_validator = __esm(() => {
|
|
|
23841
24282
|
});
|
|
23842
24283
|
|
|
23843
24284
|
// src/core/ccep/command-parser.ts
|
|
23844
|
-
import { existsSync as
|
|
23845
|
-
import { join as
|
|
24285
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
24286
|
+
import { join as join20 } from "node:path";
|
|
23846
24287
|
function readProjectId(projectRoot) {
|
|
23847
|
-
const packageJsonPath =
|
|
23848
|
-
if (
|
|
24288
|
+
const packageJsonPath = join20(projectRoot, "package.json");
|
|
24289
|
+
if (existsSync5(packageJsonPath)) {
|
|
23849
24290
|
try {
|
|
23850
24291
|
const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
23851
24292
|
if (pkg.name) {
|
|
@@ -23874,8 +24315,8 @@ function parseCommand(command, userRequest, projectRoot) {
|
|
|
23874
24315
|
maxVariance: "low"
|
|
23875
24316
|
}
|
|
23876
24317
|
};
|
|
23877
|
-
const packageJsonPath =
|
|
23878
|
-
if (
|
|
24318
|
+
const packageJsonPath = join20(projectRoot, "package.json");
|
|
24319
|
+
if (existsSync5(packageJsonPath)) {
|
|
23879
24320
|
try {
|
|
23880
24321
|
const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
23881
24322
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
@@ -23919,7 +24360,8 @@ var init_command_parser = __esm(() => {
|
|
|
23919
24360
|
prototype: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
|
|
23920
24361
|
handoff: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" },
|
|
23921
24362
|
clarify: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" },
|
|
23922
|
-
security: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "high" }
|
|
24363
|
+
security: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "high" },
|
|
24364
|
+
odd: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" }
|
|
23923
24365
|
};
|
|
23924
24366
|
});
|
|
23925
24367
|
|
|
@@ -23946,10 +24388,10 @@ var init_workflow_profile_loader = __esm(() => {
|
|
|
23946
24388
|
});
|
|
23947
24389
|
|
|
23948
24390
|
// src/core/evaluation/harness-catalog.ts
|
|
23949
|
-
import { createHash as
|
|
23950
|
-
import { existsSync as
|
|
23951
|
-
import { copyFile as copyFile2, mkdir as
|
|
23952
|
-
import { dirname as
|
|
24391
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
24392
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
|
|
24393
|
+
import { copyFile as copyFile2, mkdir as mkdir20, readFile as readFile30, readdir as readdir10, rm as rm4, writeFile as writeFile20 } from "node:fs/promises";
|
|
24394
|
+
import { dirname as dirname12, join as join21, resolve as resolve29 } from "node:path";
|
|
23953
24395
|
function catalogIds(catalog = DEFAULT_HARNESS_CATALOG) {
|
|
23954
24396
|
return catalog.components.map((c) => c.id);
|
|
23955
24397
|
}
|
|
@@ -23999,23 +24441,23 @@ function harnessFingerprint(disabled, contractVersion, ccepProfile = "feature",
|
|
|
23999
24441
|
contractVersion,
|
|
24000
24442
|
ccepProfile
|
|
24001
24443
|
};
|
|
24002
|
-
return
|
|
24444
|
+
return createHash5("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
|
|
24003
24445
|
}
|
|
24004
24446
|
async function loadHarnessCatalog(projectRoot) {
|
|
24005
|
-
const overridePath =
|
|
24447
|
+
const overridePath = resolve29(projectRoot, EVAL_DIR2, CATALOG_FILE);
|
|
24006
24448
|
try {
|
|
24007
|
-
const raw = await
|
|
24449
|
+
const raw = await readFile30(overridePath, "utf-8");
|
|
24008
24450
|
return HarnessCatalogSchema.parse($parse(raw));
|
|
24009
24451
|
} catch {
|
|
24010
24452
|
return DEFAULT_HARNESS_CATALOG;
|
|
24011
24453
|
}
|
|
24012
24454
|
}
|
|
24013
24455
|
function activeOverlayPath(projectRoot) {
|
|
24014
|
-
return
|
|
24456
|
+
return resolve29(projectRoot, EVAL_DIR2, ACTIVE_OVERLAY_FILE);
|
|
24015
24457
|
}
|
|
24016
24458
|
async function readActiveOverlay(projectRoot) {
|
|
24017
24459
|
try {
|
|
24018
|
-
const raw = await
|
|
24460
|
+
const raw = await readFile30(activeOverlayPath(projectRoot), "utf-8");
|
|
24019
24461
|
return HarnessOverlaySchema.parse($parse(raw));
|
|
24020
24462
|
} catch {
|
|
24021
24463
|
return null;
|
|
@@ -24024,7 +24466,7 @@ async function readActiveOverlay(projectRoot) {
|
|
|
24024
24466
|
function isProductGraphDisabled(projectRoot) {
|
|
24025
24467
|
try {
|
|
24026
24468
|
const path = activeOverlayPath(projectRoot);
|
|
24027
|
-
if (!
|
|
24469
|
+
if (!existsSync6(path))
|
|
24028
24470
|
return false;
|
|
24029
24471
|
const overlay = HarnessOverlaySchema.parse($parse(readFileSync4(path, "utf-8")));
|
|
24030
24472
|
return overlay.disableProductGraph === true || overlay.disabledComponents.includes("product_graph");
|
|
@@ -24069,55 +24511,55 @@ function applyCatalogToProfile(profile, disabled) {
|
|
|
24069
24511
|
return next;
|
|
24070
24512
|
}
|
|
24071
24513
|
async function restoreOverlayBackup(projectRoot) {
|
|
24072
|
-
const backupRoot =
|
|
24073
|
-
if (!
|
|
24514
|
+
const backupRoot = resolve29(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
|
|
24515
|
+
if (!existsSync6(backupRoot))
|
|
24074
24516
|
return;
|
|
24075
|
-
const configBackup =
|
|
24076
|
-
const configDest =
|
|
24077
|
-
if (
|
|
24078
|
-
await
|
|
24517
|
+
const configBackup = join21(backupRoot, "config.yml");
|
|
24518
|
+
const configDest = resolve29(projectRoot, ".codeconductor", "config.yml");
|
|
24519
|
+
if (existsSync6(configBackup)) {
|
|
24520
|
+
await mkdir20(dirname12(configDest), { recursive: true });
|
|
24079
24521
|
await copyFile2(configBackup, configDest);
|
|
24080
24522
|
}
|
|
24081
|
-
const workflowsBackup =
|
|
24082
|
-
const workflowsDest =
|
|
24083
|
-
if (
|
|
24084
|
-
await
|
|
24085
|
-
const files = await
|
|
24523
|
+
const workflowsBackup = join21(backupRoot, "workflows");
|
|
24524
|
+
const workflowsDest = resolve29(projectRoot, ".codeconductor", "workflows");
|
|
24525
|
+
if (existsSync6(workflowsBackup)) {
|
|
24526
|
+
await mkdir20(workflowsDest, { recursive: true });
|
|
24527
|
+
const files = await readdir10(workflowsBackup);
|
|
24086
24528
|
for (const file of files) {
|
|
24087
|
-
await copyFile2(
|
|
24529
|
+
await copyFile2(join21(workflowsBackup, file), join21(workflowsDest, file));
|
|
24088
24530
|
}
|
|
24089
24531
|
}
|
|
24090
|
-
const overlayBackup =
|
|
24532
|
+
const overlayBackup = join21(backupRoot, ACTIVE_OVERLAY_FILE);
|
|
24091
24533
|
const overlayDest = activeOverlayPath(projectRoot);
|
|
24092
|
-
if (
|
|
24534
|
+
if (existsSync6(overlayBackup)) {
|
|
24093
24535
|
await copyFile2(overlayBackup, overlayDest);
|
|
24094
|
-
} else if (
|
|
24536
|
+
} else if (existsSync6(overlayDest)) {
|
|
24095
24537
|
await rm4(overlayDest, { force: true });
|
|
24096
24538
|
}
|
|
24097
24539
|
}
|
|
24098
24540
|
async function snapshotForBackup(projectRoot) {
|
|
24099
|
-
const backupRoot =
|
|
24100
|
-
if (
|
|
24541
|
+
const backupRoot = resolve29(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
|
|
24542
|
+
if (existsSync6(backupRoot))
|
|
24101
24543
|
return;
|
|
24102
|
-
await
|
|
24103
|
-
const configPath =
|
|
24104
|
-
if (
|
|
24105
|
-
await copyFile2(configPath,
|
|
24106
|
-
}
|
|
24107
|
-
const workflowsDir =
|
|
24108
|
-
if (
|
|
24109
|
-
const dest =
|
|
24110
|
-
await
|
|
24111
|
-
const files = await
|
|
24544
|
+
await mkdir20(backupRoot, { recursive: true });
|
|
24545
|
+
const configPath = resolve29(projectRoot, ".codeconductor", "config.yml");
|
|
24546
|
+
if (existsSync6(configPath)) {
|
|
24547
|
+
await copyFile2(configPath, join21(backupRoot, "config.yml"));
|
|
24548
|
+
}
|
|
24549
|
+
const workflowsDir = resolve29(projectRoot, ".codeconductor", "workflows");
|
|
24550
|
+
if (existsSync6(workflowsDir)) {
|
|
24551
|
+
const dest = join21(backupRoot, "workflows");
|
|
24552
|
+
await mkdir20(dest, { recursive: true });
|
|
24553
|
+
const files = await readdir10(workflowsDir);
|
|
24112
24554
|
for (const file of files) {
|
|
24113
24555
|
if (file.endsWith(".yml")) {
|
|
24114
|
-
await copyFile2(
|
|
24556
|
+
await copyFile2(join21(workflowsDir, file), join21(dest, file));
|
|
24115
24557
|
}
|
|
24116
24558
|
}
|
|
24117
24559
|
}
|
|
24118
24560
|
const overlay = activeOverlayPath(projectRoot);
|
|
24119
|
-
if (
|
|
24120
|
-
await copyFile2(overlay,
|
|
24561
|
+
if (existsSync6(overlay)) {
|
|
24562
|
+
await copyFile2(overlay, join21(backupRoot, ACTIVE_OVERLAY_FILE));
|
|
24121
24563
|
}
|
|
24122
24564
|
}
|
|
24123
24565
|
async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
|
|
@@ -24126,13 +24568,13 @@ async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
|
|
|
24126
24568
|
await restoreOverlayBackup(projectRoot);
|
|
24127
24569
|
await snapshotForBackup(projectRoot);
|
|
24128
24570
|
}
|
|
24129
|
-
const workflowsDir =
|
|
24130
|
-
await
|
|
24131
|
-
await
|
|
24571
|
+
const workflowsDir = resolve29(projectRoot, ".codeconductor", "workflows");
|
|
24572
|
+
await mkdir20(workflowsDir, { recursive: true });
|
|
24573
|
+
await mkdir20(resolve29(projectRoot, EVAL_DIR2), { recursive: true });
|
|
24132
24574
|
for (const command of WORKFLOW_OVERLAY_COMMANDS) {
|
|
24133
24575
|
const base = loadWorkflowProfile(command);
|
|
24134
24576
|
const next = applyCatalogToProfile(base, disabled);
|
|
24135
|
-
await
|
|
24577
|
+
await writeFile20(join21(workflowsDir, `${command}.yml`), $stringify(next), "utf-8");
|
|
24136
24578
|
}
|
|
24137
24579
|
const needsConfig = disabled.includes("compile_loop") || disabled.includes("council");
|
|
24138
24580
|
if (needsConfig) {
|
|
@@ -24160,7 +24602,7 @@ async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
|
|
|
24160
24602
|
contractVersion: options.contractVersion,
|
|
24161
24603
|
ccepProfile: options.ccepProfile ?? "feature"
|
|
24162
24604
|
});
|
|
24163
|
-
await
|
|
24605
|
+
await writeFile20(activeOverlayPath(projectRoot), $stringify(overlay), "utf-8");
|
|
24164
24606
|
return ok(overlay);
|
|
24165
24607
|
} catch (e) {
|
|
24166
24608
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -24271,9 +24713,9 @@ var init_harness_catalog = __esm(() => {
|
|
|
24271
24713
|
});
|
|
24272
24714
|
|
|
24273
24715
|
// src/core/openspec/openspec-state.ts
|
|
24274
|
-
import { createHash as
|
|
24275
|
-
import { mkdir as
|
|
24276
|
-
import { resolve as
|
|
24716
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
24717
|
+
import { mkdir as mkdir21, readFile as readFile31, rename as rename4, writeFile as writeFile21 } from "node:fs/promises";
|
|
24718
|
+
import { resolve as resolve30 } from "node:path";
|
|
24277
24719
|
function canTransition(from, to) {
|
|
24278
24720
|
return ALLOWED_TRANSITIONS[from]?.includes(to) ?? false;
|
|
24279
24721
|
}
|
|
@@ -24294,13 +24736,13 @@ function applyBacklogTransition(content, itemId, from, to, progress) {
|
|
|
24294
24736
|
}
|
|
24295
24737
|
async function writeFileAtomic(path, content) {
|
|
24296
24738
|
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
24297
|
-
await
|
|
24298
|
-
await
|
|
24739
|
+
await writeFile21(tmp, content, "utf-8");
|
|
24740
|
+
await rename4(tmp, path);
|
|
24299
24741
|
}
|
|
24300
24742
|
async function loadOpenspecState(projectRoot) {
|
|
24301
24743
|
try {
|
|
24302
|
-
const filePath =
|
|
24303
|
-
const content = await
|
|
24744
|
+
const filePath = resolve30(projectRoot, STATE_FILE);
|
|
24745
|
+
const content = await readFile31(filePath, "utf-8");
|
|
24304
24746
|
const data = JSON.parse(content);
|
|
24305
24747
|
return ok(OpenspecStateSchema.parse(data));
|
|
24306
24748
|
} catch (e) {
|
|
@@ -24317,10 +24759,10 @@ async function loadOpenspecState(projectRoot) {
|
|
|
24317
24759
|
}
|
|
24318
24760
|
async function writeOpenspecState(projectRoot, state) {
|
|
24319
24761
|
try {
|
|
24320
|
-
const dir =
|
|
24321
|
-
await
|
|
24762
|
+
const dir = resolve30(projectRoot, ".codeconductor");
|
|
24763
|
+
await mkdir21(dir, { recursive: true });
|
|
24322
24764
|
const validated = OpenspecStateSchema.parse(state);
|
|
24323
|
-
await writeFileAtomic(
|
|
24765
|
+
await writeFileAtomic(resolve30(dir, "openspec-state.json"), JSON.stringify(validated, null, 2));
|
|
24324
24766
|
return ok(undefined);
|
|
24325
24767
|
} catch (e) {
|
|
24326
24768
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -24342,7 +24784,7 @@ function getNextTaskCard(state) {
|
|
|
24342
24784
|
return null;
|
|
24343
24785
|
}
|
|
24344
24786
|
function hashContent(content) {
|
|
24345
|
-
return
|
|
24787
|
+
return createHash6("sha256").update(content).digest("hex").slice(0, 16);
|
|
24346
24788
|
}
|
|
24347
24789
|
function serializeItemSnapshot(item) {
|
|
24348
24790
|
return JSON.stringify({
|
|
@@ -24424,19 +24866,19 @@ var init_openspec_state = __esm(() => {
|
|
|
24424
24866
|
});
|
|
24425
24867
|
|
|
24426
24868
|
// src/core/ccep/context-resolver.ts
|
|
24427
|
-
import { existsSync as
|
|
24428
|
-
import { readFile as
|
|
24429
|
-
import { join as
|
|
24869
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
24870
|
+
import { readFile as readFile32 } from "node:fs/promises";
|
|
24871
|
+
import { join as join22 } from "node:path";
|
|
24430
24872
|
async function loadProductKnowledge(projectRoot) {
|
|
24431
24873
|
if (isProductGraphDisabled(projectRoot)) {
|
|
24432
24874
|
return {};
|
|
24433
24875
|
}
|
|
24434
24876
|
const graphPath = productGraphPath(projectRoot);
|
|
24435
|
-
if (!
|
|
24877
|
+
if (!existsSync7(graphPath)) {
|
|
24436
24878
|
return {};
|
|
24437
24879
|
}
|
|
24438
24880
|
try {
|
|
24439
|
-
const raw = await
|
|
24881
|
+
const raw = await readFile32(graphPath, "utf-8");
|
|
24440
24882
|
const graph = ProductGraphSchema.parse(JSON.parse(raw));
|
|
24441
24883
|
return {
|
|
24442
24884
|
productName: graph.productName,
|
|
@@ -24452,18 +24894,18 @@ async function loadProductKnowledge(projectRoot) {
|
|
|
24452
24894
|
}
|
|
24453
24895
|
async function resolveAstSource(projectRoot) {
|
|
24454
24896
|
if (isProductGraphDisabled(projectRoot)) {
|
|
24455
|
-
const graphifyPath2 =
|
|
24456
|
-
if (
|
|
24897
|
+
const graphifyPath2 = join22(projectRoot, "graphify-out", "graph.json");
|
|
24898
|
+
if (existsSync7(graphifyPath2)) {
|
|
24457
24899
|
return { source: "graphify", confidence: "medium" };
|
|
24458
24900
|
}
|
|
24459
24901
|
return { source: "manual", confidence: "low" };
|
|
24460
24902
|
}
|
|
24461
24903
|
const graphPath = productGraphPath(projectRoot);
|
|
24462
|
-
if (
|
|
24904
|
+
if (existsSync7(graphPath)) {
|
|
24463
24905
|
return { source: "product-graph", confidence: "high" };
|
|
24464
24906
|
}
|
|
24465
|
-
const graphifyPath =
|
|
24466
|
-
if (
|
|
24907
|
+
const graphifyPath = join22(projectRoot, "graphify-out", "graph.json");
|
|
24908
|
+
if (existsSync7(graphifyPath)) {
|
|
24467
24909
|
return { source: "graphify", confidence: "medium" };
|
|
24468
24910
|
}
|
|
24469
24911
|
try {
|
|
@@ -24704,6 +25146,12 @@ function councilConsensus(verdicts, config = DEFAULT_CONFIG2) {
|
|
|
24704
25146
|
individualVerdicts: verdicts
|
|
24705
25147
|
};
|
|
24706
25148
|
}
|
|
25149
|
+
if (config.candidateHash !== undefined) {
|
|
25150
|
+
const mismatched = verdicts.filter((verdict) => verdict.candidateHash !== config.candidateHash);
|
|
25151
|
+
if (mismatched.length > 0) {
|
|
25152
|
+
return escalated(verdicts, `Candidate receipt mismatch for ${mismatched.length} ballot(s) — escalated for human review.`, counts);
|
|
25153
|
+
}
|
|
25154
|
+
}
|
|
24707
25155
|
const critical = allFindings.filter((f) => f.severity === "critical");
|
|
24708
25156
|
if (critical.length > 0 && policy === "reject") {
|
|
24709
25157
|
return {
|
|
@@ -25265,12 +25713,26 @@ async function ccepCommand(options) {
|
|
|
25265
25713
|
}
|
|
25266
25714
|
mergedConfig = { ...mergedConfig, ...parsedConfig.data };
|
|
25267
25715
|
}
|
|
25716
|
+
let currentCandidateHash;
|
|
25717
|
+
if (mergedConfig?.candidateHash !== undefined) {
|
|
25718
|
+
const { captureReceipt: captureReceipt2, collectReceiptPaths: collectReceiptPaths2 } = await Promise.resolve().then(() => (init_rdd_receipt(), exports_rdd_receipt));
|
|
25719
|
+
const receipt = await captureReceipt2(projectRoot, {
|
|
25720
|
+
taskId: "council",
|
|
25721
|
+
phase: "review",
|
|
25722
|
+
paths: await collectReceiptPaths2(projectRoot),
|
|
25723
|
+
outcome: "passed",
|
|
25724
|
+
coverage: "project"
|
|
25725
|
+
});
|
|
25726
|
+
currentCandidateHash = receipt.manifestHash;
|
|
25727
|
+
mergedConfig = { ...mergedConfig, candidateHash: currentCandidateHash };
|
|
25728
|
+
}
|
|
25268
25729
|
const verdict = councilConsensus(parsedBallots.verdicts, mergedConfig);
|
|
25269
25730
|
return {
|
|
25270
25731
|
code: consensusExitCode(verdict.status),
|
|
25271
25732
|
data: {
|
|
25272
25733
|
success: verdict.status === "APPROVED",
|
|
25273
25734
|
command: "ccep consensus",
|
|
25735
|
+
candidateHash: currentCandidateHash,
|
|
25274
25736
|
verdict
|
|
25275
25737
|
}
|
|
25276
25738
|
};
|
|
@@ -25508,8 +25970,8 @@ var init_backlog_validator = __esm(() => {
|
|
|
25508
25970
|
|
|
25509
25971
|
// src/core/openspec/backlog-scanner.ts
|
|
25510
25972
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
25511
|
-
import { readFile as
|
|
25512
|
-
import { resolve as
|
|
25973
|
+
import { readFile as readFile33 } from "node:fs/promises";
|
|
25974
|
+
import { resolve as resolve31 } from "node:path";
|
|
25513
25975
|
function itemSnapshot(items) {
|
|
25514
25976
|
const snap = {};
|
|
25515
25977
|
for (const item of items) {
|
|
@@ -25519,8 +25981,8 @@ function itemSnapshot(items) {
|
|
|
25519
25981
|
}
|
|
25520
25982
|
async function scanBacklog(projectRoot, previousSnapshot = {}) {
|
|
25521
25983
|
try {
|
|
25522
|
-
const filePath =
|
|
25523
|
-
const content = await
|
|
25984
|
+
const filePath = resolve31(projectRoot, BACKLOG_FILENAME);
|
|
25985
|
+
const content = await readFile33(filePath, "utf-8");
|
|
25524
25986
|
const hash = hashContent(content);
|
|
25525
25987
|
let fileChanged = true;
|
|
25526
25988
|
try {
|
|
@@ -25757,8 +26219,8 @@ var init_backlog_planner = __esm(() => {
|
|
|
25757
26219
|
});
|
|
25758
26220
|
|
|
25759
26221
|
// src/core/openspec/openspec-generator.ts
|
|
25760
|
-
import { mkdir as
|
|
25761
|
-
import { basename as basename6, resolve as
|
|
26222
|
+
import { mkdir as mkdir22, rename as rename5, writeFile as writeFile22 } from "node:fs/promises";
|
|
26223
|
+
import { basename as basename6, resolve as resolve32 } from "node:path";
|
|
25762
26224
|
function pad3(n) {
|
|
25763
26225
|
return String(n).padStart(3, "0");
|
|
25764
26226
|
}
|
|
@@ -25823,7 +26285,7 @@ The system MUST ${criterion}.
|
|
|
25823
26285
|
|
|
25824
26286
|
#### Scenario: SC-${n} ${criterion}
|
|
25825
26287
|
|
|
25826
|
-
- GIVEN the
|
|
26288
|
+
- GIVEN the relevant scoped behavior
|
|
25827
26289
|
- WHEN this change is applied
|
|
25828
26290
|
- THEN ${criterion}
|
|
25829
26291
|
`;
|
|
@@ -25837,8 +26299,8 @@ ${blocks.join(`
|
|
|
25837
26299
|
`;
|
|
25838
26300
|
}
|
|
25839
26301
|
async function writeTasksMarkdown(projectRoot, changePath, cards, options = {}) {
|
|
25840
|
-
await
|
|
25841
|
-
await
|
|
26302
|
+
await mkdir22(resolve32(projectRoot, changePath), { recursive: true });
|
|
26303
|
+
await writeFile22(resolve32(projectRoot, changePath, "tasks.md"), tasksContent(cards, options), "utf-8");
|
|
25842
26304
|
}
|
|
25843
26305
|
function tasksContent(cards, options = {}) {
|
|
25844
26306
|
const criteria = options.acceptanceCriteria ?? [];
|
|
@@ -25883,19 +26345,19 @@ function tasksContent(cards, options = {}) {
|
|
|
25883
26345
|
}
|
|
25884
26346
|
async function generateOpenspecChange(projectRoot, item, taskCards, options = {}) {
|
|
25885
26347
|
const slug = buildChangeSlug(item);
|
|
25886
|
-
const changeDir =
|
|
25887
|
-
await
|
|
26348
|
+
const changeDir = resolve32(projectRoot, "openspec", "changes", slug);
|
|
26349
|
+
await mkdir22(resolve32(changeDir, "specs"), { recursive: true });
|
|
25888
26350
|
const taskOpts = {
|
|
25889
26351
|
tddRequired: options.tddRequired,
|
|
25890
26352
|
acceptanceCriteria: options.acceptanceCriteria ?? item.acceptanceCriteria
|
|
25891
26353
|
};
|
|
25892
|
-
await
|
|
25893
|
-
await
|
|
25894
|
-
await
|
|
26354
|
+
await writeFile22(resolve32(changeDir, "proposal.md"), proposalContent(item), "utf-8");
|
|
26355
|
+
await writeFile22(resolve32(changeDir, "design.md"), designContent(item), "utf-8");
|
|
26356
|
+
await writeFile22(resolve32(changeDir, "tasks.md"), tasksContent(taskCards, taskOpts), "utf-8");
|
|
25895
26357
|
const capability = capabilitySlug(item.title);
|
|
25896
|
-
await
|
|
25897
|
-
await
|
|
25898
|
-
await
|
|
26358
|
+
await mkdir22(resolve32(changeDir, "specs", capability), { recursive: true });
|
|
26359
|
+
await writeFile22(resolve32(changeDir, "specs", capability, "spec.md"), specDelta(item), "utf-8");
|
|
26360
|
+
await writeFile22(resolve32(changeDir, "change.yaml"), `version: 1
|
|
25899
26361
|
backlogId: ${item.id}
|
|
25900
26362
|
profile: standard
|
|
25901
26363
|
capabilities:
|
|
@@ -25904,25 +26366,25 @@ capabilities:
|
|
|
25904
26366
|
return `openspec/changes/${slug}`;
|
|
25905
26367
|
}
|
|
25906
26368
|
async function archiveChangeFolder(projectRoot, changePath) {
|
|
25907
|
-
const src =
|
|
26369
|
+
const src = resolve32(projectRoot, changePath);
|
|
25908
26370
|
const slug = basename6(changePath);
|
|
25909
|
-
const destDir =
|
|
25910
|
-
await
|
|
25911
|
-
const dest =
|
|
25912
|
-
await
|
|
26371
|
+
const destDir = resolve32(projectRoot, "openspec", "changes", "archive");
|
|
26372
|
+
await mkdir22(destDir, { recursive: true });
|
|
26373
|
+
const dest = resolve32(destDir, slug);
|
|
26374
|
+
await rename5(src, dest);
|
|
25913
26375
|
return `openspec/changes/archive/${slug}`;
|
|
25914
26376
|
}
|
|
25915
26377
|
async function ensureOpenspecConfig(projectRoot) {
|
|
25916
|
-
const configPath =
|
|
26378
|
+
const configPath = resolve32(projectRoot, "openspec", "config.yaml");
|
|
25917
26379
|
try {
|
|
25918
26380
|
const { access: access7 } = await import("node:fs/promises");
|
|
25919
26381
|
await access7(configPath);
|
|
25920
26382
|
} catch {
|
|
25921
|
-
await
|
|
26383
|
+
await mkdir22(resolve32(projectRoot, "openspec"), { recursive: true });
|
|
25922
26384
|
const stub = `# OpenSpec project config (CodeConductor native compatible)
|
|
25923
26385
|
schema: spec-driven
|
|
25924
26386
|
`;
|
|
25925
|
-
await
|
|
26387
|
+
await writeFile22(configPath, stub, "utf-8");
|
|
25926
26388
|
}
|
|
25927
26389
|
}
|
|
25928
26390
|
var init_openspec_generator = __esm(() => {
|
|
@@ -25930,8 +26392,8 @@ var init_openspec_generator = __esm(() => {
|
|
|
25930
26392
|
});
|
|
25931
26393
|
|
|
25932
26394
|
// src/core/openspec/spec-quality.ts
|
|
25933
|
-
import { readdir as
|
|
25934
|
-
import { join as
|
|
26395
|
+
import { readdir as readdir11, readFile as readFile34 } from "node:fs/promises";
|
|
26396
|
+
import { join as join23, relative as relative8, resolve as resolve33 } from "node:path";
|
|
25935
26397
|
function uniqueIds(matches) {
|
|
25936
26398
|
return [...new Set(matches ?? [])];
|
|
25937
26399
|
}
|
|
@@ -26048,12 +26510,12 @@ async function walkMarkdown(dir) {
|
|
|
26048
26510
|
const out = [];
|
|
26049
26511
|
let entries;
|
|
26050
26512
|
try {
|
|
26051
|
-
entries = await
|
|
26513
|
+
entries = await readdir11(dir, { withFileTypes: true });
|
|
26052
26514
|
} catch {
|
|
26053
26515
|
return out;
|
|
26054
26516
|
}
|
|
26055
26517
|
for (const entry of entries) {
|
|
26056
|
-
const full =
|
|
26518
|
+
const full = join23(dir, entry.name);
|
|
26057
26519
|
if (entry.isDirectory()) {
|
|
26058
26520
|
out.push(...await walkMarkdown(full));
|
|
26059
26521
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -26063,8 +26525,8 @@ async function walkMarkdown(dir) {
|
|
|
26063
26525
|
return out;
|
|
26064
26526
|
}
|
|
26065
26527
|
async function assessChangeFolder(projectRoot, changePath) {
|
|
26066
|
-
const root =
|
|
26067
|
-
const changeRoot =
|
|
26528
|
+
const root = resolve33(projectRoot);
|
|
26529
|
+
const changeRoot = resolve33(root, changePath);
|
|
26068
26530
|
if (!changeRoot.startsWith(root)) {
|
|
26069
26531
|
return {
|
|
26070
26532
|
valid: false,
|
|
@@ -26082,22 +26544,22 @@ async function assessChangeFolder(projectRoot, changePath) {
|
|
|
26082
26544
|
successCriteria: []
|
|
26083
26545
|
};
|
|
26084
26546
|
}
|
|
26085
|
-
const specDir =
|
|
26547
|
+
const specDir = join23(changeRoot, "specs");
|
|
26086
26548
|
const specFiles = await walkMarkdown(specDir);
|
|
26087
|
-
const extra = ["proposal.md", "design.md"].map((name) =>
|
|
26549
|
+
const extra = ["proposal.md", "design.md"].map((name) => join23(changeRoot, name));
|
|
26088
26550
|
const reports = [];
|
|
26089
26551
|
for (const file of specFiles) {
|
|
26090
|
-
const content = await
|
|
26091
|
-
reports.push(assessSpecMarkdown(content,
|
|
26552
|
+
const content = await readFile34(file, "utf-8");
|
|
26553
|
+
reports.push(assessSpecMarkdown(content, relative8(root, file).replace(/\\/g, "/")));
|
|
26092
26554
|
}
|
|
26093
26555
|
for (const file of extra) {
|
|
26094
26556
|
let content;
|
|
26095
26557
|
try {
|
|
26096
|
-
content = await
|
|
26558
|
+
content = await readFile34(file, "utf-8");
|
|
26097
26559
|
} catch {
|
|
26098
26560
|
continue;
|
|
26099
26561
|
}
|
|
26100
|
-
const rel =
|
|
26562
|
+
const rel = relative8(root, file).replace(/\\/g, "/");
|
|
26101
26563
|
if (PLACEHOLDER.test(content)) {
|
|
26102
26564
|
reports.push({
|
|
26103
26565
|
valid: false,
|
|
@@ -26141,15 +26603,15 @@ var init_spec_quality = __esm(() => {
|
|
|
26141
26603
|
FR_ID = /\bFR-\d{3}\b/g;
|
|
26142
26604
|
SC_ID = /\bSC-\d{3}\b/g;
|
|
26143
26605
|
US_AC = /\b(?:US|AC)#?\d+\b/g;
|
|
26144
|
-
GWT = /\bGIVEN\b[\s\S]
|
|
26606
|
+
GWT = /\bGIVEN\b[\s\S]*?\bWHEN\b[\s\S]*?\bTHEN\b/i;
|
|
26145
26607
|
NEEDS_CLARIFICATION = /\[NEEDS CLARIFICATION:[^\]]*\]/gi;
|
|
26146
26608
|
PLACEHOLDER = /\(To be completed/i;
|
|
26147
26609
|
HOW_LEAK = /\b(TypeScript|PostgreSQL|Django|React|Spring Boot|package\.json|implements class)\b/i;
|
|
26148
26610
|
});
|
|
26149
26611
|
|
|
26150
26612
|
// src/core/openspec/spec-analyzer.ts
|
|
26151
|
-
import { readFile as
|
|
26152
|
-
import { join as
|
|
26613
|
+
import { readFile as readFile35, readdir as readdir12 } from "node:fs/promises";
|
|
26614
|
+
import { join as join24, resolve as resolve34 } from "node:path";
|
|
26153
26615
|
function unique(ids) {
|
|
26154
26616
|
return [...new Set(ids)];
|
|
26155
26617
|
}
|
|
@@ -26176,17 +26638,17 @@ function testSection(tasksMarkdown) {
|
|
|
26176
26638
|
async function readDeltaSpecs(specDir) {
|
|
26177
26639
|
let entries;
|
|
26178
26640
|
try {
|
|
26179
|
-
entries = await
|
|
26641
|
+
entries = await readdir12(specDir, { withFileTypes: true });
|
|
26180
26642
|
} catch {
|
|
26181
26643
|
return "";
|
|
26182
26644
|
}
|
|
26183
26645
|
const contents = [];
|
|
26184
26646
|
for (const entry of entries) {
|
|
26185
|
-
const path =
|
|
26647
|
+
const path = join24(specDir, entry.name);
|
|
26186
26648
|
if (entry.isDirectory()) {
|
|
26187
26649
|
contents.push(await readDeltaSpecs(path));
|
|
26188
26650
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
26189
|
-
contents.push(await
|
|
26651
|
+
contents.push(await readFile35(path, "utf-8"));
|
|
26190
26652
|
}
|
|
26191
26653
|
}
|
|
26192
26654
|
return contents.filter(Boolean).join(`
|
|
@@ -26264,17 +26726,17 @@ function analyzeSpecArtifacts(input) {
|
|
|
26264
26726
|
};
|
|
26265
26727
|
}
|
|
26266
26728
|
async function analyzeChangeFolder(projectRoot, changePath, options) {
|
|
26267
|
-
const changeRoot =
|
|
26268
|
-
const specMarkdown = await readDeltaSpecs(
|
|
26729
|
+
const changeRoot = resolve34(projectRoot, changePath);
|
|
26730
|
+
const specMarkdown = await readDeltaSpecs(join24(changeRoot, "specs"));
|
|
26269
26731
|
let tasksMarkdown = "";
|
|
26270
26732
|
try {
|
|
26271
|
-
tasksMarkdown = await
|
|
26733
|
+
tasksMarkdown = await readFile35(join24(changeRoot, "tasks.md"), "utf-8");
|
|
26272
26734
|
} catch {
|
|
26273
26735
|
tasksMarkdown = "";
|
|
26274
26736
|
}
|
|
26275
26737
|
let designMarkdown;
|
|
26276
26738
|
try {
|
|
26277
|
-
designMarkdown = await
|
|
26739
|
+
designMarkdown = await readFile35(join24(changeRoot, "design.md"), "utf-8");
|
|
26278
26740
|
} catch {
|
|
26279
26741
|
designMarkdown = undefined;
|
|
26280
26742
|
}
|
|
@@ -26293,18 +26755,18 @@ var init_spec_analyzer = __esm(() => {
|
|
|
26293
26755
|
});
|
|
26294
26756
|
|
|
26295
26757
|
// src/core/openspec/spec-sync.ts
|
|
26296
|
-
import { mkdir as
|
|
26297
|
-
import { join as
|
|
26758
|
+
import { mkdir as mkdir23, readFile as readFile36, readdir as readdir13 } from "node:fs/promises";
|
|
26759
|
+
import { join as join25, relative as relative9, resolve as resolve35 } from "node:path";
|
|
26298
26760
|
async function markdownFiles(dir) {
|
|
26299
26761
|
let entries;
|
|
26300
26762
|
try {
|
|
26301
|
-
entries = await
|
|
26763
|
+
entries = await readdir13(dir, { withFileTypes: true });
|
|
26302
26764
|
} catch {
|
|
26303
26765
|
return [];
|
|
26304
26766
|
}
|
|
26305
26767
|
const files = [];
|
|
26306
26768
|
for (const entry of entries) {
|
|
26307
|
-
const path =
|
|
26769
|
+
const path = join25(dir, entry.name);
|
|
26308
26770
|
if (entry.isDirectory())
|
|
26309
26771
|
files.push(...await markdownFiles(path));
|
|
26310
26772
|
else if (entry.isFile() && entry.name.endsWith(".md"))
|
|
@@ -26380,9 +26842,9 @@ ${delta.markdown}`;
|
|
|
26380
26842
|
` };
|
|
26381
26843
|
}
|
|
26382
26844
|
async function syncChangeSpecs(projectRoot, changePath) {
|
|
26383
|
-
const root =
|
|
26384
|
-
const changeRoot =
|
|
26385
|
-
const changeSpecs =
|
|
26845
|
+
const root = resolve35(projectRoot);
|
|
26846
|
+
const changeRoot = resolve35(root, changePath);
|
|
26847
|
+
const changeSpecs = join25(changeRoot, "specs");
|
|
26386
26848
|
if (changeRoot !== root && !changeRoot.startsWith(`${root}/`)) {
|
|
26387
26849
|
return { success: false, syncedPaths: [], errors: ["Change path escapes project root"] };
|
|
26388
26850
|
}
|
|
@@ -26392,34 +26854,34 @@ async function syncChangeSpecs(projectRoot, changePath) {
|
|
|
26392
26854
|
}
|
|
26393
26855
|
const writes = [];
|
|
26394
26856
|
for (const file of files) {
|
|
26395
|
-
const delta = await
|
|
26857
|
+
const delta = await readFile36(file, "utf-8");
|
|
26396
26858
|
const requirements = deltaRequirements(delta);
|
|
26397
26859
|
if (requirements.length === 0) {
|
|
26398
26860
|
return {
|
|
26399
26861
|
success: false,
|
|
26400
26862
|
syncedPaths: [],
|
|
26401
|
-
errors: [`${
|
|
26863
|
+
errors: [`${relative9(root, file)}: no delta requirements found`]
|
|
26402
26864
|
};
|
|
26403
26865
|
}
|
|
26404
|
-
const relativeSpec =
|
|
26405
|
-
const target =
|
|
26866
|
+
const relativeSpec = relative9(changeSpecs, file);
|
|
26867
|
+
const target = join25(root, "openspec", "specs", relativeSpec);
|
|
26406
26868
|
let existing = `# ${relativeSpec.replace(/\\/g, "/")}
|
|
26407
26869
|
`;
|
|
26408
26870
|
try {
|
|
26409
|
-
existing = await
|
|
26871
|
+
existing = await readFile36(target, "utf-8");
|
|
26410
26872
|
} catch {}
|
|
26411
|
-
const merged = upsertRequirements(existing, requirements,
|
|
26873
|
+
const merged = upsertRequirements(existing, requirements, relative9(root, file));
|
|
26412
26874
|
if (!merged.ok)
|
|
26413
26875
|
return { success: false, syncedPaths: [], errors: [merged.error] };
|
|
26414
26876
|
writes.push({ path: target, content: merged.content });
|
|
26415
26877
|
}
|
|
26416
26878
|
for (const write of writes) {
|
|
26417
|
-
await
|
|
26879
|
+
await mkdir23(resolve35(write.path, ".."), { recursive: true });
|
|
26418
26880
|
await writeFileAtomic(write.path, write.content);
|
|
26419
26881
|
}
|
|
26420
26882
|
return {
|
|
26421
26883
|
success: true,
|
|
26422
|
-
syncedPaths: writes.map((write) =>
|
|
26884
|
+
syncedPaths: writes.map((write) => relative9(root, write.path).replace(/\\/g, "/")),
|
|
26423
26885
|
errors: []
|
|
26424
26886
|
};
|
|
26425
26887
|
}
|
|
@@ -26435,8 +26897,8 @@ var exports_openspec_command = {};
|
|
|
26435
26897
|
__export(exports_openspec_command, {
|
|
26436
26898
|
openspecCommand: () => openspecCommand
|
|
26437
26899
|
});
|
|
26438
|
-
import { readFile as
|
|
26439
|
-
import { resolve as
|
|
26900
|
+
import { readFile as readFile37 } from "node:fs/promises";
|
|
26901
|
+
import { resolve as resolve36 } from "node:path";
|
|
26440
26902
|
async function openspecCommand(options) {
|
|
26441
26903
|
const { subcommand, itemId, projectRoot, output, reason } = options;
|
|
26442
26904
|
switch (subcommand) {
|
|
@@ -26458,6 +26920,8 @@ async function openspecCommand(options) {
|
|
|
26458
26920
|
return handleDone(projectRoot, itemId);
|
|
26459
26921
|
case "block":
|
|
26460
26922
|
return handleBlock(projectRoot, itemId, reason);
|
|
26923
|
+
case "unblock":
|
|
26924
|
+
return handleUnblock(projectRoot, itemId);
|
|
26461
26925
|
case "archive":
|
|
26462
26926
|
return handleArchive(projectRoot, itemId);
|
|
26463
26927
|
default:
|
|
@@ -26480,7 +26944,7 @@ function fail(command, errors3, extra = {}) {
|
|
|
26480
26944
|
};
|
|
26481
26945
|
}
|
|
26482
26946
|
async function persistBacklog(projectRoot, content) {
|
|
26483
|
-
await writeFileAtomic(
|
|
26947
|
+
await writeFileAtomic(resolve36(projectRoot, BACKLOG_FILENAME), content);
|
|
26484
26948
|
}
|
|
26485
26949
|
async function persistState(projectRoot, state) {
|
|
26486
26950
|
const written = await writeOpenspecState(projectRoot, state);
|
|
@@ -26490,7 +26954,7 @@ async function persistState(projectRoot, state) {
|
|
|
26490
26954
|
return { ok: true };
|
|
26491
26955
|
}
|
|
26492
26956
|
async function readBacklogMarkdown(projectRoot) {
|
|
26493
|
-
return
|
|
26957
|
+
return readFile37(resolve36(projectRoot, BACKLOG_FILENAME), "utf-8");
|
|
26494
26958
|
}
|
|
26495
26959
|
async function transitionItem(projectRoot, item, to, progress) {
|
|
26496
26960
|
const content = await readBacklogMarkdown(projectRoot);
|
|
@@ -26688,17 +27152,18 @@ async function handleAnalyze(projectRoot, itemId) {
|
|
|
26688
27152
|
const policyParts = [];
|
|
26689
27153
|
for (const name of ["AGENTS.md", "BACKLOG.md"]) {
|
|
26690
27154
|
try {
|
|
26691
|
-
policyParts.push(await
|
|
27155
|
+
policyParts.push(await readFile37(resolve36(projectRoot, name), "utf-8"));
|
|
26692
27156
|
} catch {}
|
|
26693
27157
|
}
|
|
26694
27158
|
const cards = itemCards(stateResult.data, targetId);
|
|
26695
27159
|
const tddCards = cards.filter((c) => c.phase === "test" || c.phase === "implement");
|
|
26696
27160
|
let hasTddEvidence;
|
|
26697
27161
|
if (tddCards.length > 0) {
|
|
26698
|
-
hasTddEvidence =
|
|
27162
|
+
hasTddEvidence = true;
|
|
26699
27163
|
for (const card of tddCards) {
|
|
26700
|
-
|
|
26701
|
-
|
|
27164
|
+
const expectedPhase = card.phase === "test" ? "red" : "green";
|
|
27165
|
+
if (!await hasTddRunnerEvidence(projectRoot, card.id, expectedPhase)) {
|
|
27166
|
+
hasTddEvidence = false;
|
|
26702
27167
|
break;
|
|
26703
27168
|
}
|
|
26704
27169
|
}
|
|
@@ -26883,10 +27348,11 @@ async function handleDone(projectRoot, cardId) {
|
|
|
26883
27348
|
const backlog = await loadBacklog(projectRoot);
|
|
26884
27349
|
const tddRequired = backlog.success ? backlog.data.global.tddRequired : false;
|
|
26885
27350
|
if (tddRequired && (card.phase === "test" || card.phase === "implement")) {
|
|
26886
|
-
const
|
|
27351
|
+
const expectedPhase = card.phase === "test" ? "red" : "green";
|
|
27352
|
+
const evidenced = await hasTddRunnerEvidence(projectRoot, cardId, expectedPhase);
|
|
26887
27353
|
if (!evidenced) {
|
|
26888
27354
|
return fail(command, [
|
|
26889
|
-
`Card ${cardId} (${card.phase}) requires verification-runner TDD evidence. Run captureTddSuiteEvidence before openspec done.`
|
|
27355
|
+
`Card ${cardId} (${card.phase}) requires current ${expectedPhase.toUpperCase()} verification-runner TDD evidence. Run captureTddSuiteEvidence before openspec done.`
|
|
26890
27356
|
]);
|
|
26891
27357
|
}
|
|
26892
27358
|
}
|
|
@@ -26961,6 +27427,57 @@ async function handleBlock(projectRoot, cardId, reason) {
|
|
|
26961
27427
|
}
|
|
26962
27428
|
};
|
|
26963
27429
|
}
|
|
27430
|
+
async function handleUnblock(projectRoot, cardId) {
|
|
27431
|
+
const command = "openspec unblock";
|
|
27432
|
+
if (!cardId) {
|
|
27433
|
+
return fail(command, ["Missing card id. Usage: openspec unblock <cardId>"]);
|
|
27434
|
+
}
|
|
27435
|
+
const loaded = await loadStateOrFail(projectRoot, command);
|
|
27436
|
+
if (!loaded.ok)
|
|
27437
|
+
return loaded.response;
|
|
27438
|
+
const card = findCard(loaded.state, cardId);
|
|
27439
|
+
if (!card) {
|
|
27440
|
+
return fail(command, [`Task card ${cardId} not found`]);
|
|
27441
|
+
}
|
|
27442
|
+
if (card.status !== "blocked") {
|
|
27443
|
+
return fail(command, [`Card ${cardId} must be blocked before unblock (got ${card.status})`]);
|
|
27444
|
+
}
|
|
27445
|
+
const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
|
|
27446
|
+
if (!itemLoaded.ok)
|
|
27447
|
+
return itemLoaded.response;
|
|
27448
|
+
if (itemLoaded.item.status !== "BLOCKED") {
|
|
27449
|
+
return fail(command, [
|
|
27450
|
+
`Backlog item ${itemLoaded.item.id} must be BLOCKED before unblock (got ${itemLoaded.item.status})`
|
|
27451
|
+
]);
|
|
27452
|
+
}
|
|
27453
|
+
const state = setTaskCardStatus(loaded.state, cardId, "pending");
|
|
27454
|
+
const written = await persistState(projectRoot, state);
|
|
27455
|
+
if (!written.ok)
|
|
27456
|
+
return fail(command, [written.error]);
|
|
27457
|
+
const changePath = state.changePaths[card.backlogId];
|
|
27458
|
+
if (changePath) {
|
|
27459
|
+
const backlog = await loadBacklog(projectRoot);
|
|
27460
|
+
await writeTasksMarkdown(projectRoot, changePath, itemCards(state, card.backlogId), {
|
|
27461
|
+
tddRequired: backlog.success ? backlog.data.global.tddRequired : false,
|
|
27462
|
+
acceptanceCriteria: itemLoaded.item.acceptanceCriteria
|
|
27463
|
+
});
|
|
27464
|
+
}
|
|
27465
|
+
const transition = await transitionItem(projectRoot, itemLoaded.item, "READY");
|
|
27466
|
+
if (!transition.ok)
|
|
27467
|
+
return fail(command, [transition.error]);
|
|
27468
|
+
return {
|
|
27469
|
+
code: 0,
|
|
27470
|
+
data: {
|
|
27471
|
+
success: true,
|
|
27472
|
+
command,
|
|
27473
|
+
cardId,
|
|
27474
|
+
cardStatus: "pending",
|
|
27475
|
+
itemId: itemLoaded.item.id,
|
|
27476
|
+
itemStatus: "READY",
|
|
27477
|
+
nextStep: `Run openspec plan ${itemLoaded.item.id}.`
|
|
27478
|
+
}
|
|
27479
|
+
};
|
|
27480
|
+
}
|
|
26964
27481
|
async function handleArchive(projectRoot, itemId) {
|
|
26965
27482
|
const command = "openspec archive";
|
|
26966
27483
|
if (!itemId) {
|
|
@@ -27037,7 +27554,7 @@ async function handleArchive(projectRoot, itemId) {
|
|
|
27037
27554
|
}
|
|
27038
27555
|
};
|
|
27039
27556
|
}
|
|
27040
|
-
var KNOWN_SUBCOMMANDS = "validate, scan, plan, analyze, status, next, start, done, block, archive";
|
|
27557
|
+
var KNOWN_SUBCOMMANDS = "validate, scan, plan, analyze, status, next, start, done, block, unblock, archive";
|
|
27041
27558
|
var init_openspec_command = __esm(() => {
|
|
27042
27559
|
init_backlog_parser();
|
|
27043
27560
|
init_backlog_validator();
|
|
@@ -27055,6 +27572,184 @@ var init_openspec_command = __esm(() => {
|
|
|
27055
27572
|
init_loop_engine();
|
|
27056
27573
|
});
|
|
27057
27574
|
|
|
27575
|
+
// src/core/ccep/context-assembly.ts
|
|
27576
|
+
function resumeDecision(changedPaths) {
|
|
27577
|
+
if (changedPaths.length === 0)
|
|
27578
|
+
return { status: "ready" };
|
|
27579
|
+
return {
|
|
27580
|
+
status: "needs_decision",
|
|
27581
|
+
changedPaths,
|
|
27582
|
+
question: `Workspace changed in ${changedPaths.join(", ")}. Continue with the current workspace?`
|
|
27583
|
+
};
|
|
27584
|
+
}
|
|
27585
|
+
var init_context_assembly = () => {};
|
|
27586
|
+
|
|
27587
|
+
// src/core/delivery/delivery-ledger.ts
|
|
27588
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
27589
|
+
import { mkdir as mkdir24, readFile as readFile38, writeFile as writeFile23 } from "node:fs/promises";
|
|
27590
|
+
import { resolve as resolve37, sep as sep6 } from "node:path";
|
|
27591
|
+
function ledgerPath(projectRoot, id) {
|
|
27592
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id))
|
|
27593
|
+
throw new Error("Invalid delivery ledger id");
|
|
27594
|
+
return resolve37(projectRoot, DELIVERY_DIR, `${id}.md`);
|
|
27595
|
+
}
|
|
27596
|
+
async function fingerprintWorkspace(projectRoot, paths) {
|
|
27597
|
+
const root = resolve37(projectRoot);
|
|
27598
|
+
const fingerprints = {};
|
|
27599
|
+
for (const path of paths) {
|
|
27600
|
+
const candidate = resolve37(root, path);
|
|
27601
|
+
if (candidate !== root && !candidate.startsWith(`${root}${sep6}`)) {
|
|
27602
|
+
fingerprints[path] = "outside-root";
|
|
27603
|
+
continue;
|
|
27604
|
+
}
|
|
27605
|
+
try {
|
|
27606
|
+
fingerprints[path] = createHash7("sha256").update(await readFile38(candidate)).digest("hex");
|
|
27607
|
+
} catch {
|
|
27608
|
+
fingerprints[path] = "missing";
|
|
27609
|
+
}
|
|
27610
|
+
}
|
|
27611
|
+
return fingerprints;
|
|
27612
|
+
}
|
|
27613
|
+
function render(ledger) {
|
|
27614
|
+
return `# Delivery Ledger: ${ledger.id}
|
|
27615
|
+
|
|
27616
|
+
${START}
|
|
27617
|
+
\`\`\`yaml
|
|
27618
|
+
${$stringify(ledger)}\`\`\`
|
|
27619
|
+
${END}
|
|
27620
|
+
`;
|
|
27621
|
+
}
|
|
27622
|
+
function parseLedger(content) {
|
|
27623
|
+
const match = content.match(/<!-- delivery-ledger-start -->\n```yaml\n([\s\S]*?)```\n<!-- delivery-ledger-end -->/);
|
|
27624
|
+
if (!match)
|
|
27625
|
+
throw new Error("Invalid delivery ledger format");
|
|
27626
|
+
return DeliveryLedgerSchema.parse($parse(match[1]));
|
|
27627
|
+
}
|
|
27628
|
+
async function loadDeliveryLedger(projectRoot, id) {
|
|
27629
|
+
return parseLedger(await readFile38(ledgerPath(projectRoot, id), "utf-8"));
|
|
27630
|
+
}
|
|
27631
|
+
async function createDeliveryLedger(projectRoot, input) {
|
|
27632
|
+
const request = DeliveryLedgerRequestSchema.parse(input);
|
|
27633
|
+
const path = ledgerPath(projectRoot, request.id);
|
|
27634
|
+
try {
|
|
27635
|
+
return { created: false, ledger: await loadDeliveryLedger(projectRoot, request.id) };
|
|
27636
|
+
} catch (error) {
|
|
27637
|
+
if (!(error instanceof Error) || !error.message.includes("ENOENT"))
|
|
27638
|
+
throw error;
|
|
27639
|
+
}
|
|
27640
|
+
const now = new Date().toISOString();
|
|
27641
|
+
const ledger = DeliveryLedgerSchema.parse({
|
|
27642
|
+
version: 1,
|
|
27643
|
+
id: request.id,
|
|
27644
|
+
taskCard: request.taskCard,
|
|
27645
|
+
tasks: request.tasks,
|
|
27646
|
+
evidence: request.taskCard.evidenceRequired,
|
|
27647
|
+
nextStep: request.nextStep,
|
|
27648
|
+
memoryTopicKey: `delivery:${request.id}`,
|
|
27649
|
+
workspace: await fingerprintWorkspace(projectRoot, request.taskCard.targetFiles),
|
|
27650
|
+
createdAt: now,
|
|
27651
|
+
updatedAt: now
|
|
27652
|
+
});
|
|
27653
|
+
await mkdir24(resolve37(projectRoot, DELIVERY_DIR), { recursive: true });
|
|
27654
|
+
try {
|
|
27655
|
+
await writeFile23(path, render(ledger), { encoding: "utf-8", flag: "wx" });
|
|
27656
|
+
return { created: true, ledger };
|
|
27657
|
+
} catch (error) {
|
|
27658
|
+
if (error instanceof Error && error.message.includes("EEXIST")) {
|
|
27659
|
+
return { created: false, ledger: await loadDeliveryLedger(projectRoot, request.id) };
|
|
27660
|
+
}
|
|
27661
|
+
throw error;
|
|
27662
|
+
}
|
|
27663
|
+
}
|
|
27664
|
+
async function reconcileDeliveryLedger(projectRoot, id) {
|
|
27665
|
+
const ledger = await loadDeliveryLedger(projectRoot, id);
|
|
27666
|
+
const current = await fingerprintWorkspace(projectRoot, Object.keys(ledger.workspace));
|
|
27667
|
+
const changedPaths = Object.keys(ledger.workspace).filter((path) => ledger.workspace[path] !== current[path]);
|
|
27668
|
+
return { ledger, changedPaths };
|
|
27669
|
+
}
|
|
27670
|
+
var DELIVERY_DIR = ".codeconductor/delivery", START = "<!-- delivery-ledger-start -->", END = "<!-- delivery-ledger-end -->";
|
|
27671
|
+
var init_delivery_ledger = __esm(() => {
|
|
27672
|
+
init_dist();
|
|
27673
|
+
init_schemas();
|
|
27674
|
+
});
|
|
27675
|
+
|
|
27676
|
+
// src/commands/odd.command.ts
|
|
27677
|
+
var exports_odd_command = {};
|
|
27678
|
+
__export(exports_odd_command, {
|
|
27679
|
+
oddCommand: () => oddCommand
|
|
27680
|
+
});
|
|
27681
|
+
async function oddCommand(options) {
|
|
27682
|
+
if (options.subcommand === "create") {
|
|
27683
|
+
let input = options.input;
|
|
27684
|
+
if (typeof input === "string") {
|
|
27685
|
+
try {
|
|
27686
|
+
input = JSON.parse(input);
|
|
27687
|
+
} catch {
|
|
27688
|
+
return { code: 1, data: { success: false, errors: ["Invalid JSON input"] } };
|
|
27689
|
+
}
|
|
27690
|
+
}
|
|
27691
|
+
const request = DeliveryLedgerRequestSchema.safeParse(input);
|
|
27692
|
+
if (!request.success)
|
|
27693
|
+
return { code: 1, data: { success: false, errors: request.error.issues } };
|
|
27694
|
+
if (!request.data.authorized || request.data.route !== "tracked") {
|
|
27695
|
+
return { code: 0, data: { success: true, created: false, reason: "route_not_tracked" } };
|
|
27696
|
+
}
|
|
27697
|
+
const issues = validateTaskCardForProfile(loadWorkflowProfile("odd", options.projectRoot), request.data.taskCard);
|
|
27698
|
+
if (issues.length > 0)
|
|
27699
|
+
return { code: 1, data: { success: false, errors: issues } };
|
|
27700
|
+
try {
|
|
27701
|
+
const result = await createDeliveryLedger(options.projectRoot, request.data);
|
|
27702
|
+
return { code: 0, data: { success: true, ...result, ...result.created ? {} : { existing: true } } };
|
|
27703
|
+
} catch (error) {
|
|
27704
|
+
return { code: 1, data: { success: false, errors: [String(error)] } };
|
|
27705
|
+
}
|
|
27706
|
+
}
|
|
27707
|
+
if (!options.id)
|
|
27708
|
+
return { code: 1, data: { success: false, errors: ["Missing ledger id"] } };
|
|
27709
|
+
try {
|
|
27710
|
+
if (options.subcommand === "read") {
|
|
27711
|
+
return { code: 0, data: { success: true, ledger: await loadDeliveryLedger(options.projectRoot, options.id) } };
|
|
27712
|
+
}
|
|
27713
|
+
if (options.subcommand === "reconcile") {
|
|
27714
|
+
const result = await reconcileDeliveryLedger(options.projectRoot, options.id);
|
|
27715
|
+
const memory = await loadMemoryIndex(options.projectRoot);
|
|
27716
|
+
const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
|
|
27717
|
+
const rdd = await rddCommand2({
|
|
27718
|
+
subcommand: "status",
|
|
27719
|
+
projectRoot: options.projectRoot,
|
|
27720
|
+
taskId: result.ledger.taskCard.id
|
|
27721
|
+
});
|
|
27722
|
+
const receipts = rdd.data.receipts ?? [];
|
|
27723
|
+
const staleReceipt = receipts.some((receipt) => receipt.valid === false);
|
|
27724
|
+
const changedPaths = staleReceipt ? [...result.changedPaths, "RDD receipt"].sort() : result.changedPaths;
|
|
27725
|
+
return {
|
|
27726
|
+
code: 0,
|
|
27727
|
+
data: {
|
|
27728
|
+
success: true,
|
|
27729
|
+
status: changedPaths.length === 0 ? "ready" : "conflict",
|
|
27730
|
+
resume: resumeDecision(changedPaths),
|
|
27731
|
+
rdd,
|
|
27732
|
+
memory: memory.success ? "available" : "unavailable",
|
|
27733
|
+
memoryPointers: memory.success ? memory.data.pointers.filter((pointer) => pointer.topic_key === result.ledger.memoryTopicKey) : [],
|
|
27734
|
+
...result,
|
|
27735
|
+
changedPaths
|
|
27736
|
+
}
|
|
27737
|
+
};
|
|
27738
|
+
}
|
|
27739
|
+
return { code: 1, data: { success: false, errors: [`Unknown odd subcommand: ${options.subcommand}`] } };
|
|
27740
|
+
} catch (error) {
|
|
27741
|
+
return { code: 1, data: { success: false, errors: [String(error)] } };
|
|
27742
|
+
}
|
|
27743
|
+
}
|
|
27744
|
+
var init_odd_command = __esm(() => {
|
|
27745
|
+
init_memory_index();
|
|
27746
|
+
init_context_assembly();
|
|
27747
|
+
init_task_card_validator();
|
|
27748
|
+
init_workflow_profile_loader();
|
|
27749
|
+
init_delivery_ledger();
|
|
27750
|
+
init_schemas();
|
|
27751
|
+
});
|
|
27752
|
+
|
|
27058
27753
|
// src/core/hooks/hook-runner.ts
|
|
27059
27754
|
function normalizeCommand(raw) {
|
|
27060
27755
|
return raw.replace(/\s+/g, " ").trim();
|
|
@@ -27206,8 +27901,8 @@ __export(exports_hook_command, {
|
|
|
27206
27901
|
readStdinText: () => readStdinText
|
|
27207
27902
|
});
|
|
27208
27903
|
import { spawnSync } from "node:child_process";
|
|
27209
|
-
import { existsSync as
|
|
27210
|
-
import { resolve as
|
|
27904
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
27905
|
+
import { resolve as resolve38 } from "node:path";
|
|
27211
27906
|
function detectFormat(explicit, stdinText) {
|
|
27212
27907
|
if (explicit)
|
|
27213
27908
|
return explicit;
|
|
@@ -27234,13 +27929,13 @@ function resolvePreToolInput(options, stdinText) {
|
|
|
27234
27929
|
};
|
|
27235
27930
|
}
|
|
27236
27931
|
async function sessionLines(projectRoot) {
|
|
27237
|
-
const backlogPath =
|
|
27238
|
-
const profilePath =
|
|
27932
|
+
const backlogPath = resolve38(projectRoot, "BACKLOG.md");
|
|
27933
|
+
const profilePath = resolve38(projectRoot, ".codeconductor", "evaluation", "execution-profile.yml");
|
|
27239
27934
|
return [
|
|
27240
27935
|
"[CodeConductor] Session start",
|
|
27241
27936
|
"Skills: openspec, backlog, evaluation, testing-tdd — invoke via /cc-* then CLI.",
|
|
27242
|
-
|
|
27243
|
-
|
|
27937
|
+
existsSync8(backlogPath) ? "BACKLOG.md present — run: bun run dev openspec status" : "No BACKLOG.md — author with /cc-backlog if you need an item queue.",
|
|
27938
|
+
existsSync8(profilePath) ? "Scorecard profile on disk — run: bun run dev scorecard models" : "Scorecard: bun run dev scorecard create --task <id> --from-diff",
|
|
27244
27939
|
"Hooks: bun run dev hook pre-tool | post-tool | session-start",
|
|
27245
27940
|
"Eval suites: bun run dev scorecard suite-run --suite workflow-gates"
|
|
27246
27941
|
];
|
|
@@ -27284,7 +27979,7 @@ async function hookCommand(options) {
|
|
|
27284
27979
|
};
|
|
27285
27980
|
}
|
|
27286
27981
|
function formatWrittenFile(filePath) {
|
|
27287
|
-
if (!filePath || !
|
|
27982
|
+
if (!filePath || !existsSync8(filePath))
|
|
27288
27983
|
return;
|
|
27289
27984
|
const prettier = /\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|css|scss|html|astro|ya?ml)$/i;
|
|
27290
27985
|
const eslint = /\.(ts|tsx|js|jsx|mjs|cjs)$/i;
|
|
@@ -27702,6 +28397,12 @@ function ccGainToScorecardImpact(result) {
|
|
|
27702
28397
|
|
|
27703
28398
|
// src/core/evaluation/scorecard-signals.ts
|
|
27704
28399
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
28400
|
+
function parseScopeFiles(scope) {
|
|
28401
|
+
return scope.split(/[,;]/).map((entry) => entry.trim().replace(/[.;:]$/, "").replace(/^`|`$/g, "")).filter(Boolean);
|
|
28402
|
+
}
|
|
28403
|
+
function isGeneratedBuildCache(path) {
|
|
28404
|
+
return /(^|\/)\.gradle\//.test(path);
|
|
28405
|
+
}
|
|
27705
28406
|
function collectScorecardSignals(projectRoot, scopeFiles, extra) {
|
|
27706
28407
|
const findings = [];
|
|
27707
28408
|
const overrides = {};
|
|
@@ -27748,7 +28449,7 @@ function collectScorecardSignals(projectRoot, scopeFiles, extra) {
|
|
|
27748
28449
|
changedFiles.add(m[1]);
|
|
27749
28450
|
}
|
|
27750
28451
|
if (scopeFiles && scopeFiles.length > 0) {
|
|
27751
|
-
const outOfScope = [...changedFiles].filter((f) => !scopeFiles.some((s) => f.includes(s.replace(/\*\*/g, ""))));
|
|
28452
|
+
const outOfScope = [...changedFiles].filter((file) => !isGeneratedBuildCache(file)).filter((f) => !scopeFiles.some((s) => f.includes(s.replace(/\*\*/g, ""))));
|
|
27752
28453
|
if (outOfScope.length > 0) {
|
|
27753
28454
|
overrides.minimal_diff = {
|
|
27754
28455
|
score: 0,
|
|
@@ -27819,25 +28520,25 @@ var init_scorecard_signals = __esm(() => {
|
|
|
27819
28520
|
});
|
|
27820
28521
|
|
|
27821
28522
|
// src/core/evaluation/harness-experiment.ts
|
|
27822
|
-
import { existsSync as
|
|
27823
|
-
import { cp, mkdir as
|
|
27824
|
-
import { dirname as
|
|
28523
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
28524
|
+
import { cp, mkdir as mkdir25, readFile as readFile39, readdir as readdir14, writeFile as writeFile24 } from "node:fs/promises";
|
|
28525
|
+
import { dirname as dirname13, join as join26, resolve as resolve39 } from "node:path";
|
|
27825
28526
|
function experimentDir(projectRoot, experimentId) {
|
|
27826
|
-
return
|
|
28527
|
+
return resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
|
|
27827
28528
|
}
|
|
27828
28529
|
function experimentYamlPath(projectRoot, experimentId) {
|
|
27829
|
-
return
|
|
28530
|
+
return join26(experimentDir(projectRoot, experimentId), "experiment.yml");
|
|
27830
28531
|
}
|
|
27831
28532
|
async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
|
|
27832
28533
|
const candidates = suitePath ? [suitePath] : [
|
|
27833
|
-
|
|
27834
|
-
|
|
28534
|
+
resolve39(projectRoot, "eval", "suites", suiteId, "suite.yml"),
|
|
28535
|
+
resolve39(projectRoot, EVAL_DIR2, "suites", suiteId, "suite.yml")
|
|
27835
28536
|
];
|
|
27836
28537
|
for (const path of candidates) {
|
|
27837
|
-
if (!
|
|
28538
|
+
if (!existsSync9(path))
|
|
27838
28539
|
continue;
|
|
27839
28540
|
try {
|
|
27840
|
-
const suite = HarnessSuiteSchema.parse($parse(await
|
|
28541
|
+
const suite = HarnessSuiteSchema.parse($parse(await readFile39(path, "utf-8")));
|
|
27841
28542
|
return ok({ ...suite, path });
|
|
27842
28543
|
} catch (e) {
|
|
27843
28544
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -27847,7 +28548,7 @@ async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
|
|
|
27847
28548
|
}
|
|
27848
28549
|
async function loadExperiment(projectRoot, experimentId) {
|
|
27849
28550
|
try {
|
|
27850
|
-
const raw = await
|
|
28551
|
+
const raw = await readFile39(experimentYamlPath(projectRoot, experimentId), "utf-8");
|
|
27851
28552
|
return ok(HarnessExperimentSchema.parse($parse(raw)));
|
|
27852
28553
|
} catch (e) {
|
|
27853
28554
|
if (e.code === "ENOENT") {
|
|
@@ -27857,10 +28558,10 @@ async function loadExperiment(projectRoot, experimentId) {
|
|
|
27857
28558
|
}
|
|
27858
28559
|
}
|
|
27859
28560
|
async function listExperiments(projectRoot) {
|
|
27860
|
-
const root =
|
|
27861
|
-
if (!
|
|
28561
|
+
const root = resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR);
|
|
28562
|
+
if (!existsSync9(root))
|
|
27862
28563
|
return [];
|
|
27863
|
-
const ids = await
|
|
28564
|
+
const ids = await readdir14(root);
|
|
27864
28565
|
const experiments = [];
|
|
27865
28566
|
for (const id of ids) {
|
|
27866
28567
|
const loaded = await loadExperiment(projectRoot, id);
|
|
@@ -27936,9 +28637,9 @@ function generateExperimentId(suiteId, now = new Date) {
|
|
|
27936
28637
|
return `abl-${date}-${suiteId}-${rand}`;
|
|
27937
28638
|
}
|
|
27938
28639
|
async function copyFixture(suitePath, fixtureDir, dest) {
|
|
27939
|
-
const source = fixtureDir ?
|
|
27940
|
-
await
|
|
27941
|
-
if (
|
|
28640
|
+
const source = fixtureDir ? resolve39(dirname13(suitePath), fixtureDir) : join26(dirname13(suitePath), "fixture");
|
|
28641
|
+
await mkdir25(dest, { recursive: true });
|
|
28642
|
+
if (existsSync9(source)) {
|
|
27942
28643
|
await cp(source, dest, { recursive: true });
|
|
27943
28644
|
}
|
|
27944
28645
|
}
|
|
@@ -27959,12 +28660,12 @@ async function startHarnessExperiment(projectRoot, options) {
|
|
|
27959
28660
|
suitePath: suite.data.path
|
|
27960
28661
|
};
|
|
27961
28662
|
const root = experimentDir(projectRoot, experimentId);
|
|
27962
|
-
await
|
|
27963
|
-
await
|
|
27964
|
-
await
|
|
28663
|
+
await mkdir25(root, { recursive: true });
|
|
28664
|
+
await writeFile24(experimentYamlPath(projectRoot, experimentId), $stringify(experiment), "utf-8");
|
|
28665
|
+
await writeFile24(join26(root, "EXPERIMENT.md"), formatExperimentMarkdown(experiment), "utf-8");
|
|
27965
28666
|
for (const task of suite.data.tasks) {
|
|
27966
28667
|
for (const variantId of variants) {
|
|
27967
|
-
const runRoot =
|
|
28668
|
+
const runRoot = join26(root, "runs", task.id, variantDirName(variantId));
|
|
27968
28669
|
await copyFixture(suite.data.path, suite.data.fixtureDir, runRoot);
|
|
27969
28670
|
await writeConfig(runRoot, structuredClone(DEFAULT_CONFIG), true);
|
|
27970
28671
|
const disabled = variantId === "baseline" ? [] : options.components.filter((id) => variantIdFor(id) === variantId);
|
|
@@ -27976,7 +28677,7 @@ async function startHarnessExperiment(projectRoot, options) {
|
|
|
27976
28677
|
});
|
|
27977
28678
|
if (!overlay.success)
|
|
27978
28679
|
return overlay;
|
|
27979
|
-
await
|
|
28680
|
+
await writeFile24(join26(runRoot, "TASK.md"), formatTaskCard(task, experimentId, variantId), "utf-8");
|
|
27980
28681
|
}
|
|
27981
28682
|
}
|
|
27982
28683
|
return ok(experiment);
|
|
@@ -28010,8 +28711,8 @@ var init_harness_experiment = __esm(() => {
|
|
|
28010
28711
|
|
|
28011
28712
|
// src/core/evaluation/suite-run.ts
|
|
28012
28713
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
28013
|
-
import { existsSync as
|
|
28014
|
-
import { resolve as
|
|
28714
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
28715
|
+
import { resolve as resolve40 } from "node:path";
|
|
28015
28716
|
async function runHarnessSuiteTasks(projectRoot, suiteId, suitePath) {
|
|
28016
28717
|
const suite = await loadHarnessSuite(projectRoot, suiteId, suitePath);
|
|
28017
28718
|
if (!suite.success) {
|
|
@@ -28071,7 +28772,7 @@ function runTaskCommand(projectRoot, task) {
|
|
|
28071
28772
|
if (parts.length === 0) {
|
|
28072
28773
|
return { ok: false, detail: "Empty testCommand" };
|
|
28073
28774
|
}
|
|
28074
|
-
const bin = parts[0] === "bun" &&
|
|
28775
|
+
const bin = parts[0] === "bun" && existsSync10(resolve40(projectRoot, "src/cli/main.ts")) ? "bun" : parts[0];
|
|
28075
28776
|
const args = parts[0] === "bun" ? parts.slice(1) : parts.slice(1);
|
|
28076
28777
|
const result = spawnSync2(bin, args, {
|
|
28077
28778
|
cwd: projectRoot,
|
|
@@ -28122,11 +28823,11 @@ var init_suite_run = __esm(() => {
|
|
|
28122
28823
|
});
|
|
28123
28824
|
|
|
28124
28825
|
// src/core/evaluation/execution-profile.ts
|
|
28125
|
-
import { readFile as
|
|
28126
|
-
import { resolve as
|
|
28826
|
+
import { readFile as readFile40 } from "node:fs/promises";
|
|
28827
|
+
import { resolve as resolve41 } from "node:path";
|
|
28127
28828
|
async function loadExecutionProfile(projectRoot, defaultTarget = "opencode") {
|
|
28128
28829
|
try {
|
|
28129
|
-
const content = await
|
|
28830
|
+
const content = await readFile40(resolve41(projectRoot, PROFILE_FILE), "utf-8");
|
|
28130
28831
|
const data = $parse(content);
|
|
28131
28832
|
return ExecutionProfileSchema.parse(data);
|
|
28132
28833
|
} catch {
|
|
@@ -28206,14 +28907,14 @@ var init_execution_profile = __esm(() => {
|
|
|
28206
28907
|
});
|
|
28207
28908
|
|
|
28208
28909
|
// src/core/evaluation/prompt-diff.ts
|
|
28209
|
-
import { readFile as
|
|
28210
|
-
import { join as
|
|
28910
|
+
import { readFile as readFile41, readdir as readdir15 } from "node:fs/promises";
|
|
28911
|
+
import { join as join27, resolve as resolve42 } from "node:path";
|
|
28211
28912
|
function normalizeVersion(v) {
|
|
28212
28913
|
return v.startsWith("v") ? v : `v${v}`;
|
|
28213
28914
|
}
|
|
28214
28915
|
async function listPromptFiles(dir) {
|
|
28215
28916
|
try {
|
|
28216
|
-
const entries = await
|
|
28917
|
+
const entries = await readdir15(dir, { withFileTypes: true });
|
|
28217
28918
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name);
|
|
28218
28919
|
} catch {
|
|
28219
28920
|
return [];
|
|
@@ -28256,30 +28957,30 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
|
|
|
28256
28957
|
const fromV = normalizeVersion(fromVersion);
|
|
28257
28958
|
const toV = normalizeVersion(toVersion);
|
|
28258
28959
|
const searchRoots = [
|
|
28259
|
-
|
|
28260
|
-
|
|
28261
|
-
|
|
28960
|
+
resolve42(options.projectRoot ?? PACKAGE_ROOT, ".agents", "prompts"),
|
|
28961
|
+
join27(ROOT_PRESETS_DIR, target, "prompts"),
|
|
28962
|
+
join27(ROOT_PRESETS_DIR, "opencode", "prompts")
|
|
28262
28963
|
];
|
|
28263
28964
|
let fromDir = "";
|
|
28264
28965
|
let toDir = "";
|
|
28265
28966
|
for (const root of searchRoots) {
|
|
28266
|
-
const f =
|
|
28267
|
-
const t =
|
|
28967
|
+
const f = join27(root, fromV);
|
|
28968
|
+
const t = join27(root, toV);
|
|
28268
28969
|
try {
|
|
28269
|
-
await
|
|
28970
|
+
await readFile41(join27(f, "orchestrator.md"), "utf-8");
|
|
28270
28971
|
fromDir = f;
|
|
28271
28972
|
toDir = t;
|
|
28272
28973
|
break;
|
|
28273
28974
|
} catch {}
|
|
28274
28975
|
}
|
|
28275
28976
|
if (!fromDir) {
|
|
28276
|
-
fromDir =
|
|
28277
|
-
toDir =
|
|
28977
|
+
fromDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", fromV);
|
|
28978
|
+
toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
|
|
28278
28979
|
} else {
|
|
28279
28980
|
try {
|
|
28280
|
-
await
|
|
28981
|
+
await readFile41(join27(toDir, "orchestrator.md"), "utf-8");
|
|
28281
28982
|
} catch {
|
|
28282
|
-
toDir =
|
|
28983
|
+
toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
|
|
28283
28984
|
}
|
|
28284
28985
|
}
|
|
28285
28986
|
const fromFiles = await listPromptFiles(fromDir);
|
|
@@ -28297,12 +28998,12 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
|
|
|
28297
28998
|
let oldText = "";
|
|
28298
28999
|
let newText = "";
|
|
28299
29000
|
try {
|
|
28300
|
-
oldText = await
|
|
29001
|
+
oldText = await readFile41(join27(fromDir, name), "utf-8");
|
|
28301
29002
|
} catch {
|
|
28302
29003
|
oldText = "";
|
|
28303
29004
|
}
|
|
28304
29005
|
try {
|
|
28305
|
-
newText = await
|
|
29006
|
+
newText = await readFile41(join27(toDir, name), "utf-8");
|
|
28306
29007
|
} catch {
|
|
28307
29008
|
newText = "";
|
|
28308
29009
|
}
|
|
@@ -28342,12 +29043,12 @@ var init_prompt_diff = __esm(() => {
|
|
|
28342
29043
|
|
|
28343
29044
|
// src/core/evaluation/regression-checklist.ts
|
|
28344
29045
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
28345
|
-
import { mkdir as
|
|
28346
|
-
import { dirname as
|
|
29046
|
+
import { mkdir as mkdir26, readFile as readFile42, writeFile as writeFile25 } from "node:fs/promises";
|
|
29047
|
+
import { dirname as dirname14, resolve as resolve43 } from "node:path";
|
|
28347
29048
|
async function loadRegressionChecklist() {
|
|
28348
29049
|
try {
|
|
28349
|
-
const path =
|
|
28350
|
-
const content = await
|
|
29050
|
+
const path = resolve43(ROOT_PRESETS_DIR, "templates", "regression-checklist.yml");
|
|
29051
|
+
const content = await readFile42(path, "utf-8");
|
|
28351
29052
|
const data = $parse(content);
|
|
28352
29053
|
return data.checks ?? [];
|
|
28353
29054
|
} catch {
|
|
@@ -28402,9 +29103,9 @@ async function runRegressionChecklist(projectRoot, options = {}) {
|
|
|
28402
29103
|
checks,
|
|
28403
29104
|
timestamp: new Date().toISOString()
|
|
28404
29105
|
};
|
|
28405
|
-
const outPath =
|
|
28406
|
-
await
|
|
28407
|
-
await
|
|
29106
|
+
const outPath = resolve43(projectRoot, ".codeconductor/evaluation/regression-last.json");
|
|
29107
|
+
await mkdir26(dirname14(outPath), { recursive: true });
|
|
29108
|
+
await writeFile25(outPath, JSON.stringify(report, null, 2), "utf-8");
|
|
28408
29109
|
return ok(report);
|
|
28409
29110
|
} catch (e) {
|
|
28410
29111
|
return err(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -28446,8 +29147,8 @@ var init_model_comparison = __esm(() => {
|
|
|
28446
29147
|
});
|
|
28447
29148
|
|
|
28448
29149
|
// src/core/evaluation/cost-quality-matrix.ts
|
|
28449
|
-
import { writeFile as
|
|
28450
|
-
import { resolve as
|
|
29150
|
+
import { writeFile as writeFile26 } from "node:fs/promises";
|
|
29151
|
+
import { resolve as resolve44 } from "node:path";
|
|
28451
29152
|
async function buildCostQualityMatrix(projectRoot, outcomes) {
|
|
28452
29153
|
const agg = aggregateOutcomes(outcomes);
|
|
28453
29154
|
const rows = Object.entries(agg.byModel).map(([model, entry]) => {
|
|
@@ -28467,7 +29168,7 @@ async function buildCostQualityMatrix(projectRoot, outcomes) {
|
|
|
28467
29168
|
rows
|
|
28468
29169
|
};
|
|
28469
29170
|
const dir = await ensureEvaluationDir(projectRoot);
|
|
28470
|
-
await
|
|
29171
|
+
await writeFile26(resolve44(dir, "matrix.yml"), $stringify(matrix), "utf-8");
|
|
28471
29172
|
return matrix;
|
|
28472
29173
|
}
|
|
28473
29174
|
function formatMatrixMarkdown(matrix) {
|
|
@@ -28491,8 +29192,8 @@ var init_cost_quality_matrix = __esm(() => {
|
|
|
28491
29192
|
});
|
|
28492
29193
|
|
|
28493
29194
|
// src/core/evaluation/ablation-report.ts
|
|
28494
|
-
import { mkdir as
|
|
28495
|
-
import { resolve as
|
|
29195
|
+
import { mkdir as mkdir27, writeFile as writeFile27 } from "node:fs/promises";
|
|
29196
|
+
import { resolve as resolve45 } from "node:path";
|
|
28496
29197
|
function isPass(o) {
|
|
28497
29198
|
return o.verdict === "PASS" || o.status === "pass";
|
|
28498
29199
|
}
|
|
@@ -28660,10 +29361,10 @@ function formatAblationMarkdown(report) {
|
|
|
28660
29361
|
`);
|
|
28661
29362
|
}
|
|
28662
29363
|
async function persistAblationReport(projectRoot, markdown) {
|
|
28663
|
-
const dir =
|
|
28664
|
-
await
|
|
28665
|
-
const path =
|
|
28666
|
-
await
|
|
29364
|
+
const dir = resolve45(projectRoot, EVAL_DIR2);
|
|
29365
|
+
await mkdir27(dir, { recursive: true });
|
|
29366
|
+
const path = resolve45(dir, "ablation-last.md");
|
|
29367
|
+
await writeFile27(path, markdown, "utf-8");
|
|
28667
29368
|
return path;
|
|
28668
29369
|
}
|
|
28669
29370
|
var SCORE_THRESHOLD = 0.1, PASS_RATE_THRESHOLD = 0.05;
|
|
@@ -28677,8 +29378,8 @@ var exports_scorecard_command = {};
|
|
|
28677
29378
|
__export(exports_scorecard_command, {
|
|
28678
29379
|
scorecardCommand: () => scorecardCommand
|
|
28679
29380
|
});
|
|
28680
|
-
import { readFile as
|
|
28681
|
-
import { resolve as
|
|
29381
|
+
import { readFile as readFile43 } from "node:fs/promises";
|
|
29382
|
+
import { resolve as resolve46 } from "node:path";
|
|
28682
29383
|
function contractVersion() {
|
|
28683
29384
|
return package_default.version;
|
|
28684
29385
|
}
|
|
@@ -28739,7 +29440,7 @@ async function handleCreate(projectRoot, options) {
|
|
|
28739
29440
|
if (backlog.success && options.taskId) {
|
|
28740
29441
|
const item = backlog.data.items.find((i) => i.id === options.taskId);
|
|
28741
29442
|
if (item) {
|
|
28742
|
-
scopeFiles = item.scope
|
|
29443
|
+
scopeFiles = parseScopeFiles(item.scope);
|
|
28743
29444
|
}
|
|
28744
29445
|
}
|
|
28745
29446
|
let hints = collectScorecardSignals(projectRoot, scopeFiles);
|
|
@@ -28823,7 +29524,7 @@ async function handleRecord(projectRoot, options) {
|
|
|
28823
29524
|
const variantId = options.variantId ?? overlay?.variantId;
|
|
28824
29525
|
const suiteTaskId = options.suiteTaskId;
|
|
28825
29526
|
const disabled = overlay?.disabledComponents ?? (variantId ? parseVariantId(variantId) : undefined);
|
|
28826
|
-
const
|
|
29527
|
+
const fingerprint2 = experimentId || variantId ? harnessFingerprint(disabled ?? [], overlay?.contractVersion ?? contractVersion(), overlay?.ccepProfile ?? "feature") : undefined;
|
|
28827
29528
|
const outcome = {
|
|
28828
29529
|
id: generateEvalId("out"),
|
|
28829
29530
|
taskId,
|
|
@@ -28838,10 +29539,17 @@ async function handleRecord(projectRoot, options) {
|
|
|
28838
29539
|
costUsd: options.costUsd,
|
|
28839
29540
|
tokensIn: options.tokens,
|
|
28840
29541
|
durationMs: options.durationMs,
|
|
29542
|
+
delivery: options.deliveryRoute ? {
|
|
29543
|
+
route: options.deliveryRoute,
|
|
29544
|
+
contextBytes: options.contextBytes,
|
|
29545
|
+
handoffs: options.handoffs,
|
|
29546
|
+
checks: options.checks,
|
|
29547
|
+
tokenUsage: options.tokens !== undefined ? { status: "known", total: options.tokens } : { status: "unknown" }
|
|
29548
|
+
} : undefined,
|
|
28841
29549
|
experimentId,
|
|
28842
29550
|
variantId,
|
|
28843
29551
|
suiteTaskId,
|
|
28844
|
-
harnessFingerprint:
|
|
29552
|
+
harnessFingerprint: fingerprint2,
|
|
28845
29553
|
disabledComponents: disabled && disabled.length > 0 ? [...disabled] : disabled ? [] : undefined,
|
|
28846
29554
|
status: options.verdict === "PASS" ? "pass" : options.verdict === "REJECT" ? "reject" : undefined
|
|
28847
29555
|
};
|
|
@@ -28959,13 +29667,13 @@ async function handleCompareModels(projectRoot, options) {
|
|
|
28959
29667
|
outcomes = outcomes.filter((o) => options.modelsFilter.includes(o.model));
|
|
28960
29668
|
}
|
|
28961
29669
|
const markdown = generateModelComparisonMarkdown(outcomes, options.modelsFilter);
|
|
28962
|
-
const templatePath =
|
|
29670
|
+
const templatePath = resolve46(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
|
|
28963
29671
|
try {
|
|
28964
|
-
await
|
|
29672
|
+
await readFile43(templatePath);
|
|
28965
29673
|
} catch {}
|
|
28966
|
-
const { writeFile:
|
|
28967
|
-
await
|
|
28968
|
-
await
|
|
29674
|
+
const { writeFile: writeFile28, mkdir: mkdir28 } = await import("node:fs/promises");
|
|
29675
|
+
await mkdir28(resolve46(projectRoot, ".codeconductor/evaluation"), { recursive: true });
|
|
29676
|
+
await writeFile28(templatePath, markdown, "utf-8");
|
|
28969
29677
|
return {
|
|
28970
29678
|
code: 0,
|
|
28971
29679
|
data: {
|
|
@@ -30434,7 +31142,7 @@ var exports_seo_audit_command = {};
|
|
|
30434
31142
|
__export(exports_seo_audit_command, {
|
|
30435
31143
|
seoAuditCommand: () => seoAuditCommand
|
|
30436
31144
|
});
|
|
30437
|
-
import { join as
|
|
31145
|
+
import { join as join28 } from "node:path";
|
|
30438
31146
|
async function seoAuditCommand(options) {
|
|
30439
31147
|
const { url, sitemap, format, failOn, delay: delay2, output, followRedirects, force } = options;
|
|
30440
31148
|
if (!url && !sitemap) {
|
|
@@ -30447,7 +31155,7 @@ async function seoAuditCommand(options) {
|
|
|
30447
31155
|
}
|
|
30448
31156
|
};
|
|
30449
31157
|
}
|
|
30450
|
-
const defaultMarkdownPath = output === undefined && format === "markdown" ?
|
|
31158
|
+
const defaultMarkdownPath = output === undefined && format === "markdown" ? join28("seo-reports", `audit-report-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19)}.md`) : undefined;
|
|
30451
31159
|
const outputPathToWrite = output ?? defaultMarkdownPath;
|
|
30452
31160
|
if (outputPathToWrite !== undefined) {
|
|
30453
31161
|
const preflightError = await preflightContainedOutput(options.projectRoot, outputPathToWrite, { force });
|
|
@@ -30832,6 +31540,7 @@ var COMMANDS = [
|
|
|
30832
31540
|
{ name: "orchestrate", group: "workflow", summary: "Run goal execution orchestration.", usage: ["cc-codeconductor orchestrate <subcommand>"] },
|
|
30833
31541
|
{ name: "impact", group: "workflow", summary: "Analyze change impact.", usage: ["cc-codeconductor impact [--files <paths>]"] },
|
|
30834
31542
|
{ name: "verify", group: "workflow", summary: "Verify task completion with evidence.", usage: ["cc-codeconductor verify --task <id>"] },
|
|
31543
|
+
{ name: "rdd", group: "workflow", summary: "Capture and validate Receipt-Driven Development evidence.", usage: ["cc-codeconductor rdd capture --task <id> [--phase red|green|review]", "cc-codeconductor rdd verify --receipt <id>", "cc-codeconductor rdd status [--task <id>]", "cc-codeconductor rdd git-check", "cc-codeconductor rdd install-hooks"] },
|
|
30835
31544
|
{ name: "seo", group: "tool", summary: "Audit SEO and generate llms.txt.", usage: ["cc-codeconductor seo audit --url <url>"] },
|
|
30836
31545
|
{ name: "debt-harvest", aliases: ["harvest"], group: "tool", summary: "Scan source files for deferred debt.", usage: ["cc-codeconductor debt-harvest"] }
|
|
30837
31546
|
];
|
|
@@ -31310,6 +32019,24 @@ async function routeCommand(args, projectRoot) {
|
|
|
31310
32019
|
allowCompileCheck: options["allow-compile-check"] === true || options["allow-compile-check"] === "true"
|
|
31311
32020
|
});
|
|
31312
32021
|
}
|
|
32022
|
+
case "rdd": {
|
|
32023
|
+
const validSubs = ["capture", "verify", "status", "git-check", "install-hooks"];
|
|
32024
|
+
if (!subcommand || !validSubs.includes(subcommand)) {
|
|
32025
|
+
return unknownSubcommand("rdd", subcommand ?? "", [...validSubs]);
|
|
32026
|
+
}
|
|
32027
|
+
const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
|
|
32028
|
+
const paths = typeof options.paths === "string" ? options.paths.split(",").map((path) => path.trim()).filter(Boolean) : undefined;
|
|
32029
|
+
return rddCommand2({
|
|
32030
|
+
subcommand,
|
|
32031
|
+
projectRoot,
|
|
32032
|
+
taskId: options.task ?? args.rest?.[0],
|
|
32033
|
+
receiptId: options.receipt,
|
|
32034
|
+
phase: options.phase,
|
|
32035
|
+
paths,
|
|
32036
|
+
outcome: options.outcome,
|
|
32037
|
+
command: options.command
|
|
32038
|
+
});
|
|
32039
|
+
}
|
|
31313
32040
|
case "ccep": {
|
|
31314
32041
|
const validSubs = ["parse", "profile", "resolve", "compile", "validate", "evaluate", "consensus", "taskcard"];
|
|
31315
32042
|
if (subcommand && !validSubs.includes(subcommand)) {
|
|
@@ -31348,6 +32075,7 @@ async function routeCommand(args, projectRoot) {
|
|
|
31348
32075
|
"start",
|
|
31349
32076
|
"done",
|
|
31350
32077
|
"block",
|
|
32078
|
+
"unblock",
|
|
31351
32079
|
"archive"
|
|
31352
32080
|
];
|
|
31353
32081
|
let openspecSub = "validate";
|
|
@@ -31370,6 +32098,19 @@ async function routeCommand(args, projectRoot) {
|
|
|
31370
32098
|
output: flags.output
|
|
31371
32099
|
});
|
|
31372
32100
|
}
|
|
32101
|
+
case "odd": {
|
|
32102
|
+
const validSubs = ["create", "read", "reconcile"];
|
|
32103
|
+
if (!subcommand || !validSubs.includes(subcommand)) {
|
|
32104
|
+
return unknownSubcommand(command, subcommand ?? "", validSubs);
|
|
32105
|
+
}
|
|
32106
|
+
const { oddCommand: oddCommand2 } = await Promise.resolve().then(() => (init_odd_command(), exports_odd_command));
|
|
32107
|
+
return oddCommand2({
|
|
32108
|
+
subcommand,
|
|
32109
|
+
projectRoot,
|
|
32110
|
+
input: options.input,
|
|
32111
|
+
id: options.id ?? args.rest?.[0]
|
|
32112
|
+
});
|
|
32113
|
+
}
|
|
31373
32114
|
case "hook": {
|
|
31374
32115
|
const validHookEvents = ["pre-tool", "post-tool", "session-start"];
|
|
31375
32116
|
if (subcommand && !validHookEvents.includes(subcommand)) {
|
|
@@ -31433,6 +32174,10 @@ async function routeCommand(args, projectRoot) {
|
|
|
31433
32174
|
costUsd: options.cost ? parseFloat(String(options.cost)) : undefined,
|
|
31434
32175
|
tokens: options.tokens ? parseInt(String(options.tokens), 10) : undefined,
|
|
31435
32176
|
durationMs: options.duration ? parseInt(String(options.duration), 10) : undefined,
|
|
32177
|
+
deliveryRoute: options.route,
|
|
32178
|
+
contextBytes: options["context-bytes"] ? parseInt(String(options["context-bytes"]), 10) : undefined,
|
|
32179
|
+
handoffs: options.handoffs ? parseInt(String(options.handoffs), 10) : undefined,
|
|
32180
|
+
checks: typeof options.checks === "string" ? options.checks.split(",").map((check) => check.trim()).filter(Boolean) : undefined,
|
|
31436
32181
|
verdict: options.verdict,
|
|
31437
32182
|
weightedScore: options.score ? parseFloat(String(options.score)) : undefined,
|
|
31438
32183
|
source: options.source,
|