cc-codeconductor 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/README.md +61 -13
  2. package/dist/core/verification/rdd-receipt.d.ts +39 -0
  3. package/dist/core/verification/verification-runner.d.ts +8 -2
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +1257 -330
  6. package/dist/library.js +267 -26
  7. package/dist/validation/schemas.d.ts +521 -26
  8. package/docs/generated/cli.md +20 -0
  9. package/package.json +1 -1
  10. package/presets/agy/AGENTS.md +6 -0
  11. package/presets/agy/README.md +1 -1
  12. package/presets/agy/hooks.json +1 -1
  13. package/presets/agy/scripts/invoke-hook.cjs +20 -5
  14. package/presets/agy/settings.json +1 -1
  15. package/presets/agy/skills/api-versioning/SKILL.md +394 -0
  16. package/presets/agy/skills/astro/SKILL.md +318 -0
  17. package/presets/agy/skills/auth-token-inspector/SKILL.md +30 -0
  18. package/presets/agy/skills/cc-pagespeed/SKILL.md +2 -3
  19. package/presets/agy/skills/code-review/SKILL.md +207 -0
  20. package/presets/agy/skills/django-orm/SKILL.md +460 -0
  21. package/presets/agy/skills/django-uv/SKILL.md +405 -0
  22. package/presets/agy/skills/drizzle-schema-architect/SKILL.md +50 -0
  23. package/presets/agy/skills/fastapi-pydantic-strict/SKILL.md +43 -0
  24. package/presets/agy/skills/jpa-nplusone-detector/SKILL.md +45 -0
  25. package/presets/agy/skills/jpa-postgres/SKILL.md +623 -0
  26. package/presets/agy/skills/livewire-alpine-bridge/SKILL.md +35 -0
  27. package/presets/agy/skills/nextjs-typescript/SKILL.md +390 -0
  28. package/presets/agy/skills/python/SKILL.md +611 -0
  29. package/presets/agy/skills/seo-analytics-injector/SKILL.md +43 -0
  30. package/presets/agy/skills/spring-auth-auditor/SKILL.md +29 -0
  31. package/presets/agy/skills/spring-boot-feature/SKILL.md +563 -0
  32. package/presets/agy/skills/spring-boot-testing-strategy/SKILL.md +475 -0
  33. package/presets/agy/skills/tailwind-responsive-auditor/SKILL.md +29 -0
  34. package/presets/agy/skills/tdd-mutation-tester/SKILL.md +27 -0
  35. package/presets/agy/workflows/cc-handoff.md +2 -1
  36. package/presets/agy/workflows/cc-odd.md +15 -0
  37. package/presets/agy/workflows/cc-pagespeed.md +2 -3
  38. package/presets/agy/workflows/cc-review.md +31 -0
  39. package/presets/agy/workflows/cc-security.md +1 -1
  40. package/presets/claude/CLAUDE.md +6 -0
  41. package/presets/claude/commands/cc/handoff.md +2 -1
  42. package/presets/claude/commands/cc/odd.md +15 -0
  43. package/presets/claude/commands/cc/review.md +33 -0
  44. package/presets/claude/settings.json +2 -2
  45. package/presets/claude/skills/android/SKILL.md +1 -1
  46. package/presets/claude/skills/api-versioning/SKILL.md +1 -1
  47. package/presets/claude/skills/astro/SKILL.md +318 -0
  48. package/presets/claude/skills/auth-token-inspector/SKILL.md +30 -0
  49. package/presets/claude/skills/code-review/SKILL.md +207 -0
  50. package/presets/claude/skills/django-orm/SKILL.md +1 -1
  51. package/presets/claude/skills/django-testing/SKILL.md +1 -1
  52. package/presets/claude/skills/django-uv/SKILL.md +405 -0
  53. package/presets/claude/skills/drizzle-schema-architect/SKILL.md +50 -0
  54. package/presets/claude/skills/fastapi-pydantic-strict/SKILL.md +43 -0
  55. package/presets/claude/skills/jpa-nplusone-detector/SKILL.md +45 -0
  56. package/presets/claude/skills/jpa-postgres/SKILL.md +1 -1
  57. package/presets/claude/skills/livewire-alpine-bridge/SKILL.md +35 -0
  58. package/presets/claude/skills/nextjs-typescript/SKILL.md +390 -0
  59. package/presets/claude/skills/pagespeed-perf/SKILL.md +1 -1
  60. package/presets/claude/skills/python/SKILL.md +1 -1
  61. package/presets/claude/skills/python-django-stack/SKILL.md +1 -1
  62. package/presets/claude/skills/python-fastapi-stack/SKILL.md +1 -1
  63. package/presets/claude/skills/security/SKILL.md +1 -1
  64. package/presets/claude/skills/seo-analytics-injector/SKILL.md +43 -0
  65. package/presets/claude/skills/spring-auth-auditor/SKILL.md +29 -0
  66. package/presets/claude/skills/spring-boot-feature/SKILL.md +1 -1
  67. package/presets/claude/skills/spring-boot-kotlin/SKILL.md +1 -1
  68. package/presets/claude/skills/spring-boot-testing-strategy/SKILL.md +475 -0
  69. package/presets/claude/skills/sqlalchemy/SKILL.md +1 -1
  70. package/presets/claude/skills/tailwind-responsive-auditor/SKILL.md +29 -0
  71. package/presets/claude/skills/tdd-mutation-tester/SKILL.md +27 -0
  72. package/presets/claude/skills/testing-strategy/SKILL.md +1 -1
  73. package/presets/codex/AGENTS.md +6 -0
  74. package/presets/codex/skills/android/SKILL.md +1 -1
  75. package/presets/codex/skills/api-versioning/SKILL.md +1 -1
  76. package/presets/codex/skills/astro/SKILL.md +318 -0
  77. package/presets/codex/skills/auth-token-inspector/SKILL.md +30 -0
  78. package/presets/codex/skills/cc-handoff/SKILL.md +3 -1
  79. package/presets/codex/skills/cc-odd/SKILL.md +25 -0
  80. package/presets/codex/skills/cc-openspec/SKILL.md +5 -1
  81. package/presets/codex/skills/cc-pagespeed/SKILL.md +2 -3
  82. package/presets/codex/skills/cc-review/SKILL.md +31 -0
  83. package/presets/codex/skills/cc-security/SKILL.md +1 -1
  84. package/presets/codex/skills/cc-spec-mutation/SKILL.md +5 -0
  85. package/presets/codex/skills/cc-tdd-cycle/SKILL.md +8 -0
  86. package/presets/codex/skills/code-review/SKILL.md +207 -0
  87. package/presets/codex/skills/django-orm/SKILL.md +1 -1
  88. package/presets/codex/skills/django-testing/SKILL.md +1 -1
  89. package/presets/codex/skills/django-uv/SKILL.md +405 -0
  90. package/presets/codex/skills/drizzle-schema-architect/SKILL.md +50 -0
  91. package/presets/codex/skills/fastapi-pydantic-strict/SKILL.md +43 -0
  92. package/presets/codex/skills/jpa-nplusone-detector/SKILL.md +45 -0
  93. package/presets/codex/skills/jpa-postgres/SKILL.md +1 -1
  94. package/presets/codex/skills/livewire-alpine-bridge/SKILL.md +35 -0
  95. package/presets/codex/skills/nextjs-typescript/SKILL.md +390 -0
  96. package/presets/codex/skills/pagespeed-perf/SKILL.md +1 -1
  97. package/presets/codex/skills/python/SKILL.md +1 -1
  98. package/presets/codex/skills/python-django-stack/SKILL.md +1 -1
  99. package/presets/codex/skills/python-fastapi-stack/SKILL.md +1 -1
  100. package/presets/codex/skills/security-ai-llm/SKILL.md +43 -0
  101. package/presets/codex/skills/security-blue-team/SKILL.md +43 -0
  102. package/presets/codex/skills/security-cloud/SKILL.md +43 -0
  103. package/presets/codex/skills/security-crypto/SKILL.md +43 -0
  104. package/presets/codex/skills/security-exploit-dev/SKILL.md +45 -0
  105. package/presets/codex/skills/security-grc/SKILL.md +43 -0
  106. package/presets/codex/skills/security-incident-response/SKILL.md +45 -0
  107. package/presets/codex/skills/security-log-analysis/SKILL.md +43 -0
  108. package/presets/codex/skills/security-malware-analysis/SKILL.md +44 -0
  109. package/presets/codex/skills/security-mobile/SKILL.md +43 -0
  110. package/presets/codex/skills/security-network/SKILL.md +43 -0
  111. package/presets/codex/skills/security-ot-ics/SKILL.md +43 -0
  112. package/presets/codex/skills/security-recon/SKILL.md +45 -0
  113. package/presets/codex/skills/security-red-team/SKILL.md +44 -0
  114. package/presets/codex/skills/security-reverse-engineering/SKILL.md +44 -0
  115. package/presets/codex/skills/security-soc-automation/SKILL.md +43 -0
  116. package/presets/codex/skills/security-threat-hunting/SKILL.md +43 -0
  117. package/presets/codex/skills/security-vuln-assessment/SKILL.md +45 -0
  118. package/presets/codex/skills/security-web/SKILL.md +44 -0
  119. package/presets/codex/skills/seo-analytics-injector/SKILL.md +43 -0
  120. package/presets/codex/skills/spring-auth-auditor/SKILL.md +29 -0
  121. package/presets/codex/skills/spring-boot-feature/SKILL.md +2 -2
  122. package/presets/codex/skills/spring-boot-kotlin/SKILL.md +1 -1
  123. package/presets/codex/skills/spring-boot-testing-strategy/SKILL.md +475 -0
  124. package/presets/codex/skills/sqlalchemy/SKILL.md +1 -1
  125. package/presets/codex/skills/tailwind-responsive-auditor/SKILL.md +29 -0
  126. package/presets/codex/skills/tdd-mutation-tester/SKILL.md +27 -0
  127. package/presets/codex/skills/testing-strategy/SKILL.md +1 -1
  128. package/presets/cursor/AGENTS.md +6 -0
  129. package/presets/cursor/commands/cc/handoff.md +3 -1
  130. package/presets/cursor/commands/cc/odd.md +20 -0
  131. package/presets/cursor/commands/cc/openspec.md +5 -1
  132. package/presets/cursor/commands/cc/pagespeed.md +2 -3
  133. package/presets/cursor/commands/cc/review.md +31 -0
  134. package/presets/cursor/commands/cc/security.md +1 -1
  135. package/presets/cursor/commands/cc/spec-mutation.md +5 -0
  136. package/presets/cursor/commands/cc/tdd-cycle.md +8 -0
  137. package/presets/cursor/skills/android/SKILL.md +1 -1
  138. package/presets/cursor/skills/api-versioning/SKILL.md +2 -1
  139. package/presets/cursor/skills/astro/SKILL.md +1 -1
  140. package/presets/cursor/skills/auth-token-inspector/SKILL.md +1 -1
  141. package/presets/cursor/skills/code-review/SKILL.md +1 -1
  142. package/presets/cursor/skills/django-orm/SKILL.md +3 -5
  143. package/presets/cursor/skills/django-testing/SKILL.md +1 -1
  144. package/presets/cursor/skills/django-uv/SKILL.md +1 -1
  145. package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +1 -1
  146. package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +1 -1
  147. package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +1 -1
  148. package/presets/cursor/skills/jpa-postgres/SKILL.md +2 -4
  149. package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +1 -1
  150. package/presets/cursor/skills/nextjs-typescript/SKILL.md +1 -1
  151. package/presets/cursor/skills/pagespeed-perf/SKILL.md +1 -1
  152. package/presets/cursor/skills/python/SKILL.md +6 -7
  153. package/presets/cursor/skills/python-django-stack/SKILL.md +1 -1
  154. package/presets/cursor/skills/python-fastapi-stack/SKILL.md +1 -1
  155. package/presets/cursor/skills/security/SKILL.md +1 -1
  156. package/presets/cursor/skills/seo-analytics-injector/SKILL.md +1 -1
  157. package/presets/cursor/skills/spring-auth-auditor/SKILL.md +1 -1
  158. package/presets/cursor/skills/spring-boot-feature/SKILL.md +2 -4
  159. package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +1 -1
  160. package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +1 -1
  161. package/presets/cursor/skills/sqlalchemy/SKILL.md +1 -1
  162. package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +1 -1
  163. package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +1 -1
  164. package/presets/gemini/GEMINI.md +6 -0
  165. package/presets/gemini/commands/cc/handoff.toml +3 -1
  166. package/presets/gemini/commands/cc/odd.toml +20 -0
  167. package/presets/gemini/commands/cc/openspec.toml +5 -1
  168. package/presets/gemini/commands/cc/pagespeed.toml +2 -3
  169. package/presets/gemini/commands/cc/review.toml +31 -0
  170. package/presets/gemini/commands/cc/security.toml +1 -1
  171. package/presets/gemini/commands/cc/spec-mutation.toml +5 -0
  172. package/presets/gemini/commands/cc/tdd-cycle.toml +8 -0
  173. package/presets/opencode/README.md +45 -52
  174. package/presets/opencode/agents/implementer.md +2 -0
  175. package/presets/opencode/agents/reviewer.md +2 -0
  176. package/presets/opencode/agents/tester.md +2 -0
  177. package/presets/opencode/commands/cc-handoff.md +2 -1
  178. package/presets/opencode/commands/cc-odd.md +15 -0
  179. package/presets/opencode/commands/cc-pagespeed.md +2 -3
  180. package/presets/opencode/commands/cc-review.md +31 -0
  181. package/presets/opencode/commands/cc-security.md +1 -1
  182. package/presets/opencode/opencode.jsonc +1 -1
  183. package/presets/opencode/skills/android/SKILL.md +1 -1
  184. package/presets/opencode/skills/api-versioning/SKILL.md +2 -1
  185. package/presets/opencode/skills/astro/SKILL.md +1 -1
  186. package/presets/opencode/skills/auth-token-inspector/SKILL.md +1 -1
  187. package/presets/opencode/skills/code-review/SKILL.md +1 -1
  188. package/presets/opencode/skills/django-orm/SKILL.md +3 -3
  189. package/presets/opencode/skills/django-testing/SKILL.md +1 -1
  190. package/presets/opencode/skills/django-uv/SKILL.md +1 -1
  191. package/presets/opencode/skills/drizzle-schema-architect/SKILL.md +1 -1
  192. package/presets/opencode/skills/fastapi-pydantic-strict/SKILL.md +1 -1
  193. package/presets/opencode/skills/jpa-nplusone-detector/SKILL.md +1 -1
  194. package/presets/opencode/skills/jpa-postgres/SKILL.md +2 -1
  195. package/presets/opencode/skills/livewire-alpine-bridge/SKILL.md +1 -1
  196. package/presets/opencode/skills/nextjs-typescript/SKILL.md +1 -1
  197. package/presets/opencode/skills/pagespeed-perf/SKILL.md +1 -1
  198. package/presets/opencode/skills/python/SKILL.md +6 -5
  199. package/presets/opencode/skills/python-django-stack/SKILL.md +1 -1
  200. package/presets/opencode/skills/python-fastapi-stack/SKILL.md +1 -1
  201. package/presets/opencode/skills/security/SKILL.md +1 -1
  202. package/presets/opencode/skills/seo-analytics-injector/SKILL.md +1 -1
  203. package/presets/opencode/skills/spring-auth-auditor/SKILL.md +1 -1
  204. package/presets/opencode/skills/spring-boot-feature/SKILL.md +2 -1
  205. package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +1 -1
  206. package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +1 -1
  207. package/presets/opencode/skills/sqlalchemy/SKILL.md +1 -1
  208. package/presets/opencode/skills/tailwind-responsive-auditor/SKILL.md +1 -1
  209. package/presets/opencode/skills/tdd-mutation-tester/SKILL.md +1 -1
  210. package/presets/pi/AGENTS.md +6 -0
  211. package/presets/seo-hotel/skills/astro-seo/SKILL.md +1 -1
  212. package/presets/seo-hotel/skills/geo-readiness/SKILL.md +1 -1
  213. package/presets/seo-hotel/skills/off-page/SKILL.md +1 -1
  214. package/presets/seo-hotel/skills/schema-validator/SKILL.md +1 -1
  215. package/presets/seo-hotel/skills/seo-audit/SKILL.md +1 -1
  216. package/presets/shared/invoke-hook.cjs +20 -5
  217. package/src/presets/models/roles.yml +28 -28
  218. package/src/presets/shared-skills.yml +58 -22
  219. 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.1",
75
+ version: "1.5.0",
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",
@@ -12939,18 +12989,19 @@ var init_path_containment = __esm(() => {
12939
12989
  });
12940
12990
 
12941
12991
  // src/core/presets/complementary-detector.ts
12942
- import { execFileSync } from "node:child_process";
12992
+ import { execFile } from "node:child_process";
12943
12993
  import { existsSync, readFileSync } from "node:fs";
12944
12994
  import { join as join3 } from "node:path";
12945
12995
  import { homedir } from "node:os";
12946
- function detectComplementaryTools() {
12996
+ import { promisify } from "node:util";
12997
+ async function detectComplementaryTools() {
12947
12998
  if (cachedStatus) {
12948
12999
  return cachedStatus;
12949
13000
  }
12950
- const isCmdAvailable = (cmd) => {
13001
+ const isCmdAvailable = async (cmd) => {
12951
13002
  try {
12952
13003
  const binary = process.platform === "win32" ? "where" : "which";
12953
- execFileSync(binary, [cmd], { stdio: "ignore", timeout: 1000 });
13004
+ await execFileAsync(binary, [cmd], { timeout: 1000 });
12954
13005
  return true;
12955
13006
  } catch {
12956
13007
  return false;
@@ -12979,18 +13030,28 @@ function detectComplementaryTools() {
12979
13030
  } catch {}
12980
13031
  return false;
12981
13032
  };
13033
+ const [rtk, codeReviewGraph, tokenSavior, tokenSaviorRecall, engram, gentleAi] = await Promise.all([
13034
+ isCmdAvailable("rtk"),
13035
+ isCmdAvailable("code-review-graph"),
13036
+ isCmdAvailable("token-savior"),
13037
+ isCmdAvailable("token-savior-recall"),
13038
+ isCmdAvailable("engram"),
13039
+ isCmdAvailable("gentle-ai")
13040
+ ]);
12982
13041
  cachedStatus = {
12983
- rtk: isCmdAvailable("rtk"),
12984
- codeReviewGraph: isCmdAvailable("code-review-graph"),
12985
- tokenSavior: isCmdAvailable("token-savior") || isCmdAvailable("token-savior-recall"),
13042
+ rtk,
13043
+ codeReviewGraph,
13044
+ tokenSavior: tokenSavior || tokenSaviorRecall,
12986
13045
  caveman: hasCaveman(),
12987
- engram: isCmdAvailable("engram"),
12988
- gentleAi: isCmdAvailable("gentle-ai")
13046
+ engram,
13047
+ gentleAi
12989
13048
  };
12990
13049
  return cachedStatus;
12991
13050
  }
12992
- var cachedStatus = null;
12993
- var init_complementary_detector = () => {};
13051
+ var execFileAsync, cachedStatus = null;
13052
+ var init_complementary_detector = __esm(() => {
13053
+ execFileAsync = promisify(execFile);
13054
+ });
12994
13055
 
12995
13056
  // src/core/presets/file-copier.ts
12996
13057
  import { lstat as lstat2, readdir, readFile as readFile4, stat } from "node:fs/promises";
@@ -13083,9 +13144,9 @@ function extractAgentRole(filePath) {
13083
13144
  ];
13084
13145
  return knownRoles.includes(name) ? name : null;
13085
13146
  }
13086
- function renderTemplate(content, modelConfig, filePath, locale = "en") {
13147
+ async function renderTemplate(content, modelConfig, filePath, locale = "en") {
13087
13148
  const cleanLocale = locale === "es" || locale === "en" ? locale : "en";
13088
- const tools = detectComplementaryTools();
13149
+ const tools = await detectComplementaryTools();
13089
13150
  let complementaryRules = "";
13090
13151
  if (cleanLocale === "es") {
13091
13152
  if (tools.rtk) {
@@ -13208,7 +13269,7 @@ function substituteToolNames(content, modelConfig) {
13208
13269
  const eol = fmMatch[1];
13209
13270
  const frontmatter = fmMatch[2];
13210
13271
  if (target === "opencode" && modelConfig.permissions) {
13211
- const updatedFrontmatter2 = frontmatter.replace(/^tools:\s*(.+)\r?\n?/m, "");
13272
+ const updatedFrontmatter2 = frontmatter.replace(/^tools:\s*(.+)\r?\n?/m, "").replace(/^(?:temperature|effort):[^\r\n]*\r?\n?/gm, "");
13212
13273
  return content.replace(fmMatch[0], `---${eol}${updatedFrontmatter2}${eol}---`);
13213
13274
  }
13214
13275
  if (!modelConfig.tools)
@@ -13226,7 +13287,7 @@ function substituteToolNames(content, modelConfig) {
13226
13287
  });
13227
13288
  return content.replace(fmMatch[0], `---${eol}${updatedFrontmatter}${eol}---`);
13228
13289
  }
13229
- function injectMcpServers(jsonString, filePath) {
13290
+ async function injectMcpServers(jsonString, filePath) {
13230
13291
  const isAgyMcpConfig = filePath.endsWith("mcp_config.json");
13231
13292
  const isClaudeSettings = filePath.endsWith("settings.json");
13232
13293
  const isOpencodeJsonc = filePath.endsWith("opencode.jsonc") || filePath.endsWith("opencode.json");
@@ -13239,7 +13300,7 @@ function injectMcpServers(jsonString, filePath) {
13239
13300
  cleanJson = jsonString.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "$1");
13240
13301
  }
13241
13302
  const obj = JSON.parse(cleanJson);
13242
- const tools = detectComplementaryTools();
13303
+ const tools = await detectComplementaryTools();
13243
13304
  const mcpServers = {};
13244
13305
  if (tools.tokenSavior) {
13245
13306
  mcpServers["token-savior-recall"] = {
@@ -13311,8 +13372,8 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
13311
13372
  } catch (e) {
13312
13373
  return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
13313
13374
  }
13314
- let incomingContent = isTemplate && modelConfig ? renderTemplate(content, modelConfig, srcPath, locale) : content;
13315
- incomingContent = injectMcpServers(incomingContent, destPath);
13375
+ let incomingContent = isTemplate && modelConfig ? await renderTemplate(content, modelConfig, srcPath, locale) : content;
13376
+ incomingContent = await injectMcpServers(incomingContent, destPath);
13316
13377
  let finalContent = incomingContent;
13317
13378
  let action = "written";
13318
13379
  if (strategy === "append") {
@@ -14579,7 +14640,7 @@ async function checkUpdates(basePath, isGlobal, includeConflicts = false) {
14579
14640
  let expectedContent;
14580
14641
  try {
14581
14642
  const srcContent = await readFile7(src, "utf-8");
14582
- expectedContent = isTemplate && modelConfig ? renderTemplate(srcContent, modelConfig, src, locale) : srcContent;
14643
+ expectedContent = isTemplate && modelConfig ? await renderTemplate(srcContent, modelConfig, src, locale) : srcContent;
14583
14644
  } catch {
14584
14645
  continue;
14585
14646
  }
@@ -15041,7 +15102,7 @@ async function doctorCommand(options) {
15041
15102
  message: "No product graph yet. Run `codeconductor ingest` to build product memory."
15042
15103
  });
15043
15104
  }
15044
- const compTools = detectComplementaryTools();
15105
+ const compTools = await detectComplementaryTools();
15045
15106
  const toolDetails = [
15046
15107
  { name: "rtk", key: "rtk", label: "RTK", desc: "Run `brew install rtk` to compress command outputs" },
15047
15108
  { name: "code-review-graph", key: "codeReviewGraph", label: "code-review-graph", desc: "Run `pipx install code-review-graph` to navigate symbol graph" },
@@ -15419,6 +15480,25 @@ var init_profiles = __esm(() => {
15419
15480
  routing: { default: ["validate-backlog", "discover", "design", "analyze", "test", "implement", "review"] },
15420
15481
  confirmationGate: baseGate
15421
15482
  },
15483
+ odd: {
15484
+ id: "odd",
15485
+ version: 1,
15486
+ command: "odd",
15487
+ taskCard: {
15488
+ type: "feature",
15489
+ requiredFields: ["title", "type", "risk", "scope", "context", "acceptanceCriteria"]
15490
+ },
15491
+ phases: [
15492
+ { id: "wayfinding", agent: "repo-explorer", outputSchema: "agent-output" },
15493
+ { id: "intake", agent: "task-coach", outputSchema: "planner-output", stopGate: "confirmation" },
15494
+ { id: "ledger", agent: "orchestrator", type: "cli-gate" },
15495
+ { id: "test", agent: "tester", dependsOn: ["intake"] },
15496
+ { id: "implement", agent: "implementer", dependsOn: ["test"] },
15497
+ { id: "review", agent: "reviewer" }
15498
+ ],
15499
+ routing: { default: ["wayfinding", "intake", "ledger", "test", "implement", "review"] },
15500
+ confirmationGate: baseGate
15501
+ },
15422
15502
  backlog: {
15423
15503
  id: "backlog",
15424
15504
  version: 1,
@@ -18304,13 +18384,13 @@ var init_binary_downloader = __esm(() => {
18304
18384
  });
18305
18385
 
18306
18386
  // src/core/lsp/lsp-installer.ts
18307
- import { execFile } from "node:child_process";
18387
+ import { execFile as execFile2 } from "node:child_process";
18308
18388
  import { access as access6, mkdir as mkdir6, mkdtemp, rename as rename2, rm, writeFile as writeFile5 } from "node:fs/promises";
18309
18389
  import { homedir as homedir8, tmpdir } from "node:os";
18310
18390
  import { join as join9 } from "node:path";
18311
- import { promisify } from "node:util";
18391
+ import { promisify as promisify2 } from "node:util";
18312
18392
  async function assertTarArchiveSafe(archivePath, destRoot) {
18313
- const { stdout } = await execFileAsync("tar", ["-tzf", archivePath], {
18393
+ const { stdout } = await execFileAsync2("tar", ["-tzf", archivePath], {
18314
18394
  timeout: 60000,
18315
18395
  maxBuffer: 10 * 1024 * 1024
18316
18396
  });
@@ -18322,7 +18402,7 @@ async function assertTarArchiveSafe(archivePath, destRoot) {
18322
18402
  }
18323
18403
  async function extractHardenedTar(archivePath, destRoot) {
18324
18404
  await assertTarArchiveSafe(archivePath, destRoot);
18325
- await execFileAsync("tar", ["-xzf", archivePath, "-C", destRoot, ...TAR_EXTRACT_HARDENING_FLAGS], { timeout: 120000 });
18405
+ await execFileAsync2("tar", ["-xzf", archivePath, "-C", destRoot, ...TAR_EXTRACT_HARDENING_FLAGS], { timeout: 120000 });
18326
18406
  }
18327
18407
 
18328
18408
  class LspInstaller {
@@ -18332,7 +18412,7 @@ class LspInstaller {
18332
18412
  }
18333
18413
  async checkInstalled(def) {
18334
18414
  try {
18335
- const { stdout } = await execFileAsync("which", [def.binaryName], { timeout: 5000 });
18415
+ const { stdout } = await execFileAsync2("which", [def.binaryName], { timeout: 5000 });
18336
18416
  const path = stdout.trim();
18337
18417
  if (path) {
18338
18418
  const version = await this.getVersion(def);
@@ -18341,7 +18421,7 @@ class LspInstaller {
18341
18421
  } catch {}
18342
18422
  if (def.packageManager === "npm" && def.npmDetect) {
18343
18423
  try {
18344
- const { stdout } = await execFileAsync("npm", ["list", "-g", def.npmDetect, "--depth=0"], { timeout: 1e4 });
18424
+ const { stdout } = await execFileAsync2("npm", ["list", "-g", def.npmDetect, "--depth=0"], { timeout: 1e4 });
18345
18425
  if (stdout.includes(def.npmDetect)) {
18346
18426
  const version = this.parseVersionFromNpmList(stdout, def.npmDetect);
18347
18427
  return { installed: true, version };
@@ -18350,7 +18430,7 @@ class LspInstaller {
18350
18430
  }
18351
18431
  if (def.packageManager === "pip" && def.pipDetect) {
18352
18432
  try {
18353
- const { stdout } = await execFileAsync("pip", ["show", def.pipDetect], { timeout: 1e4 });
18433
+ const { stdout } = await execFileAsync2("pip", ["show", def.pipDetect], { timeout: 1e4 });
18354
18434
  if (stdout.includes("Version:")) {
18355
18435
  const version = this.parseVersionFromPipShow(stdout);
18356
18436
  return { installed: true, version };
@@ -18414,7 +18494,7 @@ class LspInstaller {
18414
18494
  }
18415
18495
  async getVersion(def) {
18416
18496
  try {
18417
- const { stdout } = await execFileAsync(def.binaryName, [def.versionFlag], { timeout: 5000 });
18497
+ const { stdout } = await execFileAsync2(def.binaryName, [def.versionFlag], { timeout: 5000 });
18418
18498
  return stdout.trim().split(`
18419
18499
  `)[0];
18420
18500
  } catch {
@@ -18432,7 +18512,7 @@ class LspInstaller {
18432
18512
  async installNpm(def) {
18433
18513
  assertPinnedPackage(def);
18434
18514
  try {
18435
- await execFileAsync("npm", ["install", "-g", def.package], { timeout: 120000 });
18515
+ await execFileAsync2("npm", ["install", "-g", def.package], { timeout: 120000 });
18436
18516
  } catch (error) {
18437
18517
  throw new Error(`Failed to install ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
18438
18518
  }
@@ -18440,7 +18520,7 @@ class LspInstaller {
18440
18520
  async installPip(def) {
18441
18521
  assertPinnedPackage(def);
18442
18522
  try {
18443
- await execFileAsync("pip", ["install", "--user", def.package], { timeout: 120000 });
18523
+ await execFileAsync2("pip", ["install", "--user", def.package], { timeout: 120000 });
18444
18524
  } catch (error) {
18445
18525
  throw new Error(`Failed to install ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
18446
18526
  }
@@ -18482,7 +18562,7 @@ class LspInstaller {
18482
18562
  await access6(candidate);
18483
18563
  await rename2(candidate, destPath);
18484
18564
  } catch {
18485
- const { stdout: found } = await execFileAsync("find", [workDir, "-type", "f", "-name", def.binaryName], { timeout: 30000 });
18565
+ const { stdout: found } = await execFileAsync2("find", [workDir, "-type", "f", "-name", def.binaryName], { timeout: 30000 });
18486
18566
  const first = found.split(`
18487
18567
  `).map((l) => l.trim()).find((l) => l.length > 0);
18488
18568
  if (!first) {
@@ -18492,7 +18572,7 @@ class LspInstaller {
18492
18572
  await rename2(first, destPath);
18493
18573
  }
18494
18574
  if (process.platform !== "win32") {
18495
- await execFileAsync("chmod", ["+x", destPath], { timeout: 5000 });
18575
+ await execFileAsync2("chmod", ["+x", destPath], { timeout: 5000 });
18496
18576
  }
18497
18577
  } finally {
18498
18578
  await rm(workDir, { recursive: true, force: true });
@@ -18502,11 +18582,11 @@ class LspInstaller {
18502
18582
  function createLspInstaller() {
18503
18583
  return new LspInstaller;
18504
18584
  }
18505
- var execFileAsync, TAR_EXTRACT_HARDENING_FLAGS;
18585
+ var execFileAsync2, TAR_EXTRACT_HARDENING_FLAGS;
18506
18586
  var init_lsp_installer = __esm(() => {
18507
18587
  init_binary_integrity();
18508
18588
  init_binary_downloader();
18509
- execFileAsync = promisify(execFile);
18589
+ execFileAsync2 = promisify2(execFile2);
18510
18590
  TAR_EXTRACT_HARDENING_FLAGS = [
18511
18591
  "--no-same-owner",
18512
18592
  "--no-same-permissions"
@@ -20771,13 +20851,13 @@ function slugify(text) {
20771
20851
 
20772
20852
  // src/core/knowledge/source-parsers.ts
20773
20853
  import { readFile as readFile19 } from "node:fs/promises";
20774
- import { join as join15 } from "node:path";
20854
+ import { basename as basename6, join as join15, relative as relative7, sep as sep5 } from "node:path";
20775
20855
  async function parseAdrFile(filePath) {
20776
20856
  try {
20777
20857
  const content = await readFile19(filePath, "utf-8");
20778
- const basename6 = filePath.split("/").pop() ?? "";
20779
- const idMatch = basename6.match(/adr-(\d+)/i);
20780
- const id = idMatch ? `adr-${idMatch[1]}` : slugify(basename6);
20858
+ const base = basename6(filePath);
20859
+ const idMatch = base.match(/adr-(\d+)/i);
20860
+ const id = idMatch ? `adr-${idMatch[1]}` : slugify(base);
20781
20861
  const titleMatch = content.match(/^#\s+ADR-\d+:\s*(.+)$/m);
20782
20862
  const statusMatch = content.match(/\*\*Status:\*\*\s*(\w+)/i);
20783
20863
  const contextSection = extractSection(content, "Context");
@@ -20787,7 +20867,7 @@ async function parseAdrFile(filePath) {
20787
20867
  `).filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*")).map((l) => l.replace(/^[\s*-]+/, "").trim()).filter(Boolean);
20788
20868
  return {
20789
20869
  id,
20790
- title: titleMatch?.[1]?.trim() ?? basename6,
20870
+ title: titleMatch?.[1]?.trim() ?? base,
20791
20871
  status: statusMatch?.[1]?.toLowerCase() ?? "unknown",
20792
20872
  context: contextSection.trim(),
20793
20873
  decision: decisionSection.trim(),
@@ -20887,7 +20967,7 @@ async function scanSrcComponents(projectRoot) {
20887
20967
  source: "src/",
20888
20968
  confidence: "medium",
20889
20969
  relations: [],
20890
- data: { path: full.replace(projectRoot + "/", "") }
20970
+ data: { path: relative7(projectRoot, full).split(sep5).join("/") }
20891
20971
  });
20892
20972
  await walk(full, prefix ? `${prefix}-${domainSlug}` : domainSlug);
20893
20973
  }
@@ -20948,7 +21028,7 @@ var init_episodic_store = __esm(() => {
20948
21028
  // src/core/knowledge/ingest-pipeline.ts
20949
21029
  import { createHash as createHash3 } from "node:crypto";
20950
21030
  import { readFile as readFile21 } from "node:fs/promises";
20951
- import { join as join16 } from "node:path";
21031
+ import { join as join16, relative as relative8, sep as sep6 } from "node:path";
20952
21032
  import { mkdir as mkdir13, writeFile as writeFile12 } from "node:fs/promises";
20953
21033
  async function mapLimit(items, limit, fn) {
20954
21034
  if (items.length === 0)
@@ -20997,7 +21077,7 @@ async function collectDeferRisks(projectRoot) {
20997
21077
  const perFile = await mapLimit(files, WALK_READ_CONCURRENCY, async (full) => {
20998
21078
  const entities = [];
20999
21079
  const content = await readFile21(full, "utf-8");
21000
- const rel = full.replace(projectRoot + "/", "");
21080
+ const rel = relative8(projectRoot, full).split(sep6).join("/");
21001
21081
  const deferRe = new RegExp(DEFER_REGEX_SOURCE, "gm");
21002
21082
  let match;
21003
21083
  while ((match = deferRe.exec(content)) !== null) {
@@ -21487,7 +21567,10 @@ async function listScorecards(projectRoot, filter = {}) {
21487
21567
  }
21488
21568
  async function hasPassingScorecard(projectRoot, backlogId) {
21489
21569
  const listed = await listScorecards(projectRoot, { backlogId, verdict: "PASS" });
21490
- return listed.success && listed.data.length > 0;
21570
+ if (listed.success && listed.data.length > 0)
21571
+ return true;
21572
+ const outcomes = await listOutcomes(projectRoot, { backlogId, source: "review" });
21573
+ return outcomes.success && outcomes.data.some((outcome) => outcome.verdict === "PASS");
21491
21574
  }
21492
21575
  async function loadScorecard(projectRoot, id) {
21493
21576
  try {
@@ -21519,7 +21602,7 @@ function aggregateOutcomes(outcomes) {
21519
21602
  agentScoreCounts[o.agent] = (agentScoreCounts[o.agent] ?? 0) + 1;
21520
21603
  }
21521
21604
  if (!byModel[o.model])
21522
- byModel[o.model] = { count: 0, avgScore: 0 };
21605
+ byModel[o.model] = { count: 0, avgScore: 0, knownTokenMeasurements: 0 };
21523
21606
  byModel[o.model].count++;
21524
21607
  if (o.weightedScore !== undefined) {
21525
21608
  byModel[o.model].avgScore += o.weightedScore;
@@ -21529,10 +21612,11 @@ function aggregateOutcomes(outcomes) {
21529
21612
  byModel[o.model].avgCost = (byModel[o.model].avgCost ?? 0) + o.costUsd;
21530
21613
  modelCostCounts[o.model] = (modelCostCounts[o.model] ?? 0) + 1;
21531
21614
  }
21532
- if (o.tokensIn !== undefined || o.tokensOut !== undefined) {
21533
- const tokens = (o.tokensIn ?? 0) + (o.tokensOut ?? 0);
21534
- byModel[o.model].avgTokens = (byModel[o.model].avgTokens ?? 0) + tokens;
21615
+ const reportedTokens = o.delivery?.tokenUsage.status === "known" ? o.delivery.tokenUsage.total : o.tokensIn !== undefined || o.tokensOut !== undefined ? (o.tokensIn ?? 0) + (o.tokensOut ?? 0) : undefined;
21616
+ if (reportedTokens !== undefined) {
21617
+ byModel[o.model].avgTokens = (byModel[o.model].avgTokens ?? 0) + reportedTokens;
21535
21618
  modelTokenCounts[o.model] = (modelTokenCounts[o.model] ?? 0) + 1;
21619
+ byModel[o.model].knownTokenMeasurements++;
21536
21620
  }
21537
21621
  if (o.variantId) {
21538
21622
  if (!byVariant[o.variantId])
@@ -22182,11 +22266,140 @@ var init_compile_checker = __esm(() => {
22182
22266
  ];
22183
22267
  });
22184
22268
 
22269
+ // src/core/verification/rdd-receipt.ts
22270
+ var exports_rdd_receipt = {};
22271
+ __export(exports_rdd_receipt, {
22272
+ captureReceipt: () => captureReceipt,
22273
+ collectReceiptPaths: () => collectReceiptPaths,
22274
+ isRddReceipt: () => isRddReceipt,
22275
+ verifyReceipt: () => verifyReceipt
22276
+ });
22277
+ import { createHash as createHash4 } from "node:crypto";
22278
+ import { readdir as readdir7, readFile as readFile27 } from "node:fs/promises";
22279
+ import { join as join18, relative as relative9, resolve as resolve26, sep as sep7 } from "node:path";
22280
+ function sha2562(content) {
22281
+ return createHash4("sha256").update(content).digest("hex");
22282
+ }
22283
+ function canonicalPath(projectRoot, input) {
22284
+ const root = resolve26(projectRoot);
22285
+ const absolute = resolve26(root, input);
22286
+ if (absolute !== root && !absolute.startsWith(`${root}${sep7}`)) {
22287
+ throw new Error(`RDD receipt path "${input}" is outside the project root`);
22288
+ }
22289
+ const normalized = relative9(root, absolute);
22290
+ if (!normalized || normalized === ".") {
22291
+ throw new Error("RDD receipts require file paths, not the project root");
22292
+ }
22293
+ return { absolute, relative: normalized.split(sep7).join("/") };
22294
+ }
22295
+ async function fingerprint(projectRoot, paths) {
22296
+ const deduplicated = new Map;
22297
+ for (const path of paths) {
22298
+ const canonical = canonicalPath(projectRoot, path);
22299
+ deduplicated.set(canonical.relative, canonical.absolute);
22300
+ }
22301
+ const result = [];
22302
+ for (const [path, absolute] of [...deduplicated.entries()].sort(([left], [right]) => left.localeCompare(right))) {
22303
+ result.push({ path, hash: sha2562(await readFile27(absolute)) });
22304
+ }
22305
+ return result;
22306
+ }
22307
+ function manifestHash(paths) {
22308
+ return sha2562(paths.map((entry) => `${entry.path}\x00${entry.hash}`).join(`
22309
+ `));
22310
+ }
22311
+ function isRddReceipt(value) {
22312
+ if (!value || typeof value !== "object")
22313
+ return false;
22314
+ const receipt = value;
22315
+ 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";
22316
+ }
22317
+ async function captureReceipt(projectRoot, input) {
22318
+ if (!input.taskId.trim())
22319
+ throw new Error("RDD receipts require a task id");
22320
+ if (input.paths.length === 0 && input.coverage !== "project") {
22321
+ throw new Error("RDD receipts require at least one protected path");
22322
+ }
22323
+ const paths = await fingerprint(projectRoot, input.paths);
22324
+ return {
22325
+ version: 1,
22326
+ taskId: input.taskId,
22327
+ phase: input.phase,
22328
+ paths,
22329
+ coverage: input.coverage ?? "paths",
22330
+ manifestHash: manifestHash(paths),
22331
+ command: input.command,
22332
+ outcome: input.outcome,
22333
+ capturedAt: new Date().toISOString()
22334
+ };
22335
+ }
22336
+ async function collectReceiptPaths(projectRoot) {
22337
+ const root = resolve26(projectRoot);
22338
+ const paths = [];
22339
+ async function visit2(current) {
22340
+ const entries = await readdir7(current, { withFileTypes: true });
22341
+ for (const entry of entries) {
22342
+ if (entry.isDirectory()) {
22343
+ if (!EXCLUDED_DIRECTORIES.has(entry.name))
22344
+ await visit2(join18(current, entry.name));
22345
+ continue;
22346
+ }
22347
+ if (entry.isFile())
22348
+ paths.push(relative9(root, join18(current, entry.name)).split(sep7).join("/"));
22349
+ }
22350
+ }
22351
+ await visit2(root);
22352
+ return paths.sort();
22353
+ }
22354
+ async function verifyReceipt(projectRoot, receipt) {
22355
+ const expected = new Map(receipt.paths.map((entry) => [entry.path, entry.hash]));
22356
+ const changedPaths = [];
22357
+ const current = [];
22358
+ const pathsToCheck = receipt.coverage === "project" ? await collectReceiptPaths(projectRoot) : receipt.paths.map((entry) => entry.path);
22359
+ const currentByPath = new Map;
22360
+ for (const path of pathsToCheck) {
22361
+ try {
22362
+ const canonical = canonicalPath(projectRoot, path);
22363
+ const hash = sha2562(await readFile27(canonical.absolute));
22364
+ const currentEntry = { path: canonical.relative, hash };
22365
+ current.push(currentEntry);
22366
+ currentByPath.set(currentEntry.path, currentEntry);
22367
+ if (hash !== expected.get(currentEntry.path))
22368
+ changedPaths.push(currentEntry.path);
22369
+ } catch {
22370
+ changedPaths.push(path);
22371
+ }
22372
+ }
22373
+ for (const path of expected.keys()) {
22374
+ if (!currentByPath.has(path) && !changedPaths.includes(path))
22375
+ changedPaths.push(path);
22376
+ }
22377
+ changedPaths.sort();
22378
+ const actualHash = manifestHash(current.sort((left, right) => left.path.localeCompare(right.path)));
22379
+ return {
22380
+ valid: changedPaths.length === 0 && actualHash === receipt.manifestHash,
22381
+ expectedHash: receipt.manifestHash,
22382
+ actualHash,
22383
+ changedPaths
22384
+ };
22385
+ }
22386
+ var EXCLUDED_DIRECTORIES;
22387
+ var init_rdd_receipt = __esm(() => {
22388
+ EXCLUDED_DIRECTORIES = new Set([
22389
+ ".git",
22390
+ ".codeconductor",
22391
+ "node_modules",
22392
+ "dist",
22393
+ "coverage",
22394
+ "graphify-out"
22395
+ ]);
22396
+ });
22397
+
22185
22398
  // src/core/verification/verification-runner.ts
22186
22399
  import { existsSync as existsSync3 } from "node:fs";
22187
- import { readFile as readFile27 } from "node:fs/promises";
22188
- import { dirname as dirname10, join as join18, resolve as resolve26 } from "node:path";
22189
- import { mkdir as mkdir18, readdir as readdir7, writeFile as writeFile18 } from "node:fs/promises";
22400
+ import { readFile as readFile28 } from "node:fs/promises";
22401
+ import { dirname as dirname10, join as join19, resolve as resolve27 } from "node:path";
22402
+ import { mkdir as mkdir18, readdir as readdir8, writeFile as writeFile18 } from "node:fs/promises";
22190
22403
  function isPassingEvidence(ev) {
22191
22404
  switch (ev.type) {
22192
22405
  case "verification":
@@ -22203,9 +22416,17 @@ function isPassingEvidence(ev) {
22203
22416
  return false;
22204
22417
  }
22205
22418
  }
22419
+ async function isCurrentPassingEvidence(projectRoot, ev) {
22420
+ if (!isPassingEvidence(ev))
22421
+ return false;
22422
+ const receipt = ev.data?.rddReceipt;
22423
+ if (!isRddReceipt(receipt))
22424
+ return false;
22425
+ return (await verifyReceipt(projectRoot, receipt)).valid;
22426
+ }
22206
22427
  function evidenceFilePath(evDir, id) {
22207
- const path = resolve26(evDir, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
22208
- if (dirname10(path) !== resolve26(evDir)) {
22428
+ const path = resolve27(evDir, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
22429
+ if (dirname10(path) !== resolve27(evDir)) {
22209
22430
  return err(new Error(`Refusing to write evidence for "${id}" outside ${evDir}`));
22210
22431
  }
22211
22432
  return ok(path);
@@ -22231,7 +22452,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
22231
22452
  return ok({ records, invalid });
22232
22453
  let files;
22233
22454
  try {
22234
- files = await readdir7(evDir);
22455
+ files = await readdir8(evDir);
22235
22456
  } catch (e) {
22236
22457
  return err(e instanceof Error ? e : new Error(String(e)));
22237
22458
  }
@@ -22239,7 +22460,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
22239
22460
  if (!file.endsWith(".json"))
22240
22461
  continue;
22241
22462
  try {
22242
- const raw = await readFile27(join18(evDir, file), "utf-8");
22463
+ const raw = await readFile28(join19(evDir, file), "utf-8");
22243
22464
  const ev = EvidenceSchema.parse(JSON.parse(raw));
22244
22465
  if (ev.relatedTask === taskId)
22245
22466
  records.push(ev);
@@ -22260,7 +22481,7 @@ async function validateEvidenceIds(projectRoot, taskId, evidenceIds) {
22260
22481
  if (!record) {
22261
22482
  return err(new Error(`Evidence "${id}" is not a record of task ${taskId}`));
22262
22483
  }
22263
- if (!isPassingEvidence(record)) {
22484
+ if (!await isCurrentPassingEvidence(projectRoot, record)) {
22264
22485
  return err(new Error(`Evidence "${id}" does not record a passing result`));
22265
22486
  }
22266
22487
  if (record.type === "verification")
@@ -22341,7 +22562,12 @@ async function runVerification(projectRoot, taskId, goal, options) {
22341
22562
  passed: collected.data.invalid === 0,
22342
22563
  message: collected.data.invalid === 0 ? "All evidence records are readable" : `${collected.data.invalid} unreadable evidence record(s)`
22343
22564
  });
22344
- const supporting = collected.data.records.filter((ev) => ev.type !== "verification" && isPassingEvidence(ev));
22565
+ const supporting = [];
22566
+ for (const candidate of collected.data.records) {
22567
+ if (candidate.type !== "verification" && await isCurrentPassingEvidence(projectRoot, candidate)) {
22568
+ supporting.push(candidate);
22569
+ }
22570
+ }
22345
22571
  evidenceIds.push(...supporting.map((ev) => ev.id));
22346
22572
  checks.push({
22347
22573
  name: "evidence_present",
@@ -22349,6 +22575,13 @@ async function runVerification(projectRoot, taskId, goal, options) {
22349
22575
  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
22576
  });
22351
22577
  const passed = checks.every((c) => c.passed);
22578
+ const receipt = await captureReceipt(projectRoot, {
22579
+ taskId,
22580
+ phase: "verification",
22581
+ paths: await collectReceiptPaths(projectRoot),
22582
+ outcome: passed ? "passed" : "failed",
22583
+ coverage: "project"
22584
+ });
22352
22585
  const evidence = {
22353
22586
  id: `ev-verify-${taskId}-${Date.now()}`,
22354
22587
  source: "cc verify",
@@ -22357,7 +22590,7 @@ async function runVerification(projectRoot, taskId, goal, options) {
22357
22590
  relatedTask: taskId,
22358
22591
  confidence: passed ? 0.9 : 0.3,
22359
22592
  summary: passed ? "Verification passed" : "Verification failed",
22360
- data: { passed, checks }
22593
+ data: { passed, checks, rddReceipt: receipt }
22361
22594
  };
22362
22595
  const persisted = await persistEvidence(projectRoot, evidence);
22363
22596
  if (!persisted.success)
@@ -22380,19 +22613,25 @@ async function gateTaskCompletion(projectRoot, taskId, evidenceRequired, evidenc
22380
22613
  if (collected.data.invalid > 0)
22381
22614
  return ok(false);
22382
22615
  const cited = evidenceIds ? new Set(evidenceIds) : undefined;
22383
- const hasPassing = (type) => collected.data.records.some((ev) => (!cited || cited.has(ev.id)) && ev.type === type && isPassingEvidence(ev));
22616
+ const hasPassing = async (type) => {
22617
+ for (const evidence of collected.data.records) {
22618
+ if ((!cited || cited.has(evidence.id)) && evidence.type === type && await isCurrentPassingEvidence(projectRoot, evidence))
22619
+ return true;
22620
+ }
22621
+ return false;
22622
+ };
22384
22623
  for (const req of evidenceRequired) {
22385
22624
  switch (req) {
22386
22625
  case "acceptance_criteria_met":
22387
- if (!hasPassing("verification"))
22626
+ if (!await hasPassing("verification"))
22388
22627
  return ok(false);
22389
22628
  break;
22390
22629
  case "tests_passed":
22391
- if (!hasPassing("test"))
22630
+ if (!await hasPassing("test"))
22392
22631
  return ok(false);
22393
22632
  break;
22394
22633
  case "review_approved":
22395
- if (!hasPassing("review"))
22634
+ if (!await hasPassing("review"))
22396
22635
  return ok(false);
22397
22636
  break;
22398
22637
  default:
@@ -22401,11 +22640,66 @@ async function gateTaskCompletion(projectRoot, taskId, evidenceRequired, evidenc
22401
22640
  }
22402
22641
  return ok(true);
22403
22642
  }
22404
- async function hasTddRunnerEvidence(projectRoot, taskId) {
22643
+ async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
22644
+ const evDir = evidenceDir(projectRoot);
22645
+ const path = evidenceFilePath(evDir, evidenceId);
22646
+ if (!path.success)
22647
+ return path;
22648
+ let raw;
22649
+ try {
22650
+ raw = await readFile28(path.data, "utf-8");
22651
+ } catch (e) {
22652
+ return err(e instanceof Error ? e : new Error(String(e)));
22653
+ }
22654
+ let ev;
22655
+ try {
22656
+ ev = EvidenceSchema.parse(JSON.parse(raw));
22657
+ } catch {
22658
+ return err(new Error(`Evidence "${evidenceId}" is not valid TDD evidence`));
22659
+ }
22660
+ if (ev.relatedTask !== taskId) {
22661
+ return err(new Error(`Evidence "${evidenceId}" is not a record of task ${taskId}`));
22662
+ }
22663
+ if (ev.type !== "tdd" || ev.source !== TDD_EVIDENCE_SOURCE) {
22664
+ return err(new Error(`Evidence "${evidenceId}" was not captured by the verification runner`));
22665
+ }
22666
+ const capturedBy = ev.data?.capturedBy;
22667
+ const suiteFailed = ev.data?.suiteFailed;
22668
+ const suitePassed = ev.data?.suitePassed;
22669
+ if (capturedBy !== TDD_CAPTURED_BY || typeof suiteFailed !== "boolean" || typeof suitePassed !== "boolean") {
22670
+ return err(new Error(`Evidence "${evidenceId}" was not captured by the verification runner`));
22671
+ }
22672
+ const receipt = ev.data?.rddReceipt;
22673
+ if (!isRddReceipt(receipt)) {
22674
+ return err(new Error(`Evidence "${evidenceId}" has no valid RDD receipt`));
22675
+ }
22676
+ if (receipt.taskId !== taskId) {
22677
+ return err(new Error(`Evidence "${evidenceId}" receipt belongs to another task`));
22678
+ }
22679
+ const verified = await verifyReceipt(projectRoot, receipt);
22680
+ if (!verified.valid) {
22681
+ return err(new Error(`Evidence "${evidenceId}" RDD receipt is stale: ${verified.changedPaths.join(", ")}`));
22682
+ }
22683
+ return ok({ capturedBy, suiteFailed, suitePassed, receipt });
22684
+ }
22685
+ async function hasTddRunnerEvidence(projectRoot, taskId, expectedPhase) {
22405
22686
  const collected = await collectTaskEvidence(projectRoot, taskId);
22406
22687
  if (!collected.success)
22407
22688
  return false;
22408
- return collected.data.records.some((ev) => ev.type === "tdd" && ev.source === TDD_EVIDENCE_SOURCE && ev.data?.capturedBy === TDD_CAPTURED_BY);
22689
+ for (const ev of collected.data.records) {
22690
+ if (ev.type !== "tdd" || ev.source !== TDD_EVIDENCE_SOURCE || ev.data?.capturedBy !== TDD_CAPTURED_BY) {
22691
+ continue;
22692
+ }
22693
+ const loaded = await loadTddSuiteEvidence(projectRoot, taskId, ev.id);
22694
+ if (!loaded.success)
22695
+ continue;
22696
+ if (expectedPhase === "red" && (!loaded.data.suiteFailed || loaded.data.suitePassed))
22697
+ continue;
22698
+ if (expectedPhase === "green" && (!loaded.data.suitePassed || loaded.data.suiteFailed))
22699
+ continue;
22700
+ return true;
22701
+ }
22702
+ return false;
22409
22703
  }
22410
22704
  var TDD_EVIDENCE_SOURCE = "cc verify", TDD_CAPTURED_BY = "verification-runner";
22411
22705
  var init_verification_runner = __esm(() => {
@@ -22414,6 +22708,7 @@ var init_verification_runner = __esm(() => {
22414
22708
  init_compile_checker();
22415
22709
  init_paths();
22416
22710
  init_episodic_store();
22711
+ init_rdd_receipt();
22417
22712
  });
22418
22713
 
22419
22714
  // src/core/orchestrator/runtime-orchestrator.ts
@@ -22753,8 +23048,8 @@ var init_loop_state = __esm(() => {
22753
23048
  });
22754
23049
 
22755
23050
  // src/core/loop/git-stats.ts
22756
- import { execFile as execFile2 } from "node:child_process";
22757
- import { promisify as promisify2 } from "node:util";
23051
+ import { execFile as execFile3 } from "node:child_process";
23052
+ import { promisify as promisify3 } from "node:util";
22758
23053
  function countPorcelain(output) {
22759
23054
  return output.split(`
22760
23055
  `).filter((line) => line.trim().length > 0).length;
@@ -22778,12 +23073,12 @@ function countNumstat(output) {
22778
23073
  async function fetchGitChangeStats(cwd) {
22779
23074
  try {
22780
23075
  const [status, diff] = await Promise.all([
22781
- execFileAsync2("git", ["status", "--porcelain"], {
23076
+ execFileAsync3("git", ["status", "--porcelain"], {
22782
23077
  cwd,
22783
23078
  encoding: "utf-8",
22784
23079
  timeout: 15000
22785
23080
  }),
22786
- execFileAsync2("git", ["diff", "--numstat"], {
23081
+ execFileAsync3("git", ["diff", "--numstat"], {
22787
23082
  cwd,
22788
23083
  encoding: "utf-8",
22789
23084
  timeout: 15000
@@ -22807,9 +23102,9 @@ async function readGitChangeStats(cwd, options) {
22807
23102
  cache = { cwd, at: now, stats };
22808
23103
  return stats;
22809
23104
  }
22810
- var execFileAsync2, GIT_STATS_CACHE_TTL_MS = 3000, cache;
23105
+ var execFileAsync3, GIT_STATS_CACHE_TTL_MS = 3000, cache;
22811
23106
  var init_git_stats = __esm(() => {
22812
- execFileAsync2 = promisify2(execFile2);
23107
+ execFileAsync3 = promisify3(execFile3);
22813
23108
  });
22814
23109
 
22815
23110
  // src/core/loop/loop-engine.ts
@@ -23536,6 +23831,163 @@ var init_verify_command = __esm(() => {
23536
23831
  init_verification_runner();
23537
23832
  });
23538
23833
 
23834
+ // src/commands/rdd.command.ts
23835
+ var exports_rdd_command = {};
23836
+ __export(exports_rdd_command, {
23837
+ rddCommand: () => rddCommand,
23838
+ rddStatus: () => rddStatus
23839
+ });
23840
+ import { execFile as execFile4 } from "node:child_process";
23841
+ import { existsSync as existsSync4 } from "node:fs";
23842
+ import { mkdir as mkdir19, readFile as readFile29, readdir as readdir9, rename as rename3, writeFile as writeFile19, chmod } from "node:fs/promises";
23843
+ import { dirname as dirname11, resolve as resolve28 } from "node:path";
23844
+ import { promisify as promisify4 } from "node:util";
23845
+ function receiptPath(projectRoot, id) {
23846
+ const directory = resolve28(evidenceDir(projectRoot));
23847
+ const path = resolve28(directory, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
23848
+ if (dirname11(path) !== directory)
23849
+ throw new Error(`Invalid RDD receipt id: ${id}`);
23850
+ return path;
23851
+ }
23852
+ async function loadReceipt(projectRoot, id) {
23853
+ const raw = await readFile29(receiptPath(projectRoot, id), "utf-8");
23854
+ const evidence = EvidenceSchema.parse(JSON.parse(raw));
23855
+ if (evidence.type !== "rdd")
23856
+ throw new Error(`Evidence "${id}" is not an RDD receipt`);
23857
+ return { evidence, receipt: assertReceipt(evidence.data?.rddReceipt) };
23858
+ }
23859
+ function assertReceipt(value) {
23860
+ if (!isRddReceipt(value))
23861
+ throw new Error("RDD receipt payload is invalid");
23862
+ return value;
23863
+ }
23864
+ async function rddStatus(projectRoot, taskId) {
23865
+ const directory = evidenceDir(projectRoot);
23866
+ if (!existsSync4(directory))
23867
+ return [];
23868
+ const receipts = [];
23869
+ for (const file of await readdir9(directory)) {
23870
+ if (!file.endsWith(".json"))
23871
+ continue;
23872
+ try {
23873
+ const id = file.slice(0, -".json".length);
23874
+ const { evidence, receipt } = await loadReceipt(projectRoot, id);
23875
+ if (taskId && evidence.relatedTask !== taskId)
23876
+ continue;
23877
+ const verification = await verifyReceipt(projectRoot, receipt);
23878
+ receipts.push({
23879
+ id: evidence.id,
23880
+ taskId: evidence.relatedTask,
23881
+ phase: receipt.phase,
23882
+ timestamp: evidence.timestamp,
23883
+ valid: verification.valid,
23884
+ changedPaths: verification.changedPaths
23885
+ });
23886
+ } catch {}
23887
+ }
23888
+ return receipts.sort((left, right) => right.timestamp.localeCompare(left.timestamp));
23889
+ }
23890
+ async function installHooks(projectRoot) {
23891
+ const { stdout } = await execFileAsync4("git", ["rev-parse", "--git-path", "hooks"], {
23892
+ cwd: projectRoot,
23893
+ encoding: "utf8"
23894
+ });
23895
+ const hooksDir = resolve28(projectRoot, stdout.trim());
23896
+ await mkdir19(hooksDir, { recursive: true });
23897
+ const installed = [];
23898
+ const skipped = [];
23899
+ for (const hook of ["pre-commit", "pre-push"]) {
23900
+ const path = resolve28(hooksDir, hook);
23901
+ const backup = resolve28(hooksDir, `${hook}.rdd-original`);
23902
+ const marker = "# codeconductor-rdd-hook";
23903
+ if (existsSync4(path)) {
23904
+ const existing = await readFile29(path, "utf8");
23905
+ if (existing.includes(marker)) {
23906
+ skipped.push(hook);
23907
+ continue;
23908
+ }
23909
+ if (existsSync4(backup)) {
23910
+ throw new Error(`Cannot preserve ${hook}: ${backup} already exists`);
23911
+ }
23912
+ await rename3(path, backup);
23913
+ }
23914
+ const script = `#!/bin/sh
23915
+ ${marker}
23916
+ set -e
23917
+ HOOK_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
23918
+ if [ -x "$HOOK_DIR/${hook}.rdd-original" ]; then
23919
+ "$HOOK_DIR/${hook}.rdd-original" "$@"
23920
+ fi
23921
+ cc-codeconductor rdd git-check
23922
+ `;
23923
+ await writeFile19(path, script, "utf8");
23924
+ await chmod(path, 493);
23925
+ installed.push(hook);
23926
+ }
23927
+ return { installed, skipped };
23928
+ }
23929
+ async function rddCommand(options) {
23930
+ try {
23931
+ if (options.subcommand === "capture") {
23932
+ if (!options.taskId)
23933
+ throw new Error("Missing task id. Use --task <id>.");
23934
+ const protectedPaths = options.paths?.length ? options.paths : await collectReceiptPaths(options.projectRoot);
23935
+ const receipt = await captureReceipt(options.projectRoot, {
23936
+ taskId: options.taskId,
23937
+ phase: options.phase ?? "verification",
23938
+ paths: protectedPaths,
23939
+ outcome: options.outcome ?? "passed",
23940
+ command: options.command,
23941
+ coverage: options.paths?.length ? "paths" : "project"
23942
+ });
23943
+ const id = `ev-rdd-${options.taskId}-${Date.now()}`;
23944
+ const evidence = {
23945
+ id,
23946
+ source: "cc rdd",
23947
+ type: "rdd",
23948
+ timestamp: new Date().toISOString(),
23949
+ relatedTask: options.taskId,
23950
+ confidence: 0.9,
23951
+ summary: `RDD ${receipt.phase} receipt captured`,
23952
+ data: { rddReceipt: receipt }
23953
+ };
23954
+ const directory = evidenceDir(options.projectRoot);
23955
+ await mkdir19(directory, { recursive: true });
23956
+ await writeFile19(receiptPath(options.projectRoot, id), JSON.stringify(evidence, null, 2), "utf-8");
23957
+ return { code: 0, data: { success: true, receiptId: id, receipt } };
23958
+ }
23959
+ if (options.subcommand === "verify") {
23960
+ if (!options.receiptId)
23961
+ throw new Error("Missing receipt id. Use --receipt <id>.");
23962
+ const { receipt } = await loadReceipt(options.projectRoot, options.receiptId);
23963
+ const verification = await verifyReceipt(options.projectRoot, receipt);
23964
+ return {
23965
+ code: verification.valid ? 0 : 1,
23966
+ data: { success: verification.valid, receiptId: options.receiptId, ...verification }
23967
+ };
23968
+ }
23969
+ if (options.subcommand === "status") {
23970
+ return { code: 0, data: { success: true, receipts: await rddStatus(options.projectRoot, options.taskId) } };
23971
+ }
23972
+ if (options.subcommand === "git-check") {
23973
+ const current = (await rddStatus(options.projectRoot))[0];
23974
+ if (!current)
23975
+ return { code: 1, data: { success: false, errors: ["No RDD receipt exists for the Git candidate."] } };
23976
+ return current.valid ? { code: 0, data: { success: true, receiptId: current.id } } : { code: 1, data: { success: false, receiptId: current.id, changedPaths: current.changedPaths } };
23977
+ }
23978
+ return { code: 0, data: { success: true, ...await installHooks(options.projectRoot) } };
23979
+ } catch (error) {
23980
+ return { code: 1, data: { success: false, errors: [error instanceof Error ? error.message : String(error)] } };
23981
+ }
23982
+ }
23983
+ var execFileAsync4;
23984
+ var init_rdd_command = __esm(() => {
23985
+ init_schemas();
23986
+ init_paths();
23987
+ init_rdd_receipt();
23988
+ execFileAsync4 = promisify4(execFile4);
23989
+ });
23990
+
23539
23991
  // src/core/ccep/confirmation-gate.ts
23540
23992
  function matchesRiskThreshold(severity, threshold) {
23541
23993
  const order = { low: 0, medium: 1, high: 2 };
@@ -23841,11 +24293,11 @@ var init_output_validator = __esm(() => {
23841
24293
  });
23842
24294
 
23843
24295
  // src/core/ccep/command-parser.ts
23844
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
23845
- import { join as join19 } from "node:path";
24296
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
24297
+ import { basename as basename7, join as join20 } from "node:path";
23846
24298
  function readProjectId(projectRoot) {
23847
- const packageJsonPath = join19(projectRoot, "package.json");
23848
- if (existsSync4(packageJsonPath)) {
24299
+ const packageJsonPath = join20(projectRoot, "package.json");
24300
+ if (existsSync5(packageJsonPath)) {
23849
24301
  try {
23850
24302
  const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
23851
24303
  if (pkg.name) {
@@ -23853,7 +24305,7 @@ function readProjectId(projectRoot) {
23853
24305
  }
23854
24306
  } catch {}
23855
24307
  }
23856
- return projectRoot.split("/").pop() ?? "unknown";
24308
+ return basename7(projectRoot) || "unknown";
23857
24309
  }
23858
24310
  function parseCommand(command, userRequest, projectRoot) {
23859
24311
  const validatedCommand = WorkflowCommandSchema.parse(command);
@@ -23874,8 +24326,8 @@ function parseCommand(command, userRequest, projectRoot) {
23874
24326
  maxVariance: "low"
23875
24327
  }
23876
24328
  };
23877
- const packageJsonPath = join19(projectRoot, "package.json");
23878
- if (existsSync4(packageJsonPath)) {
24329
+ const packageJsonPath = join20(projectRoot, "package.json");
24330
+ if (existsSync5(packageJsonPath)) {
23879
24331
  try {
23880
24332
  const pkg = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
23881
24333
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
@@ -23919,7 +24371,8 @@ var init_command_parser = __esm(() => {
23919
24371
  prototype: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" },
23920
24372
  handoff: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" },
23921
24373
  clarify: { outputFormat: "plan", needConfirmation: false, riskThreshold: "low" },
23922
- security: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "high" }
24374
+ security: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "high" },
24375
+ odd: { outputFormat: "taskcard", needConfirmation: true, riskThreshold: "medium" }
23923
24376
  };
23924
24377
  });
23925
24378
 
@@ -23946,10 +24399,10 @@ var init_workflow_profile_loader = __esm(() => {
23946
24399
  });
23947
24400
 
23948
24401
  // src/core/evaluation/harness-catalog.ts
23949
- import { createHash as createHash4 } from "node:crypto";
23950
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
23951
- import { copyFile as copyFile2, mkdir as mkdir19, readFile as readFile28, readdir as readdir8, rm as rm4, writeFile as writeFile19 } from "node:fs/promises";
23952
- import { dirname as dirname11, join as join20, resolve as resolve27 } from "node:path";
24402
+ import { createHash as createHash5 } from "node:crypto";
24403
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
24404
+ import { copyFile as copyFile2, mkdir as mkdir20, readFile as readFile30, readdir as readdir10, rm as rm4, writeFile as writeFile20 } from "node:fs/promises";
24405
+ import { dirname as dirname12, join as join21, resolve as resolve29 } from "node:path";
23953
24406
  function catalogIds(catalog = DEFAULT_HARNESS_CATALOG) {
23954
24407
  return catalog.components.map((c) => c.id);
23955
24408
  }
@@ -23999,23 +24452,23 @@ function harnessFingerprint(disabled, contractVersion, ccepProfile = "feature",
23999
24452
  contractVersion,
24000
24453
  ccepProfile
24001
24454
  };
24002
- return createHash4("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
24455
+ return createHash5("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
24003
24456
  }
24004
24457
  async function loadHarnessCatalog(projectRoot) {
24005
- const overridePath = resolve27(projectRoot, EVAL_DIR2, CATALOG_FILE);
24458
+ const overridePath = resolve29(projectRoot, EVAL_DIR2, CATALOG_FILE);
24006
24459
  try {
24007
- const raw = await readFile28(overridePath, "utf-8");
24460
+ const raw = await readFile30(overridePath, "utf-8");
24008
24461
  return HarnessCatalogSchema.parse($parse(raw));
24009
24462
  } catch {
24010
24463
  return DEFAULT_HARNESS_CATALOG;
24011
24464
  }
24012
24465
  }
24013
24466
  function activeOverlayPath(projectRoot) {
24014
- return resolve27(projectRoot, EVAL_DIR2, ACTIVE_OVERLAY_FILE);
24467
+ return resolve29(projectRoot, EVAL_DIR2, ACTIVE_OVERLAY_FILE);
24015
24468
  }
24016
24469
  async function readActiveOverlay(projectRoot) {
24017
24470
  try {
24018
- const raw = await readFile28(activeOverlayPath(projectRoot), "utf-8");
24471
+ const raw = await readFile30(activeOverlayPath(projectRoot), "utf-8");
24019
24472
  return HarnessOverlaySchema.parse($parse(raw));
24020
24473
  } catch {
24021
24474
  return null;
@@ -24024,7 +24477,7 @@ async function readActiveOverlay(projectRoot) {
24024
24477
  function isProductGraphDisabled(projectRoot) {
24025
24478
  try {
24026
24479
  const path = activeOverlayPath(projectRoot);
24027
- if (!existsSync5(path))
24480
+ if (!existsSync6(path))
24028
24481
  return false;
24029
24482
  const overlay = HarnessOverlaySchema.parse($parse(readFileSync4(path, "utf-8")));
24030
24483
  return overlay.disableProductGraph === true || overlay.disabledComponents.includes("product_graph");
@@ -24069,55 +24522,55 @@ function applyCatalogToProfile(profile, disabled) {
24069
24522
  return next;
24070
24523
  }
24071
24524
  async function restoreOverlayBackup(projectRoot) {
24072
- const backupRoot = resolve27(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
24073
- if (!existsSync5(backupRoot))
24525
+ const backupRoot = resolve29(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
24526
+ if (!existsSync6(backupRoot))
24074
24527
  return;
24075
- const configBackup = join20(backupRoot, "config.yml");
24076
- const configDest = resolve27(projectRoot, ".codeconductor", "config.yml");
24077
- if (existsSync5(configBackup)) {
24078
- await mkdir19(dirname11(configDest), { recursive: true });
24528
+ const configBackup = join21(backupRoot, "config.yml");
24529
+ const configDest = resolve29(projectRoot, ".codeconductor", "config.yml");
24530
+ if (existsSync6(configBackup)) {
24531
+ await mkdir20(dirname12(configDest), { recursive: true });
24079
24532
  await copyFile2(configBackup, configDest);
24080
24533
  }
24081
- const workflowsBackup = join20(backupRoot, "workflows");
24082
- const workflowsDest = resolve27(projectRoot, ".codeconductor", "workflows");
24083
- if (existsSync5(workflowsBackup)) {
24084
- await mkdir19(workflowsDest, { recursive: true });
24085
- const files = await readdir8(workflowsBackup);
24534
+ const workflowsBackup = join21(backupRoot, "workflows");
24535
+ const workflowsDest = resolve29(projectRoot, ".codeconductor", "workflows");
24536
+ if (existsSync6(workflowsBackup)) {
24537
+ await mkdir20(workflowsDest, { recursive: true });
24538
+ const files = await readdir10(workflowsBackup);
24086
24539
  for (const file of files) {
24087
- await copyFile2(join20(workflowsBackup, file), join20(workflowsDest, file));
24540
+ await copyFile2(join21(workflowsBackup, file), join21(workflowsDest, file));
24088
24541
  }
24089
24542
  }
24090
- const overlayBackup = join20(backupRoot, ACTIVE_OVERLAY_FILE);
24543
+ const overlayBackup = join21(backupRoot, ACTIVE_OVERLAY_FILE);
24091
24544
  const overlayDest = activeOverlayPath(projectRoot);
24092
- if (existsSync5(overlayBackup)) {
24545
+ if (existsSync6(overlayBackup)) {
24093
24546
  await copyFile2(overlayBackup, overlayDest);
24094
- } else if (existsSync5(overlayDest)) {
24547
+ } else if (existsSync6(overlayDest)) {
24095
24548
  await rm4(overlayDest, { force: true });
24096
24549
  }
24097
24550
  }
24098
24551
  async function snapshotForBackup(projectRoot) {
24099
- const backupRoot = resolve27(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
24100
- if (existsSync5(backupRoot))
24552
+ const backupRoot = resolve29(projectRoot, EVAL_DIR2, OVERLAY_BACKUP_DIR);
24553
+ if (existsSync6(backupRoot))
24101
24554
  return;
24102
- await mkdir19(backupRoot, { recursive: true });
24103
- const configPath = resolve27(projectRoot, ".codeconductor", "config.yml");
24104
- if (existsSync5(configPath)) {
24105
- await copyFile2(configPath, join20(backupRoot, "config.yml"));
24106
- }
24107
- const workflowsDir = resolve27(projectRoot, ".codeconductor", "workflows");
24108
- if (existsSync5(workflowsDir)) {
24109
- const dest = join20(backupRoot, "workflows");
24110
- await mkdir19(dest, { recursive: true });
24111
- const files = await readdir8(workflowsDir);
24555
+ await mkdir20(backupRoot, { recursive: true });
24556
+ const configPath = resolve29(projectRoot, ".codeconductor", "config.yml");
24557
+ if (existsSync6(configPath)) {
24558
+ await copyFile2(configPath, join21(backupRoot, "config.yml"));
24559
+ }
24560
+ const workflowsDir = resolve29(projectRoot, ".codeconductor", "workflows");
24561
+ if (existsSync6(workflowsDir)) {
24562
+ const dest = join21(backupRoot, "workflows");
24563
+ await mkdir20(dest, { recursive: true });
24564
+ const files = await readdir10(workflowsDir);
24112
24565
  for (const file of files) {
24113
24566
  if (file.endsWith(".yml")) {
24114
- await copyFile2(join20(workflowsDir, file), join20(dest, file));
24567
+ await copyFile2(join21(workflowsDir, file), join21(dest, file));
24115
24568
  }
24116
24569
  }
24117
24570
  }
24118
24571
  const overlay = activeOverlayPath(projectRoot);
24119
- if (existsSync5(overlay)) {
24120
- await copyFile2(overlay, join20(backupRoot, ACTIVE_OVERLAY_FILE));
24572
+ if (existsSync6(overlay)) {
24573
+ await copyFile2(overlay, join21(backupRoot, ACTIVE_OVERLAY_FILE));
24121
24574
  }
24122
24575
  }
24123
24576
  async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
@@ -24126,13 +24579,13 @@ async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
24126
24579
  await restoreOverlayBackup(projectRoot);
24127
24580
  await snapshotForBackup(projectRoot);
24128
24581
  }
24129
- const workflowsDir = resolve27(projectRoot, ".codeconductor", "workflows");
24130
- await mkdir19(workflowsDir, { recursive: true });
24131
- await mkdir19(resolve27(projectRoot, EVAL_DIR2), { recursive: true });
24582
+ const workflowsDir = resolve29(projectRoot, ".codeconductor", "workflows");
24583
+ await mkdir20(workflowsDir, { recursive: true });
24584
+ await mkdir20(resolve29(projectRoot, EVAL_DIR2), { recursive: true });
24132
24585
  for (const command of WORKFLOW_OVERLAY_COMMANDS) {
24133
24586
  const base = loadWorkflowProfile(command);
24134
24587
  const next = applyCatalogToProfile(base, disabled);
24135
- await writeFile19(join20(workflowsDir, `${command}.yml`), $stringify(next), "utf-8");
24588
+ await writeFile20(join21(workflowsDir, `${command}.yml`), $stringify(next), "utf-8");
24136
24589
  }
24137
24590
  const needsConfig = disabled.includes("compile_loop") || disabled.includes("council");
24138
24591
  if (needsConfig) {
@@ -24160,7 +24613,7 @@ async function applyHarnessOverlay(projectRoot, disabled, options = {}) {
24160
24613
  contractVersion: options.contractVersion,
24161
24614
  ccepProfile: options.ccepProfile ?? "feature"
24162
24615
  });
24163
- await writeFile19(activeOverlayPath(projectRoot), $stringify(overlay), "utf-8");
24616
+ await writeFile20(activeOverlayPath(projectRoot), $stringify(overlay), "utf-8");
24164
24617
  return ok(overlay);
24165
24618
  } catch (e) {
24166
24619
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -24271,9 +24724,9 @@ var init_harness_catalog = __esm(() => {
24271
24724
  });
24272
24725
 
24273
24726
  // src/core/openspec/openspec-state.ts
24274
- import { createHash as createHash5 } from "node:crypto";
24275
- import { mkdir as mkdir20, readFile as readFile29, rename as rename3, writeFile as writeFile20 } from "node:fs/promises";
24276
- import { resolve as resolve28 } from "node:path";
24727
+ import { createHash as createHash6 } from "node:crypto";
24728
+ import { mkdir as mkdir21, readFile as readFile31, rename as rename4, writeFile as writeFile21 } from "node:fs/promises";
24729
+ import { resolve as resolve30 } from "node:path";
24277
24730
  function canTransition(from, to) {
24278
24731
  return ALLOWED_TRANSITIONS[from]?.includes(to) ?? false;
24279
24732
  }
@@ -24294,13 +24747,13 @@ function applyBacklogTransition(content, itemId, from, to, progress) {
24294
24747
  }
24295
24748
  async function writeFileAtomic(path, content) {
24296
24749
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
24297
- await writeFile20(tmp, content, "utf-8");
24298
- await rename3(tmp, path);
24750
+ await writeFile21(tmp, content, "utf-8");
24751
+ await rename4(tmp, path);
24299
24752
  }
24300
24753
  async function loadOpenspecState(projectRoot) {
24301
24754
  try {
24302
- const filePath = resolve28(projectRoot, STATE_FILE);
24303
- const content = await readFile29(filePath, "utf-8");
24755
+ const filePath = resolve30(projectRoot, STATE_FILE);
24756
+ const content = await readFile31(filePath, "utf-8");
24304
24757
  const data = JSON.parse(content);
24305
24758
  return ok(OpenspecStateSchema.parse(data));
24306
24759
  } catch (e) {
@@ -24317,10 +24770,10 @@ async function loadOpenspecState(projectRoot) {
24317
24770
  }
24318
24771
  async function writeOpenspecState(projectRoot, state) {
24319
24772
  try {
24320
- const dir = resolve28(projectRoot, ".codeconductor");
24321
- await mkdir20(dir, { recursive: true });
24773
+ const dir = resolve30(projectRoot, ".codeconductor");
24774
+ await mkdir21(dir, { recursive: true });
24322
24775
  const validated = OpenspecStateSchema.parse(state);
24323
- await writeFileAtomic(resolve28(dir, "openspec-state.json"), JSON.stringify(validated, null, 2));
24776
+ await writeFileAtomic(resolve30(dir, "openspec-state.json"), JSON.stringify(validated, null, 2));
24324
24777
  return ok(undefined);
24325
24778
  } catch (e) {
24326
24779
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -24342,7 +24795,7 @@ function getNextTaskCard(state) {
24342
24795
  return null;
24343
24796
  }
24344
24797
  function hashContent(content) {
24345
- return createHash5("sha256").update(content).digest("hex").slice(0, 16);
24798
+ return createHash6("sha256").update(content).digest("hex").slice(0, 16);
24346
24799
  }
24347
24800
  function serializeItemSnapshot(item) {
24348
24801
  return JSON.stringify({
@@ -24424,19 +24877,19 @@ var init_openspec_state = __esm(() => {
24424
24877
  });
24425
24878
 
24426
24879
  // src/core/ccep/context-resolver.ts
24427
- import { existsSync as existsSync6 } from "node:fs";
24428
- import { readFile as readFile30 } from "node:fs/promises";
24429
- import { join as join21 } from "node:path";
24880
+ import { existsSync as existsSync7 } from "node:fs";
24881
+ import { readFile as readFile32 } from "node:fs/promises";
24882
+ import { join as join22 } from "node:path";
24430
24883
  async function loadProductKnowledge(projectRoot) {
24431
24884
  if (isProductGraphDisabled(projectRoot)) {
24432
24885
  return {};
24433
24886
  }
24434
24887
  const graphPath = productGraphPath(projectRoot);
24435
- if (!existsSync6(graphPath)) {
24888
+ if (!existsSync7(graphPath)) {
24436
24889
  return {};
24437
24890
  }
24438
24891
  try {
24439
- const raw = await readFile30(graphPath, "utf-8");
24892
+ const raw = await readFile32(graphPath, "utf-8");
24440
24893
  const graph = ProductGraphSchema.parse(JSON.parse(raw));
24441
24894
  return {
24442
24895
  productName: graph.productName,
@@ -24452,18 +24905,18 @@ async function loadProductKnowledge(projectRoot) {
24452
24905
  }
24453
24906
  async function resolveAstSource(projectRoot) {
24454
24907
  if (isProductGraphDisabled(projectRoot)) {
24455
- const graphifyPath2 = join21(projectRoot, "graphify-out", "graph.json");
24456
- if (existsSync6(graphifyPath2)) {
24908
+ const graphifyPath2 = join22(projectRoot, "graphify-out", "graph.json");
24909
+ if (existsSync7(graphifyPath2)) {
24457
24910
  return { source: "graphify", confidence: "medium" };
24458
24911
  }
24459
24912
  return { source: "manual", confidence: "low" };
24460
24913
  }
24461
24914
  const graphPath = productGraphPath(projectRoot);
24462
- if (existsSync6(graphPath)) {
24915
+ if (existsSync7(graphPath)) {
24463
24916
  return { source: "product-graph", confidence: "high" };
24464
24917
  }
24465
- const graphifyPath = join21(projectRoot, "graphify-out", "graph.json");
24466
- if (existsSync6(graphifyPath)) {
24918
+ const graphifyPath = join22(projectRoot, "graphify-out", "graph.json");
24919
+ if (existsSync7(graphifyPath)) {
24467
24920
  return { source: "graphify", confidence: "medium" };
24468
24921
  }
24469
24922
  try {
@@ -24704,6 +25157,12 @@ function councilConsensus(verdicts, config = DEFAULT_CONFIG2) {
24704
25157
  individualVerdicts: verdicts
24705
25158
  };
24706
25159
  }
25160
+ if (config.candidateHash !== undefined) {
25161
+ const mismatched = verdicts.filter((verdict) => verdict.candidateHash !== config.candidateHash);
25162
+ if (mismatched.length > 0) {
25163
+ return escalated(verdicts, `Candidate receipt mismatch for ${mismatched.length} ballot(s) — escalated for human review.`, counts);
25164
+ }
25165
+ }
24707
25166
  const critical = allFindings.filter((f) => f.severity === "critical");
24708
25167
  if (critical.length > 0 && policy === "reject") {
24709
25168
  return {
@@ -25265,12 +25724,26 @@ async function ccepCommand(options) {
25265
25724
  }
25266
25725
  mergedConfig = { ...mergedConfig, ...parsedConfig.data };
25267
25726
  }
25727
+ let currentCandidateHash;
25728
+ if (mergedConfig?.candidateHash !== undefined) {
25729
+ const { captureReceipt: captureReceipt2, collectReceiptPaths: collectReceiptPaths2 } = await Promise.resolve().then(() => (init_rdd_receipt(), exports_rdd_receipt));
25730
+ const receipt = await captureReceipt2(projectRoot, {
25731
+ taskId: "council",
25732
+ phase: "review",
25733
+ paths: await collectReceiptPaths2(projectRoot),
25734
+ outcome: "passed",
25735
+ coverage: "project"
25736
+ });
25737
+ currentCandidateHash = receipt.manifestHash;
25738
+ mergedConfig = { ...mergedConfig, candidateHash: currentCandidateHash };
25739
+ }
25268
25740
  const verdict = councilConsensus(parsedBallots.verdicts, mergedConfig);
25269
25741
  return {
25270
25742
  code: consensusExitCode(verdict.status),
25271
25743
  data: {
25272
25744
  success: verdict.status === "APPROVED",
25273
25745
  command: "ccep consensus",
25746
+ candidateHash: currentCandidateHash,
25274
25747
  verdict
25275
25748
  }
25276
25749
  };
@@ -25507,9 +25980,9 @@ var init_backlog_validator = __esm(() => {
25507
25980
  });
25508
25981
 
25509
25982
  // src/core/openspec/backlog-scanner.ts
25510
- import { execFileSync as execFileSync2 } from "node:child_process";
25511
- import { readFile as readFile31 } from "node:fs/promises";
25512
- import { resolve as resolve29 } from "node:path";
25983
+ import { execFileSync } from "node:child_process";
25984
+ import { readFile as readFile33 } from "node:fs/promises";
25985
+ import { resolve as resolve31 } from "node:path";
25513
25986
  function itemSnapshot(items) {
25514
25987
  const snap = {};
25515
25988
  for (const item of items) {
@@ -25519,12 +25992,12 @@ function itemSnapshot(items) {
25519
25992
  }
25520
25993
  async function scanBacklog(projectRoot, previousSnapshot = {}) {
25521
25994
  try {
25522
- const filePath = resolve29(projectRoot, BACKLOG_FILENAME);
25523
- const content = await readFile31(filePath, "utf-8");
25995
+ const filePath = resolve31(projectRoot, BACKLOG_FILENAME);
25996
+ const content = await readFile33(filePath, "utf-8");
25524
25997
  const hash = hashContent(content);
25525
25998
  let fileChanged = true;
25526
25999
  try {
25527
- const diff = execFileSync2("git", ["diff", "--name-only", "--", BACKLOG_FILENAME], {
26000
+ const diff = execFileSync("git", ["diff", "--name-only", "--", BACKLOG_FILENAME], {
25528
26001
  cwd: projectRoot,
25529
26002
  encoding: "utf-8",
25530
26003
  stdio: ["ignore", "pipe", "ignore"]
@@ -25757,8 +26230,8 @@ var init_backlog_planner = __esm(() => {
25757
26230
  });
25758
26231
 
25759
26232
  // src/core/openspec/openspec-generator.ts
25760
- import { mkdir as mkdir21, rename as rename4, writeFile as writeFile21 } from "node:fs/promises";
25761
- import { basename as basename6, resolve as resolve30 } from "node:path";
26233
+ import { mkdir as mkdir22, rename as rename5, writeFile as writeFile22 } from "node:fs/promises";
26234
+ import { basename as basename8, resolve as resolve32 } from "node:path";
25762
26235
  function pad3(n) {
25763
26236
  return String(n).padStart(3, "0");
25764
26237
  }
@@ -25823,7 +26296,7 @@ The system MUST ${criterion}.
25823
26296
 
25824
26297
  #### Scenario: SC-${n} ${criterion}
25825
26298
 
25826
- - GIVEN the current behavior in ${item.scope}
26299
+ - GIVEN the relevant scoped behavior
25827
26300
  - WHEN this change is applied
25828
26301
  - THEN ${criterion}
25829
26302
  `;
@@ -25837,8 +26310,8 @@ ${blocks.join(`
25837
26310
  `;
25838
26311
  }
25839
26312
  async function writeTasksMarkdown(projectRoot, changePath, cards, options = {}) {
25840
- await mkdir21(resolve30(projectRoot, changePath), { recursive: true });
25841
- await writeFile21(resolve30(projectRoot, changePath, "tasks.md"), tasksContent(cards, options), "utf-8");
26313
+ await mkdir22(resolve32(projectRoot, changePath), { recursive: true });
26314
+ await writeFile22(resolve32(projectRoot, changePath, "tasks.md"), tasksContent(cards, options), "utf-8");
25842
26315
  }
25843
26316
  function tasksContent(cards, options = {}) {
25844
26317
  const criteria = options.acceptanceCriteria ?? [];
@@ -25883,19 +26356,19 @@ function tasksContent(cards, options = {}) {
25883
26356
  }
25884
26357
  async function generateOpenspecChange(projectRoot, item, taskCards, options = {}) {
25885
26358
  const slug = buildChangeSlug(item);
25886
- const changeDir = resolve30(projectRoot, "openspec", "changes", slug);
25887
- await mkdir21(resolve30(changeDir, "specs"), { recursive: true });
26359
+ const changeDir = resolve32(projectRoot, "openspec", "changes", slug);
26360
+ await mkdir22(resolve32(changeDir, "specs"), { recursive: true });
25888
26361
  const taskOpts = {
25889
26362
  tddRequired: options.tddRequired,
25890
26363
  acceptanceCriteria: options.acceptanceCriteria ?? item.acceptanceCriteria
25891
26364
  };
25892
- await writeFile21(resolve30(changeDir, "proposal.md"), proposalContent(item), "utf-8");
25893
- await writeFile21(resolve30(changeDir, "design.md"), designContent(item), "utf-8");
25894
- await writeFile21(resolve30(changeDir, "tasks.md"), tasksContent(taskCards, taskOpts), "utf-8");
26365
+ await writeFile22(resolve32(changeDir, "proposal.md"), proposalContent(item), "utf-8");
26366
+ await writeFile22(resolve32(changeDir, "design.md"), designContent(item), "utf-8");
26367
+ await writeFile22(resolve32(changeDir, "tasks.md"), tasksContent(taskCards, taskOpts), "utf-8");
25895
26368
  const capability = capabilitySlug(item.title);
25896
- await mkdir21(resolve30(changeDir, "specs", capability), { recursive: true });
25897
- await writeFile21(resolve30(changeDir, "specs", capability, "spec.md"), specDelta(item), "utf-8");
25898
- await writeFile21(resolve30(changeDir, "change.yaml"), `version: 1
26369
+ await mkdir22(resolve32(changeDir, "specs", capability), { recursive: true });
26370
+ await writeFile22(resolve32(changeDir, "specs", capability, "spec.md"), specDelta(item), "utf-8");
26371
+ await writeFile22(resolve32(changeDir, "change.yaml"), `version: 1
25899
26372
  backlogId: ${item.id}
25900
26373
  profile: standard
25901
26374
  capabilities:
@@ -25904,25 +26377,25 @@ capabilities:
25904
26377
  return `openspec/changes/${slug}`;
25905
26378
  }
25906
26379
  async function archiveChangeFolder(projectRoot, changePath) {
25907
- const src = resolve30(projectRoot, changePath);
25908
- const slug = basename6(changePath);
25909
- const destDir = resolve30(projectRoot, "openspec", "changes", "archive");
25910
- await mkdir21(destDir, { recursive: true });
25911
- const dest = resolve30(destDir, slug);
25912
- await rename4(src, dest);
26380
+ const src = resolve32(projectRoot, changePath);
26381
+ const slug = basename8(changePath);
26382
+ const destDir = resolve32(projectRoot, "openspec", "changes", "archive");
26383
+ await mkdir22(destDir, { recursive: true });
26384
+ const dest = resolve32(destDir, slug);
26385
+ await rename5(src, dest);
25913
26386
  return `openspec/changes/archive/${slug}`;
25914
26387
  }
25915
26388
  async function ensureOpenspecConfig(projectRoot) {
25916
- const configPath = resolve30(projectRoot, "openspec", "config.yaml");
26389
+ const configPath = resolve32(projectRoot, "openspec", "config.yaml");
25917
26390
  try {
25918
26391
  const { access: access7 } = await import("node:fs/promises");
25919
26392
  await access7(configPath);
25920
26393
  } catch {
25921
- await mkdir21(resolve30(projectRoot, "openspec"), { recursive: true });
26394
+ await mkdir22(resolve32(projectRoot, "openspec"), { recursive: true });
25922
26395
  const stub = `# OpenSpec project config (CodeConductor native compatible)
25923
26396
  schema: spec-driven
25924
26397
  `;
25925
- await writeFile21(configPath, stub, "utf-8");
26398
+ await writeFile22(configPath, stub, "utf-8");
25926
26399
  }
25927
26400
  }
25928
26401
  var init_openspec_generator = __esm(() => {
@@ -25930,8 +26403,8 @@ var init_openspec_generator = __esm(() => {
25930
26403
  });
25931
26404
 
25932
26405
  // src/core/openspec/spec-quality.ts
25933
- import { readdir as readdir9, readFile as readFile32 } from "node:fs/promises";
25934
- import { join as join22, relative as relative7, resolve as resolve31 } from "node:path";
26406
+ import { readdir as readdir11, readFile as readFile34 } from "node:fs/promises";
26407
+ import { join as join23, relative as relative10, resolve as resolve33 } from "node:path";
25935
26408
  function uniqueIds(matches) {
25936
26409
  return [...new Set(matches ?? [])];
25937
26410
  }
@@ -26048,12 +26521,12 @@ async function walkMarkdown(dir) {
26048
26521
  const out = [];
26049
26522
  let entries;
26050
26523
  try {
26051
- entries = await readdir9(dir, { withFileTypes: true });
26524
+ entries = await readdir11(dir, { withFileTypes: true });
26052
26525
  } catch {
26053
26526
  return out;
26054
26527
  }
26055
26528
  for (const entry of entries) {
26056
- const full = join22(dir, entry.name);
26529
+ const full = join23(dir, entry.name);
26057
26530
  if (entry.isDirectory()) {
26058
26531
  out.push(...await walkMarkdown(full));
26059
26532
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -26063,8 +26536,8 @@ async function walkMarkdown(dir) {
26063
26536
  return out;
26064
26537
  }
26065
26538
  async function assessChangeFolder(projectRoot, changePath) {
26066
- const root = resolve31(projectRoot);
26067
- const changeRoot = resolve31(root, changePath);
26539
+ const root = resolve33(projectRoot);
26540
+ const changeRoot = resolve33(root, changePath);
26068
26541
  if (!changeRoot.startsWith(root)) {
26069
26542
  return {
26070
26543
  valid: false,
@@ -26082,22 +26555,22 @@ async function assessChangeFolder(projectRoot, changePath) {
26082
26555
  successCriteria: []
26083
26556
  };
26084
26557
  }
26085
- const specDir = join22(changeRoot, "specs");
26558
+ const specDir = join23(changeRoot, "specs");
26086
26559
  const specFiles = await walkMarkdown(specDir);
26087
- const extra = ["proposal.md", "design.md"].map((name) => join22(changeRoot, name));
26560
+ const extra = ["proposal.md", "design.md"].map((name) => join23(changeRoot, name));
26088
26561
  const reports = [];
26089
26562
  for (const file of specFiles) {
26090
- const content = await readFile32(file, "utf-8");
26091
- reports.push(assessSpecMarkdown(content, relative7(root, file).replace(/\\/g, "/")));
26563
+ const content = await readFile34(file, "utf-8");
26564
+ reports.push(assessSpecMarkdown(content, relative10(root, file).replace(/\\/g, "/")));
26092
26565
  }
26093
26566
  for (const file of extra) {
26094
26567
  let content;
26095
26568
  try {
26096
- content = await readFile32(file, "utf-8");
26569
+ content = await readFile34(file, "utf-8");
26097
26570
  } catch {
26098
26571
  continue;
26099
26572
  }
26100
- const rel = relative7(root, file).replace(/\\/g, "/");
26573
+ const rel = relative10(root, file).replace(/\\/g, "/");
26101
26574
  if (PLACEHOLDER.test(content)) {
26102
26575
  reports.push({
26103
26576
  valid: false,
@@ -26141,15 +26614,15 @@ var init_spec_quality = __esm(() => {
26141
26614
  FR_ID = /\bFR-\d{3}\b/g;
26142
26615
  SC_ID = /\bSC-\d{3}\b/g;
26143
26616
  US_AC = /\b(?:US|AC)#?\d+\b/g;
26144
- GWT = /\bGIVEN\b[\s\S]{0,400}\bWHEN\b[\s\S]{0,400}\bTHEN\b/i;
26617
+ GWT = /\bGIVEN\b[\s\S]*?\bWHEN\b[\s\S]*?\bTHEN\b/i;
26145
26618
  NEEDS_CLARIFICATION = /\[NEEDS CLARIFICATION:[^\]]*\]/gi;
26146
26619
  PLACEHOLDER = /\(To be completed/i;
26147
26620
  HOW_LEAK = /\b(TypeScript|PostgreSQL|Django|React|Spring Boot|package\.json|implements class)\b/i;
26148
26621
  });
26149
26622
 
26150
26623
  // src/core/openspec/spec-analyzer.ts
26151
- import { readFile as readFile33, readdir as readdir10 } from "node:fs/promises";
26152
- import { join as join23, resolve as resolve32 } from "node:path";
26624
+ import { readFile as readFile35, readdir as readdir12 } from "node:fs/promises";
26625
+ import { join as join24, resolve as resolve34 } from "node:path";
26153
26626
  function unique(ids) {
26154
26627
  return [...new Set(ids)];
26155
26628
  }
@@ -26176,17 +26649,17 @@ function testSection(tasksMarkdown) {
26176
26649
  async function readDeltaSpecs(specDir) {
26177
26650
  let entries;
26178
26651
  try {
26179
- entries = await readdir10(specDir, { withFileTypes: true });
26652
+ entries = await readdir12(specDir, { withFileTypes: true });
26180
26653
  } catch {
26181
26654
  return "";
26182
26655
  }
26183
26656
  const contents = [];
26184
26657
  for (const entry of entries) {
26185
- const path = join23(specDir, entry.name);
26658
+ const path = join24(specDir, entry.name);
26186
26659
  if (entry.isDirectory()) {
26187
26660
  contents.push(await readDeltaSpecs(path));
26188
26661
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
26189
- contents.push(await readFile33(path, "utf-8"));
26662
+ contents.push(await readFile35(path, "utf-8"));
26190
26663
  }
26191
26664
  }
26192
26665
  return contents.filter(Boolean).join(`
@@ -26264,17 +26737,17 @@ function analyzeSpecArtifacts(input) {
26264
26737
  };
26265
26738
  }
26266
26739
  async function analyzeChangeFolder(projectRoot, changePath, options) {
26267
- const changeRoot = resolve32(projectRoot, changePath);
26268
- const specMarkdown = await readDeltaSpecs(join23(changeRoot, "specs"));
26740
+ const changeRoot = resolve34(projectRoot, changePath);
26741
+ const specMarkdown = await readDeltaSpecs(join24(changeRoot, "specs"));
26269
26742
  let tasksMarkdown = "";
26270
26743
  try {
26271
- tasksMarkdown = await readFile33(join23(changeRoot, "tasks.md"), "utf-8");
26744
+ tasksMarkdown = await readFile35(join24(changeRoot, "tasks.md"), "utf-8");
26272
26745
  } catch {
26273
26746
  tasksMarkdown = "";
26274
26747
  }
26275
26748
  let designMarkdown;
26276
26749
  try {
26277
- designMarkdown = await readFile33(join23(changeRoot, "design.md"), "utf-8");
26750
+ designMarkdown = await readFile35(join24(changeRoot, "design.md"), "utf-8");
26278
26751
  } catch {
26279
26752
  designMarkdown = undefined;
26280
26753
  }
@@ -26293,18 +26766,18 @@ var init_spec_analyzer = __esm(() => {
26293
26766
  });
26294
26767
 
26295
26768
  // src/core/openspec/spec-sync.ts
26296
- import { mkdir as mkdir22, readFile as readFile34, readdir as readdir11 } from "node:fs/promises";
26297
- import { join as join24, relative as relative8, resolve as resolve33 } from "node:path";
26769
+ import { mkdir as mkdir23, readFile as readFile36, readdir as readdir13 } from "node:fs/promises";
26770
+ import { join as join25, relative as relative11, resolve as resolve35 } from "node:path";
26298
26771
  async function markdownFiles(dir) {
26299
26772
  let entries;
26300
26773
  try {
26301
- entries = await readdir11(dir, { withFileTypes: true });
26774
+ entries = await readdir13(dir, { withFileTypes: true });
26302
26775
  } catch {
26303
26776
  return [];
26304
26777
  }
26305
26778
  const files = [];
26306
26779
  for (const entry of entries) {
26307
- const path = join24(dir, entry.name);
26780
+ const path = join25(dir, entry.name);
26308
26781
  if (entry.isDirectory())
26309
26782
  files.push(...await markdownFiles(path));
26310
26783
  else if (entry.isFile() && entry.name.endsWith(".md"))
@@ -26380,9 +26853,9 @@ ${delta.markdown}`;
26380
26853
  ` };
26381
26854
  }
26382
26855
  async function syncChangeSpecs(projectRoot, changePath) {
26383
- const root = resolve33(projectRoot);
26384
- const changeRoot = resolve33(root, changePath);
26385
- const changeSpecs = join24(changeRoot, "specs");
26856
+ const root = resolve35(projectRoot);
26857
+ const changeRoot = resolve35(root, changePath);
26858
+ const changeSpecs = join25(changeRoot, "specs");
26386
26859
  if (changeRoot !== root && !changeRoot.startsWith(`${root}/`)) {
26387
26860
  return { success: false, syncedPaths: [], errors: ["Change path escapes project root"] };
26388
26861
  }
@@ -26392,34 +26865,34 @@ async function syncChangeSpecs(projectRoot, changePath) {
26392
26865
  }
26393
26866
  const writes = [];
26394
26867
  for (const file of files) {
26395
- const delta = await readFile34(file, "utf-8");
26868
+ const delta = await readFile36(file, "utf-8");
26396
26869
  const requirements = deltaRequirements(delta);
26397
26870
  if (requirements.length === 0) {
26398
26871
  return {
26399
26872
  success: false,
26400
26873
  syncedPaths: [],
26401
- errors: [`${relative8(root, file)}: no delta requirements found`]
26874
+ errors: [`${relative11(root, file)}: no delta requirements found`]
26402
26875
  };
26403
26876
  }
26404
- const relativeSpec = relative8(changeSpecs, file);
26405
- const target = join24(root, "openspec", "specs", relativeSpec);
26877
+ const relativeSpec = relative11(changeSpecs, file);
26878
+ const target = join25(root, "openspec", "specs", relativeSpec);
26406
26879
  let existing = `# ${relativeSpec.replace(/\\/g, "/")}
26407
26880
  `;
26408
26881
  try {
26409
- existing = await readFile34(target, "utf-8");
26882
+ existing = await readFile36(target, "utf-8");
26410
26883
  } catch {}
26411
- const merged = upsertRequirements(existing, requirements, relative8(root, file));
26884
+ const merged = upsertRequirements(existing, requirements, relative11(root, file));
26412
26885
  if (!merged.ok)
26413
26886
  return { success: false, syncedPaths: [], errors: [merged.error] };
26414
26887
  writes.push({ path: target, content: merged.content });
26415
26888
  }
26416
26889
  for (const write of writes) {
26417
- await mkdir22(resolve33(write.path, ".."), { recursive: true });
26890
+ await mkdir23(resolve35(write.path, ".."), { recursive: true });
26418
26891
  await writeFileAtomic(write.path, write.content);
26419
26892
  }
26420
26893
  return {
26421
26894
  success: true,
26422
- syncedPaths: writes.map((write) => relative8(root, write.path).replace(/\\/g, "/")),
26895
+ syncedPaths: writes.map((write) => relative11(root, write.path).replace(/\\/g, "/")),
26423
26896
  errors: []
26424
26897
  };
26425
26898
  }
@@ -26435,8 +26908,8 @@ var exports_openspec_command = {};
26435
26908
  __export(exports_openspec_command, {
26436
26909
  openspecCommand: () => openspecCommand
26437
26910
  });
26438
- import { readFile as readFile35 } from "node:fs/promises";
26439
- import { resolve as resolve34 } from "node:path";
26911
+ import { readFile as readFile37 } from "node:fs/promises";
26912
+ import { resolve as resolve36 } from "node:path";
26440
26913
  async function openspecCommand(options) {
26441
26914
  const { subcommand, itemId, projectRoot, output, reason } = options;
26442
26915
  switch (subcommand) {
@@ -26458,6 +26931,8 @@ async function openspecCommand(options) {
26458
26931
  return handleDone(projectRoot, itemId);
26459
26932
  case "block":
26460
26933
  return handleBlock(projectRoot, itemId, reason);
26934
+ case "unblock":
26935
+ return handleUnblock(projectRoot, itemId);
26461
26936
  case "archive":
26462
26937
  return handleArchive(projectRoot, itemId);
26463
26938
  default:
@@ -26480,7 +26955,7 @@ function fail(command, errors3, extra = {}) {
26480
26955
  };
26481
26956
  }
26482
26957
  async function persistBacklog(projectRoot, content) {
26483
- await writeFileAtomic(resolve34(projectRoot, BACKLOG_FILENAME), content);
26958
+ await writeFileAtomic(resolve36(projectRoot, BACKLOG_FILENAME), content);
26484
26959
  }
26485
26960
  async function persistState(projectRoot, state) {
26486
26961
  const written = await writeOpenspecState(projectRoot, state);
@@ -26490,7 +26965,7 @@ async function persistState(projectRoot, state) {
26490
26965
  return { ok: true };
26491
26966
  }
26492
26967
  async function readBacklogMarkdown(projectRoot) {
26493
- return readFile35(resolve34(projectRoot, BACKLOG_FILENAME), "utf-8");
26968
+ return readFile37(resolve36(projectRoot, BACKLOG_FILENAME), "utf-8");
26494
26969
  }
26495
26970
  async function transitionItem(projectRoot, item, to, progress) {
26496
26971
  const content = await readBacklogMarkdown(projectRoot);
@@ -26688,17 +27163,18 @@ async function handleAnalyze(projectRoot, itemId) {
26688
27163
  const policyParts = [];
26689
27164
  for (const name of ["AGENTS.md", "BACKLOG.md"]) {
26690
27165
  try {
26691
- policyParts.push(await readFile35(resolve34(projectRoot, name), "utf-8"));
27166
+ policyParts.push(await readFile37(resolve36(projectRoot, name), "utf-8"));
26692
27167
  } catch {}
26693
27168
  }
26694
27169
  const cards = itemCards(stateResult.data, targetId);
26695
27170
  const tddCards = cards.filter((c) => c.phase === "test" || c.phase === "implement");
26696
27171
  let hasTddEvidence;
26697
27172
  if (tddCards.length > 0) {
26698
- hasTddEvidence = false;
27173
+ hasTddEvidence = true;
26699
27174
  for (const card of tddCards) {
26700
- if (await hasTddRunnerEvidence(projectRoot, card.id)) {
26701
- hasTddEvidence = true;
27175
+ const expectedPhase = card.phase === "test" ? "red" : "green";
27176
+ if (!await hasTddRunnerEvidence(projectRoot, card.id, expectedPhase)) {
27177
+ hasTddEvidence = false;
26702
27178
  break;
26703
27179
  }
26704
27180
  }
@@ -26883,10 +27359,11 @@ async function handleDone(projectRoot, cardId) {
26883
27359
  const backlog = await loadBacklog(projectRoot);
26884
27360
  const tddRequired = backlog.success ? backlog.data.global.tddRequired : false;
26885
27361
  if (tddRequired && (card.phase === "test" || card.phase === "implement")) {
26886
- const evidenced = await hasTddRunnerEvidence(projectRoot, cardId);
27362
+ const expectedPhase = card.phase === "test" ? "red" : "green";
27363
+ const evidenced = await hasTddRunnerEvidence(projectRoot, cardId, expectedPhase);
26887
27364
  if (!evidenced) {
26888
27365
  return fail(command, [
26889
- `Card ${cardId} (${card.phase}) requires verification-runner TDD evidence. Run captureTddSuiteEvidence before openspec done.`
27366
+ `Card ${cardId} (${card.phase}) requires current ${expectedPhase.toUpperCase()} verification-runner TDD evidence. Run captureTddSuiteEvidence before openspec done.`
26890
27367
  ]);
26891
27368
  }
26892
27369
  }
@@ -26961,6 +27438,57 @@ async function handleBlock(projectRoot, cardId, reason) {
26961
27438
  }
26962
27439
  };
26963
27440
  }
27441
+ async function handleUnblock(projectRoot, cardId) {
27442
+ const command = "openspec unblock";
27443
+ if (!cardId) {
27444
+ return fail(command, ["Missing card id. Usage: openspec unblock <cardId>"]);
27445
+ }
27446
+ const loaded = await loadStateOrFail(projectRoot, command);
27447
+ if (!loaded.ok)
27448
+ return loaded.response;
27449
+ const card = findCard(loaded.state, cardId);
27450
+ if (!card) {
27451
+ return fail(command, [`Task card ${cardId} not found`]);
27452
+ }
27453
+ if (card.status !== "blocked") {
27454
+ return fail(command, [`Card ${cardId} must be blocked before unblock (got ${card.status})`]);
27455
+ }
27456
+ const itemLoaded = await loadItemOrFail(projectRoot, card.backlogId, command);
27457
+ if (!itemLoaded.ok)
27458
+ return itemLoaded.response;
27459
+ if (itemLoaded.item.status !== "BLOCKED") {
27460
+ return fail(command, [
27461
+ `Backlog item ${itemLoaded.item.id} must be BLOCKED before unblock (got ${itemLoaded.item.status})`
27462
+ ]);
27463
+ }
27464
+ const state = setTaskCardStatus(loaded.state, cardId, "pending");
27465
+ const written = await persistState(projectRoot, state);
27466
+ if (!written.ok)
27467
+ return fail(command, [written.error]);
27468
+ const changePath = state.changePaths[card.backlogId];
27469
+ if (changePath) {
27470
+ const backlog = await loadBacklog(projectRoot);
27471
+ await writeTasksMarkdown(projectRoot, changePath, itemCards(state, card.backlogId), {
27472
+ tddRequired: backlog.success ? backlog.data.global.tddRequired : false,
27473
+ acceptanceCriteria: itemLoaded.item.acceptanceCriteria
27474
+ });
27475
+ }
27476
+ const transition = await transitionItem(projectRoot, itemLoaded.item, "READY");
27477
+ if (!transition.ok)
27478
+ return fail(command, [transition.error]);
27479
+ return {
27480
+ code: 0,
27481
+ data: {
27482
+ success: true,
27483
+ command,
27484
+ cardId,
27485
+ cardStatus: "pending",
27486
+ itemId: itemLoaded.item.id,
27487
+ itemStatus: "READY",
27488
+ nextStep: `Run openspec plan ${itemLoaded.item.id}.`
27489
+ }
27490
+ };
27491
+ }
26964
27492
  async function handleArchive(projectRoot, itemId) {
26965
27493
  const command = "openspec archive";
26966
27494
  if (!itemId) {
@@ -27037,7 +27565,7 @@ async function handleArchive(projectRoot, itemId) {
27037
27565
  }
27038
27566
  };
27039
27567
  }
27040
- var KNOWN_SUBCOMMANDS = "validate, scan, plan, analyze, status, next, start, done, block, archive";
27568
+ var KNOWN_SUBCOMMANDS = "validate, scan, plan, analyze, status, next, start, done, block, unblock, archive";
27041
27569
  var init_openspec_command = __esm(() => {
27042
27570
  init_backlog_parser();
27043
27571
  init_backlog_validator();
@@ -27055,12 +27583,194 @@ var init_openspec_command = __esm(() => {
27055
27583
  init_loop_engine();
27056
27584
  });
27057
27585
 
27586
+ // src/core/ccep/context-assembly.ts
27587
+ function resumeDecision(changedPaths) {
27588
+ if (changedPaths.length === 0)
27589
+ return { status: "ready" };
27590
+ return {
27591
+ status: "needs_decision",
27592
+ changedPaths,
27593
+ question: `Workspace changed in ${changedPaths.join(", ")}. Continue with the current workspace?`
27594
+ };
27595
+ }
27596
+ var init_context_assembly = () => {};
27597
+
27598
+ // src/core/delivery/delivery-ledger.ts
27599
+ import { createHash as createHash7 } from "node:crypto";
27600
+ import { mkdir as mkdir24, readFile as readFile38, writeFile as writeFile23 } from "node:fs/promises";
27601
+ import { resolve as resolve37, sep as sep8 } from "node:path";
27602
+ function ledgerPath(projectRoot, id) {
27603
+ if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id))
27604
+ throw new Error("Invalid delivery ledger id");
27605
+ return resolve37(projectRoot, DELIVERY_DIR, `${id}.md`);
27606
+ }
27607
+ async function fingerprintWorkspace(projectRoot, paths) {
27608
+ const root = resolve37(projectRoot);
27609
+ const fingerprints = {};
27610
+ for (const path of paths) {
27611
+ const candidate = resolve37(root, path);
27612
+ if (candidate !== root && !candidate.startsWith(`${root}${sep8}`)) {
27613
+ fingerprints[path] = "outside-root";
27614
+ continue;
27615
+ }
27616
+ try {
27617
+ fingerprints[path] = createHash7("sha256").update(await readFile38(candidate)).digest("hex");
27618
+ } catch {
27619
+ fingerprints[path] = "missing";
27620
+ }
27621
+ }
27622
+ return fingerprints;
27623
+ }
27624
+ function render(ledger) {
27625
+ return `# Delivery Ledger: ${ledger.id}
27626
+
27627
+ ${START}
27628
+ \`\`\`yaml
27629
+ ${$stringify(ledger)}\`\`\`
27630
+ ${END}
27631
+ `;
27632
+ }
27633
+ function parseLedger(content) {
27634
+ const match = content.match(/<!-- delivery-ledger-start -->\n```yaml\n([\s\S]*?)```\n<!-- delivery-ledger-end -->/);
27635
+ if (!match)
27636
+ throw new Error("Invalid delivery ledger format");
27637
+ return DeliveryLedgerSchema.parse($parse(match[1]));
27638
+ }
27639
+ async function loadDeliveryLedger(projectRoot, id) {
27640
+ return parseLedger(await readFile38(ledgerPath(projectRoot, id), "utf-8"));
27641
+ }
27642
+ async function createDeliveryLedger(projectRoot, input) {
27643
+ const request = DeliveryLedgerRequestSchema.parse(input);
27644
+ const path = ledgerPath(projectRoot, request.id);
27645
+ try {
27646
+ return { created: false, ledger: await loadDeliveryLedger(projectRoot, request.id) };
27647
+ } catch (error) {
27648
+ if (!(error instanceof Error) || !error.message.includes("ENOENT"))
27649
+ throw error;
27650
+ }
27651
+ const now = new Date().toISOString();
27652
+ const ledger = DeliveryLedgerSchema.parse({
27653
+ version: 1,
27654
+ id: request.id,
27655
+ taskCard: request.taskCard,
27656
+ tasks: request.tasks,
27657
+ evidence: request.taskCard.evidenceRequired,
27658
+ nextStep: request.nextStep,
27659
+ memoryTopicKey: `delivery:${request.id}`,
27660
+ workspace: await fingerprintWorkspace(projectRoot, request.taskCard.targetFiles),
27661
+ createdAt: now,
27662
+ updatedAt: now
27663
+ });
27664
+ await mkdir24(resolve37(projectRoot, DELIVERY_DIR), { recursive: true });
27665
+ try {
27666
+ await writeFile23(path, render(ledger), { encoding: "utf-8", flag: "wx" });
27667
+ return { created: true, ledger };
27668
+ } catch (error) {
27669
+ if (error instanceof Error && error.message.includes("EEXIST")) {
27670
+ return { created: false, ledger: await loadDeliveryLedger(projectRoot, request.id) };
27671
+ }
27672
+ throw error;
27673
+ }
27674
+ }
27675
+ async function reconcileDeliveryLedger(projectRoot, id) {
27676
+ const ledger = await loadDeliveryLedger(projectRoot, id);
27677
+ const current = await fingerprintWorkspace(projectRoot, Object.keys(ledger.workspace));
27678
+ const changedPaths = Object.keys(ledger.workspace).filter((path) => ledger.workspace[path] !== current[path]);
27679
+ return { ledger, changedPaths };
27680
+ }
27681
+ var DELIVERY_DIR = ".codeconductor/delivery", START = "<!-- delivery-ledger-start -->", END = "<!-- delivery-ledger-end -->";
27682
+ var init_delivery_ledger = __esm(() => {
27683
+ init_dist();
27684
+ init_schemas();
27685
+ });
27686
+
27687
+ // src/commands/odd.command.ts
27688
+ var exports_odd_command = {};
27689
+ __export(exports_odd_command, {
27690
+ oddCommand: () => oddCommand
27691
+ });
27692
+ async function oddCommand(options) {
27693
+ if (options.subcommand === "create") {
27694
+ let input = options.input;
27695
+ if (typeof input === "string") {
27696
+ try {
27697
+ input = JSON.parse(input);
27698
+ } catch {
27699
+ return { code: 1, data: { success: false, errors: ["Invalid JSON input"] } };
27700
+ }
27701
+ }
27702
+ const request = DeliveryLedgerRequestSchema.safeParse(input);
27703
+ if (!request.success)
27704
+ return { code: 1, data: { success: false, errors: request.error.issues } };
27705
+ if (!request.data.authorized || request.data.route !== "tracked") {
27706
+ return { code: 0, data: { success: true, created: false, reason: "route_not_tracked" } };
27707
+ }
27708
+ const issues = validateTaskCardForProfile(loadWorkflowProfile("odd", options.projectRoot), request.data.taskCard);
27709
+ if (issues.length > 0)
27710
+ return { code: 1, data: { success: false, errors: issues } };
27711
+ try {
27712
+ const result = await createDeliveryLedger(options.projectRoot, request.data);
27713
+ return { code: 0, data: { success: true, ...result, ...result.created ? {} : { existing: true } } };
27714
+ } catch (error) {
27715
+ return { code: 1, data: { success: false, errors: [String(error)] } };
27716
+ }
27717
+ }
27718
+ if (!options.id)
27719
+ return { code: 1, data: { success: false, errors: ["Missing ledger id"] } };
27720
+ try {
27721
+ if (options.subcommand === "read") {
27722
+ return { code: 0, data: { success: true, ledger: await loadDeliveryLedger(options.projectRoot, options.id) } };
27723
+ }
27724
+ if (options.subcommand === "reconcile") {
27725
+ const result = await reconcileDeliveryLedger(options.projectRoot, options.id);
27726
+ const memory = await loadMemoryIndex(options.projectRoot);
27727
+ const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
27728
+ const rdd = await rddCommand2({
27729
+ subcommand: "status",
27730
+ projectRoot: options.projectRoot,
27731
+ taskId: result.ledger.taskCard.id
27732
+ });
27733
+ const receipts = rdd.data.receipts ?? [];
27734
+ const staleReceipt = receipts.some((receipt) => receipt.valid === false);
27735
+ const changedPaths = staleReceipt ? [...result.changedPaths, "RDD receipt"].sort() : result.changedPaths;
27736
+ return {
27737
+ code: 0,
27738
+ data: {
27739
+ success: true,
27740
+ status: changedPaths.length === 0 ? "ready" : "conflict",
27741
+ resume: resumeDecision(changedPaths),
27742
+ rdd,
27743
+ memory: memory.success ? "available" : "unavailable",
27744
+ memoryPointers: memory.success ? memory.data.pointers.filter((pointer) => pointer.topic_key === result.ledger.memoryTopicKey) : [],
27745
+ ...result,
27746
+ changedPaths
27747
+ }
27748
+ };
27749
+ }
27750
+ return { code: 1, data: { success: false, errors: [`Unknown odd subcommand: ${options.subcommand}`] } };
27751
+ } catch (error) {
27752
+ return { code: 1, data: { success: false, errors: [String(error)] } };
27753
+ }
27754
+ }
27755
+ var init_odd_command = __esm(() => {
27756
+ init_memory_index();
27757
+ init_context_assembly();
27758
+ init_task_card_validator();
27759
+ init_workflow_profile_loader();
27760
+ init_delivery_ledger();
27761
+ init_schemas();
27762
+ });
27763
+
27058
27764
  // src/core/hooks/hook-runner.ts
27059
27765
  function normalizeCommand(raw) {
27060
27766
  return raw.replace(/\s+/g, " ").trim();
27061
27767
  }
27062
27768
  function stripGitBinary(command) {
27063
- return command.replace(/(?:^|[\\/])git(?:\.exe)?\b/i, "git");
27769
+ const match = command.match(/^(?:&\s*)?("[^"]+"|'[^']+'|[^\s]+)([\s\S]*)$/);
27770
+ if (!match)
27771
+ return command;
27772
+ const binary = match[1].replace(/^["']|["']$/g, "").replace(/\\/g, "/").split("/").pop();
27773
+ return /^git(?:\.exe)?$/i.test(binary ?? "") ? `git${match[2]}` : command;
27064
27774
  }
27065
27775
  function evaluatePath(filePath) {
27066
27776
  if (SENSITIVE_PATH.test(filePath.replace(/\//g, "\\")) || SENSITIVE_PATH.test(filePath)) {
@@ -27069,9 +27779,44 @@ function evaluatePath(filePath) {
27069
27779
  return allow();
27070
27780
  }
27071
27781
  function evaluateCommand(rawCommand) {
27072
- const command = stripGitBinary(normalizeCommand(rawCommand));
27782
+ if (/(?:curl|wget)\b.+\|\s*(sh|bash|zsh|cmd)\b/i.test(normalizeCommand(rawCommand)))
27783
+ return deny(MSG_POLICY);
27784
+ const tokens = rawCommand.match(/"[^"]*"|'[^']*'|[^"';&|\r\n]+|[;&|\r\n]+/g) ?? [];
27785
+ const commands = [];
27786
+ let current = "";
27787
+ for (const token of tokens) {
27788
+ if (/^[;&|\r\n]+$/.test(token) && !(token === "&" && !current.trim())) {
27789
+ commands.push(current);
27790
+ current = "";
27791
+ } else {
27792
+ current += token;
27793
+ }
27794
+ }
27795
+ commands.push(current);
27796
+ let verdict = allow();
27797
+ for (const part of commands) {
27798
+ const result = evaluateSingleCommand(part);
27799
+ if (result.action === "deny")
27800
+ return result;
27801
+ if (result.action === "ask")
27802
+ verdict = result;
27803
+ }
27804
+ return verdict;
27805
+ }
27806
+ function evaluateSingleCommand(rawCommand) {
27807
+ let command = stripGitBinary(normalizeCommand(rawCommand));
27073
27808
  if (!command)
27074
27809
  return allow();
27810
+ if (/^git\s/i.test(command)) {
27811
+ const words = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
27812
+ let index = 1;
27813
+ while (words[index]?.startsWith("-")) {
27814
+ const option = words[index++];
27815
+ if (/^(?:-C|-c|--git-dir|--work-tree|--namespace)$/.test(option))
27816
+ index++;
27817
+ }
27818
+ command = ["git", ...words.slice(index)].join(" ");
27819
+ }
27075
27820
  if (SENSITIVE_PATH.test(command) && READ_LIKE.test(command)) {
27076
27821
  return deny(MSG_SECRET);
27077
27822
  }
@@ -27094,9 +27839,12 @@ function evaluateCommand(rawCommand) {
27094
27839
  }
27095
27840
  if (/^rm\s+-rf\s+(\*|[\\/])/i.test(command))
27096
27841
  return deny(MSG_POLICY);
27097
- if (/^sudo\s+/i.test(command))
27842
+ const windowsTarget = /(?:^|\s)["']?(?:[a-z]:[\\/]|\\|\*)/i.test(command);
27843
+ if (windowsTarget && /^Remove-Item\b/i.test(command) && /\s-(?:Recurse|r)\b/i.test(command) && /\s-(?:Force|f)\b/i.test(command))
27098
27844
  return deny(MSG_POLICY);
27099
- if (/(?:curl|wget)\b.+\|\s*(sh|bash|zsh|cmd)\b/i.test(command))
27845
+ if (windowsTarget && /^(?:rd|rmdir|del|erase)\b/i.test(command) && /\s\/s\b/i.test(command))
27846
+ return deny(MSG_POLICY);
27847
+ if (/^sudo\s+/i.test(command))
27100
27848
  return deny(MSG_POLICY);
27101
27849
  if (/^chmod\s+777\b/i.test(command))
27102
27850
  return deny(MSG_POLICY);
@@ -27123,13 +27871,16 @@ function evaluatePreTool(input) {
27123
27871
  function formatHookOutput(verdict, format) {
27124
27872
  if (format === "agy") {
27125
27873
  if (verdict.action === "deny") {
27126
- return JSON.stringify({ decision: "deny", error: verdict.message });
27874
+ return JSON.stringify({ decision: "deny", reason: verdict.message });
27127
27875
  }
27128
27876
  if (verdict.action === "ask") {
27129
27877
  return JSON.stringify({ decision: "ask" });
27130
27878
  }
27131
27879
  return JSON.stringify({ decision: "allow" });
27132
27880
  }
27881
+ if (verdict.action === "ask") {
27882
+ return JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "ask" } });
27883
+ }
27133
27884
  return "";
27134
27885
  }
27135
27886
  function claudeExitCode(verdict) {
@@ -27140,11 +27891,24 @@ function parseAgyPayload(raw) {
27140
27891
  return {};
27141
27892
  try {
27142
27893
  const parsed = JSON.parse(raw);
27143
- const args = parsed.arguments ?? {};
27894
+ const args = parsed.toolCall?.args ?? parsed.arguments ?? {};
27144
27895
  return {
27145
- toolName: parsed.toolName,
27146
- command: args.CommandLine,
27147
- filePath: args.TargetFile ?? args.AbsolutePath
27896
+ toolName: typeof (parsed.toolCall?.name ?? parsed.toolName) === "string" ? parsed.toolCall?.name ?? parsed.toolName : undefined,
27897
+ command: typeof args.CommandLine === "string" ? args.CommandLine : undefined,
27898
+ filePath: typeof args.TargetFile === "string" ? args.TargetFile : typeof args.AbsolutePath === "string" ? args.AbsolutePath : undefined
27899
+ };
27900
+ } catch {
27901
+ return {};
27902
+ }
27903
+ }
27904
+ function parseClaudePayload(raw) {
27905
+ try {
27906
+ const parsed = JSON.parse(raw);
27907
+ const input = parsed.tool_input ?? {};
27908
+ return {
27909
+ toolName: typeof parsed.tool_name === "string" ? parsed.tool_name : undefined,
27910
+ command: typeof input.command === "string" ? input.command : undefined,
27911
+ filePath: typeof input.file_path === "string" ? input.file_path : undefined
27148
27912
  };
27149
27913
  } catch {
27150
27914
  return {};
@@ -27206,14 +27970,17 @@ __export(exports_hook_command, {
27206
27970
  readStdinText: () => readStdinText
27207
27971
  });
27208
27972
  import { spawnSync } from "node:child_process";
27209
- import { existsSync as existsSync7 } from "node:fs";
27210
- import { resolve as resolve35 } from "node:path";
27973
+ import { existsSync as existsSync8, readFileSync as readFileSync5 } from "node:fs";
27974
+ import { createRequire as createRequire2 } from "node:module";
27975
+ import { dirname as dirname13, resolve as resolve38 } from "node:path";
27211
27976
  function detectFormat(explicit, stdinText) {
27212
27977
  if (explicit)
27213
27978
  return explicit;
27214
- const trimmed = stdinText.trim();
27215
- if (trimmed.startsWith("{"))
27216
- return "agy";
27979
+ try {
27980
+ const payload = JSON.parse(stdinText);
27981
+ if (payload && (("toolCall" in payload) || ("toolName" in payload) || ("workspacePaths" in payload)))
27982
+ return "agy";
27983
+ } catch {}
27217
27984
  return "claude";
27218
27985
  }
27219
27986
  async function readStdinText() {
@@ -27225,22 +27992,22 @@ async function readStdinText() {
27225
27992
  }
27226
27993
  return Buffer.concat(chunks).toString("utf8");
27227
27994
  }
27228
- function resolvePreToolInput(options, stdinText) {
27229
- const fromStdin = parseAgyPayload(stdinText);
27995
+ function resolvePreToolInput(options, stdinText, format) {
27996
+ const fromStdin = format === "agy" ? parseAgyPayload(stdinText) : parseClaudePayload(stdinText);
27230
27997
  return {
27231
- command: options.command ?? process.env.CLAUDE_TOOL_INPUT_COMMAND ?? fromStdin.command,
27232
- filePath: options.filePath ?? process.env.CLAUDE_TOOL_INPUT_FILE_PATH ?? fromStdin.filePath,
27998
+ command: options.command ?? (process.env.CLAUDE_TOOL_INPUT_COMMAND || undefined) ?? fromStdin.command,
27999
+ filePath: options.filePath ?? (process.env.CLAUDE_TOOL_INPUT_FILE_PATH || undefined) ?? fromStdin.filePath,
27233
28000
  toolName: fromStdin.toolName
27234
28001
  };
27235
28002
  }
27236
28003
  async function sessionLines(projectRoot) {
27237
- const backlogPath = resolve35(projectRoot, "BACKLOG.md");
27238
- const profilePath = resolve35(projectRoot, ".codeconductor", "evaluation", "execution-profile.yml");
28004
+ const backlogPath = resolve38(projectRoot, "BACKLOG.md");
28005
+ const profilePath = resolve38(projectRoot, ".codeconductor", "evaluation", "execution-profile.yml");
27239
28006
  return [
27240
28007
  "[CodeConductor] Session start",
27241
28008
  "Skills: openspec, backlog, evaluation, testing-tdd — invoke via /cc-* then CLI.",
27242
- existsSync7(backlogPath) ? "BACKLOG.md present — run: bun run dev openspec status" : "No BACKLOG.md — author with /cc-backlog if you need an item queue.",
27243
- existsSync7(profilePath) ? "Scorecard profile on disk — run: bun run dev scorecard models" : "Scorecard: bun run dev scorecard create --task <id> --from-diff",
28009
+ existsSync8(backlogPath) ? "BACKLOG.md present — run: bun run dev openspec status" : "No BACKLOG.md — author with /cc-backlog if you need an item queue.",
28010
+ existsSync8(profilePath) ? "Scorecard profile on disk — run: bun run dev scorecard models" : "Scorecard: bun run dev scorecard create --task <id> --from-diff",
27244
28011
  "Hooks: bun run dev hook pre-tool | post-tool | session-start",
27245
28012
  "Eval suites: bun run dev scorecard suite-run --suite workflow-gates"
27246
28013
  ];
@@ -27256,15 +28023,15 @@ async function hookCommand(options) {
27256
28023
  return { code: 0, data: { success: true, command: "hook session-start" } };
27257
28024
  }
27258
28025
  if (event === "post-tool") {
27259
- const filePath = options.filePath ?? process.env.CLAUDE_TOOL_INPUT_FILE_PATH ?? parseAgyPayload(stdinText).filePath;
27260
- formatWrittenFile(filePath);
28026
+ const filePath = resolvePreToolInput(options, stdinText, format).filePath;
28027
+ formatWrittenFile(filePath, options.projectRoot);
27261
28028
  if (format === "agy") {
27262
28029
  process.stdout.write(`{}
27263
28030
  `);
27264
28031
  }
27265
28032
  return { code: 0, data: { success: true, command: "hook post-tool" } };
27266
28033
  }
27267
- const input = resolvePreToolInput(options, stdinText);
28034
+ const input = resolvePreToolInput(options, stdinText, format);
27268
28035
  const verdict = evaluatePreTool(input);
27269
28036
  if (format === "agy") {
27270
28037
  process.stdout.write(`${formatHookOutput(verdict, "agy")}
@@ -27278,23 +28045,40 @@ async function hookCommand(options) {
27278
28045
  process.stderr.write(`${verdict.message}
27279
28046
  `);
27280
28047
  }
28048
+ const output = formatHookOutput(verdict, "claude");
28049
+ if (output)
28050
+ process.stdout.write(`${output}
28051
+ `);
27281
28052
  return {
27282
28053
  code: claudeExitCode(verdict),
27283
28054
  data: { success: verdict.action !== "deny", command: "hook pre-tool", verdict }
27284
28055
  };
27285
28056
  }
27286
- function formatWrittenFile(filePath) {
27287
- if (!filePath || !existsSync7(filePath))
28057
+ function formatWrittenFile(filePath, projectRoot) {
28058
+ if (!filePath)
28059
+ return;
28060
+ filePath = resolve38(projectRoot, filePath);
28061
+ if (!existsSync8(filePath))
27288
28062
  return;
27289
28063
  const prettier = /\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|css|scss|html|astro|ya?ml)$/i;
27290
28064
  const eslint = /\.(ts|tsx|js|jsx|mjs|cjs)$/i;
27291
28065
  const python = /\.py$/i;
27292
- const opts = { stdio: "ignore", windowsHide: true };
28066
+ const opts = { cwd: projectRoot, stdio: "ignore", windowsHide: true, timeout: 5000 };
28067
+ const require2 = createRequire2(resolve38(projectRoot, "package.json"));
28068
+ function runNodeTool(name, args) {
28069
+ try {
28070
+ const manifestPath = require2.resolve(`${name}/package.json`);
28071
+ const manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
28072
+ const bin = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.[name];
28073
+ if (typeof bin === "string")
28074
+ spawnSync(process.execPath, [resolve38(dirname13(manifestPath), bin), ...args], opts);
28075
+ } catch {}
28076
+ }
27293
28077
  if (prettier.test(filePath)) {
27294
- spawnSync("npx", ["--no-install", "prettier", "--write", filePath], opts);
28078
+ runNodeTool("prettier", ["--write", filePath]);
27295
28079
  }
27296
28080
  if (eslint.test(filePath)) {
27297
- spawnSync("npx", ["--no-install", "eslint", "--fix", filePath], opts);
28081
+ runNodeTool("eslint", ["--fix", filePath]);
27298
28082
  }
27299
28083
  if (python.test(filePath)) {
27300
28084
  spawnSync("ruff", ["format", filePath], opts);
@@ -27701,7 +28485,13 @@ function ccGainToScorecardImpact(result) {
27701
28485
  }
27702
28486
 
27703
28487
  // src/core/evaluation/scorecard-signals.ts
27704
- import { execFileSync as execFileSync3 } from "node:child_process";
28488
+ import { execFileSync as execFileSync2 } from "node:child_process";
28489
+ function parseScopeFiles(scope) {
28490
+ return scope.split(/[,;]/).map((entry) => entry.trim().replace(/[.;:]$/, "").replace(/^`|`$/g, "")).filter(Boolean);
28491
+ }
28492
+ function isGeneratedBuildCache(path) {
28493
+ return /(^|\/)\.gradle\//.test(path);
28494
+ }
27705
28495
  function collectScorecardSignals(projectRoot, scopeFiles, extra) {
27706
28496
  const findings = [];
27707
28497
  const overrides = {};
@@ -27725,7 +28515,7 @@ function collectScorecardSignals(projectRoot, scopeFiles, extra) {
27725
28515
  }
27726
28516
  let diff = "";
27727
28517
  try {
27728
- diff = execFileSync3("git", ["diff", "HEAD"], {
28518
+ diff = execFileSync2("git", ["diff", "HEAD"], {
27729
28519
  cwd: projectRoot,
27730
28520
  encoding: "utf-8",
27731
28521
  stdio: ["ignore", "pipe", "ignore"]
@@ -27748,7 +28538,7 @@ function collectScorecardSignals(projectRoot, scopeFiles, extra) {
27748
28538
  changedFiles.add(m[1]);
27749
28539
  }
27750
28540
  if (scopeFiles && scopeFiles.length > 0) {
27751
- const outOfScope = [...changedFiles].filter((f) => !scopeFiles.some((s) => f.includes(s.replace(/\*\*/g, ""))));
28541
+ const outOfScope = [...changedFiles].filter((file) => !isGeneratedBuildCache(file)).filter((f) => !scopeFiles.some((s) => f.includes(s.replace(/\*\*/g, ""))));
27752
28542
  if (outOfScope.length > 0) {
27753
28543
  overrides.minimal_diff = {
27754
28544
  score: 0,
@@ -27819,25 +28609,25 @@ var init_scorecard_signals = __esm(() => {
27819
28609
  });
27820
28610
 
27821
28611
  // src/core/evaluation/harness-experiment.ts
27822
- import { existsSync as existsSync8 } from "node:fs";
27823
- import { cp, mkdir as mkdir23, readFile as readFile36, readdir as readdir12, writeFile as writeFile22 } from "node:fs/promises";
27824
- import { dirname as dirname12, join as join25, resolve as resolve36 } from "node:path";
28612
+ import { existsSync as existsSync9 } from "node:fs";
28613
+ import { cp, mkdir as mkdir25, readFile as readFile39, readdir as readdir14, writeFile as writeFile24 } from "node:fs/promises";
28614
+ import { dirname as dirname14, join as join26, resolve as resolve39 } from "node:path";
27825
28615
  function experimentDir(projectRoot, experimentId) {
27826
- return resolve36(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
28616
+ return resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
27827
28617
  }
27828
28618
  function experimentYamlPath(projectRoot, experimentId) {
27829
- return join25(experimentDir(projectRoot, experimentId), "experiment.yml");
28619
+ return join26(experimentDir(projectRoot, experimentId), "experiment.yml");
27830
28620
  }
27831
28621
  async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
27832
28622
  const candidates = suitePath ? [suitePath] : [
27833
- resolve36(projectRoot, "eval", "suites", suiteId, "suite.yml"),
27834
- resolve36(projectRoot, EVAL_DIR2, "suites", suiteId, "suite.yml")
28623
+ resolve39(projectRoot, "eval", "suites", suiteId, "suite.yml"),
28624
+ resolve39(projectRoot, EVAL_DIR2, "suites", suiteId, "suite.yml")
27835
28625
  ];
27836
28626
  for (const path of candidates) {
27837
- if (!existsSync8(path))
28627
+ if (!existsSync9(path))
27838
28628
  continue;
27839
28629
  try {
27840
- const suite = HarnessSuiteSchema.parse($parse(await readFile36(path, "utf-8")));
28630
+ const suite = HarnessSuiteSchema.parse($parse(await readFile39(path, "utf-8")));
27841
28631
  return ok({ ...suite, path });
27842
28632
  } catch (e) {
27843
28633
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -27847,7 +28637,7 @@ async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
27847
28637
  }
27848
28638
  async function loadExperiment(projectRoot, experimentId) {
27849
28639
  try {
27850
- const raw = await readFile36(experimentYamlPath(projectRoot, experimentId), "utf-8");
28640
+ const raw = await readFile39(experimentYamlPath(projectRoot, experimentId), "utf-8");
27851
28641
  return ok(HarnessExperimentSchema.parse($parse(raw)));
27852
28642
  } catch (e) {
27853
28643
  if (e.code === "ENOENT") {
@@ -27857,10 +28647,10 @@ async function loadExperiment(projectRoot, experimentId) {
27857
28647
  }
27858
28648
  }
27859
28649
  async function listExperiments(projectRoot) {
27860
- const root = resolve36(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR);
27861
- if (!existsSync8(root))
28650
+ const root = resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR);
28651
+ if (!existsSync9(root))
27862
28652
  return [];
27863
- const ids = await readdir12(root);
28653
+ const ids = await readdir14(root);
27864
28654
  const experiments = [];
27865
28655
  for (const id of ids) {
27866
28656
  const loaded = await loadExperiment(projectRoot, id);
@@ -27936,9 +28726,9 @@ function generateExperimentId(suiteId, now = new Date) {
27936
28726
  return `abl-${date}-${suiteId}-${rand}`;
27937
28727
  }
27938
28728
  async function copyFixture(suitePath, fixtureDir, dest) {
27939
- const source = fixtureDir ? resolve36(dirname12(suitePath), fixtureDir) : join25(dirname12(suitePath), "fixture");
27940
- await mkdir23(dest, { recursive: true });
27941
- if (existsSync8(source)) {
28729
+ const source = fixtureDir ? resolve39(dirname14(suitePath), fixtureDir) : join26(dirname14(suitePath), "fixture");
28730
+ await mkdir25(dest, { recursive: true });
28731
+ if (existsSync9(source)) {
27942
28732
  await cp(source, dest, { recursive: true });
27943
28733
  }
27944
28734
  }
@@ -27959,12 +28749,12 @@ async function startHarnessExperiment(projectRoot, options) {
27959
28749
  suitePath: suite.data.path
27960
28750
  };
27961
28751
  const root = experimentDir(projectRoot, experimentId);
27962
- await mkdir23(root, { recursive: true });
27963
- await writeFile22(experimentYamlPath(projectRoot, experimentId), $stringify(experiment), "utf-8");
27964
- await writeFile22(join25(root, "EXPERIMENT.md"), formatExperimentMarkdown(experiment), "utf-8");
28752
+ await mkdir25(root, { recursive: true });
28753
+ await writeFile24(experimentYamlPath(projectRoot, experimentId), $stringify(experiment), "utf-8");
28754
+ await writeFile24(join26(root, "EXPERIMENT.md"), formatExperimentMarkdown(experiment), "utf-8");
27965
28755
  for (const task of suite.data.tasks) {
27966
28756
  for (const variantId of variants) {
27967
- const runRoot = join25(root, "runs", task.id, variantDirName(variantId));
28757
+ const runRoot = join26(root, "runs", task.id, variantDirName(variantId));
27968
28758
  await copyFixture(suite.data.path, suite.data.fixtureDir, runRoot);
27969
28759
  await writeConfig(runRoot, structuredClone(DEFAULT_CONFIG), true);
27970
28760
  const disabled = variantId === "baseline" ? [] : options.components.filter((id) => variantIdFor(id) === variantId);
@@ -27976,7 +28766,7 @@ async function startHarnessExperiment(projectRoot, options) {
27976
28766
  });
27977
28767
  if (!overlay.success)
27978
28768
  return overlay;
27979
- await writeFile22(join25(runRoot, "TASK.md"), formatTaskCard(task, experimentId, variantId), "utf-8");
28769
+ await writeFile24(join26(runRoot, "TASK.md"), formatTaskCard(task, experimentId, variantId), "utf-8");
27980
28770
  }
27981
28771
  }
27982
28772
  return ok(experiment);
@@ -28010,8 +28800,8 @@ var init_harness_experiment = __esm(() => {
28010
28800
 
28011
28801
  // src/core/evaluation/suite-run.ts
28012
28802
  import { spawnSync as spawnSync2 } from "node:child_process";
28013
- import { existsSync as existsSync9 } from "node:fs";
28014
- import { resolve as resolve37 } from "node:path";
28803
+ import { existsSync as existsSync10 } from "node:fs";
28804
+ import { resolve as resolve40 } from "node:path";
28015
28805
  async function runHarnessSuiteTasks(projectRoot, suiteId, suitePath) {
28016
28806
  const suite = await loadHarnessSuite(projectRoot, suiteId, suitePath);
28017
28807
  if (!suite.success) {
@@ -28071,7 +28861,7 @@ function runTaskCommand(projectRoot, task) {
28071
28861
  if (parts.length === 0) {
28072
28862
  return { ok: false, detail: "Empty testCommand" };
28073
28863
  }
28074
- const bin = parts[0] === "bun" && existsSync9(resolve37(projectRoot, "src/cli/main.ts")) ? "bun" : parts[0];
28864
+ const bin = parts[0] === "bun" && existsSync10(resolve40(projectRoot, "src/cli/main.ts")) ? "bun" : parts[0];
28075
28865
  const args = parts[0] === "bun" ? parts.slice(1) : parts.slice(1);
28076
28866
  const result = spawnSync2(bin, args, {
28077
28867
  cwd: projectRoot,
@@ -28122,11 +28912,11 @@ var init_suite_run = __esm(() => {
28122
28912
  });
28123
28913
 
28124
28914
  // src/core/evaluation/execution-profile.ts
28125
- import { readFile as readFile37 } from "node:fs/promises";
28126
- import { resolve as resolve38 } from "node:path";
28915
+ import { readFile as readFile40 } from "node:fs/promises";
28916
+ import { resolve as resolve41 } from "node:path";
28127
28917
  async function loadExecutionProfile(projectRoot, defaultTarget = "opencode") {
28128
28918
  try {
28129
- const content = await readFile37(resolve38(projectRoot, PROFILE_FILE), "utf-8");
28919
+ const content = await readFile40(resolve41(projectRoot, PROFILE_FILE), "utf-8");
28130
28920
  const data = $parse(content);
28131
28921
  return ExecutionProfileSchema.parse(data);
28132
28922
  } catch {
@@ -28206,14 +28996,14 @@ var init_execution_profile = __esm(() => {
28206
28996
  });
28207
28997
 
28208
28998
  // src/core/evaluation/prompt-diff.ts
28209
- import { readFile as readFile38, readdir as readdir13 } from "node:fs/promises";
28210
- import { join as join26, resolve as resolve39 } from "node:path";
28999
+ import { readFile as readFile41, readdir as readdir15 } from "node:fs/promises";
29000
+ import { join as join27, resolve as resolve42 } from "node:path";
28211
29001
  function normalizeVersion(v) {
28212
29002
  return v.startsWith("v") ? v : `v${v}`;
28213
29003
  }
28214
29004
  async function listPromptFiles(dir) {
28215
29005
  try {
28216
- const entries = await readdir13(dir, { withFileTypes: true });
29006
+ const entries = await readdir15(dir, { withFileTypes: true });
28217
29007
  return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name);
28218
29008
  } catch {
28219
29009
  return [];
@@ -28256,30 +29046,30 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
28256
29046
  const fromV = normalizeVersion(fromVersion);
28257
29047
  const toV = normalizeVersion(toVersion);
28258
29048
  const searchRoots = [
28259
- resolve39(options.projectRoot ?? PACKAGE_ROOT, ".agents", "prompts"),
28260
- join26(ROOT_PRESETS_DIR, target, "prompts"),
28261
- join26(ROOT_PRESETS_DIR, "opencode", "prompts")
29049
+ resolve42(options.projectRoot ?? PACKAGE_ROOT, ".agents", "prompts"),
29050
+ join27(ROOT_PRESETS_DIR, target, "prompts"),
29051
+ join27(ROOT_PRESETS_DIR, "opencode", "prompts")
28262
29052
  ];
28263
29053
  let fromDir = "";
28264
29054
  let toDir = "";
28265
29055
  for (const root of searchRoots) {
28266
- const f = join26(root, fromV);
28267
- const t = join26(root, toV);
29056
+ const f = join27(root, fromV);
29057
+ const t = join27(root, toV);
28268
29058
  try {
28269
- await readFile38(join26(f, "orchestrator.md"), "utf-8");
29059
+ await readFile41(join27(f, "orchestrator.md"), "utf-8");
28270
29060
  fromDir = f;
28271
29061
  toDir = t;
28272
29062
  break;
28273
29063
  } catch {}
28274
29064
  }
28275
29065
  if (!fromDir) {
28276
- fromDir = join26(ROOT_PRESETS_DIR, "opencode", "prompts", fromV);
28277
- toDir = join26(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
29066
+ fromDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", fromV);
29067
+ toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
28278
29068
  } else {
28279
29069
  try {
28280
- await readFile38(join26(toDir, "orchestrator.md"), "utf-8");
29070
+ await readFile41(join27(toDir, "orchestrator.md"), "utf-8");
28281
29071
  } catch {
28282
- toDir = join26(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
29072
+ toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
28283
29073
  }
28284
29074
  }
28285
29075
  const fromFiles = await listPromptFiles(fromDir);
@@ -28297,12 +29087,12 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
28297
29087
  let oldText = "";
28298
29088
  let newText = "";
28299
29089
  try {
28300
- oldText = await readFile38(join26(fromDir, name), "utf-8");
29090
+ oldText = await readFile41(join27(fromDir, name), "utf-8");
28301
29091
  } catch {
28302
29092
  oldText = "";
28303
29093
  }
28304
29094
  try {
28305
- newText = await readFile38(join26(toDir, name), "utf-8");
29095
+ newText = await readFile41(join27(toDir, name), "utf-8");
28306
29096
  } catch {
28307
29097
  newText = "";
28308
29098
  }
@@ -28341,13 +29131,13 @@ var init_prompt_diff = __esm(() => {
28341
29131
  });
28342
29132
 
28343
29133
  // src/core/evaluation/regression-checklist.ts
28344
- import { execFileSync as execFileSync4 } from "node:child_process";
28345
- import { mkdir as mkdir24, readFile as readFile39, writeFile as writeFile23 } from "node:fs/promises";
28346
- import { dirname as dirname13, resolve as resolve40 } from "node:path";
29134
+ import { execFileSync as execFileSync3 } from "node:child_process";
29135
+ import { mkdir as mkdir26, readFile as readFile42, writeFile as writeFile25 } from "node:fs/promises";
29136
+ import { dirname as dirname15, resolve as resolve43 } from "node:path";
28347
29137
  async function loadRegressionChecklist() {
28348
29138
  try {
28349
- const path = resolve40(ROOT_PRESETS_DIR, "templates", "regression-checklist.yml");
28350
- const content = await readFile39(path, "utf-8");
29139
+ const path = resolve43(ROOT_PRESETS_DIR, "templates", "regression-checklist.yml");
29140
+ const content = await readFile42(path, "utf-8");
28351
29141
  const data = $parse(content);
28352
29142
  return data.checks ?? [];
28353
29143
  } catch {
@@ -28361,7 +29151,7 @@ function runCheckCommand(cmd, cwd) {
28361
29151
  if (!file)
28362
29152
  return { passed: false, message: "Empty command" };
28363
29153
  try {
28364
- execFileSync4(file, args, {
29154
+ execFileSync3(file, args, {
28365
29155
  cwd,
28366
29156
  encoding: "utf-8",
28367
29157
  stdio: ["ignore", "pipe", "pipe"],
@@ -28402,9 +29192,9 @@ async function runRegressionChecklist(projectRoot, options = {}) {
28402
29192
  checks,
28403
29193
  timestamp: new Date().toISOString()
28404
29194
  };
28405
- const outPath = resolve40(projectRoot, ".codeconductor/evaluation/regression-last.json");
28406
- await mkdir24(dirname13(outPath), { recursive: true });
28407
- await writeFile23(outPath, JSON.stringify(report, null, 2), "utf-8");
29195
+ const outPath = resolve43(projectRoot, ".codeconductor/evaluation/regression-last.json");
29196
+ await mkdir26(dirname15(outPath), { recursive: true });
29197
+ await writeFile25(outPath, JSON.stringify(report, null, 2), "utf-8");
28408
29198
  return ok(report);
28409
29199
  } catch (e) {
28410
29200
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -28446,8 +29236,8 @@ var init_model_comparison = __esm(() => {
28446
29236
  });
28447
29237
 
28448
29238
  // src/core/evaluation/cost-quality-matrix.ts
28449
- import { writeFile as writeFile24 } from "node:fs/promises";
28450
- import { resolve as resolve41 } from "node:path";
29239
+ import { writeFile as writeFile26 } from "node:fs/promises";
29240
+ import { resolve as resolve44 } from "node:path";
28451
29241
  async function buildCostQualityMatrix(projectRoot, outcomes) {
28452
29242
  const agg = aggregateOutcomes(outcomes);
28453
29243
  const rows = Object.entries(agg.byModel).map(([model, entry]) => {
@@ -28467,7 +29257,7 @@ async function buildCostQualityMatrix(projectRoot, outcomes) {
28467
29257
  rows
28468
29258
  };
28469
29259
  const dir = await ensureEvaluationDir(projectRoot);
28470
- await writeFile24(resolve41(dir, "matrix.yml"), $stringify(matrix), "utf-8");
29260
+ await writeFile26(resolve44(dir, "matrix.yml"), $stringify(matrix), "utf-8");
28471
29261
  return matrix;
28472
29262
  }
28473
29263
  function formatMatrixMarkdown(matrix) {
@@ -28491,8 +29281,8 @@ var init_cost_quality_matrix = __esm(() => {
28491
29281
  });
28492
29282
 
28493
29283
  // src/core/evaluation/ablation-report.ts
28494
- import { mkdir as mkdir25, writeFile as writeFile25 } from "node:fs/promises";
28495
- import { resolve as resolve42 } from "node:path";
29284
+ import { mkdir as mkdir27, writeFile as writeFile27 } from "node:fs/promises";
29285
+ import { resolve as resolve45 } from "node:path";
28496
29286
  function isPass(o) {
28497
29287
  return o.verdict === "PASS" || o.status === "pass";
28498
29288
  }
@@ -28660,10 +29450,10 @@ function formatAblationMarkdown(report) {
28660
29450
  `);
28661
29451
  }
28662
29452
  async function persistAblationReport(projectRoot, markdown) {
28663
- const dir = resolve42(projectRoot, EVAL_DIR2);
28664
- await mkdir25(dir, { recursive: true });
28665
- const path = resolve42(dir, "ablation-last.md");
28666
- await writeFile25(path, markdown, "utf-8");
29453
+ const dir = resolve45(projectRoot, EVAL_DIR2);
29454
+ await mkdir27(dir, { recursive: true });
29455
+ const path = resolve45(dir, "ablation-last.md");
29456
+ await writeFile27(path, markdown, "utf-8");
28667
29457
  return path;
28668
29458
  }
28669
29459
  var SCORE_THRESHOLD = 0.1, PASS_RATE_THRESHOLD = 0.05;
@@ -28677,8 +29467,8 @@ var exports_scorecard_command = {};
28677
29467
  __export(exports_scorecard_command, {
28678
29468
  scorecardCommand: () => scorecardCommand
28679
29469
  });
28680
- import { readFile as readFile40 } from "node:fs/promises";
28681
- import { resolve as resolve43 } from "node:path";
29470
+ import { readFile as readFile43 } from "node:fs/promises";
29471
+ import { resolve as resolve46 } from "node:path";
28682
29472
  function contractVersion() {
28683
29473
  return package_default.version;
28684
29474
  }
@@ -28739,7 +29529,7 @@ async function handleCreate(projectRoot, options) {
28739
29529
  if (backlog.success && options.taskId) {
28740
29530
  const item = backlog.data.items.find((i) => i.id === options.taskId);
28741
29531
  if (item) {
28742
- scopeFiles = item.scope.split(/[,;]/).map((s) => s.trim()).filter(Boolean);
29532
+ scopeFiles = parseScopeFiles(item.scope);
28743
29533
  }
28744
29534
  }
28745
29535
  let hints = collectScorecardSignals(projectRoot, scopeFiles);
@@ -28823,7 +29613,7 @@ async function handleRecord(projectRoot, options) {
28823
29613
  const variantId = options.variantId ?? overlay?.variantId;
28824
29614
  const suiteTaskId = options.suiteTaskId;
28825
29615
  const disabled = overlay?.disabledComponents ?? (variantId ? parseVariantId(variantId) : undefined);
28826
- const fingerprint = experimentId || variantId ? harnessFingerprint(disabled ?? [], overlay?.contractVersion ?? contractVersion(), overlay?.ccepProfile ?? "feature") : undefined;
29616
+ const fingerprint2 = experimentId || variantId ? harnessFingerprint(disabled ?? [], overlay?.contractVersion ?? contractVersion(), overlay?.ccepProfile ?? "feature") : undefined;
28827
29617
  const outcome = {
28828
29618
  id: generateEvalId("out"),
28829
29619
  taskId,
@@ -28838,10 +29628,17 @@ async function handleRecord(projectRoot, options) {
28838
29628
  costUsd: options.costUsd,
28839
29629
  tokensIn: options.tokens,
28840
29630
  durationMs: options.durationMs,
29631
+ delivery: options.deliveryRoute ? {
29632
+ route: options.deliveryRoute,
29633
+ contextBytes: options.contextBytes,
29634
+ handoffs: options.handoffs,
29635
+ checks: options.checks,
29636
+ tokenUsage: options.tokens !== undefined ? { status: "known", total: options.tokens } : { status: "unknown" }
29637
+ } : undefined,
28841
29638
  experimentId,
28842
29639
  variantId,
28843
29640
  suiteTaskId,
28844
- harnessFingerprint: fingerprint,
29641
+ harnessFingerprint: fingerprint2,
28845
29642
  disabledComponents: disabled && disabled.length > 0 ? [...disabled] : disabled ? [] : undefined,
28846
29643
  status: options.verdict === "PASS" ? "pass" : options.verdict === "REJECT" ? "reject" : undefined
28847
29644
  };
@@ -28959,13 +29756,13 @@ async function handleCompareModels(projectRoot, options) {
28959
29756
  outcomes = outcomes.filter((o) => options.modelsFilter.includes(o.model));
28960
29757
  }
28961
29758
  const markdown = generateModelComparisonMarkdown(outcomes, options.modelsFilter);
28962
- const templatePath = resolve43(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
29759
+ const templatePath = resolve46(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
28963
29760
  try {
28964
- await readFile40(templatePath);
29761
+ await readFile43(templatePath);
28965
29762
  } catch {}
28966
- const { writeFile: writeFile26, mkdir: mkdir26 } = await import("node:fs/promises");
28967
- await mkdir26(resolve43(projectRoot, ".codeconductor/evaluation"), { recursive: true });
28968
- await writeFile26(templatePath, markdown, "utf-8");
29763
+ const { writeFile: writeFile28, mkdir: mkdir28 } = await import("node:fs/promises");
29764
+ await mkdir28(resolve46(projectRoot, ".codeconductor/evaluation"), { recursive: true });
29765
+ await writeFile28(templatePath, markdown, "utf-8");
28969
29766
  return {
28970
29767
  code: 0,
28971
29768
  data: {
@@ -30434,7 +31231,7 @@ var exports_seo_audit_command = {};
30434
31231
  __export(exports_seo_audit_command, {
30435
31232
  seoAuditCommand: () => seoAuditCommand
30436
31233
  });
30437
- import { join as join27 } from "node:path";
31234
+ import { join as join28 } from "node:path";
30438
31235
  async function seoAuditCommand(options) {
30439
31236
  const { url, sitemap, format, failOn, delay: delay2, output, followRedirects, force } = options;
30440
31237
  if (!url && !sitemap) {
@@ -30447,7 +31244,7 @@ async function seoAuditCommand(options) {
30447
31244
  }
30448
31245
  };
30449
31246
  }
30450
- const defaultMarkdownPath = output === undefined && format === "markdown" ? join27("seo-reports", `audit-report-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19)}.md`) : undefined;
31247
+ const defaultMarkdownPath = output === undefined && format === "markdown" ? join28("seo-reports", `audit-report-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19)}.md`) : undefined;
30451
31248
  const outputPathToWrite = output ?? defaultMarkdownPath;
30452
31249
  if (outputPathToWrite !== undefined) {
30453
31250
  const preflightError = await preflightContainedOutput(options.projectRoot, outputPathToWrite, { force });
@@ -30808,6 +31605,7 @@ var COMMANDS = [
30808
31605
  { name: "setup", group: "getting-started", summary: "Configure and install CodeConductor.", usage: ["cc-codeconductor setup [--target <target>] [--locale en|es] [--yes] [--dry-run]"] },
30809
31606
  { name: "detect", group: "getting-started", summary: "Inspect the project stack and recommended presets.", usage: ["cc-codeconductor detect"] },
30810
31607
  { name: "init", group: "getting-started", summary: "Initialize low-level CodeConductor configuration.", usage: ["cc-codeconductor init [--locale en|es]"] },
31608
+ { name: "usage", group: "getting-started", summary: "Show installation examples for presets, council, and LSP.", usage: ["cc-codeconductor usage"] },
30811
31609
  { name: "install", group: "getting-started", summary: "Install harness components.", usage: ["cc-codeconductor install preset --target <target>"], options: ["--target opencode|claude|codex|gemini|cursor|agy|pi|all"], subcommands: [
30812
31610
  { name: "preset", group: "getting-started", summary: "Install agents, prompts, skills, and commands.", usage: ["cc-codeconductor install preset --target <target> [--locale en|es]"] },
30813
31611
  { name: "council", group: "getting-started", summary: "Install generated council files.", usage: ["cc-codeconductor install council --target <target>"] },
@@ -30832,6 +31630,7 @@ var COMMANDS = [
30832
31630
  { name: "orchestrate", group: "workflow", summary: "Run goal execution orchestration.", usage: ["cc-codeconductor orchestrate <subcommand>"] },
30833
31631
  { name: "impact", group: "workflow", summary: "Analyze change impact.", usage: ["cc-codeconductor impact [--files <paths>]"] },
30834
31632
  { name: "verify", group: "workflow", summary: "Verify task completion with evidence.", usage: ["cc-codeconductor verify --task <id>"] },
31633
+ { 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
31634
  { name: "seo", group: "tool", summary: "Audit SEO and generate llms.txt.", usage: ["cc-codeconductor seo audit --url <url>"] },
30836
31635
  { name: "debt-harvest", aliases: ["harvest"], group: "tool", summary: "Scan source files for deferred debt.", usage: ["cc-codeconductor debt-harvest"] }
30837
31636
  ];
@@ -30920,6 +31719,96 @@ _arguments '1:command:(${names})' '--target[target]:target:(${targets})'`;
30920
31719
  return null;
30921
31720
  }
30922
31721
 
31722
+ // src/cli/usage-guide.ts
31723
+ init_cli_bin();
31724
+ var RECOMMENDED_FLOW = [
31725
+ "cc-codeconductor setup --target claude",
31726
+ "cc-codeconductor setup --target claude --locale es --yes",
31727
+ "cc-codeconductor setup --target claude --dry-run"
31728
+ ];
31729
+ var PRIMITIVES = [
31730
+ "cc-codeconductor detect",
31731
+ "cc-codeconductor init --locale es",
31732
+ "cc-codeconductor install preset --target claude",
31733
+ "cc-codeconductor install council --target claude",
31734
+ "cc-codeconductor install lsp --target claude"
31735
+ ];
31736
+ var PRESET_EXAMPLES = [
31737
+ "cc-codeconductor install preset --target claude",
31738
+ "cc-codeconductor install preset --target claude --global",
31739
+ "cc-codeconductor install preset --target claude --force",
31740
+ "cc-codeconductor install preset --target claude --dry-run",
31741
+ "cc-codeconductor install preset --target all",
31742
+ "cc-codeconductor install preset --target claude --locale es",
31743
+ "cc-codeconductor install preset --target claude --global --force"
31744
+ ];
31745
+ var COUNCIL_EXAMPLES = [
31746
+ "cc-codeconductor install council --target claude",
31747
+ "cc-codeconductor install council --target claude --global",
31748
+ "cc-codeconductor install council --target claude --force",
31749
+ "cc-codeconductor install council --target claude --dry-run",
31750
+ "cc-codeconductor install council --target all",
31751
+ "cc-codeconductor install council --target claude --global --force"
31752
+ ];
31753
+ var LSP_EXAMPLES = [
31754
+ "cc-codeconductor install lsp --target claude --lang typescript,python",
31755
+ "cc-codeconductor install lsp --target all --lang typescript,python",
31756
+ "cc-codeconductor install lsp --target claude --global",
31757
+ "cc-codeconductor install lsp --target claude --dry-run"
31758
+ ];
31759
+ var AFTER_INSTALL_EXAMPLES = [
31760
+ ["cc-codeconductor doctor", "validates the installation"],
31761
+ ["cc-codeconductor status", "shows what is installed"],
31762
+ ["cc-codeconductor update --dry-run", "previews reconciliation of managed files after upgrading"],
31763
+ ["cc-codeconductor update --force", "applies reconciliation of managed files after upgrading"],
31764
+ ["cc-codeconductor migrate --dry-run", "previews repair of orphaned artifacts a reinstall cannot fix"]
31765
+ ];
31766
+ function renderExamples(lines, bin) {
31767
+ return lines.map((line) => ` ${formatUsage(line, bin)}`);
31768
+ }
31769
+ function renderAnnotatedExamples(entries, bin) {
31770
+ return entries.map(([line, description]) => ` ${formatUsage(line, bin)} — ${description}`);
31771
+ }
31772
+ function renderUsageGuide(bin = resolveCliBin()) {
31773
+ return [
31774
+ "CodeConductor — installation usage guide",
31775
+ "",
31776
+ "Recommended flow (one command, installs and configures everything):",
31777
+ ...renderExamples(RECOMMENDED_FLOW, bin),
31778
+ "",
31779
+ "Fine-grained primitives (for scripting or partial installs):",
31780
+ ...renderExamples(PRIMITIVES, bin),
31781
+ "",
31782
+ "Shared flags:",
31783
+ " --target opencode|claude|codex|gemini|cursor|agy|pi|all (default: opencode)",
31784
+ " --global Install to the home directory (~/.claude, ~/.opencode, etc.) instead of the project",
31785
+ " --force Allow overwriting existing files",
31786
+ " --dry-run Show what would happen without writing files",
31787
+ " --locale Instruction language for agent files: en (default) | es — install preset only",
31788
+ " --lang Comma-separated languages to install LSP servers for — install lsp only",
31789
+ " --output Output mode: human (default) | json",
31790
+ "",
31791
+ "install preset — agents, prompts, skills, and commands:",
31792
+ ...renderExamples(PRESET_EXAMPLES, bin),
31793
+ "",
31794
+ "install council — generated council spec files:",
31795
+ ...renderExamples(COUNCIL_EXAMPLES, bin),
31796
+ "",
31797
+ "install lsp — language servers:",
31798
+ ...renderExamples(LSP_EXAMPLES, bin),
31799
+ " Dedicated integration config is generated for: opencode, claude, codex, gemini, cursor, agy.",
31800
+ " pi installs the language servers but does not get a dedicated integration config file.",
31801
+ "",
31802
+ "After installing, verify and keep in sync:",
31803
+ ...renderAnnotatedExamples(AFTER_INSTALL_EXAMPLES, bin),
31804
+ "",
31805
+ "More detail (GitHub only, not bundled in the npm package):",
31806
+ " docs/usage-cli.md Detailed CLI reference, incl. per-target generated files",
31807
+ " docs/getting-started/ Installation, first project, and update walkthroughs"
31808
+ ].join(`
31809
+ `);
31810
+ }
31811
+
30923
31812
  // src/cli/router.ts
30924
31813
  var OutputModeSchema = exports_external.enum(["json", "human"]);
30925
31814
  var SeoFormatSchema = exports_external.enum(["cli", "json", "markdown"]);
@@ -31200,6 +32089,8 @@ async function routeCommand(args, projectRoot) {
31200
32089
  case "docs":
31201
32090
  case "man":
31202
32091
  return { code: 0, data: { success: true, command, output: renderDocs(subcommand) } };
32092
+ case "usage":
32093
+ return { code: 0, data: { success: true, command: "usage", output: renderUsageGuide() } };
31203
32094
  case "completion": {
31204
32095
  const output = renderCompletion(subcommand ?? "");
31205
32096
  return output ? { code: 0, data: { success: true, command: "completion", output } } : cliContractError("completion", ["Expected shell: bash, zsh, fish, or powershell."]);
@@ -31310,6 +32201,24 @@ async function routeCommand(args, projectRoot) {
31310
32201
  allowCompileCheck: options["allow-compile-check"] === true || options["allow-compile-check"] === "true"
31311
32202
  });
31312
32203
  }
32204
+ case "rdd": {
32205
+ const validSubs = ["capture", "verify", "status", "git-check", "install-hooks"];
32206
+ if (!subcommand || !validSubs.includes(subcommand)) {
32207
+ return unknownSubcommand("rdd", subcommand ?? "", [...validSubs]);
32208
+ }
32209
+ const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
32210
+ const paths = typeof options.paths === "string" ? options.paths.split(",").map((path) => path.trim()).filter(Boolean) : undefined;
32211
+ return rddCommand2({
32212
+ subcommand,
32213
+ projectRoot,
32214
+ taskId: options.task ?? args.rest?.[0],
32215
+ receiptId: options.receipt,
32216
+ phase: options.phase,
32217
+ paths,
32218
+ outcome: options.outcome,
32219
+ command: options.command
32220
+ });
32221
+ }
31313
32222
  case "ccep": {
31314
32223
  const validSubs = ["parse", "profile", "resolve", "compile", "validate", "evaluate", "consensus", "taskcard"];
31315
32224
  if (subcommand && !validSubs.includes(subcommand)) {
@@ -31348,6 +32257,7 @@ async function routeCommand(args, projectRoot) {
31348
32257
  "start",
31349
32258
  "done",
31350
32259
  "block",
32260
+ "unblock",
31351
32261
  "archive"
31352
32262
  ];
31353
32263
  let openspecSub = "validate";
@@ -31370,6 +32280,19 @@ async function routeCommand(args, projectRoot) {
31370
32280
  output: flags.output
31371
32281
  });
31372
32282
  }
32283
+ case "odd": {
32284
+ const validSubs = ["create", "read", "reconcile"];
32285
+ if (!subcommand || !validSubs.includes(subcommand)) {
32286
+ return unknownSubcommand(command, subcommand ?? "", validSubs);
32287
+ }
32288
+ const { oddCommand: oddCommand2 } = await Promise.resolve().then(() => (init_odd_command(), exports_odd_command));
32289
+ return oddCommand2({
32290
+ subcommand,
32291
+ projectRoot,
32292
+ input: options.input,
32293
+ id: options.id ?? args.rest?.[0]
32294
+ });
32295
+ }
31373
32296
  case "hook": {
31374
32297
  const validHookEvents = ["pre-tool", "post-tool", "session-start"];
31375
32298
  if (subcommand && !validHookEvents.includes(subcommand)) {
@@ -31433,6 +32356,10 @@ async function routeCommand(args, projectRoot) {
31433
32356
  costUsd: options.cost ? parseFloat(String(options.cost)) : undefined,
31434
32357
  tokens: options.tokens ? parseInt(String(options.tokens), 10) : undefined,
31435
32358
  durationMs: options.duration ? parseInt(String(options.duration), 10) : undefined,
32359
+ deliveryRoute: options.route,
32360
+ contextBytes: options["context-bytes"] ? parseInt(String(options["context-bytes"]), 10) : undefined,
32361
+ handoffs: options.handoffs ? parseInt(String(options.handoffs), 10) : undefined,
32362
+ checks: typeof options.checks === "string" ? options.checks.split(",").map((check) => check.trim()).filter(Boolean) : undefined,
31436
32363
  verdict: options.verdict,
31437
32364
  weightedScore: options.score ? parseFloat(String(options.score)) : undefined,
31438
32365
  source: options.source,