@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,53 @@
1
+ import { Service } from "../../Foundation/Service.js";
2
+ import { requiredDeliveryReviewers } from "./RequiredReviewers.js";
3
+ export class FindingPolicy extends Service {
4
+ reviewPolicy;
5
+ constructor(app, reviewPolicy) {
6
+ super(app);
7
+ this.reviewPolicy = reviewPolicy;
8
+ }
9
+ findingBlockers(progress, identity) {
10
+ return (progress.findings ?? [])
11
+ .filter((finding) => finding.blocking &&
12
+ finding.status !== "fixed" &&
13
+ finding.status !== "dismissed" &&
14
+ !(finding.status === "deferred" &&
15
+ finding.requirementsDecision &&
16
+ progress.requirementsDecisions?.some((decision) => decision.id === finding.requirementsDecision &&
17
+ decision.finding === finding.id &&
18
+ decision.status === "approved" &&
19
+ decision.requirementsSnapshot === identity.requirementsSnapshot) &&
20
+ (progress.reviewAssignments?.length ?? 0) > 0 &&
21
+ progress.reviewAssignments.every((assignment) => assignment.status === "submitted" &&
22
+ assignment.result === "passed" &&
23
+ assignment.snapshot === identity.snapshot &&
24
+ assignment.requirementsSnapshot === identity.requirementsSnapshot)))
25
+ .map((finding) => `Finding ${finding.id} is unresolved and blocking acceptance (severity=${finding.severity}, status=${finding.status}).`);
26
+ }
27
+ deliveryFindingBlockers(feature, ledger, delivery, identity) {
28
+ return (delivery.findings ?? [])
29
+ .filter((finding) => {
30
+ if (!finding.blocking || ["fixed", "dismissed"].includes(finding.status))
31
+ return false;
32
+ if (finding.status !== "deferred" || !finding.requirementsDecision)
33
+ return true;
34
+ const decision = delivery.requirementsDecisions?.find((candidate) => candidate.id === finding.requirementsDecision &&
35
+ candidate.finding === finding.id &&
36
+ candidate.status === "approved" &&
37
+ candidate.requirementsSnapshot === identity.requirementsSnapshot);
38
+ if (!decision)
39
+ return true;
40
+ const required = requiredDeliveryReviewers(ledger, delivery);
41
+ return required.some(({ agent }) => {
42
+ const matching = (delivery.reviewAssignments ?? []).filter((assignment) => assignment.agent === agent &&
43
+ assignment.status === "submitted" &&
44
+ assignment.result === "passed" &&
45
+ assignment.snapshot === identity.snapshot &&
46
+ assignment.requirementsSnapshot === identity.requirementsSnapshot);
47
+ return (matching.length !== 1 ||
48
+ !!this.reviewPolicy.reviewAssignmentBlocker(feature, delivery, matching[0], identity));
49
+ });
50
+ })
51
+ .map((finding) => `Delivery finding ${finding.id} remains ${finding.status}.`);
52
+ }
53
+ }
@@ -0,0 +1,36 @@
1
+ import { TICKET_ID, TICKET_CATEGORIES, TRACKER_STATUSES, } from "../Models/Workflow.js";
2
+ export function validateAuthor(author) {
3
+ if (!author.trim())
4
+ throw new Error("Author must not be empty.");
5
+ }
6
+ export function validateTicketId(ticket) {
7
+ if (!TICKET_ID.test(ticket))
8
+ throw new Error(`Invalid ticket ID: ${ticket}.`);
9
+ }
10
+ export function validateCategory(category) {
11
+ if (!TICKET_CATEGORIES.includes(category)) {
12
+ throw new Error(`Invalid ticket category: ${category}.`);
13
+ }
14
+ }
15
+ export function validateTriageStatus(status) {
16
+ if (!TRACKER_STATUSES.includes(status)) {
17
+ throw new Error(`Invalid ticket status: ${status}.`);
18
+ }
19
+ }
20
+ export function validateCreateOptions({ title, author }) {
21
+ if (!title.trim())
22
+ throw new Error("Title must not be empty.");
23
+ if (!author.trim())
24
+ throw new Error("Author must not be empty.");
25
+ }
26
+ export function featureSlug(title) {
27
+ const slug = title
28
+ .normalize("NFKD")
29
+ .replace(/\p{M}/gu, "")
30
+ .toLowerCase()
31
+ .replace(/[^a-z0-9]+/g, "-")
32
+ .replace(/^-|-$/g, "");
33
+ if (!slug)
34
+ throw new Error("Title must contain at least one letter or number.");
35
+ return slug;
36
+ }
@@ -0,0 +1,26 @@
1
+ import { Service } from "../../Foundation/Service.js";
2
+ export class ReadinessPolicy extends Service {
3
+ ticketRepository;
4
+ constructor(app, ticketRepository) {
5
+ super(app);
6
+ this.ticketRepository = ticketRepository;
7
+ }
8
+ evaluate(tickets) {
9
+ this.ticketRepository.validateDependencyGraph(tickets);
10
+ const byId = new Map(tickets.map((item) => [item.id, item]));
11
+ const eligible = tickets.filter((item) => item.status === "open" && !item.blocker);
12
+ const ready = [];
13
+ const unready = [];
14
+ for (const item of eligible) {
15
+ const pending = item.blocked_by.filter((id) => {
16
+ const dependencyTicket = byId.get(id);
17
+ return (!dependencyTicket || dependencyTicket.status !== "closed" || !!dependencyTicket.blocker);
18
+ });
19
+ if (pending.length === 0)
20
+ ready.push(item.id);
21
+ else
22
+ unready.push({ ticket: item.id, blockedBy: pending });
23
+ }
24
+ return { ready, unready };
25
+ }
26
+ }
@@ -0,0 +1,12 @@
1
+ export function requiredReviewers(policy, requirements = []) {
2
+ return [
3
+ ...policy.map((agent) => ({ agent })),
4
+ ...requirements.map(({ agent, details }) => ({ agent, details })),
5
+ ];
6
+ }
7
+ export function requiredTicketReviewers(ledger, progress) {
8
+ return requiredReviewers(ledger.reviewPolicy.ticket, progress.reviewRequirements ?? []);
9
+ }
10
+ export function requiredDeliveryReviewers(ledger, progress) {
11
+ return requiredReviewers(ledger.reviewPolicy.delivery, progress.reviewRequirements ?? []);
12
+ }
@@ -0,0 +1,87 @@
1
+ import { Service } from "../../Foundation/Service.js";
2
+ export class ReviewPolicy extends Service {
3
+ gitEvidence;
4
+ reportEvidence;
5
+ constructor(app, gitEvidence, reportEvidence) {
6
+ super(app);
7
+ this.gitEvidence = gitEvidence;
8
+ this.reportEvidence = reportEvidence;
9
+ }
10
+ reviewAssignmentBlocker(feature, progress, assignment, identity) {
11
+ if (assignment.status !== "pending") {
12
+ try {
13
+ const currentDigest = this.reportEvidence.readReportDigest(feature, assignment.report);
14
+ if (currentDigest !== assignment.reportSha256) {
15
+ return `The registered review report for assignment ${assignment.id} changed after submission.`;
16
+ }
17
+ }
18
+ catch {
19
+ return `The registered review report for assignment ${assignment.id} is missing or unavailable.`;
20
+ }
21
+ }
22
+ const snapshotBlocker = this.reviewSnapshotBlocker(feature, progress, assignment, identity);
23
+ if (snapshotBlocker)
24
+ return snapshotBlocker;
25
+ if (assignment.status === "pending")
26
+ return undefined;
27
+ if (assignment.result !== "passed") {
28
+ return `Review assignment ${assignment.id} is ${assignment.result}.${assignment.details ? ` Details: ${assignment.details}` : ""}`;
29
+ }
30
+ if (assignment.noFindings !== true && (assignment.findingIds?.length ?? 0) === 0) {
31
+ return `Review assignment ${assignment.id} has no explicit findings declaration.`;
32
+ }
33
+ return undefined;
34
+ }
35
+ reviewSnapshotBlocker(feature, progress, assignment, identity) {
36
+ const integratedEvidence = assignment.scope !== "delivery" &&
37
+ this.gitEvidence.integrationPreservesEvidence(progress, identity) &&
38
+ assignment.snapshot === progress.acceptedEvidence?.snapshot &&
39
+ assignment.codeSnapshot === progress.acceptedEvidence?.codeSnapshot &&
40
+ assignment.requirementsSnapshot === progress.acceptedEvidence?.requirementsSnapshot &&
41
+ JSON.stringify(assignment.inventory) === JSON.stringify(progress.acceptedEvidence?.files);
42
+ if (!integratedEvidence &&
43
+ (assignment.snapshot !== identity.snapshot ||
44
+ assignment.codeSnapshot !== identity.codeSnapshot ||
45
+ assignment.requirementsSnapshot !== identity.requirementsSnapshot)) {
46
+ return `Review assignment ${assignment.id} is stale for the current ticket snapshot.`;
47
+ }
48
+ if (!progress.baseSha)
49
+ return "Review assignment has no recorded Git baseline.";
50
+ let inventory;
51
+ try {
52
+ inventory = this.gitEvidence.changedPaths(progress.baseSha);
53
+ }
54
+ catch (error) {
55
+ return `Review inventory cannot be revalidated: ${error instanceof Error ? error.message : String(error)}`;
56
+ }
57
+ if (JSON.stringify(inventory) !== JSON.stringify(assignment.inventory)) {
58
+ return `Review assignment ${assignment.id} no longer matches the frozen Git inventory.`;
59
+ }
60
+ if (assignment.status === "pending")
61
+ return undefined;
62
+ try {
63
+ const currentDigest = this.reportEvidence.readReportDigest(feature, assignment.report);
64
+ if (currentDigest !== assignment.reportSha256) {
65
+ return `The registered review report for assignment ${assignment.id} changed after submission.`;
66
+ }
67
+ }
68
+ catch {
69
+ return `The registered review report for assignment ${assignment.id} is missing or unavailable.`;
70
+ }
71
+ return undefined;
72
+ }
73
+ reviewReportIntegrityBlocker(feature, assignment) {
74
+ if (assignment.status !== "submitted" || !assignment.reportSha256) {
75
+ return "Review report was not submitted.";
76
+ }
77
+ try {
78
+ if (this.reportEvidence.readReportDigest(feature, assignment.report) !== assignment.reportSha256) {
79
+ return "Review report content changed after submission.";
80
+ }
81
+ }
82
+ catch {
83
+ return "Review report is missing or unavailable.";
84
+ }
85
+ return undefined;
86
+ }
87
+ }
@@ -0,0 +1,85 @@
1
+ import { Service } from "../../Foundation/Service.js";
2
+ export class WorkflowPolicy extends Service {
3
+ constructor(app) {
4
+ super(app);
5
+ }
6
+ nextAction(progress, blocker) {
7
+ if (progress.adjudication?.status === "pending")
8
+ return "adjudicate";
9
+ if (progress.execution === "unstarted")
10
+ return "claim";
11
+ if (progress.execution === "claimed") {
12
+ return progress.owner && progress.baseSha ? "implement" : "resolve-claim-blocker";
13
+ }
14
+ if (progress.execution === "implementing") {
15
+ if (!progress.assignment)
16
+ return "resolve-assignment-blocker";
17
+ if (progress.assignment.status === "submitted") {
18
+ if (blocker?.startsWith("Required check "))
19
+ return "record-current-checks";
20
+ return blocker ? "restore-implementation-report" : "review";
21
+ }
22
+ return "await-implementation-report";
23
+ }
24
+ if (progress.execution === "reviewing") {
25
+ if (blocker?.startsWith("Finding "))
26
+ return "resolve-findings";
27
+ if (blocker?.startsWith("Required check "))
28
+ return "record-current-checks";
29
+ const reviews = progress.reviewAssignments ?? [];
30
+ if (reviews.length > 0) {
31
+ if (blocker?.startsWith("The registered review report"))
32
+ return "restore-review-report";
33
+ if (blocker)
34
+ return "resolve-review-blocker";
35
+ if (reviews.some((assignment) => assignment.status === "pending")) {
36
+ return "await-review-report";
37
+ }
38
+ if (reviews.some((assignment) => assignment.result !== "passed")) {
39
+ return "resolve-review-result";
40
+ }
41
+ return "accept";
42
+ }
43
+ return blocker ? "restore-implementation-report" : "review";
44
+ }
45
+ if (progress.execution === "changes-requested") {
46
+ if ((progress.findings ?? []).some((finding) => finding.status === "confirmed")) {
47
+ return "correct";
48
+ }
49
+ if (blocker?.startsWith("Finding "))
50
+ return "adjudicate-findings";
51
+ return (progress.correctionAttempts?.length ?? 0) > 0
52
+ ? "correct"
53
+ : "controller-correction-decision";
54
+ }
55
+ if (progress.execution === "accepted")
56
+ return blocker ? "resolve-check-blocker" : "integrate";
57
+ if (progress.execution === "integrated")
58
+ return blocker ? "resolve-check-blocker" : "complete";
59
+ return "resolve-blocker";
60
+ }
61
+ requireProgress(ledger, ticket) {
62
+ const progress = ledger.tickets.find((item) => item.id === ticket);
63
+ if (!progress)
64
+ throw new Error(`Ticket ${ticket} has no ledger execution record.`);
65
+ return progress;
66
+ }
67
+ acceptedInventory(progress, ticketId) {
68
+ const corrective = progress.correctionAttempts?.at(-1)?.reviewAssignments ?? [];
69
+ const assignments = corrective.length > 0 ? corrective : (progress.reviewAssignments ?? []);
70
+ if (assignments.length === 0 ||
71
+ assignments.some((assignment) => assignment.status !== "submitted")) {
72
+ throw new Error(`Ticket ${ticketId} has no complete frozen review inventory.`);
73
+ }
74
+ const inventory = assignments[0].inventory;
75
+ if (assignments.some((assignment) => JSON.stringify(assignment.inventory) !== JSON.stringify(inventory))) {
76
+ throw new Error(`Ticket ${ticketId} review assignments disagree on the accepted inventory.`);
77
+ }
78
+ return [...inventory];
79
+ }
80
+ assertDeliveryOpen(delivery) {
81
+ if (delivery.finished) {
82
+ throw new Error("Delivery is finished; final checks cannot be changed.");
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,269 @@
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 AssignmentService extends Service {
8
+ gitEvidence;
9
+ reportEvidence;
10
+ ticketRepository;
11
+ ledgerRepository;
12
+ workspaceTransaction;
13
+ assignmentPolicy;
14
+ checkPolicy;
15
+ reviewPolicy;
16
+ workflowPolicy;
17
+ featureInspectionService;
18
+ constructor(app, gitEvidence, reportEvidence, ticketRepository, ledgerRepository, workspaceTransaction, assignmentPolicy, checkPolicy, reviewPolicy, workflowPolicy, featureInspectionService) {
19
+ super(app);
20
+ this.gitEvidence = gitEvidence;
21
+ this.reportEvidence = reportEvidence;
22
+ this.ticketRepository = ticketRepository;
23
+ this.ledgerRepository = ledgerRepository;
24
+ this.workspaceTransaction = workspaceTransaction;
25
+ this.assignmentPolicy = assignmentPolicy;
26
+ this.checkPolicy = checkPolicy;
27
+ this.reviewPolicy = reviewPolicy;
28
+ this.workflowPolicy = workflowPolicy;
29
+ this.featureInspectionService = featureInspectionService;
30
+ }
31
+ claim(options) {
32
+ validateTicketId(options.ticket);
33
+ validateAuthor(options.author);
34
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
35
+ return this.workspaceTransaction.run(feature.workspace, () => {
36
+ const ledger = this.ledgerRepository.readLedger(feature);
37
+ const tickets = this.ticketRepository.readTickets(feature.workspace);
38
+ this.ticketRepository.validateDependencyGraph(tickets);
39
+ const ticket = this.ticketRepository.requireTicket(tickets, options.ticket);
40
+ const progress = this.workflowPolicy.requireProgress(ledger, options.ticket);
41
+ if (progress.execution !== "unstarted") {
42
+ throw new Error(`Ticket ${options.ticket} is already owned or has progressed to ${progress.execution}.`);
43
+ }
44
+ if (ticket.status !== "open" || ticket.blocker) {
45
+ throw new Error(`Ticket ${options.ticket} is not open and unblocked.`);
46
+ }
47
+ const ticketsById = new Map(tickets.map((item) => [item.id, item]));
48
+ const unmet = ticket.blocked_by.filter((dependency) => {
49
+ const dependencyTicket = ticketsById.get(dependency);
50
+ return (!dependencyTicket || dependencyTicket.status !== "closed" || !!dependencyTicket.blocker);
51
+ });
52
+ if (unmet.length > 0) {
53
+ throw new Error(`Ticket ${options.ticket} has open dependencies: ${unmet.join(", ")}.`);
54
+ }
55
+ const baseSha = this.gitEvidence.cleanGitBaseline(feature);
56
+ progress.execution = "claimed";
57
+ progress.status = "in-progress";
58
+ progress.owner = options.author.trim();
59
+ progress.baseSha = baseSha;
60
+ ledger.delivery.baseSha ??= baseSha;
61
+ const identity = this.gitEvidence.contentIdentity(feature, ticket, baseSha, progress.checks);
62
+ const afterTicket = matter.stringify(this.ticketRepository.appendAudit(ticket.body, options.author, `Claimed from clean Git baseline ${baseSha}.`), this.ticketRepository.metadata(ticket));
63
+ this.workspaceTransaction.writeWorkflowState(feature, ticket, afterTicket, ledger);
64
+ return {
65
+ ticket: options.ticket,
66
+ execution: "claimed",
67
+ owner: options.author.trim(),
68
+ baseSha,
69
+ ...identity,
70
+ };
71
+ });
72
+ }
73
+ implement(options) {
74
+ validateTicketId(options.ticket);
75
+ validateAuthor(options.author);
76
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
77
+ return this.workspaceTransaction.run(feature.workspace, () => {
78
+ const ledger = this.ledgerRepository.readLedger(feature);
79
+ const ticket = this.ticketRepository.requireTicket(this.ticketRepository.readTickets(feature.workspace), options.ticket);
80
+ const progress = this.workflowPolicy.requireProgress(ledger, options.ticket);
81
+ const identity = this.gitEvidence.contentIdentity(feature, ticket, progress.baseSha, progress.checks);
82
+ if (progress.execution === "implementing" && progress.assignment) {
83
+ if (progress.owner !== options.author.trim()) {
84
+ throw new Error("Controller ownership changed; resume and confirm the handoff first.");
85
+ }
86
+ return {
87
+ ticket: options.ticket,
88
+ execution: "implementing",
89
+ assignment: progress.assignment,
90
+ ...identity,
91
+ };
92
+ }
93
+ if (progress.execution !== "claimed" || !progress.baseSha || !progress.owner) {
94
+ throw new Error(`Ticket ${options.ticket} must be claimed before implementation dispatch.`);
95
+ }
96
+ if (progress.owner !== options.author.trim()) {
97
+ throw new Error("Only the recorded controller may dispatch implementation.");
98
+ }
99
+ const report = join(feature.workspace, "reports", `${options.ticket}-implementation.md`);
100
+ this.reportEvidence.assertReportPath(feature, report, false);
101
+ if (lstatIfPresent(report)) {
102
+ throw new Error(`Assigned report already exists and was preserved: ${report}.`);
103
+ }
104
+ const assignment = {
105
+ id: randomUUID(),
106
+ role: "implementation",
107
+ action: "implement",
108
+ controller: options.author.trim(),
109
+ report,
110
+ requestedAt: new Date().toISOString(),
111
+ status: "pending",
112
+ handoffs: [],
113
+ };
114
+ progress.execution = "implementing";
115
+ progress.assignment = assignment;
116
+ const afterTicket = matter.stringify(this.ticketRepository.appendAudit(ticket.body, options.author, `Implementation assignment ${assignment.id} recorded; role=implementation; report=${report}. No agent was launched.`), this.ticketRepository.metadata(ticket));
117
+ this.workspaceTransaction.writeWorkflowState(feature, ticket, afterTicket, ledger);
118
+ return { ticket: options.ticket, execution: "implementing", assignment, ...identity };
119
+ });
120
+ }
121
+ resume(options) {
122
+ if (options.scope === "delivery") {
123
+ if (options.ticket)
124
+ throw new Error("--ticket and --scope delivery are mutually exclusive.");
125
+ return this.deliveryResume(options);
126
+ }
127
+ if (options.scope !== undefined)
128
+ throw new Error("--scope must be 'delivery'.");
129
+ if (!options.ticket)
130
+ throw new Error("resume requires --ticket or --scope delivery.");
131
+ validateTicketId(options.ticket);
132
+ validateAuthor(options.author);
133
+ const feature = this.featureInspectionService.resolveFeature(options.spec);
134
+ return this.workspaceTransaction.run(feature.workspace, () => {
135
+ const ledger = this.ledgerRepository.readLedger(feature);
136
+ const ticket = this.ticketRepository.requireTicket(this.ticketRepository.readTickets(feature.workspace), options.ticket);
137
+ const progress = this.workflowPolicy.requireProgress(ledger, options.ticket);
138
+ let handoffRecorded = false;
139
+ if (progress.owner && progress.owner !== options.author.trim()) {
140
+ if (!options.confirmHandoff) {
141
+ throw new Error(`Ownership handoff from ${progress.owner} to ${options.author.trim()} requires --confirm-handoff after the previous controller has stopped editing.`);
142
+ }
143
+ const previousOwner = progress.owner;
144
+ progress.owner = options.author.trim();
145
+ if (progress.assignment) {
146
+ progress.assignment.controller = options.author.trim();
147
+ progress.assignment.handoffs.push({
148
+ at: new Date().toISOString(),
149
+ from: previousOwner,
150
+ to: options.author.trim(),
151
+ confirmedStopped: true,
152
+ });
153
+ }
154
+ const afterTicket = matter.stringify(this.ticketRepository.appendAudit(ticket.body, options.author, `Controller handoff from ${previousOwner} to ${options.author.trim()} confirmed after the previous controller stopped editing.`), this.ticketRepository.metadata(ticket));
155
+ this.workspaceTransaction.writeWorkflowState(feature, ticket, afterTicket, ledger);
156
+ handoffRecorded = true;
157
+ }
158
+ const currentTicket = this.ticketRepository.requireTicket(this.ticketRepository.readTickets(feature.workspace), options.ticket);
159
+ const identity = this.gitEvidence.contentIdentity(feature, currentTicket, progress.baseSha, progress.checks);
160
+ const reviewAssignments = progress.assignment?.action === "correct"
161
+ ? ((progress.correctionAttempts ?? []).find((attempt) => attempt.assignmentId === progress.assignment?.id)?.reviewAssignments ?? [])
162
+ : progress.reviewAssignments;
163
+ const resumeProgress = reviewAssignments === undefined ? progress : { ...progress, reviewAssignments };
164
+ const reviewBlocker = (reviewAssignments ?? [])
165
+ .map((assignment) => this.reviewPolicy.reviewAssignmentBlocker(feature, progress, assignment, identity))
166
+ .find((reason) => reason !== undefined);
167
+ const blocker = this.assignmentPolicy.resumeBlocker(feature, progress) ??
168
+ reviewBlocker ??
169
+ this.checkPolicy.checkBlockers(feature, currentTicket, progress, identity)[0];
170
+ return {
171
+ ticket: options.ticket,
172
+ gate: progress.execution,
173
+ nextAction: this.workflowPolicy.nextAction(resumeProgress, blocker),
174
+ ...(progress.owner ? { owner: progress.owner } : {}),
175
+ ...(progress.assignment ? { assignment: progress.assignment } : {}),
176
+ correctionAttempts: progress.correctionAttempts ?? [],
177
+ ...(progress.adjudication ? { adjudication: progress.adjudication } : {}),
178
+ ...(reviewAssignments ? { reviewAssignments } : {}),
179
+ ...(handoffRecorded ? { handoffRecorded: true } : {}),
180
+ ...(blocker ? { blocker } : {}),
181
+ ...identity,
182
+ };
183
+ });
184
+ }
185
+ deliveryResume(options) {
186
+ validateAuthor(options.author);
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
+ let handoffRecorded = false;
192
+ if (delivery.owner && delivery.owner !== options.author.trim()) {
193
+ if (!options.confirmHandoff) {
194
+ throw new Error(`Ownership handoff from ${delivery.owner} to ${options.author.trim()} requires --confirm-handoff after the previous controller has stopped editing.`);
195
+ }
196
+ const previousOwner = delivery.owner;
197
+ delivery.owner = options.author.trim();
198
+ if (delivery.assignment) {
199
+ delivery.assignment.controller = options.author.trim();
200
+ delivery.assignment.handoffs.push({
201
+ at: new Date().toISOString(),
202
+ from: previousOwner,
203
+ to: options.author.trim(),
204
+ confirmedStopped: true,
205
+ });
206
+ }
207
+ delivery.history.push({
208
+ at: new Date().toISOString(),
209
+ author: options.author.trim(),
210
+ action: `controller-handoff:${previousOwner}`,
211
+ });
212
+ this.workspaceTransaction.writeLedger(feature, ledger);
213
+ handoffRecorded = true;
214
+ }
215
+ const tickets = this.ticketRepository.readTickets(feature.workspace);
216
+ const identity = this.gitEvidence.deliveryIdentity(feature, ledger, tickets);
217
+ const completion = delivery.finished
218
+ ? this.featureInspectionService.deliveryStatus(feature, ledger, tickets)
219
+ : undefined;
220
+ const checkBlocker = this.checkPolicy.deliveryCheckBlockers(delivery, identity)[0];
221
+ let blocker = completion?.action === "completion-invalidated"
222
+ ? `Finished delivery completion is invalidated: ${completion.blockers.join(" ")}`
223
+ : delivery.adjudication?.status === "pending"
224
+ ? `Delivery correction is paused for adjudication (${delivery.adjudication.reason}).`
225
+ : checkBlocker;
226
+ let nextAction;
227
+ if (completion) {
228
+ nextAction = completion.action;
229
+ }
230
+ else if (delivery.adjudication?.status === "pending") {
231
+ nextAction = "adjudicate";
232
+ }
233
+ else if (delivery.assignment &&
234
+ ["pending", "unavailable"].includes(delivery.assignment.status)) {
235
+ nextAction = "await-implementation-report";
236
+ }
237
+ else if (delivery.reviewAssignments?.some((assignment) => assignment.status === "pending")) {
238
+ nextAction = "await-review-report";
239
+ }
240
+ else if (checkBlocker) {
241
+ nextAction = "record-final-checks";
242
+ }
243
+ else if (delivery.execution === "changes-requested") {
244
+ nextAction = "correct";
245
+ }
246
+ else if (delivery.execution === "reviewing") {
247
+ nextAction = "review-or-finish";
248
+ }
249
+ else {
250
+ nextAction = "review";
251
+ }
252
+ return {
253
+ scope: "delivery",
254
+ gate: delivery.execution,
255
+ nextAction,
256
+ ...(delivery.owner ? { owner: delivery.owner } : {}),
257
+ ...(delivery.assignment ? { assignment: delivery.assignment } : {}),
258
+ correctionAttempts: delivery.correctionAttempts ?? [],
259
+ ...(delivery.adjudication ? { adjudication: delivery.adjudication } : {}),
260
+ ...(delivery.reviewAssignments ? { reviewAssignments: delivery.reviewAssignments } : {}),
261
+ ...(handoffRecorded ? { handoffRecorded: true } : {}),
262
+ ...(blocker ? { blocker } : {}),
263
+ retrospectiveRequired: ledger.retrospectiveRequired,
264
+ finished: !!delivery.finished,
265
+ ...identity,
266
+ };
267
+ });
268
+ }
269
+ }