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

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,253 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { join } from "node:path";
3
+ import { Service } from "../../Foundation/Service.js";
4
+ import { lstatIfPresent } from "../Persistence/FileSystem.js";
5
+ import { validateAuthor } from "../Policies/InputValidation.js";
6
+ export class DeliveryCorrectionService extends Service {
7
+ gitEvidence;
8
+ reportEvidence;
9
+ ticketRepository;
10
+ ledgerRepository;
11
+ workspaceTransaction;
12
+ featureInspectionService;
13
+ constructor(app, gitEvidence, reportEvidence, ticketRepository, ledgerRepository, workspaceTransaction, featureInspectionService) {
14
+ super(app);
15
+ this.gitEvidence = gitEvidence;
16
+ this.reportEvidence = reportEvidence;
17
+ this.ticketRepository = ticketRepository;
18
+ this.ledgerRepository = ledgerRepository;
19
+ this.workspaceTransaction = workspaceTransaction;
20
+ this.featureInspectionService = featureInspectionService;
21
+ }
22
+ deliveryCorrect(options) {
23
+ validateAuthor(options.author);
24
+ const findingIds = [...new Set((options.findings ?? []).map((finding) => finding.trim()))];
25
+ if (findingIds.some((finding) => !/^F-\d{4,}$/.test(finding))) {
26
+ throw new Error("--findings must contain canonical finding IDs.");
27
+ }
28
+ const objective = options.details.trim();
29
+ if (!objective)
30
+ throw new Error("--details must not be empty.");
31
+ if (options.decision !== undefined && !["continue", "pause"].includes(options.decision)) {
32
+ throw new Error("--decision must be 'continue' or 'pause'.");
33
+ }
34
+ if (options.decision !== "pause" && findingIds.length === 0 && !options.checkFailure?.trim()) {
35
+ throw new Error("correct requires actionable --findings or a non-mechanical --check-failure.");
36
+ }
37
+ if (options.decision === "pause") {
38
+ if (![
39
+ "no-progress",
40
+ "repeated-failure",
41
+ "recurring-blocker",
42
+ "requirements-disagreement",
43
+ "worker-blocked",
44
+ ].includes(options.reason ?? "")) {
45
+ throw new Error("--reason is required for a pause and must identify the adjudication category.");
46
+ }
47
+ if (options.assessment || options.assessmentResult) {
48
+ throw new Error("--assessment is only valid with --decision continue.");
49
+ }
50
+ }
51
+ else if (options.reason) {
52
+ throw new Error("--reason is only valid with --decision pause.");
53
+ }
54
+ if (options.decision === "continue" &&
55
+ (!options.assessment?.trim() || options.assessmentResult !== "productive")) {
56
+ throw new Error("--assessment and --assessment-result productive are required with --decision continue.");
57
+ }
58
+ if (options.assessmentResult && options.decision !== "continue") {
59
+ throw new Error("--assessment-result is only valid with --decision continue.");
60
+ }
61
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
62
+ return this.workspaceTransaction.run(feature.workspace, () => {
63
+ const ledger = this.ledgerRepository.readLedger(feature);
64
+ const delivery = ledger.delivery;
65
+ if (delivery.owner !== options.author.trim()) {
66
+ throw new Error("Only the recorded delivery controller may dispatch a correction.");
67
+ }
68
+ const attempts = (delivery.correctionAttempts ??= []);
69
+ const identity = this.gitEvidence.deliveryIdentity(feature, ledger, this.ticketRepository.readTickets(feature.workspace));
70
+ const currentFailedCheck = (delivery.checks?.records ?? []).some((record) => record.snapshot === identity.snapshot &&
71
+ record.codeSnapshot === identity.codeSnapshot &&
72
+ record.requirementsSnapshot === identity.requirementsSnapshot &&
73
+ record.result === "failed");
74
+ if (options.decision === "pause") {
75
+ if (delivery.execution === "implementing" && delivery.assignment?.status !== "submitted") {
76
+ throw new Error("A pending delivery implementation assignment must be resumed, not paused.");
77
+ }
78
+ if (attempts.length === 0 &&
79
+ delivery.execution !== "changes-requested" &&
80
+ !currentFailedCheck) {
81
+ throw new Error("Adjudication pause requires a recorded failed correction, check, or review.");
82
+ }
83
+ const adjudication = {
84
+ status: "pending",
85
+ reason: options.reason,
86
+ details: objective,
87
+ recordedAt: new Date().toISOString(),
88
+ recordedBy: options.author.trim(),
89
+ };
90
+ if (delivery.adjudication?.status === "pending" &&
91
+ delivery.adjudication.reason === adjudication.reason &&
92
+ delivery.adjudication.details === adjudication.details &&
93
+ delivery.adjudication.recordedBy === adjudication.recordedBy) {
94
+ return {
95
+ scope: "delivery",
96
+ execution: delivery.execution,
97
+ adjudication: delivery.adjudication,
98
+ paused: true,
99
+ idempotent: true,
100
+ ...identity,
101
+ };
102
+ }
103
+ delivery.adjudication = adjudication;
104
+ delivery.execution = "changes-requested";
105
+ delivery.history.push({
106
+ at: adjudication.recordedAt,
107
+ author: adjudication.recordedBy,
108
+ action: `correction-paused:${adjudication.reason}`,
109
+ });
110
+ this.workspaceTransaction.writeLedger(feature, ledger);
111
+ return {
112
+ scope: "delivery",
113
+ execution: delivery.execution,
114
+ adjudication,
115
+ paused: true,
116
+ ...identity,
117
+ };
118
+ }
119
+ if (delivery.execution === "implementing" &&
120
+ delivery.assignment?.action === "correct" &&
121
+ delivery.assignment.status !== "submitted" &&
122
+ delivery.assignment.findingIds?.join(",") === findingIds.join(",") &&
123
+ delivery.assignment.checkFailure === options.checkFailure?.trim() &&
124
+ delivery.assignment.objective === objective) {
125
+ const current = attempts.find((attempt) => attempt.number === delivery.assignment?.attempt);
126
+ if (!current)
127
+ throw new Error("Delivery correction assignment has no persisted attempt record.");
128
+ return {
129
+ scope: "delivery",
130
+ execution: "implementing",
131
+ attempt: current,
132
+ assignment: delivery.assignment,
133
+ idempotent: true,
134
+ ...identity,
135
+ };
136
+ }
137
+ if (delivery.adjudication?.status === "pending" && options.decision !== "continue") {
138
+ throw new Error(`Delivery correction is paused for adjudication (${delivery.adjudication.reason}).`);
139
+ }
140
+ if (delivery.execution !== "changes-requested" && !currentFailedCheck) {
141
+ throw new Error("Delivery must have a failed aggregate review or current failed check before correction.");
142
+ }
143
+ if (delivery.assignment?.action === "correct" && delivery.assignment.status !== "submitted") {
144
+ throw new Error("The current delivery correction assignment must be resumed, not replaced.");
145
+ }
146
+ if (attempts.length > 0 || delivery.adjudication?.status === "pending") {
147
+ if (options.decision !== "continue" || !options.assessment?.trim()) {
148
+ throw new Error("A subsequent delivery correction requires --decision continue and --assessment.");
149
+ }
150
+ const previous = attempts.at(-1);
151
+ previous.progressAssessment = {
152
+ decision: "continue",
153
+ result: "productive",
154
+ details: options.assessment.trim(),
155
+ recordedAt: new Date().toISOString(),
156
+ recordedBy: options.author.trim(),
157
+ };
158
+ }
159
+ else if (options.decision || options.assessment) {
160
+ throw new Error("The first delivery correction does not accept a continuation assessment.");
161
+ }
162
+ for (const id of findingIds) {
163
+ if (!delivery.findings?.some((finding) => finding.id === id && finding.status === "confirmed")) {
164
+ throw new Error(`Delivery finding ${id} is not an actionable unresolved finding.`);
165
+ }
166
+ }
167
+ const checkFailure = options.checkFailure?.trim();
168
+ if (checkFailure) {
169
+ const record = (delivery.checks?.records ?? [])
170
+ .filter((candidate) => candidate.command === checkFailure &&
171
+ candidate.snapshot === identity.snapshot &&
172
+ candidate.codeSnapshot === identity.codeSnapshot &&
173
+ candidate.requirementsSnapshot === identity.requirementsSnapshot)
174
+ .at(-1);
175
+ if (!record || record.result !== "failed") {
176
+ throw new Error(`Delivery check failure '${checkFailure}' must be an explicitly recorded failed check for the current snapshot.`);
177
+ }
178
+ }
179
+ const number = attempts.length + 1;
180
+ const requestedAt = new Date().toISOString();
181
+ const assignment = {
182
+ id: randomUUID(),
183
+ role: "implementation",
184
+ action: "correct",
185
+ controller: options.author.trim(),
186
+ attempt: number,
187
+ findingIds,
188
+ ...(checkFailure ? { checkFailure } : {}),
189
+ objective,
190
+ report: join(feature.workspace, "reports", `final-round-${number}-fix.md`),
191
+ requestedAt,
192
+ status: "pending",
193
+ handoffs: [],
194
+ };
195
+ this.reportEvidence.assertReportPath(feature, assignment.report, false);
196
+ if (lstatIfPresent(assignment.report)) {
197
+ throw new Error(`Assigned report already exists and was preserved: ${assignment.report}.`);
198
+ }
199
+ const attempt = {
200
+ number,
201
+ findingIds,
202
+ ...(checkFailure ? { checkFailure } : {}),
203
+ objective,
204
+ startedAt: requestedAt,
205
+ startedBy: options.author.trim(),
206
+ assignmentId: assignment.id,
207
+ assignment,
208
+ ...(delivery.assignment ? { priorAssignment: delivery.assignment } : {}),
209
+ ...(checkFailure
210
+ ? {
211
+ checks: (delivery.checks?.records ?? []).filter((record) => record.snapshot === identity.snapshot &&
212
+ record.codeSnapshot === identity.codeSnapshot &&
213
+ record.requirementsSnapshot === identity.requirementsSnapshot),
214
+ }
215
+ : {}),
216
+ };
217
+ if (attempts.length > 0 || delivery.adjudication?.status === "pending") {
218
+ attempt.progressAssessment = {
219
+ decision: "continue",
220
+ result: "productive",
221
+ details: options.assessment.trim(),
222
+ recordedAt: new Date().toISOString(),
223
+ recordedBy: options.author.trim(),
224
+ };
225
+ }
226
+ attempts.push(attempt);
227
+ delivery.assignment = assignment;
228
+ delivery.execution = "implementing";
229
+ if (delivery.adjudication?.status === "pending") {
230
+ delivery.adjudication.status = "resolved";
231
+ delivery.adjudication.resolution = {
232
+ details: options.assessment.trim(),
233
+ recordedAt: new Date().toISOString(),
234
+ recordedBy: options.author.trim(),
235
+ };
236
+ }
237
+ delivery.history.push({
238
+ at: requestedAt,
239
+ author: options.author.trim(),
240
+ action: `correction-dispatched:${number}`,
241
+ });
242
+ this.workspaceTransaction.writeLedger(feature, ledger);
243
+ return {
244
+ scope: "delivery",
245
+ execution: "implementing",
246
+ attempt,
247
+ assignment,
248
+ ...(delivery.adjudication ? { adjudication: delivery.adjudication } : {}),
249
+ ...identity,
250
+ };
251
+ });
252
+ }
253
+ }
@@ -0,0 +1,302 @@
1
+ import { isAbsolute, resolve } from "node:path";
2
+ import { Service } from "../../Foundation/Service.js";
3
+ import { validateTicketId } from "../Policies/InputValidation.js";
4
+ import { validateAuthor } from "../Policies/InputValidation.js";
5
+ import { requiredDeliveryReviewers } from "../Policies/RequiredReviewers.js";
6
+ export class DeliveryFindingService extends Service {
7
+ gitEvidence;
8
+ reportEvidence;
9
+ ticketRepository;
10
+ ledgerRepository;
11
+ workspaceTransaction;
12
+ checkPolicy;
13
+ findingPolicy;
14
+ reviewPolicy;
15
+ featureInspectionService;
16
+ constructor(app, gitEvidence, reportEvidence, ticketRepository, ledgerRepository, workspaceTransaction, checkPolicy, findingPolicy, reviewPolicy, 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.checkPolicy = checkPolicy;
24
+ this.findingPolicy = findingPolicy;
25
+ this.reviewPolicy = reviewPolicy;
26
+ this.featureInspectionService = featureInspectionService;
27
+ }
28
+ deliveryFindingCreate(options) {
29
+ validateAuthor(options.author);
30
+ if (!["high", "medium", "low"].includes(options.severity)) {
31
+ throw new Error(`Invalid finding severity: ${options.severity}.`);
32
+ }
33
+ if (options.blocking === options.nonBlocking) {
34
+ throw new Error("Choose exactly one of --blocking or --non-blocking.");
35
+ }
36
+ if (!options.summary.trim())
37
+ throw new Error("Finding summary must not be empty.");
38
+ if (!options.details.trim())
39
+ throw new Error("Finding details must not be empty.");
40
+ if (options.relatedTicket)
41
+ validateTicketId(options.relatedTicket);
42
+ if (options.link !== undefined && options.relatedTicket !== undefined) {
43
+ throw new Error("--related-ticket cannot be combined with --link.");
44
+ }
45
+ if (!/^[0-9a-f]{64}$/i.test(options.snapshot)) {
46
+ throw new Error("Finding snapshot must be a CLI-returned content snapshot.");
47
+ }
48
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
49
+ return this.workspaceTransaction.run(feature.workspace, () => {
50
+ const ledger = this.ledgerRepository.readLedger(feature);
51
+ const delivery = ledger.delivery;
52
+ if (!delivery.owner || delivery.owner !== options.author.trim()) {
53
+ throw new Error("Only the current delivery controller may register findings.");
54
+ }
55
+ const report = options.source
56
+ ? isAbsolute(options.source)
57
+ ? resolve(options.source)
58
+ : resolve(feature.workspace, options.source)
59
+ : "";
60
+ const assignment = delivery.reviewAssignments?.find((candidate) => candidate.report === report &&
61
+ candidate.snapshot === options.snapshot &&
62
+ candidate.scope === "delivery");
63
+ if (!assignment) {
64
+ throw new Error("Finding source report and snapshot must match a frozen delivery review assignment.");
65
+ }
66
+ if (assignment.status === "submitted") {
67
+ throw new Error("Findings must be registered before submitting the delivery assignment.");
68
+ }
69
+ const identity = this.gitEvidence.deliveryIdentity(feature, ledger, this.ticketRepository.readTickets(feature.workspace));
70
+ if (options.relatedTicket) {
71
+ const relatedTicket = ledger.tickets.find((item) => item.id === options.relatedTicket);
72
+ if (!relatedTicket) {
73
+ throw new Error(`Related ticket not found: ${options.relatedTicket}.`);
74
+ }
75
+ }
76
+ const stale = this.reviewPolicy.reviewSnapshotBlocker(feature, delivery, assignment, identity);
77
+ if (stale)
78
+ throw new Error(stale);
79
+ this.reportEvidence.readReportDigest(feature, report);
80
+ const source = {
81
+ report,
82
+ snapshot: options.snapshot,
83
+ author: options.author.trim(),
84
+ summary: options.summary.trim(),
85
+ details: options.details.trim(),
86
+ };
87
+ const findings = delivery.findings ?? [];
88
+ if (options.link !== undefined) {
89
+ const canonical = findings.find((finding) => finding.id === options.link);
90
+ if (!canonical)
91
+ throw new Error(`Canonical delivery finding not found: ${options.link}.`);
92
+ const prior = canonical.provenance.find((item) => item.report === source.report && item.snapshot === source.snapshot);
93
+ if (prior) {
94
+ if (prior.author !== source.author ||
95
+ prior.summary !== source.summary ||
96
+ prior.details !== source.details) {
97
+ throw new Error("This report and snapshot are already linked with different details.");
98
+ }
99
+ return { scope: "delivery", finding: canonical, idempotent: true, ...identity };
100
+ }
101
+ canonical.provenance.push(source);
102
+ canonical.history.push({
103
+ at: new Date().toISOString(),
104
+ author: options.author.trim(),
105
+ action: "observation-linked",
106
+ details: `Linked observation from ${report} at snapshot ${options.snapshot}: ${source.summary} — ${source.details}`,
107
+ });
108
+ this.workspaceTransaction.writeLedger(feature, ledger);
109
+ return { scope: "delivery", finding: canonical, ...identity };
110
+ }
111
+ const duplicate = findings.find((finding) => finding.author === options.author.trim() &&
112
+ finding.relatedTicket === options.relatedTicket &&
113
+ finding.severity === options.severity &&
114
+ finding.blocking === (options.blocking === true) &&
115
+ finding.summary === options.summary.trim() &&
116
+ finding.details === options.details.trim() &&
117
+ finding.provenance.some((item) => item.report === source.report &&
118
+ item.snapshot === source.snapshot &&
119
+ item.author === source.author &&
120
+ item.summary === source.summary &&
121
+ item.details === source.details));
122
+ if (duplicate)
123
+ return { scope: "delivery", finding: duplicate, idempotent: true, ...identity };
124
+ const highest = findings.reduce((current, finding) => {
125
+ const match = /^F-(\d{4,})$/.exec(finding.id);
126
+ return match && BigInt(match[1]) > current ? BigInt(match[1]) : current;
127
+ }, 0n);
128
+ const at = new Date().toISOString();
129
+ const finding = {
130
+ id: `F-${String(highest + 1n).padStart(4, "0")}`,
131
+ ...(options.relatedTicket ? { relatedTicket: options.relatedTicket } : {}),
132
+ severity: options.severity,
133
+ blocking: options.blocking === true,
134
+ status: "open",
135
+ summary: options.summary.trim(),
136
+ details: options.details.trim(),
137
+ author: options.author.trim(),
138
+ createdAt: at,
139
+ provenance: [source],
140
+ history: [
141
+ {
142
+ at,
143
+ author: options.author.trim(),
144
+ action: "registered",
145
+ details: `Registered from ${report} at snapshot ${options.snapshot}.`,
146
+ },
147
+ ],
148
+ };
149
+ delivery.findings = findings;
150
+ findings.push(finding);
151
+ delivery.history.push({
152
+ at,
153
+ author: options.author.trim(),
154
+ action: `finding-registered:${finding.id}`,
155
+ });
156
+ this.workspaceTransaction.writeLedger(feature, ledger);
157
+ return { scope: "delivery", finding, snapshot: identity.snapshot, headSha: identity.headSha };
158
+ });
159
+ }
160
+ deliveryFindingUpdate(options) {
161
+ validateAuthor(options.author);
162
+ if (!/^F-\d{4,}$/.test(options.finding)) {
163
+ throw new Error(`Invalid finding ID: ${options.finding}.`);
164
+ }
165
+ if (!["open", "confirmed", "fixed", "dismissed", "deferred"].includes(options.status)) {
166
+ throw new Error(`Invalid finding status: ${options.status}.`);
167
+ }
168
+ if (["dismissed", "deferred"].includes(options.status) && !options.details?.trim()) {
169
+ throw new Error(`--details is required for a ${options.status} finding disposition.`);
170
+ }
171
+ if (options.status === "deferred" && !options.followUp?.trim()) {
172
+ throw new Error("--follow-up is required for a deferred delivery finding.");
173
+ }
174
+ if (options.status === "deferred" && !options.requirementsDecision?.trim()) {
175
+ throw new Error("--requirements-decision is required for a deferred delivery finding.");
176
+ }
177
+ if (options.status === "fixed" && !options.evidence?.trim()) {
178
+ throw new Error("--evidence is required to mark a finding fixed.");
179
+ }
180
+ if (options.status !== "deferred" &&
181
+ (options.followUp !== undefined || options.requirementsDecision !== undefined)) {
182
+ throw new Error("--follow-up and --requirements-decision are only valid for a deferred finding.");
183
+ }
184
+ if (options.status !== "fixed" && options.evidence !== undefined) {
185
+ throw new Error("--evidence is only valid for a fixed finding.");
186
+ }
187
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
188
+ return this.workspaceTransaction.run(feature.workspace, () => {
189
+ const ledger = this.ledgerRepository.readLedger(feature);
190
+ const delivery = ledger.delivery;
191
+ if (delivery.owner !== options.author.trim()) {
192
+ throw new Error("Only the current delivery controller may record a finding ruling.");
193
+ }
194
+ const finding = delivery.findings?.find((candidate) => candidate.id === options.finding);
195
+ if (!finding)
196
+ throw new Error(`Delivery finding not found: ${options.finding}.`);
197
+ const identity = this.gitEvidence.deliveryIdentity(feature, ledger, this.ticketRepository.readTickets(feature.workspace));
198
+ const rulingDetails = options.details?.trim();
199
+ let requirementsDecision;
200
+ if (options.status === "deferred") {
201
+ requirementsDecision = delivery.requirementsDecisions?.find((decision) => decision.id === options.requirementsDecision &&
202
+ decision.finding === finding.id &&
203
+ decision.status === "approved" &&
204
+ decision.requirementsSnapshot === identity.requirementsSnapshot);
205
+ if (!requirementsDecision) {
206
+ throw new Error("--requirements-decision must identify an approved decision for this finding and current requirements.");
207
+ }
208
+ const requiredReviewers = requiredDeliveryReviewers(ledger, delivery);
209
+ const assignments = delivery.reviewAssignments ?? [];
210
+ for (const { agent } of requiredReviewers) {
211
+ const matching = assignments.filter((assignment) => assignment.agent === agent &&
212
+ assignment.snapshot === identity.snapshot &&
213
+ assignment.requirementsSnapshot === identity.requirementsSnapshot);
214
+ if (matching.length !== 1 ||
215
+ matching[0].status !== "submitted" ||
216
+ matching[0].result !== "passed" ||
217
+ this.reviewPolicy.reviewAssignmentBlocker(feature, delivery, matching[0], identity)) {
218
+ throw new Error(`A deferred delivery finding requires fresh passing aggregate review from '${agent}'.`);
219
+ }
220
+ }
221
+ }
222
+ let verification;
223
+ if (options.status === "fixed") {
224
+ if (!/^[0-9a-f]{64}$/i.test(options.evidence)) {
225
+ throw new Error("--evidence must be a CLI-returned content snapshot.");
226
+ }
227
+ if (options.evidence !== identity.snapshot) {
228
+ throw new Error("Verification evidence must match the current delivery snapshot.");
229
+ }
230
+ if (finding.provenance.some((source) => source.snapshot === identity.snapshot)) {
231
+ throw new Error("Verification evidence must reflect changed content since every finding observation.");
232
+ }
233
+ const checks = this.checkPolicy.deliveryCheckBlockers(delivery, identity);
234
+ const required = delivery.checks?.required.filter((check) => !check.removedAt) ?? [];
235
+ if (required.length === 0 || checks.length > 0) {
236
+ throw new Error(`Verification evidence is insufficient:\n${checks.join("\n") || "No required delivery checks are declared."}`);
237
+ }
238
+ verification = {
239
+ snapshot: identity.snapshot,
240
+ checkCommands: required.map((check) => check.command),
241
+ verifiedAt: new Date().toISOString(),
242
+ verifiedBy: options.author.trim(),
243
+ };
244
+ }
245
+ const last = finding.history.at(-1);
246
+ if (finding.status === options.status &&
247
+ finding.rulingDetails === rulingDetails &&
248
+ last?.action === `status:${options.status}` &&
249
+ last.author === options.author.trim()) {
250
+ return { scope: "delivery", finding, idempotent: true, snapshot: identity.snapshot };
251
+ }
252
+ const current = this.gitEvidence.deliveryIdentity(feature, ledger, this.ticketRepository.readTickets(feature.workspace));
253
+ if (current.snapshot !== identity.snapshot) {
254
+ throw new Error("Delivery content changed while recording the finding ruling.");
255
+ }
256
+ const wasBlocking = finding.blocking && !["fixed", "dismissed"].includes(finding.status);
257
+ finding.status = options.status;
258
+ if (["dismissed", "deferred"].includes(options.status))
259
+ finding.rulingDetails = rulingDetails;
260
+ else
261
+ delete finding.rulingDetails;
262
+ if (options.status === "deferred") {
263
+ finding.followUp = options.followUp.trim();
264
+ finding.requirementsDecision = requirementsDecision.id;
265
+ }
266
+ else {
267
+ delete finding.followUp;
268
+ delete finding.requirementsDecision;
269
+ }
270
+ if (verification)
271
+ finding.verification = verification;
272
+ else
273
+ delete finding.verification;
274
+ const details = rulingDetails ?? (verification ? `Verified at ${verification.snapshot}.` : options.status);
275
+ finding.history.push({
276
+ at: new Date().toISOString(),
277
+ author: options.author.trim(),
278
+ action: `status:${options.status}`,
279
+ details,
280
+ });
281
+ delivery.history.push({
282
+ at: new Date().toISOString(),
283
+ author: options.author.trim(),
284
+ action: `finding-${options.status}:${finding.id}`,
285
+ });
286
+ if (wasBlocking && ["fixed", "dismissed"].includes(options.status)) {
287
+ delivery.execution = "reviewing";
288
+ }
289
+ else if (finding.blocking && options.status === "deferred") {
290
+ delivery.execution =
291
+ this.findingPolicy.deliveryFindingBlockers(feature, ledger, delivery, identity).length > 0
292
+ ? "changes-requested"
293
+ : "reviewing";
294
+ }
295
+ else if (finding.blocking && !["fixed", "dismissed"].includes(options.status)) {
296
+ delivery.execution = "changes-requested";
297
+ }
298
+ this.workspaceTransaction.writeLedger(feature, ledger);
299
+ return { scope: "delivery", finding, snapshot: identity.snapshot };
300
+ });
301
+ }
302
+ }