toolroll 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1113 -0
- package/THIRD_PARTY_NOTICES.md +449 -0
- package/dist/accent-colors.d.ts +48 -0
- package/dist/accent-colors.js +122 -0
- package/dist/action-ledger.d.ts +26 -0
- package/dist/action-ledger.js +75 -0
- package/dist/agent-fence.d.ts +42 -0
- package/dist/agent-fence.js +183 -0
- package/dist/agentconfig.d.ts +216 -0
- package/dist/agentconfig.js +454 -0
- package/dist/api-tokens.d.ts +16 -0
- package/dist/api-tokens.js +27 -0
- package/dist/approval-policy.d.ts +69 -0
- package/dist/approval-policy.js +137 -0
- package/dist/approval-rules-ui.d.ts +25 -0
- package/dist/approval-rules-ui.js +39 -0
- package/dist/assignment-adapters.d.ts +180 -0
- package/dist/assignment-adapters.js +239 -0
- package/dist/assignment-brief.d.ts +71 -0
- package/dist/assignment-brief.js +129 -0
- package/dist/assignment-delivery.d.ts +56 -0
- package/dist/assignment-delivery.js +160 -0
- package/dist/assignment-presentation.d.ts +26 -0
- package/dist/assignment-presentation.js +80 -0
- package/dist/assignment-status.d.ts +62 -0
- package/dist/assignment-status.js +152 -0
- package/dist/assignment-ui.d.ts +71 -0
- package/dist/assignment-ui.js +103 -0
- package/dist/assignment.d.ts +222 -0
- package/dist/assignment.js +399 -0
- package/dist/attest.d.ts +56 -0
- package/dist/attest.js +153 -0
- package/dist/backend.d.ts +97 -0
- package/dist/backend.js +166 -0
- package/dist/backup-ui.d.ts +22 -0
- package/dist/backup-ui.js +59 -0
- package/dist/backup.d.ts +84 -0
- package/dist/backup.js +421 -0
- package/dist/beads.d.ts +34 -0
- package/dist/beads.js +135 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +22 -0
- package/dist/board.d.ts +150 -0
- package/dist/board.js +210 -0
- package/dist/boot-identity.d.ts +63 -0
- package/dist/boot-identity.js +99 -0
- package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
- package/dist/browser/workspace.css +4 -0
- package/dist/browser/workspace.js +225 -0
- package/dist/browser-crew.d.ts +11 -0
- package/dist/browser-crew.js +50 -0
- package/dist/browser-shell.d.ts +8 -0
- package/dist/browser-shell.js +60 -0
- package/dist/browser-workspace.d.ts +780 -0
- package/dist/browser-workspace.js +47 -0
- package/dist/budget-alerts.d.ts +19 -0
- package/dist/budget-alerts.js +46 -0
- package/dist/builder.d.ts +370 -0
- package/dist/builder.js +3262 -0
- package/dist/capscan.d.ts +35 -0
- package/dist/capscan.js +113 -0
- package/dist/chat-acceptance.d.ts +71 -0
- package/dist/chat-acceptance.js +120 -0
- package/dist/chat-actions.d.ts +265 -0
- package/dist/chat-actions.js +1273 -0
- package/dist/chat-channel.d.ts +109 -0
- package/dist/chat-channel.js +652 -0
- package/dist/chat-continuity.d.ts +32 -0
- package/dist/chat-continuity.js +317 -0
- package/dist/chat-controls.d.ts +113 -0
- package/dist/chat-controls.js +53 -0
- package/dist/chat-delivery-state.d.ts +159 -0
- package/dist/chat-delivery-state.js +309 -0
- package/dist/chat-delivery.d.ts +38 -0
- package/dist/chat-delivery.js +693 -0
- package/dist/chat-display.d.ts +1 -0
- package/dist/chat-display.js +18 -0
- package/dist/chat-evidence.d.ts +123 -0
- package/dist/chat-evidence.js +207 -0
- package/dist/chat-flow.d.ts +44 -0
- package/dist/chat-flow.js +215 -0
- package/dist/chat-inbox.d.ts +57 -0
- package/dist/chat-inbox.js +123 -0
- package/dist/chat-polish.d.ts +16 -0
- package/dist/chat-polish.js +135 -0
- package/dist/chat-review.d.ts +46 -0
- package/dist/chat-review.js +98 -0
- package/dist/chat-rooms.d.ts +123 -0
- package/dist/chat-rooms.js +216 -0
- package/dist/chat-task-actions.d.ts +44 -0
- package/dist/chat-task-actions.js +79 -0
- package/dist/check-progress.d.ts +51 -0
- package/dist/check-progress.js +290 -0
- package/dist/child-database.d.ts +12 -0
- package/dist/child-database.js +30 -0
- package/dist/claim.d.ts +688 -0
- package/dist/claim.js +1740 -0
- package/dist/cli.d.ts +137 -0
- package/dist/cli.js +1461 -0
- package/dist/codex-limits.d.ts +15 -0
- package/dist/codex-limits.js +145 -0
- package/dist/coding-context.d.ts +55 -0
- package/dist/coding-context.js +92 -0
- package/dist/coding-handoff.d.ts +57 -0
- package/dist/coding-handoff.js +277 -0
- package/dist/coding-provider.d.ts +72 -0
- package/dist/coding-provider.js +424 -0
- package/dist/coding-shipping-ui.d.ts +4 -0
- package/dist/coding-shipping-ui.js +12 -0
- package/dist/coding-types.d.ts +62 -0
- package/dist/coding-types.js +1 -0
- package/dist/coding-ui.d.ts +17 -0
- package/dist/coding-ui.js +364 -0
- package/dist/coding-update.d.ts +15 -0
- package/dist/coding-update.js +149 -0
- package/dist/coding-workspace.d.ts +124 -0
- package/dist/coding-workspace.js +768 -0
- package/dist/container-state.d.ts +5 -0
- package/dist/container-state.js +62 -0
- package/dist/containment.d.ts +197 -0
- package/dist/containment.js +559 -0
- package/dist/contest.d.ts +318 -0
- package/dist/contest.js +753 -0
- package/dist/control-setup.d.ts +35 -0
- package/dist/control-setup.js +40 -0
- package/dist/control-ui.d.ts +29 -0
- package/dist/control-ui.js +53 -0
- package/dist/controller-service.d.ts +1 -0
- package/dist/controller-service.js +19 -0
- package/dist/controller-supervisor.d.ts +30 -0
- package/dist/controller-supervisor.js +104 -0
- package/dist/converse.d.ts +304 -0
- package/dist/converse.js +849 -0
- package/dist/coordinator-proposals.d.ts +29 -0
- package/dist/coordinator-proposals.js +137 -0
- package/dist/coordinator.d.ts +186 -0
- package/dist/coordinator.js +447 -0
- package/dist/credentials-ui.d.ts +25 -0
- package/dist/credentials-ui.js +48 -0
- package/dist/daemon.d.ts +209 -0
- package/dist/daemon.js +604 -0
- package/dist/decision.d.ts +120 -0
- package/dist/decision.js +388 -0
- package/dist/demo.d.ts +55 -0
- package/dist/demo.js +992 -0
- package/dist/desktop-access.d.ts +28 -0
- package/dist/desktop-access.js +88 -0
- package/dist/desktop-bundle.d.ts +16 -0
- package/dist/desktop-bundle.js +74 -0
- package/dist/desktop-host.d.ts +54 -0
- package/dist/desktop-host.js +508 -0
- package/dist/desktop-update-gate.d.ts +12 -0
- package/dist/desktop-update-gate.js +91 -0
- package/dist/desktop-update-recovery.d.ts +14 -0
- package/dist/desktop-update-recovery.js +243 -0
- package/dist/desktop-update.d.ts +110 -0
- package/dist/desktop-update.js +740 -0
- package/dist/discord-api.d.ts +20 -0
- package/dist/discord-api.js +138 -0
- package/dist/discord-chat.d.ts +16 -0
- package/dist/discord-chat.js +381 -0
- package/dist/discord-settings.d.ts +7 -0
- package/dist/discord-settings.js +75 -0
- package/dist/discord.d.ts +10 -0
- package/dist/discord.js +169 -0
- package/dist/discover.d.ts +75 -0
- package/dist/discover.js +150 -0
- package/dist/dispatch.d.ts +100 -0
- package/dist/dispatch.js +311 -0
- package/dist/dispose.d.ts +161 -0
- package/dist/dispose.js +803 -0
- package/dist/email-settings.d.ts +34 -0
- package/dist/email-settings.js +50 -0
- package/dist/envelope.d.ts +47 -0
- package/dist/envelope.js +105 -0
- package/dist/evidence-pack.d.ts +185 -0
- package/dist/evidence-pack.js +258 -0
- package/dist/evidence.d.ts +345 -0
- package/dist/evidence.js +782 -0
- package/dist/exec.d.ts +285 -0
- package/dist/exec.js +1841 -0
- package/dist/exhaustion.d.ts +85 -0
- package/dist/exhaustion.js +141 -0
- package/dist/export-ui.d.ts +4 -0
- package/dist/export-ui.js +17 -0
- package/dist/export.d.ts +48 -0
- package/dist/export.js +305 -0
- package/dist/flow-actions.d.ts +77 -0
- package/dist/flow-actions.js +203 -0
- package/dist/flow-code.d.ts +50 -0
- package/dist/flow-code.js +159 -0
- package/dist/flow-draft.d.ts +38 -0
- package/dist/flow-draft.js +80 -0
- package/dist/flow-engine.d.ts +66 -0
- package/dist/flow-engine.js +416 -0
- package/dist/flow-insights.d.ts +95 -0
- package/dist/flow-insights.js +149 -0
- package/dist/flow-live.d.ts +24 -0
- package/dist/flow-live.js +150 -0
- package/dist/flow-people.d.ts +24 -0
- package/dist/flow-people.js +91 -0
- package/dist/flow-replies.d.ts +48 -0
- package/dist/flow-replies.js +113 -0
- package/dist/flow-scripts.d.ts +38 -0
- package/dist/flow-scripts.js +73 -0
- package/dist/flow-secrets.d.ts +13 -0
- package/dist/flow-secrets.js +45 -0
- package/dist/flow-sort.d.ts +82 -0
- package/dist/flow-sort.js +153 -0
- package/dist/flow-steps.d.ts +40 -0
- package/dist/flow-steps.js +408 -0
- package/dist/flow-triggers.d.ts +244 -0
- package/dist/flow-triggers.js +959 -0
- package/dist/flows-ui.d.ts +20 -0
- package/dist/flows-ui.js +175 -0
- package/dist/flows.d.ts +229 -0
- package/dist/flows.js +968 -0
- package/dist/fonts.d.ts +19 -0
- package/dist/fonts.js +19 -0
- package/dist/gaps.d.ts +35 -0
- package/dist/gaps.js +101 -0
- package/dist/gate-failure.d.ts +40 -0
- package/dist/gate-failure.js +66 -0
- package/dist/git.d.ts +54 -0
- package/dist/git.js +94 -0
- package/dist/google-mail.d.ts +58 -0
- package/dist/google-mail.js +151 -0
- package/dist/grant.d.ts +136 -0
- package/dist/grant.js +238 -0
- package/dist/graph.d.ts +164 -0
- package/dist/graph.js +383 -0
- package/dist/guides.d.ts +22 -0
- package/dist/guides.js +363 -0
- package/dist/held.d.ts +150 -0
- package/dist/held.js +799 -0
- package/dist/invoke.d.ts +112 -0
- package/dist/invoke.js +780 -0
- package/dist/issues.d.ts +41 -0
- package/dist/issues.js +131 -0
- package/dist/job-object-helper.ps1 +252 -0
- package/dist/jsonl-discriminants.d.ts +11 -0
- package/dist/jsonl-discriminants.js +93 -0
- package/dist/keys.d.ts +104 -0
- package/dist/keys.js +229 -0
- package/dist/kits-ui.d.ts +17 -0
- package/dist/kits-ui.js +46 -0
- package/dist/kits.d.ts +86 -0
- package/dist/kits.js +215 -0
- package/dist/knowledge-cli.d.ts +101 -0
- package/dist/knowledge-cli.js +92 -0
- package/dist/knowledge-ui.d.ts +24 -0
- package/dist/knowledge-ui.js +40 -0
- package/dist/lead-context.d.ts +8 -0
- package/dist/lead-context.js +56 -0
- package/dist/lead-follow.d.ts +22 -0
- package/dist/lead-follow.js +167 -0
- package/dist/lead-status.d.ts +75 -0
- package/dist/lead-status.js +285 -0
- package/dist/ledger-chain.d.ts +46 -0
- package/dist/ledger-chain.js +187 -0
- package/dist/ledger-csv.d.ts +4 -0
- package/dist/ledger-csv.js +11 -0
- package/dist/ledger-view.d.ts +24 -0
- package/dist/ledger-view.js +58 -0
- package/dist/limits-ui.d.ts +26 -0
- package/dist/limits-ui.js +67 -0
- package/dist/link.d.ts +72 -0
- package/dist/link.js +217 -0
- package/dist/live.d.ts +96 -0
- package/dist/live.js +366 -0
- package/dist/liveness.d.ts +30 -0
- package/dist/liveness.js +42 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.js +25 -0
- package/dist/mailbox.d.ts +57 -0
- package/dist/mailbox.js +150 -0
- package/dist/maintenance.d.ts +11 -0
- package/dist/maintenance.js +35 -0
- package/dist/mate-cli.d.ts +52 -0
- package/dist/mate-cli.js +345 -0
- package/dist/mate-contract.d.ts +10 -0
- package/dist/mate-contract.js +30 -0
- package/dist/mate-doors.d.ts +69 -0
- package/dist/mate-doors.js +548 -0
- package/dist/mate-progress.d.ts +29 -0
- package/dist/mate-progress.js +121 -0
- package/dist/mate-tools.d.ts +94 -0
- package/dist/mate-tools.js +1878 -0
- package/dist/mate.d.ts +92 -0
- package/dist/mate.js +435 -0
- package/dist/mcp-connect.d.ts +103 -0
- package/dist/mcp-connect.js +252 -0
- package/dist/mcp.d.ts +27 -0
- package/dist/mcp.js +651 -0
- package/dist/memory-cli.d.ts +466 -0
- package/dist/memory-cli.js +111 -0
- package/dist/memory-pass.d.ts +160 -0
- package/dist/memory-pass.js +409 -0
- package/dist/metrics.d.ts +3 -0
- package/dist/metrics.js +56 -0
- package/dist/mobile-viewport.d.ts +3 -0
- package/dist/mobile-viewport.js +37 -0
- package/dist/model-catalog.d.ts +118 -0
- package/dist/model-catalog.js +376 -0
- package/dist/models-cli.d.ts +102 -0
- package/dist/models-cli.js +66 -0
- package/dist/models-ui.d.ts +34 -0
- package/dist/models-ui.js +68 -0
- package/dist/modes.d.ts +80 -0
- package/dist/modes.js +182 -0
- package/dist/monitoring-settings.d.ts +44 -0
- package/dist/monitoring-settings.js +173 -0
- package/dist/monitoring-ui.d.ts +20 -0
- package/dist/monitoring-ui.js +51 -0
- package/dist/monitoring.d.ts +34 -0
- package/dist/monitoring.js +221 -0
- package/dist/names.d.ts +67 -0
- package/dist/names.js +102 -0
- package/dist/observations.d.ts +40 -0
- package/dist/observations.js +211 -0
- package/dist/oidc.d.ts +71 -0
- package/dist/oidc.js +182 -0
- package/dist/onboard.d.ts +108 -0
- package/dist/onboard.js +325 -0
- package/dist/openrouter-models.d.ts +30 -0
- package/dist/openrouter-models.js +120 -0
- package/dist/operate.d.ts +118 -0
- package/dist/operate.js +11321 -0
- package/dist/peek-cli.d.ts +97 -0
- package/dist/peek-cli.js +215 -0
- package/dist/peek.d.ts +134 -0
- package/dist/peek.js +578 -0
- package/dist/phase-routing.d.ts +307 -0
- package/dist/phase-routing.js +658 -0
- package/dist/plan-auto.d.ts +14 -0
- package/dist/plan-auto.js +92 -0
- package/dist/plan.d.ts +186 -0
- package/dist/plan.js +401 -0
- package/dist/planner-source.d.ts +251 -0
- package/dist/planner-source.js +460 -0
- package/dist/planner.d.ts +75 -0
- package/dist/planner.js +992 -0
- package/dist/policy-ui.d.ts +20 -0
- package/dist/policy-ui.js +41 -0
- package/dist/policy.d.ts +111 -0
- package/dist/policy.js +231 -0
- package/dist/prepared-evidence.d.ts +16 -0
- package/dist/prepared-evidence.js +96 -0
- package/dist/pricing.d.ts +45 -0
- package/dist/pricing.js +77 -0
- package/dist/principal.d.ts +42 -0
- package/dist/principal.js +82 -0
- package/dist/probe.d.ts +46 -0
- package/dist/probe.js +79 -0
- package/dist/process-custody.d.ts +17 -0
- package/dist/process-custody.js +91 -0
- package/dist/process-liveness.d.ts +10 -0
- package/dist/process-liveness.js +81 -0
- package/dist/process-recovery-anchor.d.ts +75 -0
- package/dist/process-recovery-anchor.js +188 -0
- package/dist/process-recovery-coalition.d.ts +25 -0
- package/dist/process-recovery-coalition.js +105 -0
- package/dist/process-recovery-eligibility.d.ts +178 -0
- package/dist/process-recovery-eligibility.js +170 -0
- package/dist/process-recovery-native.d.ts +172 -0
- package/dist/process-recovery-native.js +592 -0
- package/dist/process-recovery-provenance.d.ts +163 -0
- package/dist/process-recovery-provenance.js +292 -0
- package/dist/process-recovery-services.d.ts +56 -0
- package/dist/process-recovery-services.js +191 -0
- package/dist/process-recovery-settlement.d.ts +32 -0
- package/dist/process-recovery-settlement.js +132 -0
- package/dist/process-recovery.d.ts +41 -0
- package/dist/process-recovery.js +91 -0
- package/dist/process-tree.d.ts +41 -0
- package/dist/process-tree.js +264 -0
- package/dist/project-access.d.ts +14 -0
- package/dist/project-access.js +34 -0
- package/dist/project-cli.d.ts +18 -0
- package/dist/project-cli.js +104 -0
- package/dist/project-delete-ui.d.ts +22 -0
- package/dist/project-delete-ui.js +48 -0
- package/dist/project-delete.d.ts +55 -0
- package/dist/project-delete.js +413 -0
- package/dist/project-knowledge.d.ts +111 -0
- package/dist/project-knowledge.js +241 -0
- package/dist/project-learning.d.ts +100 -0
- package/dist/project-learning.js +438 -0
- package/dist/project-memory.d.ts +81 -0
- package/dist/project-memory.js +264 -0
- package/dist/project-skills.d.ts +152 -0
- package/dist/project-skills.js +660 -0
- package/dist/project-tools.d.ts +219 -0
- package/dist/project-tools.js +796 -0
- package/dist/project.d.ts +61 -0
- package/dist/project.js +125 -0
- package/dist/prompt.d.ts +22 -0
- package/dist/prompt.js +72 -0
- package/dist/proof.d.ts +513 -0
- package/dist/proof.js +1140 -0
- package/dist/proposal.d.ts +82 -0
- package/dist/proposal.js +210 -0
- package/dist/provider-connection.d.ts +22 -0
- package/dist/provider-connection.js +98 -0
- package/dist/provider-limits.d.ts +35 -0
- package/dist/provider-limits.js +95 -0
- package/dist/provider.d.ts +279 -0
- package/dist/provider.js +935 -0
- package/dist/publish.d.ts +107 -0
- package/dist/publish.js +650 -0
- package/dist/pulls.d.ts +118 -0
- package/dist/pulls.js +240 -0
- package/dist/push.d.ts +95 -0
- package/dist/push.js +353 -0
- package/dist/quality.d.ts +8 -0
- package/dist/quality.js +6 -0
- package/dist/recipe-ui.d.ts +18 -0
- package/dist/recipe-ui.js +152 -0
- package/dist/recipes.d.ts +81 -0
- package/dist/recipes.js +332 -0
- package/dist/remote.d.ts +67 -0
- package/dist/remote.js +122 -0
- package/dist/render.d.ts +64 -0
- package/dist/render.js +587 -0
- package/dist/report-summary.d.ts +13 -0
- package/dist/report-summary.js +15 -0
- package/dist/repos.d.ts +65 -0
- package/dist/repos.js +277 -0
- package/dist/repository-context-ui.d.ts +2 -0
- package/dist/repository-context-ui.js +10 -0
- package/dist/repository-context.d.ts +67 -0
- package/dist/repository-context.js +376 -0
- package/dist/restart-certification.d.ts +134 -0
- package/dist/restart-certification.js +226 -0
- package/dist/result-actions.d.ts +28 -0
- package/dist/result-actions.js +270 -0
- package/dist/result-completion.d.ts +18 -0
- package/dist/result-completion.js +66 -0
- package/dist/result-review.d.ts +213 -0
- package/dist/result-review.js +430 -0
- package/dist/retention-ui.d.ts +12 -0
- package/dist/retention-ui.js +39 -0
- package/dist/retention.d.ts +72 -0
- package/dist/retention.js +288 -0
- package/dist/review-context.d.ts +243 -0
- package/dist/review-context.js +1043 -0
- package/dist/review-evidence.d.ts +33 -0
- package/dist/review-evidence.js +56 -0
- package/dist/reviewer.d.ts +97 -0
- package/dist/reviewer.js +202 -0
- package/dist/routine.d.ts +228 -0
- package/dist/routine.js +764 -0
- package/dist/runner.d.ts +266 -0
- package/dist/runner.js +399 -0
- package/dist/scan.d.ts +32 -0
- package/dist/scan.js +92 -0
- package/dist/scope.d.ts +695 -0
- package/dist/scope.js +1176 -0
- package/dist/scout-report.d.ts +42 -0
- package/dist/scout-report.js +108 -0
- package/dist/scout.d.ts +69 -0
- package/dist/scout.js +351 -0
- package/dist/serve.d.ts +313 -0
- package/dist/serve.js +21776 -0
- package/dist/session-brief.d.ts +8 -0
- package/dist/session-brief.js +24 -0
- package/dist/session-cli.d.ts +13 -0
- package/dist/session-cli.js +284 -0
- package/dist/session-contract.d.ts +183 -0
- package/dist/session-contract.js +122 -0
- package/dist/session-http.d.ts +19 -0
- package/dist/session-http.js +154 -0
- package/dist/session-server.d.ts +12 -0
- package/dist/session-server.js +37 -0
- package/dist/session-service.d.ts +22 -0
- package/dist/session-service.js +157 -0
- package/dist/setup-guide.d.ts +39 -0
- package/dist/setup-guide.js +93 -0
- package/dist/sign-in-guard.d.ts +45 -0
- package/dist/sign-in-guard.js +74 -0
- package/dist/skills-ui.d.ts +11 -0
- package/dist/skills-ui.js +58 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.js +276 -0
- package/dist/slack-api.d.ts +55 -0
- package/dist/slack-api.js +229 -0
- package/dist/slack-chat.d.ts +28 -0
- package/dist/slack-chat.js +413 -0
- package/dist/slack-settings.d.ts +7 -0
- package/dist/slack-settings.js +75 -0
- package/dist/slack-state.d.ts +13 -0
- package/dist/slack-state.js +9 -0
- package/dist/slack.d.ts +13 -0
- package/dist/slack.js +167 -0
- package/dist/spend-ui.d.ts +28 -0
- package/dist/spend-ui.js +95 -0
- package/dist/spend.d.ts +142 -0
- package/dist/spend.js +313 -0
- package/dist/sqlite-runtime.d.ts +3 -0
- package/dist/sqlite-runtime.js +6 -0
- package/dist/sso-settings.d.ts +33 -0
- package/dist/sso-settings.js +103 -0
- package/dist/sso-ui.d.ts +22 -0
- package/dist/sso-ui.js +39 -0
- package/dist/storage.d.ts +15 -0
- package/dist/storage.js +84 -0
- package/dist/store.d.ts +7754 -0
- package/dist/store.js +23279 -0
- package/dist/structured-output.d.ts +58 -0
- package/dist/structured-output.js +103 -0
- package/dist/style-asset.d.ts +7 -0
- package/dist/style-asset.js +41 -0
- package/dist/subscription-chat.d.ts +46 -0
- package/dist/subscription-chat.js +275 -0
- package/dist/summary.d.ts +33 -0
- package/dist/summary.js +71 -0
- package/dist/supervisor.mjs +297 -0
- package/dist/surface.d.ts +63 -0
- package/dist/surface.js +352 -0
- package/dist/sync.d.ts +94 -0
- package/dist/sync.js +279 -0
- package/dist/task-composer.d.ts +13 -0
- package/dist/task-composer.js +52 -0
- package/dist/task-control.d.ts +165 -0
- package/dist/task-control.js +214 -0
- package/dist/task-outcome-cli.d.ts +10 -0
- package/dist/task-outcome-cli.js +92 -0
- package/dist/task-text.d.ts +30 -0
- package/dist/task-text.js +41 -0
- package/dist/team-cli.d.ts +15 -0
- package/dist/team-cli.js +618 -0
- package/dist/team-contract.d.ts +92 -0
- package/dist/team-contract.js +1 -0
- package/dist/team-http.d.ts +17 -0
- package/dist/team-http.js +144 -0
- package/dist/team-leads.d.ts +81 -0
- package/dist/team-leads.js +565 -0
- package/dist/team-runtime.d.ts +27 -0
- package/dist/team-runtime.js +264 -0
- package/dist/team-ui.d.ts +3 -0
- package/dist/team-ui.js +9 -0
- package/dist/team-updates.d.ts +8 -0
- package/dist/team-updates.js +57 -0
- package/dist/teammate-admin.d.ts +53 -0
- package/dist/teammate-admin.js +111 -0
- package/dist/teammate-desk.d.ts +60 -0
- package/dist/teammate-desk.js +154 -0
- package/dist/teammate-memory.d.ts +64 -0
- package/dist/teammate-memory.js +196 -0
- package/dist/teammate-question.d.ts +59 -0
- package/dist/teammate-question.js +175 -0
- package/dist/teammate-tools.d.ts +108 -0
- package/dist/teammate-tools.js +271 -0
- package/dist/teammate-week.d.ts +68 -0
- package/dist/teammate-week.js +132 -0
- package/dist/teammate-work.d.ts +76 -0
- package/dist/teammate-work.js +280 -0
- package/dist/teammates-ui.d.ts +20 -0
- package/dist/teammates-ui.js +166 -0
- package/dist/teammates.d.ts +180 -0
- package/dist/teammates.js +253 -0
- package/dist/teams-api.d.ts +33 -0
- package/dist/teams-api.js +204 -0
- package/dist/teams-chat.d.ts +26 -0
- package/dist/teams-chat.js +215 -0
- package/dist/teams-settings.d.ts +12 -0
- package/dist/teams-settings.js +57 -0
- package/dist/teams.d.ts +21 -0
- package/dist/teams.js +124 -0
- package/dist/telegram-flow.d.ts +49 -0
- package/dist/telegram-flow.js +110 -0
- package/dist/telegram-mate.d.ts +137 -0
- package/dist/telegram-mate.js +707 -0
- package/dist/telegram-progress.d.ts +14 -0
- package/dist/telegram-progress.js +129 -0
- package/dist/telegram-settings.d.ts +9 -0
- package/dist/telegram-settings.js +36 -0
- package/dist/telegram-status.d.ts +75 -0
- package/dist/telegram-status.js +265 -0
- package/dist/telegram-team.d.ts +85 -0
- package/dist/telegram-team.js +359 -0
- package/dist/telegram.d.ts +230 -0
- package/dist/telegram.js +1931 -0
- package/dist/templates.d.ts +65 -0
- package/dist/templates.js +118 -0
- package/dist/tool-launcher.d.ts +1 -0
- package/dist/tool-launcher.js +101 -0
- package/dist/tools-ui.d.ts +30 -0
- package/dist/tools-ui.js +40 -0
- package/dist/transitions-recipes.d.ts +1 -0
- package/dist/transitions-recipes.js +203 -0
- package/dist/tree-proof.d.ts +34 -0
- package/dist/tree-proof.js +58 -0
- package/dist/verification-evidence.d.ts +44 -0
- package/dist/verification-evidence.js +238 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +10 -0
- package/dist/webhooks.d.ts +91 -0
- package/dist/webhooks.js +283 -0
- package/dist/work-index.d.ts +68 -0
- package/dist/work-index.js +384 -0
- package/dist/work-summary.d.ts +57 -0
- package/dist/work-summary.js +116 -0
- package/dist/workspace-motion.d.ts +4 -0
- package/dist/workspace-motion.js +133 -0
- package/dist/workspace-revision.d.ts +19 -0
- package/dist/workspace-revision.js +92 -0
- package/dist/workspace-ui.d.ts +204 -0
- package/dist/workspace-ui.js +411 -0
- package/dist/worktree-notices.d.ts +13 -0
- package/dist/worktree-notices.js +17 -0
- package/dist/worktree.d.ts +249 -0
- package/dist/worktree.js +720 -0
- package/package.json +122 -0
- package/scripts/canary-assertions.mjs +42 -0
- package/scripts/crash-canary.mjs +253 -0
- package/scripts/fixtures/crash-process.mjs +74 -0
- package/scripts/fixtures/pilot-scenarios.mjs +85 -0
- package/scripts/fixtures/restart-service.mjs +27 -0
- package/scripts/launchd-certification.mjs +143 -0
- package/scripts/pilot.mjs +121 -0
- package/scripts/proof-preflight.mjs +140 -0
- package/scripts/provider-canary.mjs +379 -0
- package/scripts/recovery-canary.mjs +135 -0
- package/scripts/restart-certification.mjs +98 -0
package/dist/dispose.js
ADDED
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
// Assignment tracking grants no repair authority or attempts.
|
|
2
|
+
import { originalTaskBase, focusedTestCommandSupported } from "./observations.js";
|
|
3
|
+
/**
|
|
4
|
+
* The build disposition service (Parity II Phase 2, v4 Q2 / v6 W1): the
|
|
5
|
+
* operations that actually END an attempt — sealing parks, accepting
|
|
6
|
+
* completions behind the completion fence, recording failures with their
|
|
7
|
+
* strikes and holds, opening publication intents — extracted from tick's
|
|
8
|
+
* and the standalone build command's finalizers into ONE place, so the
|
|
9
|
+
* held-session coordinator can end a run through exactly the machinery
|
|
10
|
+
* every other road uses. The two historical roads differ deliberately
|
|
11
|
+
* (the standalone command completes no task, strikes nothing, publishes
|
|
12
|
+
* nothing), and the policy record says so explicitly instead of forking
|
|
13
|
+
* the logic: behavior on both is byte-identical to what the callers
|
|
14
|
+
* inlined before.
|
|
15
|
+
*/
|
|
16
|
+
import { createHash } from "node:crypto";
|
|
17
|
+
import { completeFenced, finalizeFailureFenced, finalizeMalformedFenced, finalizeParkFenced, interruptIfStopped, release, } from "./claim.js";
|
|
18
|
+
import { bodyHashOf, publicationBody } from "./publish.js";
|
|
19
|
+
import { modeTermsFromJson } from "./modes.js";
|
|
20
|
+
import { readVerifiedArtifact, writeEvidenceFile } from "./evidence.js";
|
|
21
|
+
import {} from "./proof.js";
|
|
22
|
+
import { verificationEvidence, failedVerificationEvidence } from "./verification-evidence.js";
|
|
23
|
+
import { classifyGateFailure, describeGateFailure, gateFailureSummary } from "./gate-failure.js";
|
|
24
|
+
import { propose, approve } from "./scope.js";
|
|
25
|
+
import { headWithin } from "./names.js";
|
|
26
|
+
/** The reasons tick classifies as the attempt itself breaking. */
|
|
27
|
+
const TICK_FAILURE_REASONS = new Set([
|
|
28
|
+
"agent",
|
|
29
|
+
"agent-reported",
|
|
30
|
+
"no-op",
|
|
31
|
+
"moved-head",
|
|
32
|
+
"moved-branch",
|
|
33
|
+
"timeout",
|
|
34
|
+
"git",
|
|
35
|
+
"commit-failure",
|
|
36
|
+
"provider-init",
|
|
37
|
+
"setup",
|
|
38
|
+
"revision-brief",
|
|
39
|
+
"stopped",
|
|
40
|
+
// Phase 3 (C1): the gateway's typed refusals consume the task strike
|
|
41
|
+
// budget like every other infrastructure failure — the pre-claim skip
|
|
42
|
+
// keeps the NORMAL road from ever reaching here; these arms bound the
|
|
43
|
+
// races.
|
|
44
|
+
"provider-unattested",
|
|
45
|
+
"provider-protocol",
|
|
46
|
+
]);
|
|
47
|
+
/** The standalone road's historical "broke" list — narrower, deliberately. */
|
|
48
|
+
const STANDALONE_BROKE_REASONS = new Set([
|
|
49
|
+
"agent",
|
|
50
|
+
"agent-reported",
|
|
51
|
+
"no-op",
|
|
52
|
+
"moved-head",
|
|
53
|
+
"timeout",
|
|
54
|
+
"git",
|
|
55
|
+
]);
|
|
56
|
+
/** Compatibility for historical integrations: finished work is handed to the
|
|
57
|
+
* lead. No model review is requested and no signed history is rewritten. */
|
|
58
|
+
export function maybeRequestAutoReview(_store, _repo, _runId, _committed, _noChange, _now) { }
|
|
59
|
+
export function disposeBuildOutcome(context, result) {
|
|
60
|
+
// Stop and every terminal side effect compete under the same SQLite
|
|
61
|
+
// write lock. A separate preliminary transaction leaves a completion gap.
|
|
62
|
+
return context.store.transact(() => disposeBuildOutcomeLocked(context, result));
|
|
63
|
+
}
|
|
64
|
+
function disposeBuildOutcomeLocked(context, result) {
|
|
65
|
+
const { store, policy, leaseId, runId, taskId, taskRef, runner, repo, branch, origin, provider, model, worktreePath, clock } = context;
|
|
66
|
+
// THE STOP FENCE at settlement (v52), before any arm below and inside
|
|
67
|
+
// its own transaction: a stop recorded against this attempt (or the
|
|
68
|
+
// ancestor it runs under) before this instant WINS — a late success is
|
|
69
|
+
// never accepted as done, a park is never sealed, a failure never takes
|
|
70
|
+
// a strike. The attempt ends as interrupted with its work preserved (a
|
|
71
|
+
// commit it made stays on the branch as a reviewable artifact), its
|
|
72
|
+
// stop settles, and the task waits under the stop's own hold. The
|
|
73
|
+
// already-sealed revision endings are exempt exactly as below: their
|
|
74
|
+
// finalizer fenced the stop itself, inside its own transaction.
|
|
75
|
+
if (result.ok || (result.reason !== "plan-revised" && result.reason !== "plan-revision-blocked")) {
|
|
76
|
+
const stopped = store.transact(() => {
|
|
77
|
+
const run = store.getRun(runId);
|
|
78
|
+
const fenceLease = leaseId ?? run?.leaseId;
|
|
79
|
+
if (run === null || run.outcome !== null || fenceLease === undefined)
|
|
80
|
+
return null;
|
|
81
|
+
return interruptIfStopped(store, {
|
|
82
|
+
leaseId: fenceLease,
|
|
83
|
+
runId,
|
|
84
|
+
taskId,
|
|
85
|
+
...(result.ok && result.parked === undefined ? { committed: result.committed } : {}),
|
|
86
|
+
now: clock(),
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
if (stopped !== null)
|
|
90
|
+
return { kind: "stopped", stopRun: stopped.stopRun, requeued: stopped.requeued };
|
|
91
|
+
}
|
|
92
|
+
if (result.ok && result.parked !== undefined) {
|
|
93
|
+
if (leaseId === undefined)
|
|
94
|
+
return { kind: "park-fenced" };
|
|
95
|
+
const sealed = finalizeParkFenced(store, {
|
|
96
|
+
leaseId,
|
|
97
|
+
runId,
|
|
98
|
+
taskId,
|
|
99
|
+
decision: result.parked.decision,
|
|
100
|
+
artifactIds: result.parked.artifactIds,
|
|
101
|
+
now: clock(),
|
|
102
|
+
});
|
|
103
|
+
if (!sealed.ok)
|
|
104
|
+
return { kind: "park-fenced" };
|
|
105
|
+
// A park is the system working, and it ends any failure streak — on the
|
|
106
|
+
// loop's road. The standalone command never touched strikes.
|
|
107
|
+
if (policy === "tick")
|
|
108
|
+
store.resetStrikes(taskRef);
|
|
109
|
+
return { kind: "parked", decisionId: sealed.decisionId };
|
|
110
|
+
}
|
|
111
|
+
if (result.ok) {
|
|
112
|
+
if (policy === "continuation") {
|
|
113
|
+
// The taskless success (v4 Q7 + v5 P5): run finished, claim released,
|
|
114
|
+
// publication intent under the SAME completion latch the ordinary
|
|
115
|
+
// road holds — and the parent task untouched in state, strikes,
|
|
116
|
+
// holds, and derived stats. All one transaction.
|
|
117
|
+
const sealed = store.transact(() => {
|
|
118
|
+
const latchOpen = store.mirrorAllowsCompletion(taskId);
|
|
119
|
+
store.finishRun(runId, {
|
|
120
|
+
outcome: result.noChange === true ? "no-change" : "built",
|
|
121
|
+
...(result.noChange === true ? { reason: "handoff" } : {}),
|
|
122
|
+
committed: result.committed,
|
|
123
|
+
now: clock(),
|
|
124
|
+
});
|
|
125
|
+
if (leaseId !== undefined)
|
|
126
|
+
release(store, leaseId, clock());
|
|
127
|
+
if (!latchOpen)
|
|
128
|
+
return { disowned: true };
|
|
129
|
+
if (result.noChange !== true && result.committed) {
|
|
130
|
+
const grant = store.publicationGrantFor(repo);
|
|
131
|
+
const headSha = store.getRun(runId)?.headRevision ?? null;
|
|
132
|
+
if (grant !== null &&
|
|
133
|
+
headSha !== null &&
|
|
134
|
+
headWithin(branch, grant.headPrefix) &&
|
|
135
|
+
(grant.selector === "all" || origin === "ours")) {
|
|
136
|
+
const intentId = store.createPublicationIntent({
|
|
137
|
+
run: runId,
|
|
138
|
+
taskRef,
|
|
139
|
+
githubRepo: grant.githubRepo,
|
|
140
|
+
remote: grant.remote,
|
|
141
|
+
base: grant.base,
|
|
142
|
+
head: branch,
|
|
143
|
+
headSha,
|
|
144
|
+
bodyHash: "",
|
|
145
|
+
draft: grant.draft,
|
|
146
|
+
}, clock());
|
|
147
|
+
const publication = store.publicationForRun(runId);
|
|
148
|
+
if (publication !== null) {
|
|
149
|
+
store.handle
|
|
150
|
+
.prepare("UPDATE publication SET body_hash = ? WHERE id = ?")
|
|
151
|
+
.run(bodyHashOf(publicationBody(store, publication)), intentId);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { disowned: false };
|
|
156
|
+
});
|
|
157
|
+
store.clearQuota(runner, provider, model ?? "");
|
|
158
|
+
if (sealed.disowned)
|
|
159
|
+
return { kind: "disowned" };
|
|
160
|
+
return { kind: "built", committed: result.committed, noChange: result.noChange === true };
|
|
161
|
+
}
|
|
162
|
+
if (policy === "standalone") {
|
|
163
|
+
store.finishRun(runId, {
|
|
164
|
+
outcome: result.noChange === true ? "no-change" : "built",
|
|
165
|
+
...(result.noChange === true ? { reason: "handoff" } : {}),
|
|
166
|
+
committed: result.committed,
|
|
167
|
+
now: clock(),
|
|
168
|
+
});
|
|
169
|
+
return { kind: "built", committed: result.committed, noChange: result.noChange === true };
|
|
170
|
+
}
|
|
171
|
+
if (leaseId === undefined)
|
|
172
|
+
return { kind: "built-fenced" };
|
|
173
|
+
// The completion has to be *accepted*, not assumed — one transaction
|
|
174
|
+
// around the fenced release, the run's outcome, and the publication
|
|
175
|
+
// intent, so "done" and "this must reach a PR" cannot come apart.
|
|
176
|
+
const sealed = store.transact(() => {
|
|
177
|
+
const fence = completeFenced(store, leaseId, "done", clock());
|
|
178
|
+
if (!fence.ok)
|
|
179
|
+
return fence;
|
|
180
|
+
// The disowned arm (external dispatch, v4 §24): the tracker closed
|
|
181
|
+
// this mirror while it was being built.
|
|
182
|
+
if (fence.arm === "disowned") {
|
|
183
|
+
store.finishRun(runId, { outcome: "failed", reason: "external-closed", committed: result.committed, now: clock() });
|
|
184
|
+
store.enqueueNotification({
|
|
185
|
+
source: { run: runId },
|
|
186
|
+
dedupeKey: `run:${runId}:external-closed`,
|
|
187
|
+
kind: "external-closed",
|
|
188
|
+
subject: `${taskId}: the tracker closed this while it was being built`,
|
|
189
|
+
body: `The branch ${branch} is kept as evidence; nothing is published. Reopen the tracker item and \`toolroll task reopen ${taskId}\` if the work should continue.`,
|
|
190
|
+
}, clock());
|
|
191
|
+
return fence;
|
|
192
|
+
}
|
|
193
|
+
store.finishRun(runId, {
|
|
194
|
+
outcome: result.noChange === true ? "no-change" : "built",
|
|
195
|
+
...(result.noChange === true ? { reason: "handoff" } : {}),
|
|
196
|
+
committed: result.committed,
|
|
197
|
+
now: clock(),
|
|
198
|
+
});
|
|
199
|
+
// A stated no-change publishes nothing.
|
|
200
|
+
if (result.noChange !== true && result.committed) {
|
|
201
|
+
const grant = store.publicationGrantFor(repo);
|
|
202
|
+
const headSha = store.getRun(runId)?.headRevision ?? null;
|
|
203
|
+
if (grant !== null &&
|
|
204
|
+
headSha !== null &&
|
|
205
|
+
headWithin(branch, grant.headPrefix) &&
|
|
206
|
+
(grant.selector === "all" || origin === "ours")) {
|
|
207
|
+
const intentId = store.createPublicationIntent({
|
|
208
|
+
run: runId,
|
|
209
|
+
taskRef,
|
|
210
|
+
githubRepo: grant.githubRepo,
|
|
211
|
+
remote: grant.remote,
|
|
212
|
+
base: grant.base,
|
|
213
|
+
head: branch,
|
|
214
|
+
headSha,
|
|
215
|
+
bodyHash: "",
|
|
216
|
+
draft: grant.draft,
|
|
217
|
+
}, clock());
|
|
218
|
+
// The body's identity is computed from the rows this very
|
|
219
|
+
// transaction made durable — reproducible after any crash.
|
|
220
|
+
const publication = store.publicationForRun(runId);
|
|
221
|
+
if (publication !== null) {
|
|
222
|
+
store.handle
|
|
223
|
+
.prepare("UPDATE publication SET body_hash = ? WHERE id = ?")
|
|
224
|
+
.run(bodyHashOf(publicationBody(store, publication)), intentId);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return fence;
|
|
229
|
+
});
|
|
230
|
+
if (sealed.ok && sealed.arm === "disowned")
|
|
231
|
+
return { kind: "disowned" };
|
|
232
|
+
if (sealed.ok) {
|
|
233
|
+
// A concluded success ends the failure streak and its backoff — and
|
|
234
|
+
// proves the credential, clearing any quota stamp.
|
|
235
|
+
store.resetStrikes(taskRef);
|
|
236
|
+
store.clearQuota(runner, provider, model ?? "");
|
|
237
|
+
return { kind: "built", committed: result.committed, noChange: result.noChange === true };
|
|
238
|
+
}
|
|
239
|
+
store.transact(() => {
|
|
240
|
+
store.finishRun(runId, { outcome: "failed", reason: "fenced", committed: result.committed, now: clock() });
|
|
241
|
+
store.enqueueNotification({
|
|
242
|
+
source: { run: runId },
|
|
243
|
+
dedupeKey: `run:${runId}:fenced`,
|
|
244
|
+
kind: "build-fenced",
|
|
245
|
+
subject: `${taskId}: completed, but the lease was gone`,
|
|
246
|
+
body: `The commit exists on ${branch}, but the world moved past this lease before the completion was accepted. Look before anything reuses it.`,
|
|
247
|
+
}, clock());
|
|
248
|
+
});
|
|
249
|
+
return { kind: "built-fenced" };
|
|
250
|
+
}
|
|
251
|
+
// ---- refusals and failures -----------------------------------------------
|
|
252
|
+
// THE ALREADY-SEALED ENDINGS (adaptive execution plans). Every other arm
|
|
253
|
+
// below ends the attempt itself — releases the claim, writes the run's
|
|
254
|
+
// outcome, counts what it costs. These two arrive with all of that
|
|
255
|
+
// already done: `finalizeRevisionFenced` released the lease, appended the
|
|
256
|
+
// ledger row, placed the hold when one was owed, finished the run, and
|
|
257
|
+
// paged, all inside ONE fenced transaction — the planner road's shape,
|
|
258
|
+
// where claim.ts owns the ending and dispose only reports it.
|
|
259
|
+
//
|
|
260
|
+
// So this branch exists to do NOTHING, deliberately, and it is placed
|
|
261
|
+
// above the policy arms so that every road — tick, standalone, held,
|
|
262
|
+
// continuation — skips them alike.
|
|
263
|
+
//
|
|
264
|
+
// Falling through instead would be wrong twice over. `release()` would
|
|
265
|
+
// survive it (an already-released lease whose `released_by` is
|
|
266
|
+
// 'released' reads as this same lease's duplicate hand-back, not a
|
|
267
|
+
// fence), but `finishRun` overwrites unconditionally, so the sealed
|
|
268
|
+
// `finished_at` would be restamped by a second, later writer; and the
|
|
269
|
+
// bottom of the chain returns `invariant`, which means "the dispatcher
|
|
270
|
+
// broke a rule it was supposed to uphold" — the opposite of what
|
|
271
|
+
// happened here, where the machine worked exactly as designed.
|
|
272
|
+
if (result.reason === "plan-revised" || result.reason === "plan-revision-blocked") {
|
|
273
|
+
return { kind: "skipped", reason: result.reason };
|
|
274
|
+
}
|
|
275
|
+
if (policy === "continuation") {
|
|
276
|
+
// The taskless failure (v4 Q7): the run says what happened, the claim
|
|
277
|
+
// releases — NO strikes, NO holds, NO done→failed demotion; three
|
|
278
|
+
// failed continuations still leave the parent exactly as it finished.
|
|
279
|
+
const outcome = result.reason === "fenced" ? "refused" : "failed";
|
|
280
|
+
store.finishRun(runId, { outcome, reason: result.reason, now: clock() });
|
|
281
|
+
if (leaseId !== undefined)
|
|
282
|
+
release(store, leaseId, clock());
|
|
283
|
+
return { kind: "recorded", outcome };
|
|
284
|
+
}
|
|
285
|
+
if (policy === "standalone") {
|
|
286
|
+
if (result.reason === "malformed-decision" && leaseId !== undefined) {
|
|
287
|
+
const sealed = finalizeMalformedFenced(store, {
|
|
288
|
+
leaseId,
|
|
289
|
+
runId,
|
|
290
|
+
taskId,
|
|
291
|
+
problems: result.problems ?? [],
|
|
292
|
+
now: clock(),
|
|
293
|
+
});
|
|
294
|
+
return { kind: "malformed", sealed: sealed.ok };
|
|
295
|
+
}
|
|
296
|
+
const outcome = STANDALONE_BROKE_REASONS.has(result.reason) ? "failed" : "refused";
|
|
297
|
+
store.finishRun(runId, { outcome, reason: result.reason, now: clock() });
|
|
298
|
+
return { kind: "recorded", outcome };
|
|
299
|
+
}
|
|
300
|
+
// The attended refusal family (v28 sweep): typed, no strike, release and
|
|
301
|
+
// move on — the pre-claim gates make every one a rare race, and the
|
|
302
|
+
// invariant arm they used to fall through is for BUGS, not races.
|
|
303
|
+
if (result.reason === "attended-only" ||
|
|
304
|
+
result.reason === "attended-held" ||
|
|
305
|
+
result.reason === "stale-authorization" ||
|
|
306
|
+
result.reason === "session-cap" ||
|
|
307
|
+
result.reason === "run-held") {
|
|
308
|
+
if (leaseId !== undefined)
|
|
309
|
+
release(store, leaseId, clock());
|
|
310
|
+
store.finishRun(runId, { outcome: "refused", reason: result.reason, now: clock() });
|
|
311
|
+
return { kind: "skipped", reason: result.reason };
|
|
312
|
+
}
|
|
313
|
+
if (result.reason === "unapproved" || result.reason === "scope-changed") {
|
|
314
|
+
// Approval drifted between the prefilter and the builder's own gate.
|
|
315
|
+
if (leaseId !== undefined)
|
|
316
|
+
release(store, leaseId, clock());
|
|
317
|
+
store.finishRun(runId, { outcome: "refused", reason: result.reason, now: clock() });
|
|
318
|
+
return { kind: "skipped", reason: result.reason };
|
|
319
|
+
}
|
|
320
|
+
if (result.reason === "fenced") {
|
|
321
|
+
// The lease did not survive the build. Nothing is ours to release.
|
|
322
|
+
store.finishRun(runId, { outcome: "refused", reason: "fenced", now: clock() });
|
|
323
|
+
return { kind: "fenced" };
|
|
324
|
+
}
|
|
325
|
+
if (result.reason === "malformed-decision") {
|
|
326
|
+
if (leaseId === undefined)
|
|
327
|
+
return { kind: "malformed", sealed: false };
|
|
328
|
+
const sealed = finalizeMalformedFenced(store, {
|
|
329
|
+
leaseId,
|
|
330
|
+
runId,
|
|
331
|
+
taskId,
|
|
332
|
+
problems: result.problems ?? [],
|
|
333
|
+
now: clock(),
|
|
334
|
+
});
|
|
335
|
+
return { kind: "malformed", sealed: sealed.ok };
|
|
336
|
+
}
|
|
337
|
+
if (TICK_FAILURE_REASONS.has(result.reason)) {
|
|
338
|
+
// One fenced transaction decides what the failure means. The
|
|
339
|
+
// classification trusts only what the machine itself observed.
|
|
340
|
+
const failureClass = result.reason === "agent-reported"
|
|
341
|
+
? "agent-reported"
|
|
342
|
+
: result.reason === "no-op" || result.reason === "moved-head" || result.reason === "moved-branch"
|
|
343
|
+
? "no-op"
|
|
344
|
+
: result.reason === "timeout" || result.reason === "git" || result.reason === "provider-init" || result.reason === "setup" || result.reason === "stopped" || result.reason === "provider-unattested" || result.reason === "provider-protocol"
|
|
345
|
+
? "retryable-infra"
|
|
346
|
+
: result.reason === "commit-failure"
|
|
347
|
+
? "commit-failure"
|
|
348
|
+
: "unknown";
|
|
349
|
+
// The attempt's ending, in words, on the run itself: the class is what
|
|
350
|
+
// the strikes count, the message is what a person reads (an agent's
|
|
351
|
+
// own verdict was already recorded by the builder under the same key).
|
|
352
|
+
if (result.reason !== "agent-reported" && result.message.trim() !== "") {
|
|
353
|
+
store.recordOutcomeFacts(runId, { handoff: result.message });
|
|
354
|
+
}
|
|
355
|
+
if (leaseId === undefined) {
|
|
356
|
+
store.finishRun(runId, { outcome: "failed", reason: result.reason, now: clock() });
|
|
357
|
+
return { kind: "failed", failureClass, disposition: null, strikes: null, sealed: false };
|
|
358
|
+
}
|
|
359
|
+
const sealed = finalizeFailureFenced(store, {
|
|
360
|
+
leaseId,
|
|
361
|
+
runId,
|
|
362
|
+
taskId,
|
|
363
|
+
failureClass,
|
|
364
|
+
message: result.message,
|
|
365
|
+
worktree: worktreePath,
|
|
366
|
+
now: clock(),
|
|
367
|
+
});
|
|
368
|
+
return {
|
|
369
|
+
kind: "failed",
|
|
370
|
+
failureClass,
|
|
371
|
+
disposition: sealed.ok ? sealed.disposition : null,
|
|
372
|
+
strikes: sealed.ok && "strikes" in sealed ? sealed.strikes : null,
|
|
373
|
+
sealed: sealed.ok,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
// no-claim, not-yours, not-leased, protected-branch, wrong-branch:
|
|
377
|
+
// invariants the dispatcher was supposed to uphold.
|
|
378
|
+
if (leaseId !== undefined)
|
|
379
|
+
release(store, leaseId, clock());
|
|
380
|
+
store.finishRun(runId, { outcome: "refused", reason: result.reason, now: clock() });
|
|
381
|
+
if (result.reason === "stale-approval") {
|
|
382
|
+
// A deterministic refusal (setup review): the approval no longer
|
|
383
|
+
// matches the routing, and nothing about the next pass changes that.
|
|
384
|
+
// Retrying every pass wrote a thousand refused runs in minutes. So:
|
|
385
|
+
// release, record, HOLD the task under a backoff the approval door
|
|
386
|
+
// lifts, and page once per approval — the operator re-approves (or
|
|
387
|
+
// reconfigures the build agent) and the hold goes with the yes.
|
|
388
|
+
if (leaseId !== undefined)
|
|
389
|
+
release(store, leaseId, clock());
|
|
390
|
+
store.finishRun(runId, { outcome: "refused", reason: "stale-approval", now: clock() });
|
|
391
|
+
holdStaleApproval(store, { taskRef, taskId, message: result.message }, clock());
|
|
392
|
+
return { kind: "skipped", reason: "stale-approval" };
|
|
393
|
+
}
|
|
394
|
+
return { kind: "invariant", reason: result.reason };
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* The stale-approval HOLD, shared by the post-build disposition above and
|
|
398
|
+
* the tick's pre-admission refusal (v48 integrity): a pre-routing row
|
|
399
|
+
* whose sealed profile no longer matches what would resolve is refused
|
|
400
|
+
* BEFORE any run row exists — held under the backoff the approval door
|
|
401
|
+
* lifts, paged once per approval — so the fail-closed admission never
|
|
402
|
+
* turns into a refusal every pass.
|
|
403
|
+
*/
|
|
404
|
+
export function holdStaleApproval(store, args, now) {
|
|
405
|
+
const scope = store.getScope(args.taskId);
|
|
406
|
+
store.holdOwned({
|
|
407
|
+
taskRef: args.taskRef,
|
|
408
|
+
ownerKind: "backoff",
|
|
409
|
+
ownerId: `stale:${args.taskRef}`,
|
|
410
|
+
reason: "stale-approval — the approval no longer matches how builds are routed; approve the scope again on its page",
|
|
411
|
+
until: new Date(now.getTime() + 6 * 60 * 60_000),
|
|
412
|
+
}, now);
|
|
413
|
+
store.enqueueNotification({
|
|
414
|
+
source: { taskRef: args.taskRef },
|
|
415
|
+
dedupeKey: `stale-approval:${args.taskRef}:${scope?.approvedDigest ?? "none"}`,
|
|
416
|
+
kind: "stale-approval",
|
|
417
|
+
pushClass: "attention",
|
|
418
|
+
link: `/t/${encodeURIComponent(args.taskId)}`,
|
|
419
|
+
subject: `${args.taskId}: its approval no longer matches the build routing — approve it again`,
|
|
420
|
+
body: `${args.message}\nNothing runs on it until the scope is approved again; the hold lifts with the yes.`,
|
|
421
|
+
}, now);
|
|
422
|
+
}
|
|
423
|
+
// ---- the bounded repair loop (v40, evidence-review-v1) --------------------
|
|
424
|
+
/** Whether a run's proof reasons name a LIE about the signed terms — an
|
|
425
|
+
* altered criterion statement, or an overclaimed changed path — rather
|
|
426
|
+
* than a gap in the work. The exact phrases `adjudicate()` uses for those
|
|
427
|
+
* two rules, and no others; the integrity stop's own predicate. */
|
|
428
|
+
function isIntegrityRefutation(reasons) {
|
|
429
|
+
return reasons.some(one => one.includes("was signed as") || one.includes("not in the sealed diff"));
|
|
430
|
+
}
|
|
431
|
+
/** Strict shrink: `to` is a proper subset of `from` — gnhf's no-progress
|
|
432
|
+
* rule (docs/DESIGN.md:229), extended: an attempt that does not strictly
|
|
433
|
+
* shrink the unresolved-criterion set counts as a failure. */
|
|
434
|
+
function strictlyShrunk(from, to) {
|
|
435
|
+
if (to.length >= from.length)
|
|
436
|
+
return false;
|
|
437
|
+
const fromSet = new Set(from);
|
|
438
|
+
return to.every(id => fromSet.has(id));
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* The chain's happy exit (v40): the instant one of its own attempts
|
|
442
|
+
* reaches `verified` or `attested`, the chain is done. Checked at every
|
|
443
|
+
* completed build's own verdict-save point — not only after a review —
|
|
444
|
+
* since a review can never UPGRADE a verdict past what `adjudicate`
|
|
445
|
+
* already found (foldReview's monotonicity), so only the structural save
|
|
446
|
+
* can ever produce one of these two words.
|
|
447
|
+
*/
|
|
448
|
+
export function maybeSettleRepairChain(store, taskId, verdict, now) {
|
|
449
|
+
if (verdict !== "verified" && verdict !== "attested")
|
|
450
|
+
return;
|
|
451
|
+
const chain = store.repairChainForDraft(taskId);
|
|
452
|
+
if (chain === null || chain.outcome !== "drafted")
|
|
453
|
+
return;
|
|
454
|
+
store.settleRepairChain(chain.id, "resolved", now);
|
|
455
|
+
}
|
|
456
|
+
/** Read the sealed receipt, log and candidate inventory behind a failed gate
|
|
457
|
+
* and say whether the failure is one no repair attempt could fix. An
|
|
458
|
+
* unreadable or truncated inventory leaves the failure repairable. */
|
|
459
|
+
function unrelatedGateFailure(store, evidenceRoot, failure, runId) {
|
|
460
|
+
const receipt = JSON.parse(failure.receipt);
|
|
461
|
+
let changed = null;
|
|
462
|
+
const stat = store.artifactsFor(runId).find(a => a.kind === "diff-stat");
|
|
463
|
+
if (stat && !stat.truncated && stat.captureStatus === "ok") {
|
|
464
|
+
const read = readVerifiedArtifact(evidenceRoot, stat);
|
|
465
|
+
if (read.ok) {
|
|
466
|
+
try {
|
|
467
|
+
const inventory = JSON.parse(read.content.toString("utf8"));
|
|
468
|
+
if (inventory.filesTruncated === false && Array.isArray(inventory.files)) {
|
|
469
|
+
changed = inventory.files.map(one => one.path).filter((path) => typeof path === "string");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
changed = null;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const classified = classifyGateFailure(receipt.result, failure.log, changed, receipt.command?.timeoutMs ?? null);
|
|
478
|
+
return classified.kind === "repairable" ? null : classified;
|
|
479
|
+
}
|
|
480
|
+
export function regateTask(store, taskId, now, approval) {
|
|
481
|
+
const ref = store.lookupRef(taskId);
|
|
482
|
+
if (ref === null)
|
|
483
|
+
return { ok: false, reason: "no-task", message: `no task ${taskId}` };
|
|
484
|
+
const last = store.lastBuilderRun(ref.id);
|
|
485
|
+
if (last === null || !last.headRevision)
|
|
486
|
+
return { ok: false, reason: "no-attempt", message: `${taskId} has no finished attempt to rerun the check on` };
|
|
487
|
+
const verdict = store.proofVerdictFor(last.id);
|
|
488
|
+
if (verdict !== null && (verdict.verdict === "verified" || verdict.verdict === "attested"))
|
|
489
|
+
return { ok: false, reason: "accepted-result", message: `${taskId}'s last result already reads ${verdict.verdict}; there is nothing to rerun` };
|
|
490
|
+
if (store.proofAcceptance(last.id) !== null)
|
|
491
|
+
return { ok: false, reason: "accepted-result", message: `${taskId}'s last result was accepted by an operator` };
|
|
492
|
+
// Rejected: the proof was refuted, or the machine never got a passing
|
|
493
|
+
// gate (a timed-out check reads "short", not "refuted"). A short verdict
|
|
494
|
+
// whose gate passed is a result awaiting review, not one to rerun.
|
|
495
|
+
const machinePassed = verdict?.machineVerdict === "verified" || verdict?.machineVerdict === "attested";
|
|
496
|
+
if (verdict === null || !(verdict.verdict === "refuted" || (verdict.verdict === "short" && !machinePassed))) {
|
|
497
|
+
return { ok: false, reason: "not-rejected", message: `${taskId}'s last result is still being assessed; wait for it or stop it` };
|
|
498
|
+
}
|
|
499
|
+
// A pushed or opened branch is revised through its pull request, never
|
|
500
|
+
// rerun underneath it; a live claim means an attempt is still running.
|
|
501
|
+
const publication = store.publicationForRun(last.id);
|
|
502
|
+
if (publication !== null && (publication.state === "pushed" || publication.state === "opened"))
|
|
503
|
+
return { ok: false, reason: "published", message: `${taskId}'s last result is published; revise it through its pull request` };
|
|
504
|
+
if (store.hasLiveClaim(ref.id, now))
|
|
505
|
+
return { ok: false, reason: "claimed", message: `a runner holds ${taskId} right now — wait for the attempt to end, or stop it` };
|
|
506
|
+
const scope = store.getScope(taskId);
|
|
507
|
+
if (scope === null)
|
|
508
|
+
return { ok: false, reason: "no-attempt", message: `${taskId} has no scope` };
|
|
509
|
+
const head = last.headRevision;
|
|
510
|
+
const actor = approval.kind === "operator" ? approval.name : `mode ${approval.name}`;
|
|
511
|
+
return store.transact(() => {
|
|
512
|
+
const proposed = propose(store, {
|
|
513
|
+
taskId, goal: scope.goal, outOfScope: scope.outOfScope, touches: scope.touches, budgetMicrousd: scope.budgetMicrousd,
|
|
514
|
+
acceptance: scope.acceptance, qualityMode: scope.qualityMode ?? "default", riskLevel: scope.riskLevel ?? "routine",
|
|
515
|
+
candidate: head, now,
|
|
516
|
+
});
|
|
517
|
+
let sealed = false;
|
|
518
|
+
if (approval.kind === "operator")
|
|
519
|
+
sealed = approve(store, taskId, approval.name, now, proposed.digest, approval.token).ok;
|
|
520
|
+
else
|
|
521
|
+
sealed = store.sealScopeApproval(taskId, actor, now, {}, { kind: "mode", modeDigest: approval.modeDigest });
|
|
522
|
+
if (!sealed)
|
|
523
|
+
return { ok: false, reason: "not-approved", message: `${taskId}'s rerun scope could not be approved` };
|
|
524
|
+
// A finished task returns to the queue; one already queued (a park, a
|
|
525
|
+
// stop) simply dispatches with the rerun scope on its next turn.
|
|
526
|
+
if (store.getTask(taskId)?.state !== "queued") {
|
|
527
|
+
const queued = store.requeueTask(taskId, actor, now);
|
|
528
|
+
if (!queued.ok) {
|
|
529
|
+
const why = { claimed: "claimed", "accepted-result": "accepted-result", published: "published" };
|
|
530
|
+
return { ok: false, reason: why[queued.reason] ?? "not-rejected", message: `${taskId} could not be queued again: ${queued.reason}` };
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
store.addRunNote(last.id, "Toolroll", `The check will run again on this exact commit (${head.slice(0, 7)}) as a new attempt, ${approval.kind === "operator" ? `on ${approval.name}'s say-so` : "automatically once, because the failure was outside this change"}.`, now);
|
|
534
|
+
return { ok: true, run: last.id, head };
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* An explicitly requested repair from saved check or historical review findings.
|
|
539
|
+
* Automatic calls are retired. Composes at
|
|
540
|
+
* most one durable revision draft naming exactly the unmet criterion ids,
|
|
541
|
+
* inheriting the source scope and rubric verbatim — or settles the chain
|
|
542
|
+
* at one of its four independent stops (integrity, no-progress, attempts
|
|
543
|
+
* cap; the fourth — existing spend/run rails — is the ordinary tick's own
|
|
544
|
+
* job once a mode-approved draft dispatches as a normal builder run).
|
|
545
|
+
*
|
|
546
|
+
* Never dispatches anything itself: a mode-authorized draft is
|
|
547
|
+
* auto-APPROVED here, but still builds through the ordinary tick, under
|
|
548
|
+
* the ordinary rails and strikes — this function only ever composes a
|
|
549
|
+
* task and, at most, one scope approval.
|
|
550
|
+
*/
|
|
551
|
+
export function maybeTriggerRepair(store, repo, evidenceRoot, sourceRunId, verdict, now, cause = "review", automatic = false) {
|
|
552
|
+
if (verdict !== "short" && verdict !== "refuted")
|
|
553
|
+
return { kind: "none" };
|
|
554
|
+
// Findings go to the lead. Explicit repair/revision actions remain available.
|
|
555
|
+
// Historical mode signatures remain intact; no automatic follow-up spends.
|
|
556
|
+
if (automatic)
|
|
557
|
+
return { kind: "none" };
|
|
558
|
+
// One attempt per source run, ever (source_run UNIQUE) — checked first so
|
|
559
|
+
// a re-fired trigger is a silent no-op, never a duplicate.
|
|
560
|
+
if (store.repairChainFor(sourceRunId) !== null)
|
|
561
|
+
return { kind: "none" };
|
|
562
|
+
const run = store.getRun(sourceRunId);
|
|
563
|
+
if (run === null)
|
|
564
|
+
return { kind: "none" };
|
|
565
|
+
const stored = store.proofVerdictFor(sourceRunId);
|
|
566
|
+
if (stored === null)
|
|
567
|
+
return { kind: "none" };
|
|
568
|
+
// THE RERUN STOP (v70): this attempt was a rerun of the same commit — the
|
|
569
|
+
// scope names that commit as its prepared candidate and the commit has
|
|
570
|
+
// been attempted before. The machine has had its one retry; whatever
|
|
571
|
+
// rejected it this time, a failed check or a contradicting review, goes
|
|
572
|
+
// to a person. No repair draft, no third pass, on either road.
|
|
573
|
+
{
|
|
574
|
+
const rerunTask = store.refById(run.taskRef)?.externalId ?? "";
|
|
575
|
+
if (run.headRevision && store.getScope(rerunTask)?.candidate === run.headRevision && store.builderAttemptsAt(run.taskRef, run.headRevision) >= 2) {
|
|
576
|
+
const contradicted = cause === "review";
|
|
577
|
+
store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `regate-failed:${sourceRunId}`, kind: "repair-evidence", pushClass: "attention",
|
|
578
|
+
subject: contradicted ? "The rerun on the same commit was contradicted by its review" : "The project check failed again on the same commit",
|
|
579
|
+
body: `Attempt #${sourceRunId} reran commit ${run.headRevision.slice(0, 7)} and ${contradicted ? "its review contradicted the result" : "the approved check failed again"}. No repair task was filed: decide whether the code or the check is wrong, then run \`toolroll task regate ${rerunTask}\` or re-scope a corrected candidate.`,
|
|
580
|
+
link: `/t/${encodeURIComponent(rerunTask)}` }, now);
|
|
581
|
+
return { kind: "none" };
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const direct = stored.matrix.length > 0 && stored.matrix.every(row => row.assessment !== undefined);
|
|
585
|
+
const observation = cause === "review" && direct && !stored.matrix.some(row => row.review?.judgement === "contradicts") && stored.matrix.some(row => row.review?.judgement === "cannot-tell");
|
|
586
|
+
if (cause === "review" && direct && !stored.matrix.some(row => row.review?.judgement === "contradicts") && !observation)
|
|
587
|
+
return { kind: "none" };
|
|
588
|
+
if (store.applicableStopFor(sourceRunId) !== null || store.activeHolds(run.taskRef, now).length > 0)
|
|
589
|
+
return { kind: "none" };
|
|
590
|
+
let observationGate;
|
|
591
|
+
if (observation) {
|
|
592
|
+
if (store.refForId(run.taskRef)?.repo !== repo)
|
|
593
|
+
return { kind: "none" };
|
|
594
|
+
const missing = stored.matrix.filter(row => row.review?.judgement === "cannot-tell");
|
|
595
|
+
const supported = missing.every(row => row.requiredEvidence.includes("check") && !row.requiredEvidence.includes("screenshot") && !row.requiredEvidence.includes("manual-review")) && focusedTestCommandSupported(store.liveVerifyCommand(repo)?.command ?? "");
|
|
596
|
+
if (!supported) {
|
|
597
|
+
store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `assessment-evidence:${sourceRunId}`, kind: "repair-evidence", pushClass: "attention", subject: "More evidence is needed", body: missing.flatMap(row => row.detail).join("\n"), link: `/r/${sourceRunId}` }, now);
|
|
598
|
+
return { kind: "none" };
|
|
599
|
+
}
|
|
600
|
+
const gate = verificationEvidence(store, evidenceRoot, sourceRunId);
|
|
601
|
+
const originalBase = originalTaskBase(store, store.externalIdFor(run.taskRef));
|
|
602
|
+
if (!gate.ok || !gate.bytes || !originalBase || JSON.parse(gate.bytes).result?.exitCode !== 0 || stored.machineVerdict !== "verified")
|
|
603
|
+
return { kind: "none" };
|
|
604
|
+
observationGate = { digest: gate.digest, originalBase };
|
|
605
|
+
}
|
|
606
|
+
const mode = store.activeMode(repo, now);
|
|
607
|
+
const terms = mode === null ? null : modeTermsFromJson(mode.termsJson);
|
|
608
|
+
let verification;
|
|
609
|
+
if (cause === "verification") {
|
|
610
|
+
// A signed repair mode is required for this automatic producer. The
|
|
611
|
+
// existing review/manual road continues to offer unapproved drafts.
|
|
612
|
+
if (terms?.repairAuto !== true || run.finishedAt === null || !run.headRevision ||
|
|
613
|
+
(run.outcome !== "built" && run.outcome !== "no-change"))
|
|
614
|
+
return { kind: "none" };
|
|
615
|
+
const owner = store.refForId(run.taskRef);
|
|
616
|
+
if (owner === null || owner.repo !== repo || store.applicableStopFor(sourceRunId) !== null ||
|
|
617
|
+
store.activeHolds(run.taskRef, now).length > 0 ||
|
|
618
|
+
(!run.committed && !(run.outcome === "no-change" && store.repairChainForDraft(owner.externalId) !== null)))
|
|
619
|
+
return { kind: "none" };
|
|
620
|
+
const failure = failedVerificationEvidence(store, evidenceRoot, sourceRunId);
|
|
621
|
+
if (failure.kind === "unavailable") {
|
|
622
|
+
store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `repair-evidence:${sourceRunId}`, kind: "repair-evidence", pushClass: "attention", subject: "Repair needs complete evidence", body: failure.problem, link: `/t/${encodeURIComponent(store.refById(run.taskRef)?.externalId ?? "")}` }, now);
|
|
623
|
+
return { kind: "none" };
|
|
624
|
+
}
|
|
625
|
+
if (failure.kind !== "failed")
|
|
626
|
+
return { kind: "none" };
|
|
627
|
+
// A failure the change cannot have caused — the whole gate timing out,
|
|
628
|
+
// or an untouched test's own timeout — is handed to a person, not to
|
|
629
|
+
// another code-writing attempt (2026-09-18: mayhem-spire run 1784
|
|
630
|
+
// drafted a repair for a balance probe the diff never touched).
|
|
631
|
+
const unrelated = unrelatedGateFailure(store, evidenceRoot, failure, sourceRunId);
|
|
632
|
+
if (unrelated !== null) {
|
|
633
|
+
const taskId = store.refById(run.taskRef)?.externalId ?? "";
|
|
634
|
+
// One automatic rerun of the check on the same commit, under the mode
|
|
635
|
+
// that already authorizes automatic repair (a rerun is strictly less
|
|
636
|
+
// than a repair). A second failure on the same commit goes to a person.
|
|
637
|
+
const rerunBefore = run.headRevision ? store.builderAttemptsAt(run.taskRef, run.headRevision) : 0;
|
|
638
|
+
if (mode !== null && terms?.repairAuto === true && run.headRevision && rerunBefore < 2) {
|
|
639
|
+
const rerun = regateTask(store, taskId, now, { kind: "mode", name: mode.name, modeDigest: mode.digest });
|
|
640
|
+
if (rerun.ok) {
|
|
641
|
+
store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `regate:${sourceRunId}`, kind: "repair-evidence",
|
|
642
|
+
subject: unrelated.kind === "gate-timed-out" ? "The project check ran out of time; running it once more" : "A slow test outside this change failed the check; running it once more",
|
|
643
|
+
body: `${gateFailureSummary(unrelated)} The check runs again once on the same commit ${run.headRevision.slice(0, 7)}, with no agent. If it fails again, a person decides.`,
|
|
644
|
+
link: `/t/${encodeURIComponent(taskId)}` }, now);
|
|
645
|
+
return { kind: "none" };
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `repair-unrelated:${sourceRunId}`, kind: "repair-evidence", pushClass: "attention",
|
|
649
|
+
subject: unrelated.kind === "gate-timed-out" ? "The project check ran out of time" : "A slow test outside this change failed the project check",
|
|
650
|
+
body: describeGateFailure(unrelated, taskId), link: `/t/${encodeURIComponent(taskId)}` }, now);
|
|
651
|
+
return { kind: "none" };
|
|
652
|
+
}
|
|
653
|
+
verification = { sourceRun: sourceRunId, digest: failure.digest };
|
|
654
|
+
}
|
|
655
|
+
const unresolved = stored.matrix
|
|
656
|
+
.filter(row => cause === "review" && direct ? row.review?.judgement === (observation ? "cannot-tell" : "contradicts") : row.state === "missing" || row.state === "failed")
|
|
657
|
+
.map(row => row.id)
|
|
658
|
+
.sort();
|
|
659
|
+
// The project gate is independent of agent-reported criterion passes.
|
|
660
|
+
// This ledger target is not a new acceptance criterion or a changed verdict.
|
|
661
|
+
if (verification !== undefined && !unresolved.includes("project checks"))
|
|
662
|
+
unresolved.push("project checks");
|
|
663
|
+
unresolved.sort();
|
|
664
|
+
if (unresolved.length === 0)
|
|
665
|
+
return { kind: "none" };
|
|
666
|
+
const brief = store.refById(run.taskRef);
|
|
667
|
+
const ref = brief === null ? null : store.lookupRef(brief.externalId);
|
|
668
|
+
if (ref === null)
|
|
669
|
+
return { kind: "none" };
|
|
670
|
+
// Never repair: a scout task (a report, not a branch — nothing to build
|
|
671
|
+
// back into).
|
|
672
|
+
if (ref.deliverable === "report")
|
|
673
|
+
return { kind: "none" };
|
|
674
|
+
// Never repair: a run already published or holding a merge blocker.
|
|
675
|
+
const publication = store.publicationForRun(sourceRunId);
|
|
676
|
+
if (publication !== null && !(verification !== undefined && publication.state === "intended" && publication.attempts === 0))
|
|
677
|
+
return { kind: "none" };
|
|
678
|
+
// Never repair: a task with an open decision or a pending steering note.
|
|
679
|
+
if (store.decisionsForTask(ref.id).some(d => d.state === "open" || d.state === "expired"))
|
|
680
|
+
return { kind: "none" };
|
|
681
|
+
if (store.pendingSteerCount(ref.id) > 0)
|
|
682
|
+
return { kind: "none" };
|
|
683
|
+
// Never repair: a run whose proof was already accepted.
|
|
684
|
+
if (store.proofAcceptance(sourceRunId) !== null)
|
|
685
|
+
return { kind: "none" };
|
|
686
|
+
const task = store.getTask(ref.externalId);
|
|
687
|
+
const scope = store.getScope(ref.externalId);
|
|
688
|
+
if (task === null || scope === null)
|
|
689
|
+
return { kind: "none" };
|
|
690
|
+
// THE LINEAGE (contract handoff task 2): the chain this task CONTINUES
|
|
691
|
+
// is found through its revision ancestry, not only its own draft row —
|
|
692
|
+
// an annotation or CI detour between two repair attempts keeps the
|
|
693
|
+
// root, the attempts already spent, and the remaining automatic bound.
|
|
694
|
+
// The next attempt number is one past the chain's highest, whichever
|
|
695
|
+
// branch spent it; nothing resets because a person annotated a draft.
|
|
696
|
+
const lineage = store.repairLineageOf(ref.externalId);
|
|
697
|
+
if (lineage.problem !== undefined) {
|
|
698
|
+
store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `repair-lineage:${sourceRunId}`, kind: "repair-lineage", subject: `${ref.externalId}: repair ancestry needs attention`, body: lineage.problem, link: `/t/${encodeURIComponent(ref.externalId)}` }, now);
|
|
699
|
+
return { kind: "stopped", reason: "repair-refused-integrity" };
|
|
700
|
+
}
|
|
701
|
+
const priorChain = lineage.continues;
|
|
702
|
+
const rootTask = lineage.rootTask;
|
|
703
|
+
const attempt = lineage.attempts.reduce((highest, row) => Math.max(highest, row.attempt), 0) + 1;
|
|
704
|
+
const auto = terms?.repairAuto === true;
|
|
705
|
+
const basis = auto ? "mode" : "human";
|
|
706
|
+
const modeDigest = auto && mode !== null ? mode.digest : null;
|
|
707
|
+
/** Settle the row this task continues when it is still open; a stop
|
|
708
|
+
* with no open row to settle records its own settled row instead. */
|
|
709
|
+
const settleOrRecord = (outcome) => {
|
|
710
|
+
if (priorChain !== null && priorChain.outcome === "drafted")
|
|
711
|
+
store.settleRepairChain(priorChain.id, outcome, now);
|
|
712
|
+
else
|
|
713
|
+
store.recordRepairStop({ rootTask, sourceRun: sourceRunId, attempt, basis, modeDigest, unresolved, outcome }, now);
|
|
714
|
+
};
|
|
715
|
+
// THE INTEGRITY STOP (unconditional, both roads): a refutation that lies
|
|
716
|
+
// about the signed terms is never handed back to the same machine
|
|
717
|
+
// unattended — park for a human instead.
|
|
718
|
+
if (isIntegrityRefutation(stored.reasons)) {
|
|
719
|
+
settleOrRecord("integrity-refused");
|
|
720
|
+
return { kind: "stopped", reason: "repair-refused-integrity" };
|
|
721
|
+
}
|
|
722
|
+
// THE NO-PROGRESS STOP (unconditional, both roads): two consecutive
|
|
723
|
+
// attempts that fail to strictly shrink the unresolved set.
|
|
724
|
+
if (priorChain !== null) {
|
|
725
|
+
const history = store.repairChainForRoot(rootTask).map(row => [...row.unresolved].sort());
|
|
726
|
+
const sequence = [...history, unresolved];
|
|
727
|
+
if (sequence.length >= 3) {
|
|
728
|
+
const last = sequence[sequence.length - 1];
|
|
729
|
+
const mid = sequence[sequence.length - 2];
|
|
730
|
+
const first = sequence[sequence.length - 3];
|
|
731
|
+
if (!strictlyShrunk(mid, last) && !strictlyShrunk(first, mid)) {
|
|
732
|
+
settleOrRecord("no-progress");
|
|
733
|
+
return { kind: "stopped", reason: "repair-no-progress" };
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
// THE ATTEMPT CAP: bounds only the AUTOMATIC road (a mode's signed
|
|
738
|
+
// repairMaxAttempts) — the default road's loop is already bounded by
|
|
739
|
+
// requiring a fresh human "yes" for every attempt.
|
|
740
|
+
if (auto && terms !== null && attempt > terms.repairMaxAttempts) {
|
|
741
|
+
settleOrRecord("attempts-spent");
|
|
742
|
+
return { kind: "stopped", reason: "repair-attempts-spent" };
|
|
743
|
+
}
|
|
744
|
+
// THE DRAFT: no log content, no new instructions — exactly the unmet
|
|
745
|
+
// ids, their matrix detail sentences, and the reviewer's own
|
|
746
|
+
// contradiction notes.
|
|
747
|
+
const contradictions = store.criterionReviewsFor(sourceRunId).filter(one => one.judgement === "contradicts");
|
|
748
|
+
const unresolvedDetail = stored.matrix.filter(row => unresolved.includes(row.id)).map(row => ({ id: row.id, statement: row.statement, detail: row.detail }));
|
|
749
|
+
const draftBrief = {
|
|
750
|
+
schema: 1,
|
|
751
|
+
kind: observation ? "evidence-observation" : "criterion-repair",
|
|
752
|
+
sourceTask: ref.externalId,
|
|
753
|
+
sourceRun: sourceRunId,
|
|
754
|
+
sourceScopeDigest: scope.digest,
|
|
755
|
+
head: run.headRevision,
|
|
756
|
+
rootTask,
|
|
757
|
+
attempt,
|
|
758
|
+
unresolved: unresolvedDetail,
|
|
759
|
+
reviewerContradictions: contradictions.map(one => ({ id: one.criterionId, author: one.author, note: one.note })),
|
|
760
|
+
...(verification === undefined ? {} : { verification }),
|
|
761
|
+
...(observationGate === undefined ? {} : { originalBase: observationGate.originalBase, gateDigest: observationGate.digest }),
|
|
762
|
+
};
|
|
763
|
+
const briefBytes = Buffer.from(JSON.stringify(draftBrief, null, 2), "utf8");
|
|
764
|
+
const key = writeEvidenceFile(evidenceRoot, sourceRunId, `repair-brief-${sourceRunId}-${attempt}.json`, briefBytes);
|
|
765
|
+
// Suffixes survive truncation (the CI-repair rule, verbatim): the prefix
|
|
766
|
+
// gives way, the identity-bearing tail never does.
|
|
767
|
+
const suffix = observation ? `-evidence-${attempt}` : `-fix-${attempt}`;
|
|
768
|
+
const draftId = `${rootTask.slice(0, 64 - suffix.length)}${suffix}`;
|
|
769
|
+
// The draft files through the ONE revision boundary: the source's goal,
|
|
770
|
+
// exclusions, touches, rubric, risk, quality, posture, budget, overrides
|
|
771
|
+
// and pins are read from the SOURCE ROWS inside the seal — the scope
|
|
772
|
+
// read above is only the digest this draft binds to, re-proved there.
|
|
773
|
+
const drafted = store.openRepairDraft({
|
|
774
|
+
source: { task: ref.externalId, run: sourceRunId, scopeDigest: scope.digest },
|
|
775
|
+
brief: { evidenceRoot, key, sha256: createHash("sha256").update(briefBytes).digest("hex"), bytes: briefBytes.length, capture: "machine-authored repair brief (exit 0)" },
|
|
776
|
+
child: {
|
|
777
|
+
id: draftId,
|
|
778
|
+
title: observation ? `Collect missing evidence for ${ref.externalId}` : verification === undefined ? `repair ${ref.externalId}: ${unresolved.length} criteri${unresolved.length === 1 ? "on" : "a"} unmet` : `Fix failed checks for ${ref.externalId}`,
|
|
779
|
+
repair: observation ? `Collect only the missing observations for ${unresolved.join(", ")}. Keep the saved candidate unchanged. The machine runs supported focused test observations and reuses its intact passing gate; a fresh review assesses the added evidence.` : verification === undefined ? `repair exactly the unmet criteria named below; a comment cannot widen the scope. Unmet: ${unresolved.join(", ")}.` : "Diagnose the saved failed project check and repair within the original scope. Preserve the acceptance criteria and verification command; the revision brief binds the exact failure evidence.",
|
|
780
|
+
},
|
|
781
|
+
rootTask,
|
|
782
|
+
attempt,
|
|
783
|
+
basis,
|
|
784
|
+
modeDigest,
|
|
785
|
+
unresolved,
|
|
786
|
+
}, now);
|
|
787
|
+
if (!drafted.ok)
|
|
788
|
+
return { kind: "none" };
|
|
789
|
+
let approved = false;
|
|
790
|
+
if (auto && mode !== null) {
|
|
791
|
+
const draftScope = store.getScope(drafted.id);
|
|
792
|
+
// A scope whose profile could not resolve is unapprovable by the human
|
|
793
|
+
// road; the mode road refuses the same way — the draft stays honestly
|
|
794
|
+
// unapproved rather than sealing an approval nobody could act on.
|
|
795
|
+
if (draftScope?.profileState === "resolved") {
|
|
796
|
+
approved = store.sealScopeApproval(drafted.id, `mode ${mode.name}`, now, {}, { kind: "mode", modeDigest: mode.digest });
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (observation && !approved)
|
|
800
|
+
store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `assessment-evidence:${sourceRunId}`,
|
|
801
|
+
kind: "repair-evidence", pushClass: "attention", subject: "Review evidence collection", body: "A follow-up is ready to collect the missing observations without changing the saved code.", link: `/t/${encodeURIComponent(drafted.id)}` }, now);
|
|
802
|
+
return { kind: "drafted", draftTaskId: drafted.id, attempt, approved };
|
|
803
|
+
}
|