@use-the-fork/spindle 0.0.0-stage → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -2
  3. package/build/Assets/Blueprints/Catalog.js +110 -0
  4. package/build/Assets/Blueprints/agents/general.agent.md +67 -0
  5. package/build/Assets/Blueprints/agents/sdd-code-reviewer.agent.md +62 -0
  6. package/build/Assets/Blueprints/agents/sdd-code-slop-reviewer.agent.md +59 -0
  7. package/build/Assets/Blueprints/agents/sdd-implementer.agent.md +77 -0
  8. package/build/Assets/Blueprints/agents/sdd-orchestrator.agent.md +42 -0
  9. package/build/Assets/Blueprints/agents/sdd-re-review.agent.md +57 -0
  10. package/build/Assets/Blueprints/agents/sdd-reviewer.agent.md +89 -0
  11. package/build/Assets/Blueprints/agents/sdd-task-reviewer.agent.md +54 -0
  12. package/build/Assets/Blueprints/agents/shared-codebase-analyzer.agent.md +151 -0
  13. package/build/Assets/Blueprints/agents/shared-codebase-locator.agent.md +130 -0
  14. package/build/Assets/Blueprints/agents/shared-codebase-pattern-finder.agent.md +247 -0
  15. package/build/Assets/Blueprints/agents/shared-explore.agent.md +53 -0
  16. package/build/Assets/Blueprints/agents/shared-r.agent.md +17 -0
  17. package/build/Assets/Blueprints/agents/shared-rw.agent.md +17 -0
  18. package/build/Assets/Blueprints/agents/shared-rwx.agent.md +17 -0
  19. package/build/Assets/Blueprints/agents/shared-web-search-researcher.agent.md +122 -0
  20. package/build/Assets/Blueprints/instructions/code-commenting.instructions.md +39 -0
  21. package/build/Assets/Blueprints/instructions/source-architecture.instructions.md +15 -0
  22. package/build/Assets/Blueprints/skills/asd-ste100/CREDITS.MD +1 -0
  23. package/build/Assets/Blueprints/skills/asd-ste100/SKILL.md +83 -0
  24. package/build/Assets/Blueprints/skills/asd-ste100/examples/before-after.md +107 -0
  25. package/build/Assets/Blueprints/skills/asd-ste100/references/writing-rules.md +65 -0
  26. package/build/Assets/Blueprints/skills/author-agent/SKILL.md +26 -0
  27. package/build/Assets/Blueprints/skills/author-design/SKILL.md +38 -0
  28. package/build/Assets/Blueprints/skills/author-instruction/SKILL.md +28 -0
  29. package/build/Assets/Blueprints/skills/author-skill/SKILL.md +26 -0
  30. package/build/Assets/Blueprints/skills/grill/SKILL.md +33 -0
  31. package/build/Assets/Blueprints/skills/grill-design/SKILL.md +41 -0
  32. package/build/Assets/Blueprints/skills/grill-with-docs/SKILL.md +14 -0
  33. package/build/Assets/Blueprints/skills/implement/SKILL.md +79 -0
  34. package/build/Assets/Blueprints/skills/implement-spec/SKILL.md +34 -0
  35. package/build/Assets/Blueprints/skills/improve-codebase-architecture/HTML-REPORT.md +129 -0
  36. package/build/Assets/Blueprints/skills/improve-codebase-architecture/SKILL.md +74 -0
  37. package/build/Assets/Blueprints/skills/int-code-review/SKILL.md +76 -0
  38. package/build/Assets/Blueprints/skills/int-code-slop-review/SKILL.md +238 -0
  39. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/_sections.md +36 -0
  40. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/_template.md +42 -0
  41. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-closing-brace-labels.md +112 -0
  42. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-empty-docblocks.md +132 -0
  43. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-narration.md +118 -0
  44. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-placeholder.md +130 -0
  45. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-generic-catch.md +153 -0
  46. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-impossible-null.md +157 -0
  47. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-missing-real.md +157 -0
  48. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-generic-placeholders.md +120 -0
  49. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-over-descriptive.md +108 -0
  50. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-suffix-abuse.md +167 -0
  51. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-type-in-name.md +100 -0
  52. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-dependency-creep.md +114 -0
  53. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-premature-interface.md +136 -0
  54. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-single-method-class.md +161 -0
  55. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-useless-wrapper.md +140 -0
  56. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-as-any-escape.md +127 -0
  57. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-debug-artifacts.md +144 -0
  58. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-hyper-consistent.md +84 -0
  59. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-no-hack-scars.md +113 -0
  60. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-trivial-boilerplate.md +191 -0
  61. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-doesnt-throw.md +132 -0
  62. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-mirror-implementation.md +125 -0
  63. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-mock-everything.md +148 -0
  64. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-snapshot-abuse.md +131 -0
  65. package/build/Assets/Blueprints/skills/int-codebase-design/SKILL.md +116 -0
  66. package/build/Assets/Blueprints/skills/int-codebase-design/references/DEEPENING.md +37 -0
  67. package/build/Assets/Blueprints/skills/int-codebase-design/references/DESIGN-IT-TWICE.md +44 -0
  68. package/build/Assets/Blueprints/skills/int-domain-modeling/SKILL.md +78 -0
  69. package/build/Assets/Blueprints/skills/int-domain-modeling/references/ADR-FORMAT.md +47 -0
  70. package/build/Assets/Blueprints/skills/int-domain-modeling/references/GLOSSARY-FORMAT.md +60 -0
  71. package/build/Assets/Blueprints/skills/int-prototype/LOGIC.md +67 -0
  72. package/build/Assets/Blueprints/skills/int-prototype/SKILL.md +30 -0
  73. package/build/Assets/Blueprints/skills/int-prototype/UI.md +112 -0
  74. package/build/Assets/Blueprints/skills/int-retrospective/SKILL.md +88 -0
  75. package/build/Assets/Blueprints/skills/int-retrospective/assets/retrospective-prompt.md +38 -0
  76. package/build/Assets/Blueprints/skills/int-retrospective/references/assessment.md +56 -0
  77. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/SKILL.md +64 -0
  78. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/implementer-prompt.md +40 -0
  79. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/reviewer-prompt.md +43 -0
  80. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/aggregate-review.md +7 -0
  81. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/corrective-rounds.md +72 -0
  82. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/example-workflow.md +25 -0
  83. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/full-spec.md +72 -0
  84. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/implementer-corrections.md +9 -0
  85. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/review-inputs.md +26 -0
  86. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/verification-evidence.md +7 -0
  87. package/build/Assets/Blueprints/skills/int-test-driven-development/SKILL.md +51 -0
  88. package/build/Assets/Blueprints/skills/int-test-driven-development/references/mocking.md +31 -0
  89. package/build/Assets/Blueprints/skills/int-test-driven-development/references/tests.md +68 -0
  90. package/build/Assets/Blueprints/skills/int-testing-agent-guidance/SKILL.md +53 -0
  91. package/build/Assets/Blueprints/skills/int-verification-before-completion/SKILL.md +25 -0
  92. package/build/Assets/Blueprints/skills/int-writing-agents/SKILL.md +66 -0
  93. package/build/Assets/Blueprints/skills/int-writing-for-agents/SKILL.md +86 -0
  94. package/build/Assets/Blueprints/skills/int-writing-instruction/SKILL.md +57 -0
  95. package/build/Assets/Blueprints/skills/int-writing-skills/SKILL.md +129 -0
  96. package/build/Assets/Blueprints/skills/int-writing-skills/assets/interactive.md +83 -0
  97. package/build/Assets/Blueprints/skills/int-writing-skills/assets/reference.md +32 -0
  98. package/build/Assets/Blueprints/skills/int-writing-skills/references/arguments.md +50 -0
  99. package/build/Assets/Blueprints/skills/int-writing-skills/references/interactive-skills.md +53 -0
  100. package/build/Assets/Blueprints/skills/int-writing-skills/references/persuasion-principles.md +220 -0
  101. package/build/Assets/Blueprints/skills/int-writing-skills/references/reference-skills.md +20 -0
  102. package/build/Assets/Blueprints/skills/int-writing-skills/references/testing-skills.md +48 -0
  103. package/build/Assets/Blueprints/skills/retrospective/SKILL.md +16 -0
  104. package/build/Assets/Blueprints/skills/setup-project/SKILL.md +66 -0
  105. package/build/Assets/Blueprints/skills/to-spec/SKILL.md +53 -0
  106. package/build/Assets/Blueprints/skills/to-tickets/SKILL.md +49 -0
  107. package/build/Assets/Blueprints/skills/update-tooling/SKILL.md +86 -0
  108. package/build/Assets/Templates/Catalog.js +12 -0
  109. package/build/Assets/Templates/Catalog.ts +17 -0
  110. package/build/Assets/Templates/project/copilot-instructions.md +17 -0
  111. package/build/Assets/Templates/project/domain.md +51 -0
  112. package/build/Assets/Templates/project/ticket-tracker.md +33 -0
  113. package/build/Assets/Templates/sdd/design.md +25 -0
  114. package/build/Assets/Templates/sdd/spec.md +29 -0
  115. package/build/Assets/Templates/sdd/ticket.md +15 -0
  116. package/build/BlueprintManagement/BlueprintManagementServiceProvider.js +15 -0
  117. package/build/BlueprintManagement/Catalog/BlueprintCatalog.js +238 -0
  118. package/build/BlueprintManagement/Commands/BlueprintCommandOptions.js +9 -0
  119. package/build/BlueprintManagement/Commands/ConflictPolicy.js +6 -0
  120. package/build/BlueprintManagement/Commands/InitCommand.js +117 -0
  121. package/build/BlueprintManagement/Commands/Prompts.js +66 -0
  122. package/build/BlueprintManagement/Commands/UpdateCommand.js +86 -0
  123. package/build/BlueprintManagement/Handlers/Agents/AgentHandler.js +21 -0
  124. package/build/BlueprintManagement/Handlers/BlueprintHandlers.js +4 -0
  125. package/build/BlueprintManagement/Handlers/Instructions/InstructionHandler.js +21 -0
  126. package/build/BlueprintManagement/Handlers/Skills/SkillHandler.js +10 -0
  127. package/build/BlueprintManagement/Reconciliation/BlueprintCatalogPlanner.js +87 -0
  128. package/build/BlueprintManagement/Reconciliation/BlueprintRemovalCoordinator.js +78 -0
  129. package/build/BlueprintManagement/Services/BlueprintReconciliationService.js +391 -0
  130. package/build/BlueprintManagement/Services/InstallationService.js +54 -0
  131. package/build/BlueprintManagement/Support/BlueprintFiles.js +113 -0
  132. package/build/BlueprintManagement/Types/Blueprint.js +1 -0
  133. package/build/BlueprintManagement/Types/Installation.js +1 -0
  134. package/build/BlueprintManagement/Types/Reconciliation.js +1 -0
  135. package/build/BlueprintManagement/Versioning/BlueprintVersion.js +70 -0
  136. package/build/BlueprintManagement/Versioning/BlueprintVersioning.js +55 -0
  137. package/build/Commands/Command.js +3 -0
  138. package/build/Commands/CommandOptions.js +12 -0
  139. package/build/Config/Config.js +3 -0
  140. package/build/Foundation/Application.js +112 -0
  141. package/build/Foundation/ApplicationBuilder.js +23 -0
  142. package/build/Foundation/Bootstrap/Bootstrapper.js +2 -0
  143. package/build/Foundation/Bootstrap/DiscoverGitRoot.js +8 -0
  144. package/build/Foundation/Bootstrap/LoadConsoleArgs.js +47 -0
  145. package/build/Foundation/Bootstrap/LoadProjectConfiguration.js +144 -0
  146. package/build/Foundation/Bootstrap/RegisterProviders.js +12 -0
  147. package/build/Foundation/Bootstrap/RequireProjectConfiguration.js +14 -0
  148. package/build/Foundation/ConsoleInvocation.js +1 -0
  149. package/build/Foundation/Container.js +185 -0
  150. package/build/Foundation/FoundationServiceProvider.js +5 -0
  151. package/build/Foundation/Kernel.js +30 -0
  152. package/build/Foundation/Service.js +23 -0
  153. package/build/Foundation/ServiceProvider.js +12 -0
  154. package/build/Git/GitServiceProvider.js +8 -0
  155. package/build/Git/Services/GitService.js +26 -0
  156. package/build/Project/ProjectServiceProvider.js +8 -0
  157. package/build/Project/Services/ProjectService.js +124 -0
  158. package/build/SubagentDrivenDevelopment/Commands/ChecksRecordCommand.js +35 -0
  159. package/build/SubagentDrivenDevelopment/Commands/ChecksRequireCommand.js +33 -0
  160. package/build/SubagentDrivenDevelopment/Commands/ClaimCommand.js +23 -0
  161. package/build/SubagentDrivenDevelopment/Commands/CorrectCommand.js +37 -0
  162. package/build/SubagentDrivenDevelopment/Commands/CreateCommand.js +26 -0
  163. package/build/SubagentDrivenDevelopment/Commands/FindingCreateCommand.js +39 -0
  164. package/build/SubagentDrivenDevelopment/Commands/FindingUpdateCommand.js +36 -0
  165. package/build/SubagentDrivenDevelopment/Commands/FinishCommand.js +23 -0
  166. package/build/SubagentDrivenDevelopment/Commands/ImplementCommand.js +23 -0
  167. package/build/SubagentDrivenDevelopment/Commands/NextCommand.js +21 -0
  168. package/build/SubagentDrivenDevelopment/Commands/ReadyCommand.js +21 -0
  169. package/build/SubagentDrivenDevelopment/Commands/RequirementsDecideCommand.js +33 -0
  170. package/build/SubagentDrivenDevelopment/Commands/ResumeCommand.js +31 -0
  171. package/build/SubagentDrivenDevelopment/Commands/ReviewCommand.js +55 -0
  172. package/build/SubagentDrivenDevelopment/Commands/ReviewRequireCommand.js +12 -0
  173. package/build/SubagentDrivenDevelopment/Commands/StatusCommand.js +21 -0
  174. package/build/SubagentDrivenDevelopment/Commands/SubmitCommand.js +59 -0
  175. package/build/SubagentDrivenDevelopment/Commands/TicketCommentCommand.js +24 -0
  176. package/build/SubagentDrivenDevelopment/Commands/TicketCreateCommand.js +25 -0
  177. package/build/SubagentDrivenDevelopment/Commands/TicketUpdateCommand.js +31 -0
  178. package/build/SubagentDrivenDevelopment/Commands/groups.js +36 -0
  179. package/build/SubagentDrivenDevelopment/Evidence/GitEvidence.js +449 -0
  180. package/build/SubagentDrivenDevelopment/Evidence/ReportEvidence.js +80 -0
  181. package/build/SubagentDrivenDevelopment/Models/Assignments.js +1 -0
  182. package/build/SubagentDrivenDevelopment/Models/Evidence.js +1 -0
  183. package/build/SubagentDrivenDevelopment/Models/Findings.js +1 -0
  184. package/build/SubagentDrivenDevelopment/Models/Options.js +1 -0
  185. package/build/SubagentDrivenDevelopment/Models/Workflow.js +16 -0
  186. package/build/SubagentDrivenDevelopment/Persistence/AssignmentSchema.js +155 -0
  187. package/build/SubagentDrivenDevelopment/Persistence/CheckSchema.js +71 -0
  188. package/build/SubagentDrivenDevelopment/Persistence/FeatureWorkspace.js +76 -0
  189. package/build/SubagentDrivenDevelopment/Persistence/FileSystem.js +28 -0
  190. package/build/SubagentDrivenDevelopment/Persistence/FindingSchema.js +81 -0
  191. package/build/SubagentDrivenDevelopment/Persistence/LedgerRepository.js +322 -0
  192. package/build/SubagentDrivenDevelopment/Persistence/PathSafety.js +62 -0
  193. package/build/SubagentDrivenDevelopment/Persistence/ProgressSchema.js +95 -0
  194. package/build/SubagentDrivenDevelopment/Persistence/ReadinessReader.js +40 -0
  195. package/build/SubagentDrivenDevelopment/Persistence/ReviewSchema.js +68 -0
  196. package/build/SubagentDrivenDevelopment/Persistence/TicketRepository.js +147 -0
  197. package/build/SubagentDrivenDevelopment/Persistence/WorkspaceTransaction.js +282 -0
  198. package/build/SubagentDrivenDevelopment/Policies/AcceptancePolicy.js +100 -0
  199. package/build/SubagentDrivenDevelopment/Policies/AssignmentPolicy.js +36 -0
  200. package/build/SubagentDrivenDevelopment/Policies/CheckPolicy.js +93 -0
  201. package/build/SubagentDrivenDevelopment/Policies/FindingPolicy.js +53 -0
  202. package/build/SubagentDrivenDevelopment/Policies/InputValidation.js +36 -0
  203. package/build/SubagentDrivenDevelopment/Policies/ReadinessPolicy.js +26 -0
  204. package/build/SubagentDrivenDevelopment/Policies/RequiredReviewers.js +12 -0
  205. package/build/SubagentDrivenDevelopment/Policies/ReviewPolicy.js +87 -0
  206. package/build/SubagentDrivenDevelopment/Policies/WorkflowPolicy.js +85 -0
  207. package/build/SubagentDrivenDevelopment/Services/AssignmentService.js +269 -0
  208. package/build/SubagentDrivenDevelopment/Services/ChecksService.js +319 -0
  209. package/build/SubagentDrivenDevelopment/Services/CorrectionService.js +273 -0
  210. package/build/SubagentDrivenDevelopment/Services/DeliveryCorrectionService.js +253 -0
  211. package/build/SubagentDrivenDevelopment/Services/DeliveryFindingService.js +302 -0
  212. package/build/SubagentDrivenDevelopment/Services/DeliveryService.js +160 -0
  213. package/build/SubagentDrivenDevelopment/Services/DeliverySubmissionService.js +273 -0
  214. package/build/SubagentDrivenDevelopment/Services/FeatureCreationService.js +138 -0
  215. package/build/SubagentDrivenDevelopment/Services/FeatureInspectionService.js +399 -0
  216. package/build/SubagentDrivenDevelopment/Services/FindingService.js +392 -0
  217. package/build/SubagentDrivenDevelopment/Services/IntegrationService.js +258 -0
  218. package/build/SubagentDrivenDevelopment/Services/ReadinessService.js +21 -0
  219. package/build/SubagentDrivenDevelopment/Services/RequirementsService.js +216 -0
  220. package/build/SubagentDrivenDevelopment/Services/ReviewService.js +471 -0
  221. package/build/SubagentDrivenDevelopment/Services/SubmissionService.js +299 -0
  222. package/build/SubagentDrivenDevelopment/Services/TicketService.js +186 -0
  223. package/build/SubagentDrivenDevelopment/SubagentDrivenDevelopmentServiceProvider.js +113 -0
  224. package/build/Support/Records.js +5 -0
  225. package/build/Support/Repository.js +139 -0
  226. package/build/Support/Stacks.js +10 -0
  227. package/build/TemplateRendering/Computed/ComputedContextService.js +13 -0
  228. package/build/TemplateRendering/Computed/ComputedVariableProvider.js +6 -0
  229. package/build/TemplateRendering/Computed/ComputedVariableProviders.js +6 -0
  230. package/build/TemplateRendering/Computed/Providers/InternalSkillDescriptionProvider.js +6 -0
  231. package/build/TemplateRendering/Computed/Providers/SpindlePackageManagerProvider.js +11 -0
  232. package/build/TemplateRendering/Computed/ResolveRenderContext.js +18 -0
  233. package/build/TemplateRendering/Rendering/RenderText.js +43 -0
  234. package/build/TemplateRendering/TemplateRenderingServiceProvider.js +8 -0
  235. package/build/Tooling/Commands/ToolingAddCommand.js +29 -0
  236. package/build/Tooling/Commands/ToolingListCommand.js +19 -0
  237. package/build/Tooling/Commands/ToolingRemoveCommand.js +19 -0
  238. package/build/Tooling/Commands/ToolingUpdateCommand.js +30 -0
  239. package/build/Tooling/Commands/groups.js +4 -0
  240. package/build/Tooling/Models/ToolingEntry.js +1 -0
  241. package/build/Tooling/Services/ToolingService.js +278 -0
  242. package/build/Tooling/ToolingServiceProvider.js +15 -0
  243. package/build/cli.js +36 -0
  244. package/build/index.js +31 -0
  245. package/package.json +54 -3
@@ -0,0 +1,319 @@
1
+ import { Service } from "../../Foundation/Service.js";
2
+ import { isSafeCoveragePath } from "../Persistence/CheckSchema.js";
3
+ import { validateAuthor, validateTicketId, } from "../Policies/InputValidation.js";
4
+ export class ChecksService extends Service {
5
+ gitEvidence;
6
+ ticketRepository;
7
+ ledgerRepository;
8
+ workspaceTransaction;
9
+ workflowPolicy;
10
+ featureInspectionService;
11
+ constructor(app, gitEvidence, ticketRepository, ledgerRepository, workspaceTransaction, workflowPolicy, featureInspectionService) {
12
+ super(app);
13
+ this.gitEvidence = gitEvidence;
14
+ this.ticketRepository = ticketRepository;
15
+ this.ledgerRepository = ledgerRepository;
16
+ this.workspaceTransaction = workspaceTransaction;
17
+ this.workflowPolicy = workflowPolicy;
18
+ this.featureInspectionService = featureInspectionService;
19
+ }
20
+ checksRequire(options) {
21
+ if (options.scope === "delivery") {
22
+ if (options.ticket)
23
+ throw new Error("--ticket and --scope delivery are mutually exclusive.");
24
+ return this.deliveryChecksRequire(options);
25
+ }
26
+ if (options.scope !== undefined)
27
+ throw new Error("--scope must be 'delivery'.");
28
+ if (!options.ticket)
29
+ throw new Error("checks require needs --ticket or --scope delivery.");
30
+ validateTicketId(options.ticket);
31
+ validateAuthor(options.author);
32
+ const command = options.command.trim();
33
+ if (!command)
34
+ throw new Error("Check command must not be empty.");
35
+ if (options.remove && !options.details?.trim()) {
36
+ throw new Error("Removing a required check requires non-empty --details rationale.");
37
+ }
38
+ if (!options.remove && options.details !== undefined) {
39
+ throw new Error("--details is only valid when removing a required check.");
40
+ }
41
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
42
+ return this.workspaceTransaction.run(feature.workspace, () => {
43
+ const ledger = this.ledgerRepository.readLedger(feature);
44
+ const progress = this.workflowPolicy.requireProgress(ledger, options.ticket);
45
+ if (progress.execution === "reviewing" && (progress.reviewAssignments?.length ?? 0) > 0) {
46
+ throw new Error("The review snapshot is frozen; required checks cannot change while review assignments are active.");
47
+ }
48
+ const checks = (progress.checks ??= { required: [], records: [], invalidations: [] });
49
+ const active = checks.required.find((requirement) => requirement.command === command && !requirement.removedAt);
50
+ if (options.remove) {
51
+ if (!active)
52
+ throw new Error(`Required check is not declared: ${command}.`);
53
+ active.removedAt = new Date().toISOString();
54
+ active.removedBy = options.author.trim();
55
+ active.removalDetails = options.details.trim();
56
+ }
57
+ else {
58
+ if (active) {
59
+ return {
60
+ ticket: options.ticket,
61
+ checks,
62
+ idempotent: true,
63
+ ...this.gitEvidence.contentIdentity(feature, this.ticketRepository.requireTicket(this.ticketRepository.readTickets(feature.workspace), options.ticket), progress.baseSha, checks),
64
+ };
65
+ }
66
+ checks.required.push({
67
+ command,
68
+ addedAt: new Date().toISOString(),
69
+ addedBy: options.author.trim(),
70
+ });
71
+ }
72
+ let invalidatedDecision;
73
+ if (progress.execution === "accepted" || progress.execution === "integrated") {
74
+ const from = progress.execution;
75
+ progress.execution = "reviewing";
76
+ const invalidation = {
77
+ at: new Date().toISOString(),
78
+ author: options.author.trim(),
79
+ command,
80
+ from,
81
+ to: "reviewing",
82
+ };
83
+ (checks.invalidations ??= []).push(invalidation);
84
+ invalidatedDecision = { from, to: "reviewing" };
85
+ }
86
+ const currentTicket = this.ticketRepository.requireTicket(this.ticketRepository.readTickets(feature.workspace), options.ticket);
87
+ const identity = this.gitEvidence.contentIdentity(feature, currentTicket, progress.baseSha, checks);
88
+ this.workspaceTransaction.writeLedger(feature, ledger);
89
+ return {
90
+ ticket: options.ticket,
91
+ checks,
92
+ ...(invalidatedDecision ? { invalidatedDecision } : {}),
93
+ ...identity,
94
+ };
95
+ });
96
+ }
97
+ checksRecord(options) {
98
+ if (options.scope === "delivery") {
99
+ if (options.ticket)
100
+ throw new Error("--ticket and --scope delivery are mutually exclusive.");
101
+ return this.deliveryChecksRecord(options);
102
+ }
103
+ if (options.scope !== undefined)
104
+ throw new Error("--scope must be 'delivery'.");
105
+ if (!options.ticket)
106
+ throw new Error("checks record needs --ticket or --scope delivery.");
107
+ validateTicketId(options.ticket);
108
+ validateAuthor(options.author);
109
+ const command = options.command.trim();
110
+ if (!command)
111
+ throw new Error("Check command must not be empty.");
112
+ if (!/^[0-9a-f]{64}$/i.test(options.snapshot)) {
113
+ throw new Error("Snapshot must be a 64-character hexadecimal content identity.");
114
+ }
115
+ if (!["passed", "failed", "unavailable"].includes(options.result)) {
116
+ throw new Error(`Invalid check result: ${options.result}.`);
117
+ }
118
+ if (options.result !== "passed" && !options.details?.trim()) {
119
+ throw new Error("--details is required for failed or unavailable checks.");
120
+ }
121
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
122
+ return this.workspaceTransaction.run(feature.workspace, () => {
123
+ const ledger = this.ledgerRepository.readLedger(feature);
124
+ const progress = this.workflowPolicy.requireProgress(ledger, options.ticket);
125
+ const checks = progress.checks;
126
+ if (!checks)
127
+ throw new Error("No required checks are declared for this ticket.");
128
+ const requirement = checks.required.find((item) => item.command === command && !item.removedAt);
129
+ if (!requirement)
130
+ throw new Error(`Check command is not currently required: ${command}.`);
131
+ const coveredPaths = options.paths === undefined
132
+ ? undefined
133
+ : [...new Set(options.paths.map((path) => path.trim()))].sort();
134
+ if (coveredPaths &&
135
+ (coveredPaths.length === 0 || coveredPaths.some((path) => !isSafeCoveragePath(path)))) {
136
+ throw new Error("--paths must contain unique safe checkout-relative file paths.");
137
+ }
138
+ const ticket = this.ticketRepository.requireTicket(this.ticketRepository.readTickets(feature.workspace), options.ticket);
139
+ const identity = this.gitEvidence.contentIdentity(feature, ticket, progress.baseSha, checks);
140
+ if (identity.snapshot !== options.snapshot) {
141
+ throw new Error(`Snapshot is stale; current content identity is ${identity.snapshot}. No check result was recorded.`);
142
+ }
143
+ const latestForCommand = [...checks.records]
144
+ .reverse()
145
+ .find((record) => record.command === command);
146
+ const repeat = latestForCommand &&
147
+ latestForCommand.result === options.result &&
148
+ latestForCommand.author === options.author.trim() &&
149
+ latestForCommand.snapshot === options.snapshot &&
150
+ latestForCommand.details === options.details?.trim() &&
151
+ JSON.stringify(latestForCommand.coveredPaths) === JSON.stringify(coveredPaths)
152
+ ? latestForCommand
153
+ : undefined;
154
+ if (repeat)
155
+ return { ticket: options.ticket, record: repeat, idempotent: true, ...identity };
156
+ const record = {
157
+ command,
158
+ result: options.result,
159
+ author: options.author.trim(),
160
+ snapshot: identity.snapshot,
161
+ codeSnapshot: identity.codeSnapshot,
162
+ requirementsSnapshot: identity.requirementsSnapshot,
163
+ headSha: identity.headSha,
164
+ recordedAt: new Date().toISOString(),
165
+ ...(coveredPaths
166
+ ? {
167
+ coveredPaths,
168
+ coverageSnapshot: this.gitEvidence.contentIdentity(feature, ticket, progress.baseSha, checks, coveredPaths).codeSnapshot,
169
+ }
170
+ : {}),
171
+ ...(options.details?.trim() ? { details: options.details.trim() } : {}),
172
+ };
173
+ checks.records.push(record);
174
+ let correctionCheckHistory;
175
+ if (progress.assignment?.action === "correct") {
176
+ const attempt = progress.correctionAttempts?.find((candidate) => candidate.number === progress.assignment?.attempt);
177
+ if (attempt) {
178
+ const recorded = (attempt.checks ??= []);
179
+ correctionCheckHistory = recorded;
180
+ if (!recorded.some((candidate) => candidate.recordedAt === record.recordedAt)) {
181
+ recorded.push(record);
182
+ }
183
+ }
184
+ }
185
+ const confirmedTicket = this.ticketRepository.requireTicket(this.ticketRepository.readTickets(feature.workspace), options.ticket);
186
+ const confirmedIdentity = this.gitEvidence.contentIdentity(feature, confirmedTicket, progress.baseSha, checks);
187
+ if (confirmedIdentity.snapshot !== identity.snapshot) {
188
+ checks.records.pop();
189
+ correctionCheckHistory?.pop();
190
+ throw new Error("Content changed while recording the check; retry with a fresh snapshot.");
191
+ }
192
+ this.workspaceTransaction.writeLedger(feature, ledger);
193
+ return { ticket: options.ticket, record, ...identity };
194
+ });
195
+ }
196
+ deliveryChecksRequire(options) {
197
+ validateAuthor(options.author);
198
+ const command = options.command.trim();
199
+ if (!command)
200
+ throw new Error("Check command must not be empty.");
201
+ if (options.remove && !options.details?.trim()) {
202
+ throw new Error("Removing a required check requires non-empty --details rationale.");
203
+ }
204
+ if (!options.remove && options.details !== undefined) {
205
+ throw new Error("--details is only valid when removing a required check.");
206
+ }
207
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
208
+ return this.workspaceTransaction.run(feature.workspace, () => {
209
+ const ledger = this.ledgerRepository.readLedger(feature);
210
+ const delivery = ledger.delivery;
211
+ this.workflowPolicy.assertDeliveryOpen(delivery);
212
+ if (delivery.owner && delivery.owner !== options.author.trim()) {
213
+ throw new Error("Only the recorded delivery controller may change required checks.");
214
+ }
215
+ if (delivery.execution === "reviewing" && (delivery.reviewAssignments?.length ?? 0) > 0) {
216
+ throw new Error("Aggregate review is frozen; required delivery checks cannot change.");
217
+ }
218
+ delivery.owner = options.author.trim();
219
+ const checks = (delivery.checks ??= { required: [], records: [], invalidations: [] });
220
+ const active = checks.required.find((requirement) => requirement.command === command && !requirement.removedAt);
221
+ if (options.remove) {
222
+ if (!active)
223
+ throw new Error(`Required delivery check is not declared: ${command}.`);
224
+ active.removedAt = new Date().toISOString();
225
+ active.removedBy = options.author.trim();
226
+ active.removalDetails = options.details.trim();
227
+ }
228
+ else {
229
+ if (active) {
230
+ const identity = this.gitEvidence.deliveryIdentity(feature, ledger, this.ticketRepository.readTickets(feature.workspace));
231
+ return { scope: "delivery", checks, idempotent: true, ...identity };
232
+ }
233
+ checks.required.push({
234
+ command,
235
+ addedAt: new Date().toISOString(),
236
+ addedBy: options.author.trim(),
237
+ });
238
+ }
239
+ const identity = this.gitEvidence.deliveryIdentity(feature, ledger, this.ticketRepository.readTickets(feature.workspace));
240
+ delivery.history.push({
241
+ at: new Date().toISOString(),
242
+ author: options.author.trim(),
243
+ action: `${options.remove ? "delivery-check-removed" : "delivery-check-required"}:${command}`,
244
+ });
245
+ this.workspaceTransaction.writeLedger(feature, ledger);
246
+ return { scope: "delivery", checks, ...identity };
247
+ });
248
+ }
249
+ deliveryChecksRecord(options) {
250
+ validateAuthor(options.author);
251
+ const command = options.command.trim();
252
+ if (!command)
253
+ throw new Error("Check command must not be empty.");
254
+ if (!/^[0-9a-f]{64}$/i.test(options.snapshot)) {
255
+ throw new Error("Snapshot must be a 64-character hexadecimal content identity.");
256
+ }
257
+ if (!["passed", "failed", "unavailable"].includes(options.result)) {
258
+ throw new Error(`Invalid check result: ${options.result}.`);
259
+ }
260
+ if (options.result !== "passed" && !options.details?.trim()) {
261
+ throw new Error("--details is required for failed or unavailable checks.");
262
+ }
263
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
264
+ return this.workspaceTransaction.run(feature.workspace, () => {
265
+ const ledger = this.ledgerRepository.readLedger(feature);
266
+ const delivery = ledger.delivery;
267
+ this.workflowPolicy.assertDeliveryOpen(delivery);
268
+ if (!delivery.checks)
269
+ throw new Error("No required checks are declared for delivery.");
270
+ if (delivery.owner !== options.author.trim()) {
271
+ throw new Error("Only the recorded delivery controller may record final checks.");
272
+ }
273
+ const requirement = delivery.checks.required.find((item) => item.command === command && !item.removedAt);
274
+ if (!requirement)
275
+ throw new Error(`Check command is not currently required for delivery: ${command}.`);
276
+ const identity = this.gitEvidence.deliveryIdentity(feature, ledger, this.ticketRepository.readTickets(feature.workspace));
277
+ if (identity.snapshot !== options.snapshot) {
278
+ throw new Error(`Snapshot is stale; current delivery identity is ${identity.snapshot}. No check result was recorded.`);
279
+ }
280
+ const prior = [...delivery.checks.records]
281
+ .reverse()
282
+ .find((record) => record.command === command);
283
+ if (prior &&
284
+ prior.result === options.result &&
285
+ prior.author === options.author.trim() &&
286
+ prior.snapshot === identity.snapshot &&
287
+ prior.details === options.details?.trim()) {
288
+ return { scope: "delivery", record: prior, idempotent: true, ...identity };
289
+ }
290
+ const record = {
291
+ command,
292
+ result: options.result,
293
+ author: options.author.trim(),
294
+ snapshot: identity.snapshot,
295
+ codeSnapshot: identity.codeSnapshot,
296
+ requirementsSnapshot: identity.requirementsSnapshot,
297
+ headSha: identity.headSha,
298
+ recordedAt: new Date().toISOString(),
299
+ ...(options.details?.trim() ? { details: options.details.trim() } : {}),
300
+ };
301
+ delivery.checks.records.push(record);
302
+ if (record.result !== "passed")
303
+ delivery.execution = "changes-requested";
304
+ delivery.history.push({
305
+ at: record.recordedAt,
306
+ author: record.author,
307
+ action: `delivery-check-recorded:${command}:${record.result}`,
308
+ });
309
+ const confirmed = this.gitEvidence.deliveryIdentity(feature, ledger, this.ticketRepository.readTickets(feature.workspace));
310
+ if (confirmed.snapshot !== identity.snapshot) {
311
+ delivery.checks.records.pop();
312
+ delivery.history.pop();
313
+ throw new Error("Content changed while recording the delivery check; retry with a fresh snapshot.");
314
+ }
315
+ this.workspaceTransaction.writeLedger(feature, ledger);
316
+ return { scope: "delivery", record, ...identity };
317
+ });
318
+ }
319
+ }
@@ -0,0 +1,273 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { join } from "node:path";
3
+ import matter from "gray-matter";
4
+ import { Service } from "../../Foundation/Service.js";
5
+ import { lstatIfPresent } from "../Persistence/FileSystem.js";
6
+ import { validateAuthor, validateTicketId, } from "../Policies/InputValidation.js";
7
+ export class CorrectionService extends Service {
8
+ gitEvidence;
9
+ reportEvidence;
10
+ ticketRepository;
11
+ ledgerRepository;
12
+ workspaceTransaction;
13
+ workflowPolicy;
14
+ deliveryCorrectionService;
15
+ featureInspectionService;
16
+ constructor(app, gitEvidence, reportEvidence, ticketRepository, ledgerRepository, workspaceTransaction, workflowPolicy, deliveryCorrectionService, featureInspectionService) {
17
+ super(app);
18
+ this.gitEvidence = gitEvidence;
19
+ this.reportEvidence = reportEvidence;
20
+ this.ticketRepository = ticketRepository;
21
+ this.ledgerRepository = ledgerRepository;
22
+ this.workspaceTransaction = workspaceTransaction;
23
+ this.workflowPolicy = workflowPolicy;
24
+ this.deliveryCorrectionService = deliveryCorrectionService;
25
+ this.featureInspectionService = featureInspectionService;
26
+ }
27
+ correct(options) {
28
+ if (options.scope === "delivery") {
29
+ if (options.ticket)
30
+ throw new Error("--ticket and --scope delivery are mutually exclusive.");
31
+ return this.deliveryCorrectionService.deliveryCorrect(options);
32
+ }
33
+ if (options.scope !== undefined)
34
+ throw new Error("--scope must be 'delivery'.");
35
+ if (!options.ticket)
36
+ throw new Error("correct requires --ticket or --scope delivery.");
37
+ validateTicketId(options.ticket);
38
+ validateAuthor(options.author);
39
+ const findingIds = [...new Set((options.findings ?? []).map((finding) => finding.trim()))];
40
+ if (findingIds.some((finding) => !/^F-\d{4,}$/.test(finding))) {
41
+ throw new Error("--findings must contain canonical finding IDs.");
42
+ }
43
+ const objective = options.details.trim();
44
+ if (!objective)
45
+ throw new Error("--details must not be empty.");
46
+ if (options.decision !== undefined && !["continue", "pause"].includes(options.decision)) {
47
+ throw new Error("--decision must be 'continue' or 'pause'.");
48
+ }
49
+ if (options.decision !== "pause" && findingIds.length === 0 && !options.checkFailure?.trim()) {
50
+ throw new Error("correct requires actionable --findings or a non-mechanical --check-failure.");
51
+ }
52
+ if (options.decision === "pause") {
53
+ if (![
54
+ "no-progress",
55
+ "repeated-failure",
56
+ "recurring-blocker",
57
+ "requirements-disagreement",
58
+ "worker-blocked",
59
+ ].includes(options.reason ?? "")) {
60
+ throw new Error("--reason is required for a pause and must identify the adjudication category.");
61
+ }
62
+ if (options.assessment || options.assessmentResult)
63
+ throw new Error("--assessment is only valid with --decision continue.");
64
+ }
65
+ else if (options.reason) {
66
+ throw new Error("--reason is only valid with --decision pause.");
67
+ }
68
+ if (options.decision === "continue" &&
69
+ (!options.assessment?.trim() || options.assessmentResult !== "productive")) {
70
+ throw new Error("--assessment and --assessment-result productive are required with --decision continue.");
71
+ }
72
+ if (options.assessmentResult && options.decision !== "continue") {
73
+ throw new Error("--assessment-result is only valid with --decision continue.");
74
+ }
75
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
76
+ return this.workspaceTransaction.run(feature.workspace, () => {
77
+ const ledger = this.ledgerRepository.readLedger(feature);
78
+ const ticket = this.ticketRepository.requireTicket(this.ticketRepository.readTickets(feature.workspace), options.ticket);
79
+ const progress = this.workflowPolicy.requireProgress(ledger, options.ticket);
80
+ if (progress.owner !== options.author.trim()) {
81
+ throw new Error("Only the recorded ticket controller may dispatch a correction.");
82
+ }
83
+ const attempts = (progress.correctionAttempts ??= []);
84
+ if (options.decision === "pause") {
85
+ if (progress.execution === "implementing" && progress.assignment?.status !== "submitted") {
86
+ throw new Error("A pending implementation assignment must be resumed, not paused.");
87
+ }
88
+ const identity = this.gitEvidence.contentIdentity(feature, ticket, progress.baseSha, progress.checks);
89
+ const hasCurrentFailure = (progress.checks?.records ?? []).some((record) => record.snapshot === identity.snapshot &&
90
+ record.codeSnapshot === identity.codeSnapshot &&
91
+ record.requirementsSnapshot === identity.requirementsSnapshot &&
92
+ record.result === "failed");
93
+ if (attempts.length === 0 &&
94
+ progress.execution !== "changes-requested" &&
95
+ !hasCurrentFailure) {
96
+ throw new Error("Adjudication pause requires a recorded failed correction, check, or review.");
97
+ }
98
+ const adjudication = {
99
+ status: "pending",
100
+ reason: options.reason,
101
+ details: objective,
102
+ recordedAt: new Date().toISOString(),
103
+ recordedBy: options.author.trim(),
104
+ };
105
+ if (progress.adjudication?.status === "pending" &&
106
+ progress.adjudication.reason === adjudication.reason &&
107
+ progress.adjudication.details === adjudication.details &&
108
+ progress.adjudication.recordedBy === adjudication.recordedBy) {
109
+ return {
110
+ ticket: options.ticket,
111
+ execution: progress.execution,
112
+ adjudication: progress.adjudication,
113
+ paused: true,
114
+ idempotent: true,
115
+ ...identity,
116
+ };
117
+ }
118
+ progress.adjudication = adjudication;
119
+ const afterTicket = matter.stringify(this.ticketRepository.appendAudit(ticket.body, options.author, `Corrective work paused for adjudication (${adjudication.reason}): ${adjudication.details}.`), this.ticketRepository.metadata(ticket));
120
+ this.workspaceTransaction.writeWorkflowState(feature, ticket, afterTicket, ledger);
121
+ return {
122
+ ticket: options.ticket,
123
+ execution: progress.execution,
124
+ adjudication,
125
+ paused: true,
126
+ ...identity,
127
+ };
128
+ }
129
+ if (progress.execution === "implementing" &&
130
+ progress.assignment?.action === "correct" &&
131
+ progress.assignment.status !== "submitted" &&
132
+ progress.assignment.findingIds?.join(",") === findingIds.join(",") &&
133
+ progress.assignment.checkFailure === options.checkFailure?.trim() &&
134
+ progress.assignment.objective === objective) {
135
+ const current = attempts.find((attempt) => attempt.number === progress.assignment?.attempt);
136
+ if (!current)
137
+ throw new Error("Corrective assignment has no persisted attempt record.");
138
+ const identity = this.gitEvidence.contentIdentity(feature, ticket, progress.baseSha, progress.checks);
139
+ return {
140
+ ticket: options.ticket,
141
+ execution: "implementing",
142
+ attempt: current,
143
+ assignment: progress.assignment,
144
+ idempotent: true,
145
+ ...identity,
146
+ };
147
+ }
148
+ const pendingAdjudication = progress.adjudication?.status === "pending";
149
+ if (pendingAdjudication && options.decision !== "continue") {
150
+ throw new Error(`Ticket ${options.ticket} is paused for adjudication (${progress.adjudication?.reason}); resolve it before another correction.`);
151
+ }
152
+ const checkDriven = !!options.checkFailure?.trim();
153
+ if (!checkDriven && progress.execution !== "changes-requested") {
154
+ throw new Error(`Ticket ${options.ticket} must have a failed review before correction.`);
155
+ }
156
+ if (checkDriven && !["reviewing", "changes-requested"].includes(progress.execution)) {
157
+ throw new Error(`Ticket ${options.ticket} must have a submitted implementation before correction.`);
158
+ }
159
+ if (!progress.baseSha)
160
+ throw new Error("Correction requires the ticket's recorded Git baseline.");
161
+ if (progress.assignment?.action === "correct" && progress.assignment.status !== "submitted") {
162
+ throw new Error("The current correction assignment must be resumed, not replaced.");
163
+ }
164
+ if (attempts.length > 0 || pendingAdjudication) {
165
+ if (options.decision !== "continue" || !options.assessment?.trim()) {
166
+ throw new Error("A subsequent correction requires --decision continue and an explicit --assessment.");
167
+ }
168
+ const previous = attempts.at(-1);
169
+ if (previous.progressAssessment?.decision !== "continue" ||
170
+ previous.progressAssessment.result !== "productive" ||
171
+ previous.progressAssessment.recordedBy !== options.author.trim() ||
172
+ previous.progressAssessment.details !== options.assessment.trim()) {
173
+ previous.progressAssessment = {
174
+ decision: "continue",
175
+ result: "productive",
176
+ details: options.assessment.trim(),
177
+ recordedAt: new Date().toISOString(),
178
+ recordedBy: options.author.trim(),
179
+ };
180
+ }
181
+ }
182
+ else if (options.decision || options.assessment) {
183
+ throw new Error("The first correction does not accept a continuation assessment.");
184
+ }
185
+ const identity = this.gitEvidence.contentIdentity(feature, ticket, progress.baseSha, progress.checks);
186
+ const findings = progress.findings ?? [];
187
+ for (const id of findingIds) {
188
+ const finding = findings.find((candidate) => candidate.id === id);
189
+ if (!finding || finding.status !== "confirmed") {
190
+ throw new Error(`Finding ${id} is not an actionable unresolved finding.`);
191
+ }
192
+ }
193
+ const checkFailure = options.checkFailure?.trim();
194
+ if (checkFailure) {
195
+ const record = progress.checks?.records
196
+ .filter((candidate) => candidate.command === checkFailure &&
197
+ candidate.snapshot === identity.snapshot &&
198
+ candidate.codeSnapshot === identity.codeSnapshot &&
199
+ candidate.requirementsSnapshot === identity.requirementsSnapshot)
200
+ .at(-1);
201
+ if (!record || record.result !== "failed") {
202
+ throw new Error(`Check failure '${checkFailure}' must be an explicitly recorded failed check for the current snapshot.`);
203
+ }
204
+ }
205
+ const number = attempts.length + 1;
206
+ const assignment = {
207
+ id: randomUUID(),
208
+ role: "implementation",
209
+ action: "correct",
210
+ controller: options.author.trim(),
211
+ attempt: number,
212
+ findingIds,
213
+ ...(checkFailure ? { checkFailure } : {}),
214
+ objective,
215
+ report: join(feature.workspace, "reports", `${options.ticket}-round-${number}-fix.md`),
216
+ requestedAt: new Date().toISOString(),
217
+ status: "pending",
218
+ handoffs: [],
219
+ };
220
+ this.reportEvidence.assertReportPath(feature, assignment.report, false);
221
+ if (lstatIfPresent(assignment.report)) {
222
+ throw new Error(`Assigned report already exists and was preserved: ${assignment.report}.`);
223
+ }
224
+ const attempt = {
225
+ number,
226
+ findingIds,
227
+ ...(checkFailure ? { checkFailure } : {}),
228
+ objective,
229
+ startedAt: assignment.requestedAt,
230
+ startedBy: options.author.trim(),
231
+ assignmentId: assignment.id,
232
+ assignment,
233
+ ...(progress.assignment ? { priorAssignment: progress.assignment } : {}),
234
+ ...(checkFailure
235
+ ? {
236
+ checks: (progress.checks?.records ?? []).filter((record) => record.snapshot === identity.snapshot &&
237
+ record.codeSnapshot === identity.codeSnapshot &&
238
+ record.requirementsSnapshot === identity.requirementsSnapshot),
239
+ }
240
+ : {}),
241
+ };
242
+ if (attempts.length > 0 || pendingAdjudication) {
243
+ attempt.progressAssessment = {
244
+ decision: "continue",
245
+ result: "productive",
246
+ details: options.assessment.trim(),
247
+ recordedAt: new Date().toISOString(),
248
+ recordedBy: options.author.trim(),
249
+ };
250
+ }
251
+ attempts.push(attempt);
252
+ progress.execution = "implementing";
253
+ progress.assignment = assignment;
254
+ if (pendingAdjudication && progress.adjudication) {
255
+ progress.adjudication.status = "resolved";
256
+ progress.adjudication.resolution = {
257
+ details: options.assessment.trim(),
258
+ recordedAt: new Date().toISOString(),
259
+ recordedBy: options.author.trim(),
260
+ };
261
+ }
262
+ const afterTicket = matter.stringify(this.ticketRepository.appendAudit(ticket.body, options.author, `Corrective attempt ${number} recorded; objective=${objective}; findings=${findingIds.join(",") || "none"}${checkFailure ? `; check-failure=${checkFailure}` : ""}; report=${assignment.report}. No agent was launched.`), this.ticketRepository.metadata(ticket));
263
+ this.workspaceTransaction.writeWorkflowState(feature, ticket, afterTicket, ledger);
264
+ return {
265
+ ticket: options.ticket,
266
+ execution: "implementing",
267
+ attempt,
268
+ assignment,
269
+ ...identity,
270
+ };
271
+ });
272
+ }
273
+ }