cloving 1.3.0 → 1.4.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/README.md +235 -206
- package/dist/src/agent-harness-shell.d.ts +2 -1
- package/dist/src/agent-harness-shell.js +8 -5
- package/dist/src/authority/command-wait-checks.d.ts +8 -0
- package/dist/src/authority/command-wait-checks.js +88 -0
- package/dist/src/authority/command-wait-results.d.ts +15 -0
- package/dist/src/authority/command-wait-results.js +73 -0
- package/dist/src/authority/command-wait-view.d.ts +21 -0
- package/dist/src/authority/command-wait-view.js +34 -0
- package/dist/src/authority/command-waits.d.ts +39 -0
- package/dist/src/authority/command-waits.js +97 -0
- package/dist/src/authority/commands.js +22 -1
- package/dist/src/authority/dispatch-decision.d.ts +8 -0
- package/dist/src/authority/dispatch-decision.js +26 -1
- package/dist/src/authority/effect-requests.js +6 -3
- package/dist/src/authority/effect-results.js +4 -2
- package/dist/src/authority/history-state.d.ts +2 -0
- package/dist/src/authority/history-state.js +13 -1
- package/dist/src/authority/instants.d.ts +1 -0
- package/dist/src/authority/instants.js +21 -0
- package/dist/src/authority/reduction-state.d.ts +2 -0
- package/dist/src/authority/reduction-state.js +5 -0
- package/dist/src/authority/reduction.js +24 -0
- package/dist/src/authority/status-input.d.ts +2 -0
- package/dist/src/authority/status.d.ts +2 -0
- package/dist/src/authority/status.js +1 -0
- package/dist/src/authority/stop-sequence.d.ts +1 -0
- package/dist/src/authority/stop-sequence.js +2 -1
- package/dist/src/authority/transitions.d.ts +6 -4
- package/dist/src/authority/transitions.js +7 -0
- package/dist/src/broker/actor/authority-queries.d.ts +2 -1
- package/dist/src/broker/actor/authority-queries.js +14 -5
- package/dist/src/broker/actor/authority-receipts.js +1 -1
- package/dist/src/broker/actor/authority-settings.js +2 -2
- package/dist/src/broker/actor/authority-upgrades.d.ts +19 -2
- package/dist/src/broker/actor/authority-upgrades.js +7 -2
- package/dist/src/broker/actor/delivery-intents.js +2 -0
- package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
- package/dist/src/broker/actor/effect-delivery.js +15 -2
- package/dist/src/broker/actor/effect-plan.js +11 -2
- package/dist/src/broker/actor/effect-queries.d.ts +4 -0
- package/dist/src/broker/actor/effect-queries.js +9 -1
- package/dist/src/broker/actor/effect-turn.js +2 -0
- package/dist/src/broker/actor/stop-plan.js +9 -3
- package/dist/src/broker/actor/stop-turn.d.ts +2 -0
- package/dist/src/broker/actor/stop-turn.js +24 -11
- package/dist/src/broker/actor/turn-draft.d.ts +1 -0
- package/dist/src/broker/actor/turn-draft.js +4 -0
- package/dist/src/broker/actor/turn-state.js +5 -1
- package/dist/src/broker/authority-admission.js +60 -0
- package/dist/src/broker/authority-restore.d.ts +2 -0
- package/dist/src/broker/authority-restore.js +35 -1
- package/dist/src/broker/command-wait-restore.d.ts +31 -0
- package/dist/src/broker/command-wait-restore.js +86 -0
- package/dist/src/broker/decision-restore.d.ts +1 -1
- package/dist/src/broker/decision-restore.js +1 -0
- package/dist/src/broker/effect-upgrade.d.ts +2 -0
- package/dist/src/broker/effect-upgrade.js +8 -2
- package/dist/src/cli/arguments.d.ts +13 -0
- package/dist/src/cli/arguments.js +42 -3
- package/dist/src/cli/chat-stores.d.ts +2 -0
- package/dist/src/cli/chat-stores.js +113 -0
- package/dist/src/cli/client.d.ts +4 -1
- package/dist/src/cli/client.js +9 -3
- package/dist/src/cli/command-requests.d.ts +17 -0
- package/dist/src/cli/command-requests.js +43 -0
- package/dist/src/cli/command-waits.d.ts +9 -0
- package/dist/src/cli/command-waits.js +51 -0
- package/dist/src/cli/commands.js +14 -41
- package/dist/src/cli/config-file.d.ts +2 -2
- package/dist/src/cli/config-file.js +4 -2
- package/dist/src/cli/doctor-workflows.d.ts +5 -2
- package/dist/src/cli/doctor-workflows.js +12 -7
- package/dist/src/cli/doctor.js +6 -9
- package/dist/src/cli/harness-probes.d.ts +2 -0
- package/dist/src/cli/harness-probes.js +1 -1
- package/dist/src/cli/harness-processes.d.ts +6 -0
- package/dist/src/cli/harness-processes.js +36 -0
- package/dist/src/cli/init-command.d.ts +9 -0
- package/dist/src/cli/init-command.js +52 -0
- package/dist/src/cli/init-prompt-command.d.ts +10 -0
- package/dist/src/cli/init-prompt-command.js +145 -0
- package/dist/src/cli/init-prompt.d.ts +35 -0
- package/dist/src/cli/init-prompt.js +74 -0
- package/dist/src/cli/init-selection.d.ts +13 -0
- package/dist/src/cli/init-selection.js +75 -0
- package/dist/src/cli/main.d.ts +2 -0
- package/dist/src/cli/main.js +37 -7
- package/dist/src/cli/mcp-server.d.ts +1 -0
- package/dist/src/cli/mcp-server.js +156 -0
- package/dist/src/cli/mcp-tools.d.ts +15 -0
- package/dist/src/cli/mcp-tools.js +80 -0
- package/dist/src/cli/render.d.ts +1 -1
- package/dist/src/cli/render.js +17 -30
- package/dist/src/cli/report-ci-jobs.d.ts +23 -0
- package/dist/src/cli/report-ci-jobs.js +103 -0
- package/dist/src/cli/report-ci-usage.d.ts +9 -0
- package/dist/src/cli/report-ci-usage.js +185 -0
- package/dist/src/cli/report-ci.d.ts +13 -0
- package/dist/src/cli/report-ci.js +74 -0
- package/dist/src/cli/report-closures.d.ts +6 -0
- package/dist/src/cli/report-closures.js +97 -0
- package/dist/src/cli/report-flow.d.ts +7 -0
- package/dist/src/cli/report-flow.js +122 -0
- package/dist/src/cli/report-github-reader.d.ts +29 -0
- package/dist/src/cli/report-github-reader.js +157 -0
- package/dist/src/cli/report-github.d.ts +15 -8
- package/dist/src/cli/report-github.js +131 -150
- package/dist/src/cli/report-operator.d.ts +3 -0
- package/dist/src/cli/report-operator.js +40 -0
- package/dist/src/cli/report-ready.d.ts +3 -0
- package/dist/src/cli/report-ready.js +96 -0
- package/dist/src/cli/report-text.d.ts +9 -0
- package/dist/src/cli/report-text.js +33 -0
- package/dist/src/cli/report.d.ts +1 -0
- package/dist/src/cli/report.js +104 -27
- package/dist/src/cli/runtime-arguments.d.ts +2 -1
- package/dist/src/cli/runtime-arguments.js +7 -2
- package/dist/src/cli/schedule-arguments.d.ts +14 -0
- package/dist/src/cli/schedule-arguments.js +26 -0
- package/dist/src/cli/schedule-render.d.ts +22 -0
- package/dist/src/cli/schedule-render.js +73 -0
- package/dist/src/cli/terminal-text.d.ts +3 -0
- package/dist/src/cli/terminal-text.js +28 -0
- package/dist/src/cli/usage-render.d.ts +1 -0
- package/dist/src/cli/usage-render.js +28 -1
- package/dist/src/cli/usage.d.ts +1 -1
- package/dist/src/cli/usage.js +50 -7
- package/dist/src/cli/watch-actions.js +2 -2
- package/dist/src/cli/watch-board-view.js +19 -2
- package/dist/src/cli/watch-board.d.ts +4 -2
- package/dist/src/cli/watch-board.js +53 -43
- package/dist/src/cli/watch-card-forms.d.ts +1 -1
- package/dist/src/cli/watch-card-forms.js +11 -10
- package/dist/src/cli/watch-client.d.ts +1 -1
- package/dist/src/cli/watch-client.js +2 -11
- package/dist/src/cli/watch-header-view.d.ts +0 -5
- package/dist/src/cli/watch-header-view.js +8 -20
- package/dist/src/cli/watch-model.d.ts +4 -1
- package/dist/src/cli/watch-model.js +17 -11
- package/dist/src/cli/watch-page.js +12 -14
- package/dist/src/cli/watch-phases.d.ts +12 -3
- package/dist/src/cli/watch-phases.js +68 -11
- package/dist/src/cli/watch-pr-board.js +1 -1
- package/dist/src/cli/watch-pr-command.d.ts +1 -0
- package/dist/src/cli/watch-pull-request.js +18 -8
- package/dist/src/cli/watch-reads.d.ts +2 -0
- package/dist/src/cli/watch-reads.js +17 -16
- package/dist/src/cli/watch-render.d.ts +14 -0
- package/dist/src/cli/watch-run-input.d.ts +0 -1
- package/dist/src/cli/watch-run-input.js +0 -13
- package/dist/src/cli/watch-server.js +6 -6
- package/dist/src/cli/watch-view.d.ts +0 -3
- package/dist/src/cli/watch-view.js +13 -13
- package/dist/src/cli.js +59 -5
- package/dist/src/command-wait.d.ts +4 -0
- package/dist/src/command-wait.js +45 -0
- package/dist/src/configuration-change.d.ts +7 -0
- package/dist/src/configuration-change.js +14 -0
- package/dist/src/configuration-schema.d.ts +9 -0
- package/dist/src/configuration-schema.js +6 -2
- package/dist/src/daemon/attachment-file.d.ts +6 -0
- package/dist/src/daemon/attachment-file.js +86 -0
- package/dist/src/daemon/attachments.d.ts +14 -0
- package/dist/src/daemon/attachments.js +17 -0
- package/dist/src/daemon/claude-recovery.d.ts +50 -0
- package/dist/src/daemon/claude-recovery.js +204 -0
- package/dist/src/daemon/claude-stream.d.ts +27 -0
- package/dist/src/daemon/claude-stream.js +63 -0
- package/dist/src/daemon/claude-turn.d.ts +11 -25
- package/dist/src/daemon/claude-turn.js +64 -81
- package/dist/src/daemon/claude-worker.d.ts +2 -1
- package/dist/src/daemon/claude-worker.js +124 -122
- package/dist/src/daemon/daemon-adapters.d.ts +2 -0
- package/dist/src/daemon/daemon-adapters.js +26 -2
- package/dist/src/daemon/daemon-loops.d.ts +3 -1
- package/dist/src/daemon/daemon-loops.js +3 -1
- package/dist/src/daemon/daemon.js +14 -6
- package/dist/src/daemon/github-attach.d.ts +2 -0
- package/dist/src/daemon/github-attach.js +149 -0
- package/dist/src/daemon/github-ci-filters.d.ts +6 -0
- package/dist/src/daemon/github-ci-filters.js +138 -0
- package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
- package/dist/src/daemon/github-ci-workflows.js +148 -0
- package/dist/src/daemon/github-ci.js +22 -4
- package/dist/src/daemon/github-issue-methods.d.ts +2 -0
- package/dist/src/daemon/github-issue-methods.js +37 -2
- package/dist/src/daemon/github-merge-call.d.ts +2 -1
- package/dist/src/daemon/github-merge-call.js +10 -3
- package/dist/src/daemon/github-merge.js +52 -24
- package/dist/src/daemon/github-methods.js +12 -2
- package/dist/src/daemon/github-operation.d.ts +4 -0
- package/dist/src/daemon/github-operation.js +6 -0
- package/dist/src/daemon/github-worker.d.ts +4 -0
- package/dist/src/daemon/github-worker.js +6 -0
- package/dist/src/daemon/issue-queue.d.ts +1 -1
- package/dist/src/daemon/issue-queue.js +16 -11
- package/dist/src/daemon/notify-events.d.ts +6 -0
- package/dist/src/daemon/notify-events.js +84 -0
- package/dist/src/daemon/notify-journal.js +1 -1
- package/dist/src/daemon/notify-message.d.ts +4 -1
- package/dist/src/daemon/notify-message.js +36 -0
- package/dist/src/daemon/notify.d.ts +1 -1
- package/dist/src/daemon/notify.js +5 -77
- package/dist/src/daemon/operation-journal.d.ts +1 -0
- package/dist/src/daemon/queue-status.d.ts +9 -3
- package/dist/src/daemon/queue-status.js +17 -4
- package/dist/src/daemon/resume-index-lock.d.ts +13 -0
- package/dist/src/daemon/resume-index-lock.js +160 -0
- package/dist/src/daemon/resume-worktree.d.ts +7 -0
- package/dist/src/daemon/resume-worktree.js +29 -0
- package/dist/src/daemon/retrying-turn.d.ts +2 -12
- package/dist/src/daemon/retrying-turn.js +98 -172
- package/dist/src/daemon/schedule-control.d.ts +26 -0
- package/dist/src/daemon/schedule-control.js +110 -0
- package/dist/src/daemon/schedule-journal.d.ts +2 -15
- package/dist/src/daemon/schedule-journal.js +67 -4
- package/dist/src/daemon/ticket-registration.d.ts +2 -7
- package/dist/src/daemon/ticket-registration.js +15 -81
- package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
- package/dist/src/daemon/turn-attempt-journal.js +136 -0
- package/dist/src/daemon/turn-attempts.d.ts +47 -0
- package/dist/src/daemon/turn-attempts.js +166 -0
- package/dist/src/daemon/turn-report.d.ts +8 -1
- package/dist/src/daemon/turn-report.js +39 -0
- package/dist/src/daemon/work-tree.d.ts +9 -0
- package/dist/src/daemon/work-tree.js +20 -0
- package/dist/src/daemon/worker-profile.d.ts +8 -0
- package/dist/src/daemon/worker-profile.js +40 -0
- package/dist/src/daemon/workflow-schedules.d.ts +3 -0
- package/dist/src/daemon/workflow-schedules.js +98 -45
- package/dist/src/daemon/worktree-processes.d.ts +9 -0
- package/dist/src/daemon/worktree-processes.js +151 -0
- package/dist/src/doctor-report.d.ts +3 -5
- package/dist/src/doctor-report.js +6 -13
- package/dist/src/harness-start.d.ts +15 -0
- package/dist/src/harness-start.js +37 -0
- package/dist/src/host/command-wait.d.ts +38 -0
- package/dist/src/host/command-wait.js +115 -0
- package/dist/src/host/start-delivery.d.ts +5 -7
- package/dist/src/host/start-delivery.js +3 -15
- package/dist/src/host/step-bridge-host.d.ts +2 -0
- package/dist/src/host/step-bridge-host.js +21 -0
- package/dist/src/operation-identity.d.ts +1 -0
- package/dist/src/operation-identity.js +4 -0
- package/dist/src/private-config.schema.json +40 -0
- package/dist/src/project-config.schema.json +40 -0
- package/dist/src/project-settings.d.ts +2 -2
- package/dist/src/project-settings.js +3 -1
- package/dist/src/queue-spend.d.ts +4 -0
- package/dist/src/queue-spend.js +21 -0
- package/dist/src/relay/relay-service.d.ts +3 -0
- package/dist/src/relay/relay-service.js +5 -3
- package/dist/src/report-ci.d.ts +164 -0
- package/dist/src/report-ci.js +32 -0
- package/dist/src/report-effects.d.ts +15 -0
- package/dist/src/report-effects.js +212 -0
- package/dist/src/report-flow-types.d.ts +38 -0
- package/dist/src/report-flow-types.js +7 -0
- package/dist/src/report-flow.d.ts +43 -0
- package/dist/src/report-flow.js +193 -0
- package/dist/src/report-operator.d.ts +67 -0
- package/dist/src/report-operator.js +25 -0
- package/dist/src/report-quality.d.ts +221 -0
- package/dist/src/report-quality.js +94 -0
- package/dist/src/report-ready.d.ts +23 -0
- package/dist/src/report-ready.js +117 -0
- package/dist/src/report-statistics.d.ts +2 -1
- package/dist/src/report-statistics.js +40 -4
- package/dist/src/report.d.ts +588 -47
- package/dist/src/report.js +156 -41
- package/dist/src/schedule-progress.d.ts +33 -0
- package/dist/src/schedule-progress.js +45 -0
- package/dist/src/service/active-tickets.js +6 -6
- package/dist/src/service/command-waits.d.ts +34 -0
- package/dist/src/service/command-waits.js +86 -0
- package/dist/src/service/expiry.js +2 -0
- package/dist/src/service/gateway.js +3 -1
- package/dist/src/service/queue-spend.d.ts +6 -0
- package/dist/src/service/queue-spend.js +68 -0
- package/dist/src/service/replies.d.ts +8 -1
- package/dist/src/service/replies.js +11 -0
- package/dist/src/service/run-handlers.js +22 -2
- package/dist/src/service/run-metadata.d.ts +15 -1
- package/dist/src/service/run-metadata.js +93 -8
- package/dist/src/service/service.d.ts +4 -0
- package/dist/src/service/service.js +8 -8
- package/dist/src/service/submission-handler.js +18 -4
- package/dist/src/service/worker-port.js +9 -1
- package/dist/src/service/worker-totals.d.ts +2 -0
- package/dist/src/service/worker-totals.js +1 -1
- package/dist/src/step-bridge.d.ts +20 -0
- package/dist/src/steps/command-wait.d.ts +12 -0
- package/dist/src/steps/command-wait.js +26 -0
- package/dist/src/steps/open-decision-wait.d.ts +2 -0
- package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
- package/dist/src/steps/review-by-person.js +4 -1
- package/dist/src/steps/wait-for-command.d.ts +3 -0
- package/dist/src/steps/wait-for-command.js +17 -0
- package/dist/src/steps/wait-for-decision.js +1 -1
- package/dist/src/storage/command-wait-rows.d.ts +30 -0
- package/dist/src/storage/command-wait-rows.js +96 -0
- package/dist/src/storage/decision-snapshots.d.ts +8 -1
- package/dist/src/storage/decision-snapshots.js +15 -1
- package/dist/src/storage/domain-changes.d.ts +2 -0
- package/dist/src/storage/domain-changes.js +3 -0
- package/dist/src/storage/domain-schema.d.ts +13 -1
- package/dist/src/storage/domain-schema.js +31 -4
- package/dist/src/storage/effect-schema.d.ts +1 -0
- package/dist/src/storage/effect-schema.js +5 -0
- package/dist/src/testing-requests.d.ts +15 -0
- package/dist/src/testing-requests.js +93 -0
- package/dist/src/testing.d.ts +4 -10
- package/dist/src/testing.js +33 -86
- package/dist/src/worker-usage.d.ts +1 -0
- package/dist/src/worker-usage.js +3 -0
- package/dist/src/workflow-candidate.d.ts +1 -0
- package/dist/src/workflow-candidate.js +12 -1
- package/dist/src/workflow-contracts.d.ts +1 -0
- package/dist/src/workflow-contracts.js +9 -2
- package/dist/src/workflow-discovery.d.ts +2 -13
- package/dist/src/workflow-discovery.js +1 -13
- package/dist/src/workflow-refusals.js +0 -1
- package/dist/src/workflows.d.ts +1 -1
- package/dist/src/workflows.js +1 -1
- package/dist/tools/red-green-command.d.ts +1 -0
- package/dist/tools/red-green-command.js +174 -0
- package/dist/tools/red-green-process.d.ts +10 -0
- package/dist/tools/red-green-process.js +59 -0
- package/dist/tools/self-host-processes.d.ts +2 -0
- package/dist/tools/self-host-processes.js +32 -13
- package/dist/tools/self-host-project.js +13 -78
- package/dist/tools/self-host-runtime.js +1 -1
- package/dist/tools/self-host.js +42 -10
- package/dist/tools/template-command.d.ts +1 -0
- package/dist/tools/template-command.js +160 -0
- package/dist/tools/template-copy.d.ts +11 -0
- package/dist/tools/template-copy.js +130 -0
- package/dist/tools/template-files.d.ts +17 -0
- package/dist/tools/template-files.js +74 -0
- package/dist/tools/workflow-bundles.js +1 -0
- package/dist/tools/workflow-candidate-counts.js +2 -2
- package/dist/tools/workflow-candidate-shape.d.ts +3 -0
- package/dist/tools/workflow-candidate-shape.js +90 -0
- package/dist/tools/workflow-candidate-source.js +15 -1
- package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
- package/dist/tools/workflow-candidate-syntax.js +24 -94
- package/dist/tools/workflow-command.js +1 -2
- package/dist/tools/workflow-folders.d.ts +2 -3
- package/dist/tools/workflow-folders.js +1 -1
- package/package.json +16 -8
- package/templates/develop/README.md +12 -0
- package/templates/develop/develop/flow-tests.ts +37 -0
- package/templates/develop/develop/pull.ts +5 -1
- package/templates/develop/develop/refusal-tests.ts +77 -0
- package/templates/develop/develop/turn.ts +2 -1
- package/templates/idea-to-issue/README.md +27 -0
- package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
- package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
- package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
- package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
- package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
- package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
- package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
- package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
- package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
- package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
- package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
- package/templates/idea-to-issue/idea-to-issue.ts +49 -0
- package/templates/idea-to-issue/template.json +81 -0
- package/templates/review-day/README.md +97 -0
- package/templates/review-day/review-day/critique.ts +58 -0
- package/templates/review-day/review-day/decision.ts +29 -0
- package/templates/review-day/review-day/facts.ts +69 -0
- package/templates/review-day/review-day/label.ts +29 -0
- package/templates/review-day/review-day/next.ts +28 -0
- package/templates/review-day/review-day/post.ts +27 -0
- package/templates/review-day/review-day/report.ts +98 -0
- package/templates/review-day/review-day/rules.ts +12 -0
- package/templates/review-day/review-day/scenarios.ts +76 -0
- package/templates/review-day/review-day/turn.ts +73 -0
- package/templates/review-day/review-day.declaration.json +16 -0
- package/templates/review-day/review-day.test.ts +362 -0
- package/templates/review-day/review-day.ts +37 -0
- package/templates/review-day/template.json +56 -0
- package/dist/src/cli/watch-start-view.d.ts +0 -18
- package/dist/src/cli/watch-start-view.js +0 -77
- package/dist/src/steps/decision-gate.d.ts +0 -3
- package/dist/workflows/develop-prd.d.ts +0 -7
- package/dist/workflows/develop-prd.js +0 -165
- package/dist/workflows/develop-ticket.d.ts +0 -9
- package/dist/workflows/develop-ticket.js +0 -207
- package/dist/workflows/issue-labels.d.ts +0 -16
- package/dist/workflows/issue-labels.js +0 -70
- package/dist/workflows/ticket-prompts.d.ts +0 -35
- package/dist/workflows/ticket-prompts.js +0 -147
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { passiveCandidateModule, workflowShape, } from "./workflow-candidate-shape.js";
|
|
1
2
|
import ts from "typescript";
|
|
2
3
|
import { dirname, relative, sep } from "node:path";
|
|
3
4
|
import { candidateProgram, resolvedSymbol, } from "./workflow-candidate-program.js";
|
|
4
|
-
import { protectedEffectFact,
|
|
5
|
+
import { protectedEffectFact, commandWaitCall, unsupportedCandidateNode, } from "./workflow-candidate-syntax.js";
|
|
6
|
+
import { literalField } from "./workflow-candidate-fields.js";
|
|
5
7
|
import { sourceEffectNodes } from "./workflow-source-effects.js";
|
|
6
8
|
import { candidateEffectTotals } from "./workflow-candidate-counts.js";
|
|
7
9
|
import { helperEffectTotals } from "./workflow-candidate-helper-counts.js";
|
|
@@ -32,11 +34,14 @@ function sourceFacts(symbols) {
|
|
|
32
34
|
const { source } = symbols;
|
|
33
35
|
const imports = [];
|
|
34
36
|
const effects = [];
|
|
37
|
+
const commandWaits = [];
|
|
35
38
|
const unsupportedForms = [];
|
|
36
39
|
function visit(node) {
|
|
37
40
|
const imported = sourceImport(node);
|
|
38
41
|
if (imported)
|
|
39
42
|
imports.push(imported);
|
|
43
|
+
if (ts.isCallExpression(node) && commandWaitCall(node, symbols))
|
|
44
|
+
commandWaits.push(literalField(node.arguments[0], "name"));
|
|
40
45
|
const effect = protectedEffectFact(node, symbols);
|
|
41
46
|
if (effect)
|
|
42
47
|
effects.push(effect);
|
|
@@ -47,6 +52,7 @@ function sourceFacts(symbols) {
|
|
|
47
52
|
visit(source);
|
|
48
53
|
return {
|
|
49
54
|
imports,
|
|
55
|
+
...(commandWaits.length === 0 ? {} : { commandWaits }),
|
|
50
56
|
effects,
|
|
51
57
|
directEffects: sourceEffectNodes(source, {
|
|
52
58
|
checker: symbols.checker,
|
|
@@ -99,6 +105,11 @@ export function analyzeWorkflowCandidate(text, installed = {}) {
|
|
|
99
105
|
subpath: "cloving/workflows/steps/handoff",
|
|
100
106
|
name: "handoff",
|
|
101
107
|
}),
|
|
108
|
+
commandWaitSymbol: protectedHelper(modules, {
|
|
109
|
+
checker,
|
|
110
|
+
subpath: "cloving/workflows/steps/wait-for-command",
|
|
111
|
+
name: "waitForCommand",
|
|
112
|
+
}),
|
|
102
113
|
commandSymbol: protectedHelper(modules, {
|
|
103
114
|
checker,
|
|
104
115
|
subpath: "cloving/workflows/steps/run-command",
|
|
@@ -154,6 +165,9 @@ function combineFacts(input) {
|
|
|
154
165
|
const { localSources, facts } = input;
|
|
155
166
|
return {
|
|
156
167
|
localSources,
|
|
168
|
+
...(facts.some((entry) => entry.commandWaits !== undefined)
|
|
169
|
+
? { commandWaits: facts.flatMap((entry) => entry.commandWaits ?? []) }
|
|
170
|
+
: {}),
|
|
157
171
|
imports: facts.flatMap((entry) => entry.imports),
|
|
158
172
|
effects: facts.flatMap((entry) => entry.effects),
|
|
159
173
|
directEffects: facts.flatMap((entry) => entry.directEffects),
|
|
@@ -5,6 +5,7 @@ export interface CandidateSymbols {
|
|
|
5
5
|
readonly source: ts.SourceFile;
|
|
6
6
|
readonly protectedSymbol: ts.Symbol | undefined;
|
|
7
7
|
readonly handoffSymbol: ts.Symbol | undefined;
|
|
8
|
+
readonly commandWaitSymbol?: ts.Symbol | undefined;
|
|
8
9
|
readonly commandSymbol: ts.Symbol | undefined;
|
|
9
10
|
readonly reviewSymbol: ts.Symbol | undefined;
|
|
10
11
|
readonly mergeSymbols: readonly ts.Symbol[];
|
|
@@ -12,10 +13,9 @@ export interface CandidateSymbols {
|
|
|
12
13
|
export declare function protectedCall(node: ts.Node, symbols: CandidateSymbols): node is ts.CallExpression;
|
|
13
14
|
export declare function handoffCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
|
|
14
15
|
export declare function commandCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
|
|
16
|
+
export declare function commandWaitCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
|
|
15
17
|
export declare function reviewCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
|
|
16
18
|
export declare function helperCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
|
|
17
19
|
export declare function mergeCall(node: ts.CallExpression, symbols: CandidateSymbols): boolean;
|
|
18
20
|
export declare function protectedEffectFact(node: ts.Node, symbols: CandidateSymbols): CandidateEffect | undefined;
|
|
19
21
|
export declare function unsupportedCandidateNode(node: ts.Node, symbols: CandidateSymbols): boolean;
|
|
20
|
-
export declare function workflowShape(source: ts.SourceFile): boolean;
|
|
21
|
-
export declare function passiveCandidateModule(source: ts.SourceFile, kind?: "workflow" | "helper"): boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { resolvedSymbol } from "./workflow-candidate-program.js";
|
|
3
3
|
import { referenceSymbol } from "./workflow-source-effects.js";
|
|
4
|
-
import { invalidFields, literalField, propertyInitializer, } from "./workflow-candidate-fields.js";
|
|
4
|
+
import { invalidFields, literalField, propertyInitializer, propertyName, } from "./workflow-candidate-fields.js";
|
|
5
5
|
export function protectedCall(node, symbols) {
|
|
6
6
|
if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression))
|
|
7
7
|
return false;
|
|
@@ -11,6 +11,7 @@ export function protectedCall(node, symbols) {
|
|
|
11
11
|
symbols.protectedSymbol,
|
|
12
12
|
symbols.handoffSymbol,
|
|
13
13
|
symbols.commandSymbol,
|
|
14
|
+
symbols.commandWaitSymbol,
|
|
14
15
|
symbols.reviewSymbol,
|
|
15
16
|
...symbols.mergeSymbols,
|
|
16
17
|
].includes(symbol));
|
|
@@ -26,6 +27,10 @@ export function commandCall(node, symbols) {
|
|
|
26
27
|
const symbol = symbols.commandSymbol;
|
|
27
28
|
return symbol !== undefined && calledSymbol(node, symbols) === symbol;
|
|
28
29
|
}
|
|
30
|
+
export function commandWaitCall(node, symbols) {
|
|
31
|
+
const symbol = symbols.commandWaitSymbol;
|
|
32
|
+
return symbol !== undefined && calledSymbol(node, symbols) === symbol;
|
|
33
|
+
}
|
|
29
34
|
export function reviewCall(node, symbols) {
|
|
30
35
|
const symbol = symbols.reviewSymbol;
|
|
31
36
|
return symbol !== undefined && calledSymbol(node, symbols) === symbol;
|
|
@@ -40,7 +45,7 @@ export function mergeCall(node, symbols) {
|
|
|
40
45
|
export function protectedEffectFact(node, symbols) {
|
|
41
46
|
if (!protectedCall(node, symbols))
|
|
42
47
|
return undefined;
|
|
43
|
-
if (handoffCall(node, symbols))
|
|
48
|
+
if (handoffCall(node, symbols) || commandWaitCall(node, symbols))
|
|
44
49
|
return undefined;
|
|
45
50
|
const argument = node.arguments[0];
|
|
46
51
|
if (!argument || !ts.isObjectLiteralExpression(argument))
|
|
@@ -81,6 +86,7 @@ function escapedHelper(node, symbols) {
|
|
|
81
86
|
symbols.protectedSymbol,
|
|
82
87
|
symbols.handoffSymbol,
|
|
83
88
|
symbols.commandSymbol,
|
|
89
|
+
symbols.commandWaitSymbol,
|
|
84
90
|
symbols.reviewSymbol,
|
|
85
91
|
...symbols.mergeSymbols,
|
|
86
92
|
].includes(symbol))
|
|
@@ -115,9 +121,22 @@ function invalidRequest(node, symbols) {
|
|
|
115
121
|
? propertyInitializer(argument, "reviewer")
|
|
116
122
|
: undefined;
|
|
117
123
|
return (invalidFields(argument) ||
|
|
118
|
-
(
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
invalidCommandWaitFields(node, symbols) ||
|
|
125
|
+
invalidReviewer(reviewer));
|
|
126
|
+
}
|
|
127
|
+
function invalidReviewer(reviewer) {
|
|
128
|
+
return (reviewer !== undefined &&
|
|
129
|
+
ts.isObjectLiteralExpression(reviewer) &&
|
|
130
|
+
invalidFields(reviewer));
|
|
131
|
+
}
|
|
132
|
+
function invalidCommandWaitFields(node, symbols) {
|
|
133
|
+
if (!commandWaitCall(node, symbols))
|
|
134
|
+
return false;
|
|
135
|
+
const argument = node.arguments[0];
|
|
136
|
+
return (!!argument &&
|
|
137
|
+
ts.isObjectLiteralExpression(argument) &&
|
|
138
|
+
argument.properties.some((entry) => entry.name &&
|
|
139
|
+
!["name", "input"].includes(propertyName(entry.name) ?? "")));
|
|
121
140
|
}
|
|
122
141
|
function workflowBodyCall(node) {
|
|
123
142
|
let child = node;
|
|
@@ -172,92 +191,3 @@ export function unsupportedCandidateNode(node, symbols) {
|
|
|
172
191
|
];
|
|
173
192
|
return checks.some(Boolean);
|
|
174
193
|
}
|
|
175
|
-
export function workflowShape(source) {
|
|
176
|
-
const exported = source.statements.filter((node) => ts.isFunctionDeclaration(node) &&
|
|
177
|
-
(ts.getModifiers(node) ?? []).some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword));
|
|
178
|
-
const candidate = exported[0];
|
|
179
|
-
if (exported.length !== 1 ||
|
|
180
|
-
!candidate ||
|
|
181
|
-
!ts.isFunctionDeclaration(candidate))
|
|
182
|
-
return false;
|
|
183
|
-
if (!workflowFunction(candidate))
|
|
184
|
-
return false;
|
|
185
|
-
const directives = [];
|
|
186
|
-
function visit(node) {
|
|
187
|
-
if (ts.isStringLiteral(node) && node.text === "use workflow")
|
|
188
|
-
directives.push(node);
|
|
189
|
-
ts.forEachChild(node, visit);
|
|
190
|
-
}
|
|
191
|
-
visit(source);
|
|
192
|
-
return directives.length === 1;
|
|
193
|
-
}
|
|
194
|
-
function workflowFunction(candidate) {
|
|
195
|
-
if (!candidate.name || !candidate.body || candidate.asteriskToken)
|
|
196
|
-
return false;
|
|
197
|
-
const modifiers = ts.getModifiers(candidate) ?? [];
|
|
198
|
-
if (!modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) ||
|
|
199
|
-
modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword))
|
|
200
|
-
return false;
|
|
201
|
-
const first = candidate.body.statements[0];
|
|
202
|
-
return first !== undefined && workflowDirective(first);
|
|
203
|
-
}
|
|
204
|
-
function workflowDirective(node) {
|
|
205
|
-
return (ts.isExpressionStatement(node) &&
|
|
206
|
-
ts.isStringLiteral(node.expression) &&
|
|
207
|
-
node.expression.text === "use workflow");
|
|
208
|
-
}
|
|
209
|
-
function unwrappedInitializer(node) {
|
|
210
|
-
if (ts.isParenthesizedExpression(node) ||
|
|
211
|
-
ts.isAsExpression(node) ||
|
|
212
|
-
ts.isSatisfiesExpression(node) ||
|
|
213
|
-
ts.isTypeAssertionExpression(node))
|
|
214
|
-
return node.expression;
|
|
215
|
-
return undefined;
|
|
216
|
-
}
|
|
217
|
-
function passiveCompound(node) {
|
|
218
|
-
if (ts.isArrayLiteralExpression(node))
|
|
219
|
-
return node.elements.every((element) => ts.isOmittedExpression(element) || passiveInitializer(element));
|
|
220
|
-
if (ts.isObjectLiteralExpression(node))
|
|
221
|
-
return node.properties.every((property) => ts.isPropertyAssignment(property) &&
|
|
222
|
-
!ts.isComputedPropertyName(property.name) &&
|
|
223
|
-
passiveInitializer(property.initializer));
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
function passiveInitializer(node) {
|
|
227
|
-
if (!node)
|
|
228
|
-
return false;
|
|
229
|
-
if (ts.isLiteralExpression(node))
|
|
230
|
-
return true;
|
|
231
|
-
if ([
|
|
232
|
-
ts.SyntaxKind.TrueKeyword,
|
|
233
|
-
ts.SyntaxKind.FalseKeyword,
|
|
234
|
-
ts.SyntaxKind.NullKeyword,
|
|
235
|
-
].includes(node.kind))
|
|
236
|
-
return true;
|
|
237
|
-
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
238
|
-
return true;
|
|
239
|
-
if (ts.isPrefixUnaryExpression(node))
|
|
240
|
-
return ([ts.SyntaxKind.PlusToken, ts.SyntaxKind.MinusToken].includes(node.operator) && ts.isNumericLiteral(node.operand));
|
|
241
|
-
const inner = unwrappedInitializer(node);
|
|
242
|
-
return inner ? passiveInitializer(inner) : passiveCompound(node);
|
|
243
|
-
}
|
|
244
|
-
function passiveStatement(node, kind) {
|
|
245
|
-
if (ts.isVariableStatement(node))
|
|
246
|
-
return ((node.declarationList.flags & ts.NodeFlags.Const) !== 0 &&
|
|
247
|
-
node.declarationList.declarations.every((declaration) => (kind === "helper" ||
|
|
248
|
-
(ts.getCombinedModifierFlags(declaration) &
|
|
249
|
-
ts.ModifierFlags.Export) ===
|
|
250
|
-
0) &&
|
|
251
|
-
ts.isIdentifier(declaration.name) &&
|
|
252
|
-
passiveInitializer(declaration.initializer)));
|
|
253
|
-
if (ts.isExpressionStatement(node))
|
|
254
|
-
return ts.isStringLiteral(node.expression);
|
|
255
|
-
return (ts.isImportDeclaration(node) ||
|
|
256
|
-
ts.isTypeAliasDeclaration(node) ||
|
|
257
|
-
ts.isInterfaceDeclaration(node) ||
|
|
258
|
-
ts.isFunctionDeclaration(node) ||
|
|
259
|
-
ts.isEmptyStatement(node));
|
|
260
|
-
}
|
|
261
|
-
export function passiveCandidateModule(source, kind = "workflow") {
|
|
262
|
-
return source.statements.every((node) => passiveStatement(node, kind));
|
|
263
|
-
}
|
|
@@ -80,8 +80,7 @@ function presentWorkflows(workflows, options) {
|
|
|
80
80
|
}
|
|
81
81
|
async function testWorkflows(workflows, options) {
|
|
82
82
|
const { id, repository } = options;
|
|
83
|
-
const selected = workflows.filter((workflow) => workflow.
|
|
84
|
-
(id === undefined || workflow.workflowId === id));
|
|
83
|
+
const selected = workflows.filter((workflow) => id === undefined || workflow.workflowId === id);
|
|
85
84
|
if (id !== undefined &&
|
|
86
85
|
!workflows.some((workflow) => workflow.workflowId === id)) {
|
|
87
86
|
process.stderr.write("unknown-workflow: " + id + "\n");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WorkflowDeclaration } from "../src/workflow-declaration.ts";
|
|
2
2
|
import { type CandidateDeclaration, type WorkflowCandidateCode } from "../src/workflow-candidate.ts";
|
|
3
|
-
import { type WorkflowConflictCode
|
|
3
|
+
import { type WorkflowConflictCode } from "../src/workflow-discovery.ts";
|
|
4
4
|
import type { CandidatePackageOptions } from "./workflow-candidate-program.ts";
|
|
5
5
|
import { type WorkflowCaptureCode } from "./workflow-folder-capture.ts";
|
|
6
6
|
export interface WorkflowFolderInput {
|
|
@@ -8,7 +8,6 @@ export interface WorkflowFolderInput {
|
|
|
8
8
|
readonly home: string;
|
|
9
9
|
readonly xdgConfigHome?: string | undefined;
|
|
10
10
|
readonly userFolder?: string | undefined;
|
|
11
|
-
readonly bundled: readonly WorkflowReservation[];
|
|
12
11
|
}
|
|
13
12
|
interface WorkflowLabel {
|
|
14
13
|
readonly scope: "project" | "user";
|
|
@@ -28,7 +27,7 @@ export interface WorkflowFolderRefusal extends WorkflowLabel {
|
|
|
28
27
|
readonly codes: readonly (WorkflowCandidateCode | WorkflowCaptureCode | WorkflowConflictCode | "compile-failed")[];
|
|
29
28
|
}
|
|
30
29
|
export interface WorkflowListing {
|
|
31
|
-
readonly scope: "
|
|
30
|
+
readonly scope: "project" | "user";
|
|
32
31
|
readonly workflowId: string | null;
|
|
33
32
|
readonly file: string;
|
|
34
33
|
readonly admitted: boolean;
|
|
@@ -162,7 +162,7 @@ export async function loadWorkflowFolders(input, installed = {}) {
|
|
|
162
162
|
const candidates = [];
|
|
163
163
|
const refusals = [];
|
|
164
164
|
const { repository, snapshots } = await scanFolders(input, refusals);
|
|
165
|
-
const conflicts = workflowConflicts(snapshots.map(identity)
|
|
165
|
+
const conflicts = workflowConflicts(snapshots.map(identity));
|
|
166
166
|
for (const [index, entry] of snapshots.entries()) {
|
|
167
167
|
const label = entry.kind === "captured" ? entry.snapshot : entry;
|
|
168
168
|
const codes = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloving",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Local durable development workflow service",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,10 +31,6 @@
|
|
|
31
31
|
"types": "./dist/src/steps/protected-effect.d.ts",
|
|
32
32
|
"import": "./dist/src/steps/protected-effect.js"
|
|
33
33
|
},
|
|
34
|
-
"./workflows/steps/decision-gate": {
|
|
35
|
-
"types": "./dist/src/steps/decision-gate.d.ts",
|
|
36
|
-
"import": "./dist/src/steps/decision-gate.js"
|
|
37
|
-
},
|
|
38
34
|
"./workflows/steps/review-subject": {
|
|
39
35
|
"types": "./dist/src/steps/review-subject.d.ts",
|
|
40
36
|
"import": "./dist/src/steps/review-subject.js"
|
|
@@ -55,6 +51,10 @@
|
|
|
55
51
|
"types": "./dist/src/steps/merge-when-green.d.ts",
|
|
56
52
|
"import": "./dist/src/steps/merge-when-green.js"
|
|
57
53
|
},
|
|
54
|
+
"./workflows/steps/open-decision-wait": {
|
|
55
|
+
"types": "./dist/src/steps/open-decision-wait.d.ts",
|
|
56
|
+
"import": "./dist/src/steps/open-decision-wait.js"
|
|
57
|
+
},
|
|
58
58
|
"./workflows/steps/wait-for-decision": {
|
|
59
59
|
"types": "./dist/src/steps/wait-for-decision.d.ts",
|
|
60
60
|
"import": "./dist/src/steps/wait-for-decision.js"
|
|
@@ -78,12 +78,19 @@
|
|
|
78
78
|
"./workflows/steps/sleep": {
|
|
79
79
|
"types": "./dist/src/steps/sleep.d.ts",
|
|
80
80
|
"import": "./dist/src/steps/sleep.js"
|
|
81
|
+
},
|
|
82
|
+
"./workflows/steps/wait-for-command": {
|
|
83
|
+
"types": "./dist/src/steps/wait-for-command.d.ts",
|
|
84
|
+
"import": "./dist/src/steps/wait-for-command.js"
|
|
85
|
+
},
|
|
86
|
+
"./workflows/steps/command-wait": {
|
|
87
|
+
"types": "./dist/src/steps/command-wait.d.ts",
|
|
88
|
+
"import": "./dist/src/steps/command-wait.js"
|
|
81
89
|
}
|
|
82
90
|
},
|
|
83
91
|
"files": [
|
|
84
92
|
"dist/src",
|
|
85
93
|
"dist/tools",
|
|
86
|
-
"dist/workflows",
|
|
87
94
|
"templates",
|
|
88
95
|
"assets/watch-fonts/fira-code-latin-400-normal.woff2",
|
|
89
96
|
"assets/watch-fonts/fira-code-latin-500-normal.woff2",
|
|
@@ -112,7 +119,7 @@
|
|
|
112
119
|
"x64"
|
|
113
120
|
],
|
|
114
121
|
"scripts": {
|
|
115
|
-
"build": "tsc --project tsconfig.build.json
|
|
122
|
+
"build": "tsc --project tsconfig.build.json",
|
|
116
123
|
"build:workflows": "node tools/compile-workflows.ts",
|
|
117
124
|
"check:setup": "node tools/check-shells.ts && tsc --project tsconfig.json --noEmit",
|
|
118
125
|
"check:quality": "node tools/check-quality.ts",
|
|
@@ -150,6 +157,7 @@
|
|
|
150
157
|
"preact": "11.0.0",
|
|
151
158
|
"solid-objects": "1.0.2",
|
|
152
159
|
"typescript": "6.0.3",
|
|
153
|
-
"workflow": "5.1.0"
|
|
160
|
+
"workflow": "5.1.0",
|
|
161
|
+
"yaml": "2.9.1"
|
|
154
162
|
}
|
|
155
163
|
}
|
|
@@ -14,6 +14,18 @@ review or repairable merge result gives its full output and remaining work to
|
|
|
14
14
|
the next worker turn. Review, local-check, conflict and CI fixes share a limit
|
|
15
15
|
of three fix rounds. Pending CI and an unconfigured check stop the run.
|
|
16
16
|
|
|
17
|
+
`develop/pull.ts` keeps the first line of the ticket as the pull request title,
|
|
18
|
+
removes leading `#` characters and limits it to 200 characters. For a task with
|
|
19
|
+
a GitHub issue, the pull request body links that issue and names its number:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
Refs cloving-ai/cloving-ai#375
|
|
23
|
+
|
|
24
|
+
Cloving made this change for the ticket 375.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A task without a GitHub issue uses only `Cloving made this change for this task.`
|
|
28
|
+
|
|
17
29
|
`develop.test.ts` and `develop/flow-tests.ts` test the default composition through the public fake
|
|
18
30
|
step bridge. The alternative configurations use `develop/option-tests.ts`, with the selected
|
|
19
31
|
point IDs in `develop/scenarios.ts`. For an edited copy, replace the default
|
|
@@ -116,6 +116,10 @@ flowTests.push({
|
|
|
116
116
|
(request) => request[0] === "effect" && request[1].method === "open-pr",
|
|
117
117
|
);
|
|
118
118
|
assert.ok(opened && opened[0] === "effect");
|
|
119
|
+
assert.deepEqual(JSON.parse(opened[1].payload), {
|
|
120
|
+
title: "Fix the counter",
|
|
121
|
+
body: "Refs acme/demo#42\n\nCloving made this change for the ticket 42.",
|
|
122
|
+
});
|
|
119
123
|
assert.doesNotMatch(opened[1].payload, /private-check-output/);
|
|
120
124
|
},
|
|
121
125
|
});
|
|
@@ -355,3 +359,36 @@ flowTests.push({
|
|
|
355
359
|
});
|
|
356
360
|
},
|
|
357
361
|
});
|
|
362
|
+
|
|
363
|
+
flowTests.push({
|
|
364
|
+
name: "the develop turns keep fail-closed checks and operator steps out of the remaining list",
|
|
365
|
+
run: async () => {
|
|
366
|
+
const scenario = effects();
|
|
367
|
+
for (const key of [
|
|
368
|
+
"claude run-turn",
|
|
369
|
+
"github open-pr",
|
|
370
|
+
"github wait-review",
|
|
371
|
+
] as const)
|
|
372
|
+
scenario[key] = [...scenario[key], ...scenario[key]];
|
|
373
|
+
scenario["github merge"] = [
|
|
374
|
+
completed({ reason: "check-failed", output: "Check output.\n" }),
|
|
375
|
+
...scenario["github merge"],
|
|
376
|
+
];
|
|
377
|
+
using bridge = fakeStepBridge({ effects: scenario });
|
|
378
|
+
await develop(input);
|
|
379
|
+
const payloads = bridge.requests.flatMap((request) =>
|
|
380
|
+
request[0] === "effect" && request[1].method === "run-turn"
|
|
381
|
+
? [request[1].payload]
|
|
382
|
+
: [],
|
|
383
|
+
);
|
|
384
|
+
for (const sentence of [
|
|
385
|
+
"List as remaining only work that the task asks for.",
|
|
386
|
+
"A check that the repository keeps fail-closed on purpose is not remaining work.",
|
|
387
|
+
"A step that the task gives to the operator is not remaining work.",
|
|
388
|
+
])
|
|
389
|
+
assert.ok(payloads[0]?.includes(sentence), sentence);
|
|
390
|
+
assert.ok(
|
|
391
|
+
payloads[1]?.includes("Drop an item that the task does not ask for"),
|
|
392
|
+
);
|
|
393
|
+
},
|
|
394
|
+
});
|
|
@@ -21,6 +21,7 @@ export async function openPull(request: {
|
|
|
21
21
|
readonly round: number;
|
|
22
22
|
}): Promise<PullRequest> {
|
|
23
23
|
const { input, round } = request;
|
|
24
|
+
const issue = input.issue;
|
|
24
25
|
const result = await protectedEffect({
|
|
25
26
|
scope: { phase: "pull-request", operation: "github-open-pr", round: round },
|
|
26
27
|
capability: "github",
|
|
@@ -30,7 +31,10 @@ export async function openPull(request: {
|
|
|
30
31
|
title: (input.ticket.split("\n")[0] ?? "Develop task")
|
|
31
32
|
.replace(/^#+\s*/, "")
|
|
32
33
|
.slice(0, 200),
|
|
33
|
-
body:
|
|
34
|
+
body:
|
|
35
|
+
issue === null
|
|
36
|
+
? "Cloving made this change for this task."
|
|
37
|
+
: `Refs ${issue.owner}/${issue.repository}#${String(issue.number)}\n\nCloving made this change for the ticket ${String(issue.number)}.`,
|
|
34
38
|
}),
|
|
35
39
|
units: 1,
|
|
36
40
|
});
|
|
@@ -9,6 +9,83 @@ import { effects } from "./scenarios.ts";
|
|
|
9
9
|
|
|
10
10
|
export const refusalTests: FlowTest[] = [];
|
|
11
11
|
|
|
12
|
+
refusalTests.push({
|
|
13
|
+
name: "CLV-260 a linked pull request names the issue and preserves the ticket title",
|
|
14
|
+
run: async () => {
|
|
15
|
+
using bridge = fakeStepBridge({
|
|
16
|
+
turn: completed({ number: 7, url: "pull/7", commit }),
|
|
17
|
+
});
|
|
18
|
+
await openPull({
|
|
19
|
+
input: {
|
|
20
|
+
cycleId: input.cycleId,
|
|
21
|
+
repository: input.repository,
|
|
22
|
+
ticket:
|
|
23
|
+
"# CLV-228: Stop the registration of the bundled workflows\n\nGitHub issue: cloving-ai/cloving-ai#375 (open)",
|
|
24
|
+
issue: { owner: "cloving-ai", repository: "cloving-ai", number: 375 },
|
|
25
|
+
},
|
|
26
|
+
round: 1,
|
|
27
|
+
});
|
|
28
|
+
assert.equal(bridge.requests.length, 1);
|
|
29
|
+
const opened = bridge.requests[0];
|
|
30
|
+
assert.ok(opened && opened[0] === "effect");
|
|
31
|
+
assert.equal(opened[1].method, "open-pr");
|
|
32
|
+
assert.deepEqual(JSON.parse(opened[1].payload), {
|
|
33
|
+
title: "CLV-228: Stop the registration of the bundled workflows",
|
|
34
|
+
body: "Refs cloving-ai/cloving-ai#375\n\nCloving made this change for the ticket 375.",
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
refusalTests.push({
|
|
40
|
+
name: "CLV-260 a task without an issue opens a pull request without a reference",
|
|
41
|
+
run: async () => {
|
|
42
|
+
using bridge = fakeStepBridge({
|
|
43
|
+
turn: completed({ number: 7, url: "pull/7", commit }),
|
|
44
|
+
});
|
|
45
|
+
await openPull({
|
|
46
|
+
input: {
|
|
47
|
+
cycleId: input.cycleId,
|
|
48
|
+
repository: input.repository,
|
|
49
|
+
ticket: "### Manual task\n\nGitHub issue: acme/demo#42 (open)",
|
|
50
|
+
issue: null,
|
|
51
|
+
},
|
|
52
|
+
round: 1,
|
|
53
|
+
});
|
|
54
|
+
assert.equal(bridge.requests.length, 1);
|
|
55
|
+
const opened = bridge.requests[0];
|
|
56
|
+
assert.ok(opened && opened[0] === "effect");
|
|
57
|
+
assert.equal(opened[1].method, "open-pr");
|
|
58
|
+
assert.deepEqual(JSON.parse(opened[1].payload), {
|
|
59
|
+
title: "Manual task",
|
|
60
|
+
body: "Cloving made this change for this task.",
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
refusalTests.push({
|
|
66
|
+
name: "CLV-260 pull request titles use at most 200 characters from the first line",
|
|
67
|
+
run: async () => {
|
|
68
|
+
using bridge = fakeStepBridge({
|
|
69
|
+
turn: completed({ number: 7, url: "pull/7", commit }),
|
|
70
|
+
});
|
|
71
|
+
await openPull({
|
|
72
|
+
input: {
|
|
73
|
+
cycleId: input.cycleId,
|
|
74
|
+
repository: input.repository,
|
|
75
|
+
ticket: `### ${"Title".repeat(50)}\nSecond line`,
|
|
76
|
+
issue: null,
|
|
77
|
+
},
|
|
78
|
+
round: 1,
|
|
79
|
+
});
|
|
80
|
+
const opened = bridge.requests[0];
|
|
81
|
+
assert.ok(opened && opened[0] === "effect");
|
|
82
|
+
assert.deepEqual(JSON.parse(opened[1].payload), {
|
|
83
|
+
title: "Title".repeat(40),
|
|
84
|
+
body: "Cloving made this change for this task.",
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
12
89
|
for (const operation of [
|
|
13
90
|
"claude run-turn",
|
|
14
91
|
"github open-pr",
|
|
@@ -32,6 +32,7 @@ function turnPrompt(input: DevelopTurnInput): string {
|
|
|
32
32
|
"Do not push, publish, deploy, or switch branches.",
|
|
33
33
|
"The branch will become a public pull request. Keep local paths out of tracked files and commit messages.",
|
|
34
34
|
"Report the changes and check results. Finish with Ticket status: complete or Ticket status: partial. For partial work, precede the status with Remaining: and one unfinished item per '- ' line.",
|
|
35
|
+
"List as remaining only work that the task asks for. A check that the repository keeps fail-closed on purpose is not remaining work. A step that the task gives to the operator is not remaining work.",
|
|
35
36
|
"",
|
|
36
37
|
"Task:",
|
|
37
38
|
task,
|
|
@@ -41,7 +42,7 @@ function turnPrompt(input: DevelopTurnInput): string {
|
|
|
41
42
|
prompt,
|
|
42
43
|
"",
|
|
43
44
|
`This is fix round ${String(input.round)}. Address ${input.feedback.reason}, then commit the fix.`,
|
|
44
|
-
"Reassess every unfinished item and retain unresolved items in the next Remaining: list:",
|
|
45
|
+
"Reassess every unfinished item. Drop an item that the task does not ask for, and retain the unresolved items in the next Remaining: list:",
|
|
45
46
|
input.feedback.remaining.map((item) => `- ${item}`).join("\n"),
|
|
46
47
|
"",
|
|
47
48
|
"Output from the previous result:",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Idea to issue
|
|
2
|
+
|
|
3
|
+
Copy this template with `cloving templates copy idea-to-issue`. Answer its three
|
|
4
|
+
questions using `cloving templates show idea-to-issue --json`, apply the indicated
|
|
5
|
+
imports and declaration edits, and run `cloving workflows test idea-to-issue`.
|
|
6
|
+
The copied workflow accepts an `idea` string.
|
|
7
|
+
|
|
8
|
+
The default workflow researches the idea, runs a turn to correct its draft, posts
|
|
9
|
+
after the existing privacy check, and hands the issue to the repository workflow
|
|
10
|
+
named `develop`. Copy the develop template or set `targetWorkflow` in
|
|
11
|
+
`idea-to-issue/next.ts` and `workflow.handoffs` in the declaration to your declared
|
|
12
|
+
development workflow. Its input must accept a `ticket` string.
|
|
13
|
+
|
|
14
|
+
To read the draft yourself, select `review.person`. To approve publication, select
|
|
15
|
+
`post.person`. Each choice captures the draft through `github capture-draft`
|
|
16
|
+
before opening its approval gate. The gates show the captured text. The issue post
|
|
17
|
+
checks every captured digest against the text it posts. A changed draft, denial,
|
|
18
|
+
or expired gate stops the run and leaves the work tree and draft available.
|
|
19
|
+
|
|
20
|
+
To queue the issue, select `next.queue`; this adds `cloving: ready`. To leave it
|
|
21
|
+
alone, select `next.nothing`. Both declarations have `handoffs: []`, and neither
|
|
22
|
+
needs a develop workflow. A failed handoff returns the issue to the queue.
|
|
23
|
+
|
|
24
|
+
Every post requires `privateTexts` containing at least one nonblank item. Human
|
|
25
|
+
approval does not bypass the privacy check. Run `cloving doctor` for the `needs`
|
|
26
|
+
codes in the selected options. Update `selection` in `idea-to-issue/scenarios.ts`
|
|
27
|
+
to match the choices so the copied tests verify their expected behavior.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { CycleOutcome, ProtectedResult } from "cloving/workflows";
|
|
2
|
+
|
|
3
|
+
export interface IdeaInput {
|
|
4
|
+
readonly cycleId: string;
|
|
5
|
+
readonly input: { readonly idea: string };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const draft = "cloving-prd-draft.md";
|
|
9
|
+
|
|
10
|
+
export function failureReason(failure: unknown, fallback: string): string {
|
|
11
|
+
if (typeof failure !== "object" || failure === null) return fallback;
|
|
12
|
+
const cause = "cause" in failure ? failure.cause : failure;
|
|
13
|
+
if (
|
|
14
|
+
typeof cause !== "object" ||
|
|
15
|
+
cause === null ||
|
|
16
|
+
!("message" in cause) ||
|
|
17
|
+
typeof cause.message !== "string"
|
|
18
|
+
)
|
|
19
|
+
return fallback;
|
|
20
|
+
return /^[a-z][a-z0-9-]{0,127}$/.test(cause.message)
|
|
21
|
+
? cause.message
|
|
22
|
+
: fallback;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function effectResult(result: ProtectedResult): string | null {
|
|
26
|
+
if (result.kind !== "completed")
|
|
27
|
+
throw new Error(result.reason ?? result.kind);
|
|
28
|
+
return result.result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resultFields(result: string | null): Record<string, unknown> {
|
|
32
|
+
let value: unknown;
|
|
33
|
+
try {
|
|
34
|
+
value = JSON.parse(result ?? "null");
|
|
35
|
+
} catch {
|
|
36
|
+
throw new Error("invalid-issue-result");
|
|
37
|
+
}
|
|
38
|
+
if (!resultObject(value)) throw new Error("invalid-issue-result");
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function resultObject(value: unknown): value is Record<string, unknown> {
|
|
43
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function issueOutcome(input: IdeaInput, number: number): CycleOutcome {
|
|
47
|
+
return {
|
|
48
|
+
kind: "handoff_requested",
|
|
49
|
+
cycleId: input.cycleId,
|
|
50
|
+
promotionId: `issue-${String(number)}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CycleOutcome } from "cloving/workflows";
|
|
2
|
+
import { issueOutcome, type IdeaInput } from "./effects.ts";
|
|
3
|
+
import type { PostedIssue } from "./post.ts";
|
|
4
|
+
|
|
5
|
+
export async function leaveIssue(
|
|
6
|
+
input: IdeaInput,
|
|
7
|
+
issue: PostedIssue,
|
|
8
|
+
): Promise<CycleOutcome> {
|
|
9
|
+
return Promise.resolve(issueOutcome(input, issue.number));
|
|
10
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CycleOutcome } from "cloving/workflows";
|
|
2
|
+
import { handoff } from "cloving/workflows/steps/handoff";
|
|
3
|
+
import { issueOutcome, failureReason, type IdeaInput } from "./effects.ts";
|
|
4
|
+
import type { PostedIssue } from "./post.ts";
|
|
5
|
+
import { queueIssue } from "./queue.ts";
|
|
6
|
+
|
|
7
|
+
export const targetWorkflow = "develop";
|
|
8
|
+
|
|
9
|
+
export async function handoffIssue(
|
|
10
|
+
input: IdeaInput,
|
|
11
|
+
issue: PostedIssue,
|
|
12
|
+
): Promise<CycleOutcome> {
|
|
13
|
+
const outcome = issueOutcome(input, issue.number);
|
|
14
|
+
try {
|
|
15
|
+
await handoff({
|
|
16
|
+
workflowId: targetWorkflow,
|
|
17
|
+
input: { ticket: String(issue.number) },
|
|
18
|
+
});
|
|
19
|
+
return outcome;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
const reason = failureReason(error, "handoff-failed");
|
|
22
|
+
try {
|
|
23
|
+
await queueIssue(input, issue);
|
|
24
|
+
return Object.assign(outcome, { reason });
|
|
25
|
+
} catch {
|
|
26
|
+
return Object.assign(outcome, { reason, issueUpdate: "failed" as const });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|