cloving 1.1.0 → 1.3.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 +584 -144
- package/assets/watch-fonts/fira-code-OFL.md +93 -0
- package/assets/watch-fonts/fira-code-latin-400-normal.woff2 +0 -0
- package/assets/watch-fonts/fira-code-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-OFL.md +93 -0
- package/assets/watch-fonts/heebo-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-latin-700-normal.woff2 +0 -0
- package/assets/watch-fonts/heebo-latin-800-normal.woff2 +0 -0
- package/assets/watch-fonts/manifest.json +112 -0
- package/assets/watch-fonts/space-grotesk-OFL.md +93 -0
- package/assets/watch-fonts/space-grotesk-latin-400-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-500-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-600-normal.woff2 +0 -0
- package/assets/watch-fonts/space-grotesk-latin-700-normal.woff2 +0 -0
- package/dist/src/agent-harness-shell.d.ts +12 -0
- package/dist/src/agent-harness-shell.js +90 -0
- package/dist/src/agent-harness-versions.d.ts +8 -0
- package/dist/src/agent-harness-versions.js +49 -0
- package/dist/src/agent-harnesses.d.ts +4 -0
- package/dist/src/agent-harnesses.js +13 -0
- package/dist/src/authority/commands.d.ts +2 -0
- package/dist/src/authority/commands.js +49 -8
- package/dist/src/authority/dispatch-decision.d.ts +1 -0
- package/dist/src/authority/dispatch-decision.js +7 -4
- package/dist/src/authority/events.js +2 -2
- package/dist/src/authority/gate-reduction.d.ts +1 -0
- package/dist/src/authority/gate-reduction.js +18 -3
- package/dist/src/authority/gates.d.ts +2 -1
- package/dist/src/authority/gates.js +5 -1
- package/dist/src/authority/handoff-chain.d.ts +24 -0
- package/dist/src/authority/handoff-chain.js +46 -0
- package/dist/src/authority/operations.js +1 -1
- package/dist/src/authority/reconciliation.d.ts +1 -0
- package/dist/src/authority/reconciliation.js +18 -11
- package/dist/src/authority/reduction.d.ts +1 -1
- package/dist/src/authority/reduction.js +6 -1
- package/dist/src/authority/status-input.d.ts +1 -1
- package/dist/src/authority/status.d.ts +1 -0
- package/dist/src/authority/status.js +27 -3
- package/dist/src/authority/transitions.d.ts +5 -5
- package/dist/src/authority/transitions.js +9 -0
- package/dist/src/broker/actor/authority-delivery.js +1 -0
- package/dist/src/broker/actor/authority-queries.d.ts +11 -1
- package/dist/src/broker/actor/authority-queries.js +67 -3
- package/dist/src/broker/actor/authority-receipts.js +11 -1
- package/dist/src/broker/actor/authority-session.js +2 -2
- package/dist/src/broker/actor/authority-settings.js +4 -0
- package/dist/src/broker/actor/delivery-intents.d.ts +3 -1
- package/dist/src/broker/actor/delivery-intents.js +4 -4
- package/dist/src/broker/actor/effect-delivery.js +13 -1
- package/dist/src/broker/actor/effect-queries.d.ts +1 -0
- package/dist/src/broker/actor/effect-queries.js +7 -0
- package/dist/src/broker/actor/pin-check.js +1 -1
- package/dist/src/broker/actor/stop-plan.js +1 -1
- package/dist/src/broker/actor/turn-state.js +1 -1
- package/dist/src/broker/authority-admission.js +26 -5
- package/dist/src/broker/authority-restore.js +1 -0
- package/dist/src/broker/decision-restore.js +4 -4
- package/dist/src/broker/socket-address.d.ts +3 -1
- package/dist/src/broker/socket-address.js +2 -2
- package/dist/src/cli/arguments.d.ts +11 -13
- package/dist/src/cli/arguments.js +37 -107
- package/dist/src/cli/command-effects.d.ts +7 -0
- package/dist/src/cli/command-effects.js +39 -0
- package/dist/src/cli/commands.js +11 -4
- package/dist/src/cli/config-arguments.d.ts +11 -0
- package/dist/src/cli/config-arguments.js +59 -0
- package/dist/src/cli/config-command.d.ts +2 -0
- package/dist/src/cli/config-command.js +105 -0
- package/dist/src/cli/config-file.d.ts +3 -0
- package/dist/src/cli/config-file.js +50 -0
- package/dist/src/cli/config-lock.d.ts +7 -0
- package/dist/src/cli/config-lock.js +61 -0
- package/dist/src/cli/config-notice.d.ts +5 -0
- package/dist/src/cli/config-notice.js +46 -0
- package/dist/src/cli/doctor-github.d.ts +9 -0
- package/dist/src/cli/doctor-github.js +138 -0
- package/dist/src/cli/doctor-harness.d.ts +13 -0
- package/dist/src/cli/doctor-harness.js +242 -0
- package/dist/src/cli/doctor-local.d.ts +18 -0
- package/dist/src/cli/doctor-local.js +77 -0
- package/dist/src/cli/doctor-runtime.d.ts +15 -0
- package/dist/src/cli/doctor-runtime.js +176 -0
- package/dist/src/cli/doctor-service.d.ts +1 -0
- package/dist/src/cli/doctor-service.js +11 -0
- package/dist/src/cli/doctor-workflows.d.ts +3 -0
- package/dist/src/cli/doctor-workflows.js +45 -0
- package/dist/src/cli/doctor.d.ts +15 -0
- package/dist/src/cli/doctor.js +190 -0
- package/dist/src/cli/github-ci-progress.d.ts +8 -0
- package/dist/src/cli/github-ci-progress.js +83 -0
- package/dist/src/cli/github-retry.d.ts +6 -0
- package/dist/src/cli/github-retry.js +47 -0
- package/dist/src/cli/harness-probes.d.ts +22 -0
- package/dist/src/cli/harness-probes.js +183 -0
- package/dist/src/cli/main.d.ts +7 -1
- package/dist/src/cli/main.js +39 -61
- package/dist/src/cli/phone-arguments.d.ts +14 -0
- package/dist/src/cli/phone-arguments.js +37 -0
- package/dist/src/cli/phone-client.d.ts +2 -0
- package/dist/src/cli/phone-client.js +32 -0
- package/dist/src/cli/phone-command.d.ts +13 -0
- package/dist/src/cli/phone-command.js +101 -0
- package/dist/src/cli/probe-command.d.ts +36 -0
- package/dist/src/cli/probe-command.js +59 -0
- package/dist/src/cli/project-settings.d.ts +4 -0
- package/dist/src/cli/project-settings.js +33 -0
- package/dist/src/cli/render.d.ts +3 -1
- package/dist/src/cli/render.js +102 -39
- package/dist/src/cli/report-github.d.ts +18 -0
- package/dist/src/cli/report-github.js +200 -0
- package/dist/src/cli/report.d.ts +17 -0
- package/dist/src/cli/report.js +192 -0
- package/dist/src/cli/repository-arguments.d.ts +5 -0
- package/dist/src/cli/repository-arguments.js +34 -0
- package/dist/src/cli/runtime-arguments.d.ts +4 -0
- package/dist/src/cli/runtime-arguments.js +15 -0
- package/dist/src/cli/runtime-directory.d.ts +6 -2
- package/dist/src/cli/runtime-directory.js +72 -14
- package/dist/src/cli/toolchain.d.ts +2 -0
- package/dist/src/cli/toolchain.js +22 -2
- package/dist/src/cli/usage-render.d.ts +5 -0
- package/dist/src/cli/usage-render.js +40 -1
- package/dist/src/cli/usage.d.ts +3 -3
- package/dist/src/cli/usage.js +74 -45
- package/dist/src/cli/watch-actions.d.ts +2 -0
- package/dist/src/cli/watch-actions.js +4 -4
- package/dist/src/cli/watch-board-view.d.ts +12 -1
- package/dist/src/cli/watch-board-view.js +64 -12
- package/dist/src/cli/watch-board.d.ts +7 -1
- package/dist/src/cli/watch-board.js +61 -16
- package/dist/src/cli/watch-card-forms.js +2 -2
- package/dist/src/cli/watch-client.d.ts +1 -1
- package/dist/src/cli/watch-client.js +12 -3
- package/dist/src/cli/watch-header-view.d.ts +27 -0
- package/dist/src/cli/watch-header-view.js +116 -0
- package/dist/src/cli/watch-model.d.ts +10 -4
- package/dist/src/cli/watch-model.js +49 -59
- package/dist/src/cli/watch-page.js +67 -14
- package/dist/src/cli/watch-phases.js +3 -3
- package/dist/src/cli/watch-phone-client.d.ts +1 -0
- package/dist/src/cli/watch-phone-client.js +84 -0
- package/dist/src/cli/watch-phone-view.d.ts +56 -0
- package/dist/src/cli/watch-phone-view.js +113 -0
- package/dist/src/cli/watch-phone.d.ts +5 -0
- package/dist/src/cli/watch-phone.js +53 -0
- package/dist/src/cli/watch-pr-command.d.ts +5 -2
- package/dist/src/cli/watch-pr-command.js +41 -4
- package/dist/src/cli/watch-pr-identity.d.ts +14 -2
- package/dist/src/cli/watch-pr-identity.js +25 -0
- package/dist/src/cli/watch-pr-observer.d.ts +1 -0
- package/dist/src/cli/watch-pr-observer.js +48 -6
- package/dist/src/cli/watch-pull-request.d.ts +12 -0
- package/dist/src/cli/watch-pull-request.js +134 -0
- package/dist/src/cli/watch-reads.d.ts +7 -1
- package/dist/src/cli/watch-reads.js +42 -20
- package/dist/src/cli/watch-render.d.ts +23 -2
- package/dist/src/cli/watch-render.js +2 -1
- package/dist/src/cli/watch-server.js +56 -7
- package/dist/src/cli/watch-turn.d.ts +4 -1
- package/dist/src/cli/watch-turn.js +38 -2
- package/dist/src/cli/watch-usage.js +5 -1
- package/dist/src/cli/watch-view.d.ts +10 -17
- package/dist/src/cli/watch-view.js +33 -77
- package/dist/src/cli.js +106 -23
- package/dist/src/command-settings.d.ts +16 -0
- package/dist/src/command-settings.js +42 -0
- package/dist/src/configuration-change.d.ts +11 -0
- package/dist/src/configuration-change.js +62 -0
- package/dist/src/configuration-display.d.ts +21 -0
- package/dist/src/configuration-display.js +69 -0
- package/dist/src/configuration-schema.d.ts +49 -0
- package/dist/src/configuration-schema.js +159 -0
- package/dist/src/daemon/claude-turn.d.ts +20 -5
- package/dist/src/daemon/claude-turn.js +116 -53
- package/dist/src/daemon/claude-worker.d.ts +3 -5
- package/dist/src/daemon/claude-worker.js +64 -80
- package/dist/src/daemon/command-journal.d.ts +5 -0
- package/dist/src/daemon/command-journal.js +50 -0
- package/dist/src/daemon/command-process.d.ts +28 -0
- package/dist/src/daemon/command-process.js +164 -0
- package/dist/src/daemon/command-worker.d.ts +19 -0
- package/dist/src/daemon/command-worker.js +182 -0
- package/dist/src/daemon/cursor-stream.d.ts +4 -0
- package/dist/src/daemon/cursor-stream.js +154 -0
- package/dist/src/daemon/daemon-adapters.d.ts +58 -0
- package/dist/src/daemon/daemon-adapters.js +102 -0
- package/dist/src/daemon/daemon-loops.d.ts +14 -0
- package/dist/src/daemon/daemon-loops.js +39 -0
- package/dist/src/daemon/daemon.d.ts +3 -30
- package/dist/src/daemon/daemon.js +132 -110
- package/dist/src/daemon/declared-input.d.ts +3 -0
- package/dist/src/daemon/declared-input.js +9 -0
- package/dist/src/daemon/delivery-router.js +7 -1
- package/dist/src/daemon/gemini-stream.d.ts +4 -0
- package/dist/src/daemon/gemini-stream.js +129 -0
- package/dist/src/daemon/github-calls.d.ts +4 -0
- package/dist/src/daemon/github-calls.js +16 -7
- package/dist/src/daemon/github-ci-rerun.d.ts +26 -0
- package/dist/src/daemon/github-ci-rerun.js +92 -0
- package/dist/src/daemon/github-ci.d.ts +6 -0
- package/dist/src/daemon/github-ci.js +251 -0
- package/dist/src/daemon/github-merge-call.d.ts +16 -0
- package/dist/src/daemon/github-merge-call.js +73 -0
- package/dist/src/daemon/github-merge.d.ts +3 -5
- package/dist/src/daemon/github-merge.js +59 -98
- package/dist/src/daemon/github-methods.d.ts +28 -0
- package/dist/src/daemon/github-methods.js +90 -16
- package/dist/src/daemon/github-operation.d.ts +3 -0
- package/dist/src/daemon/github-retry.d.ts +6 -0
- package/dist/src/daemon/github-retry.js +13 -0
- package/dist/src/daemon/github-ticket-status.js +27 -1
- package/dist/src/daemon/github-worker.js +2 -1
- package/dist/src/daemon/handoff-worker.d.ts +10 -0
- package/dist/src/daemon/handoff-worker.js +232 -0
- package/dist/src/daemon/issue-queue.d.ts +2 -0
- package/dist/src/daemon/issue-queue.js +92 -63
- package/dist/src/daemon/notify-journal.d.ts +18 -0
- package/dist/src/daemon/notify-journal.js +108 -0
- package/dist/src/daemon/notify-message.js +2 -1
- package/dist/src/daemon/notify.js +177 -37
- package/dist/src/daemon/opencode-stream.d.ts +5 -0
- package/dist/src/daemon/opencode-stream.js +87 -0
- package/dist/src/daemon/queue-status.d.ts +4 -0
- package/dist/src/daemon/queue-status.js +38 -0
- package/dist/src/daemon/retry-worktree.d.ts +18 -0
- package/dist/src/daemon/retry-worktree.js +139 -0
- package/dist/src/daemon/retrying-turn.d.ts +5 -0
- package/dist/src/daemon/retrying-turn.js +145 -21
- package/dist/src/daemon/schedule-journal.d.ts +19 -0
- package/dist/src/daemon/schedule-journal.js +57 -0
- package/dist/src/daemon/text-output.d.ts +18 -0
- package/dist/src/daemon/text-output.js +44 -0
- package/dist/src/daemon/ticket-dependency-waits.d.ts +18 -0
- package/dist/src/daemon/ticket-dependency-waits.js +60 -0
- package/dist/src/daemon/ticket-registration.d.ts +10 -8
- package/dist/src/daemon/ticket-registration.js +146 -47
- package/dist/src/daemon/ticket-source.d.ts +4 -1
- package/dist/src/daemon/ticket-source.js +8 -2
- package/dist/src/daemon/turn-command.d.ts +10 -0
- package/dist/src/daemon/turn-command.js +33 -0
- package/dist/src/daemon/turn-report.d.ts +18 -0
- package/dist/src/daemon/turn-report.js +79 -0
- package/dist/src/daemon/work-tree.d.ts +2 -1
- package/dist/src/daemon/work-tree.js +15 -12
- package/dist/src/daemon/workflow-generations.d.ts +32 -0
- package/dist/src/daemon/workflow-generations.js +57 -0
- package/dist/src/daemon/workflow-replay.d.ts +16 -0
- package/dist/src/daemon/workflow-replay.js +58 -0
- package/dist/src/daemon/workflow-schedules.d.ts +27 -0
- package/dist/src/daemon/workflow-schedules.js +185 -0
- package/dist/src/decision-visibility.d.ts +2 -0
- package/dist/src/decision-visibility.js +10 -0
- package/dist/src/decision-wait.d.ts +1 -0
- package/dist/src/decision-wait.js +3 -0
- package/dist/src/delivery-backoff.d.ts +1 -0
- package/dist/src/delivery-backoff.js +3 -0
- package/dist/src/doctor-report.d.ts +24 -0
- package/dist/src/doctor-report.js +70 -0
- package/dist/src/execution-pin.d.ts +9 -0
- package/dist/src/execution-pin.js +86 -2
- package/dist/src/grok-result.d.ts +12 -0
- package/dist/src/grok-result.js +61 -0
- package/dist/src/harness-defaults.d.ts +35 -0
- package/dist/src/harness-defaults.js +121 -0
- package/dist/src/host/decision-resume.d.ts +35 -0
- package/dist/src/host/decision-resume.js +86 -0
- package/dist/src/host/gate-bridge.d.ts +48 -0
- package/dist/src/host/gate-bridge.js +124 -0
- package/dist/src/host/run-wait.d.ts +26 -0
- package/dist/src/host/run-wait.js +37 -0
- package/dist/src/host/start-delivery.d.ts +3 -2
- package/dist/src/host/start-delivery.js +11 -4
- package/dist/src/host/step-bridge-host.d.ts +6 -19
- package/dist/src/host/step-bridge-host.js +57 -55
- package/dist/src/host/world-reconciliation.d.ts +1 -0
- package/dist/src/host/world-reconciliation.js +3 -0
- package/dist/src/host/world-runs.js +5 -0
- package/dist/src/issue-identity.d.ts +6 -0
- package/dist/src/issue-identity.js +23 -0
- package/dist/src/merge-request.d.ts +19 -0
- package/dist/src/merge-request.js +32 -0
- package/dist/src/phone/canonical-json.d.ts +1 -0
- package/dist/src/phone/canonical-json.js +16 -0
- package/dist/src/phone/phone-link.d.ts +7 -0
- package/dist/src/phone/phone-link.js +21 -0
- package/dist/src/phone/qr-data.d.ts +8 -0
- package/dist/src/phone/qr-data.js +133 -0
- package/dist/src/phone/qr-matrix.d.ts +6 -0
- package/dist/src/phone/qr-matrix.js +175 -0
- package/dist/src/phone/qr-penalty.d.ts +3 -0
- package/dist/src/phone/qr-penalty.js +67 -0
- package/dist/src/phone/qr-picture.d.ts +5 -0
- package/dist/src/phone/qr-picture.js +24 -0
- package/dist/src/private-config.schema.json +263 -0
- package/dist/src/project-config.schema.json +65 -0
- package/dist/src/project-settings.d.ts +19 -0
- package/dist/src/project-settings.js +131 -0
- package/dist/src/relay/answer-check.d.ts +24 -0
- package/dist/src/relay/answer-check.js +145 -0
- package/dist/src/relay/answer-inbox.d.ts +15 -0
- package/dist/src/relay/answer-inbox.js +173 -0
- package/dist/src/relay/daemon-keys.d.ts +16 -0
- package/dist/src/relay/daemon-keys.js +75 -0
- package/dist/src/relay/decision-check.d.ts +3 -0
- package/dist/src/relay/decision-check.js +77 -0
- package/dist/src/relay/decision-close.d.ts +6 -0
- package/dist/src/relay/decision-close.js +72 -0
- package/dist/src/relay/decision-envelopes.d.ts +29 -0
- package/dist/src/relay/decision-envelopes.js +93 -0
- package/dist/src/relay/decision-watch.d.ts +14 -0
- package/dist/src/relay/decision-watch.js +68 -0
- package/dist/src/relay/envelope-post.d.ts +8 -0
- package/dist/src/relay/envelope-post.js +71 -0
- package/dist/src/relay/envelope.d.ts +28 -0
- package/dist/src/relay/envelope.js +53 -0
- package/dist/src/relay/hpke.d.ts +25 -0
- package/dist/src/relay/hpke.js +108 -0
- package/dist/src/relay/pairing-checks.d.ts +42 -0
- package/dist/src/relay/pairing-checks.js +172 -0
- package/dist/src/relay/pairing-follow.d.ts +8 -0
- package/dist/src/relay/pairing-follow.js +95 -0
- package/dist/src/relay/pairing-session.d.ts +29 -0
- package/dist/src/relay/pairing-session.js +87 -0
- package/dist/src/relay/phone-delivery.d.ts +51 -0
- package/dist/src/relay/phone-delivery.js +139 -0
- package/dist/src/relay/phone-devices.d.ts +36 -0
- package/dist/src/relay/phone-devices.js +46 -0
- package/dist/src/relay/phone-inbox.d.ts +18 -0
- package/dist/src/relay/phone-inbox.js +17 -0
- package/dist/src/relay/phone-operations.d.ts +18 -0
- package/dist/src/relay/phone-operations.js +121 -0
- package/dist/src/relay/phone-outbox.d.ts +21 -0
- package/dist/src/relay/phone-outbox.js +25 -0
- package/dist/src/relay/phone-socket.d.ts +6 -0
- package/dist/src/relay/phone-socket.js +63 -0
- package/dist/src/relay/relay-control.d.ts +7 -0
- package/dist/src/relay/relay-control.js +24 -0
- package/dist/src/relay/relay-files.d.ts +2 -0
- package/dist/src/relay/relay-files.js +23 -0
- package/dist/src/relay/relay-heartbeat.d.ts +17 -0
- package/dist/src/relay/relay-heartbeat.js +81 -0
- package/dist/src/relay/relay-http.d.ts +14 -0
- package/dist/src/relay/relay-http.js +63 -0
- package/dist/src/relay/relay-registration.d.ts +15 -0
- package/dist/src/relay/relay-registration.js +63 -0
- package/dist/src/relay/relay-service.d.ts +25 -0
- package/dist/src/relay/relay-service.js +216 -0
- package/dist/src/relay/relay-store.d.ts +48 -0
- package/dist/src/relay/relay-store.js +131 -0
- package/dist/src/relay/sealed-post.d.ts +25 -0
- package/dist/src/relay/sealed-post.js +52 -0
- package/dist/src/relay.d.ts +1 -0
- package/dist/src/relay.js +8 -0
- package/dist/src/report-statistics.d.ts +50 -0
- package/dist/src/report-statistics.js +70 -0
- package/dist/src/report.d.ts +171 -0
- package/dist/src/report.js +119 -0
- package/dist/src/repository-identity.d.ts +4 -0
- package/dist/src/repository-identity.js +4 -0
- package/dist/src/review-subject.d.ts +41 -0
- package/dist/src/review-subject.js +91 -0
- package/dist/src/run-wait.d.ts +23 -0
- package/dist/src/run-wait.js +20 -0
- package/dist/src/service/active-tickets.d.ts +4 -0
- package/dist/src/service/active-tickets.js +49 -0
- package/dist/src/service/command-effects.d.ts +4 -0
- package/dist/src/service/command-effects.js +16 -0
- package/dist/src/service/decision-handler.d.ts +4 -0
- package/dist/src/service/decision-handler.js +39 -4
- package/dist/src/service/gateway.js +18 -3
- package/dist/src/service/queue-runs.d.ts +3 -0
- package/dist/src/service/queue-runs.js +39 -0
- package/dist/src/service/registry.d.ts +22 -3
- package/dist/src/service/registry.js +51 -8
- package/dist/src/service/replies.d.ts +10 -0
- package/dist/src/service/replies.js +12 -0
- package/dist/src/service/run-handlers.js +45 -54
- package/dist/src/service/run-metadata.d.ts +8 -0
- package/dist/src/service/run-metadata.js +53 -0
- package/dist/src/service/run-waits.d.ts +3 -0
- package/dist/src/service/run-waits.js +41 -0
- package/dist/src/service/service.d.ts +21 -2
- package/dist/src/service/service.js +23 -5
- package/dist/src/service/submission-handler.js +70 -19
- package/dist/src/service/worker-port.d.ts +1 -0
- package/dist/src/service/worker-totals.js +10 -1
- package/dist/src/step-bridge.d.ts +32 -0
- package/dist/src/steps/decision-gate.d.ts +2 -1
- package/dist/src/steps/decision-gate.js +7 -0
- package/dist/src/steps/handoff.d.ts +2 -0
- package/dist/src/steps/handoff.js +13 -0
- package/dist/src/steps/merge-by-person.d.ts +2 -0
- package/dist/src/steps/merge-by-person.js +18 -0
- package/dist/src/steps/merge-when-green.d.ts +2 -0
- package/dist/src/steps/merge-when-green.js +62 -0
- package/dist/src/steps/review-by-agent.d.ts +5 -0
- package/dist/src/steps/review-by-agent.js +124 -0
- package/dist/src/steps/review-by-person.d.ts +4 -0
- package/dist/src/steps/review-by-person.js +16 -0
- package/dist/src/steps/review-subject.d.ts +2 -0
- package/dist/src/steps/review-subject.js +11 -0
- package/dist/src/steps/run-command.d.ts +2 -0
- package/dist/src/steps/run-command.js +15 -0
- package/dist/src/steps/sleep.d.ts +3 -0
- package/dist/src/steps/sleep.js +19 -0
- package/dist/src/steps/wait-for-decision.d.ts +2 -0
- package/dist/src/steps/wait-for-decision.js +71 -0
- package/dist/src/steps/wait-for-run.d.ts +5 -0
- package/dist/src/steps/wait-for-run.js +9 -0
- package/dist/src/storage/decision-snapshots.d.ts +1 -0
- package/dist/src/storage/decision-snapshots.js +7 -1
- package/dist/src/submission.d.ts +4 -0
- package/dist/src/submission.js +6 -2
- package/dist/src/testing.d.ts +29 -0
- package/dist/src/testing.js +264 -0
- package/dist/src/ticket-dependencies.d.ts +1 -0
- package/dist/src/ticket-dependencies.js +15 -0
- package/dist/src/ticket-status-stream.d.ts +9 -0
- package/dist/src/ticket-status-stream.js +94 -0
- package/dist/src/ticket-status.js +7 -4
- package/dist/src/token-prices.d.ts +14 -4
- package/dist/src/token-prices.js +56 -11
- package/dist/src/turn-profile.d.ts +1 -0
- package/dist/src/turn-profile.js +8 -1
- package/dist/src/worker-usage.d.ts +25 -0
- package/dist/src/worker-usage.js +58 -1
- package/dist/src/workflow-admission.d.ts +6 -0
- package/dist/src/workflow-candidate.d.ts +7 -2
- package/dist/src/workflow-candidate.js +48 -5
- package/dist/src/workflow-contracts.d.ts +2 -0
- package/dist/src/workflow-contracts.js +8 -3
- package/dist/src/workflow-declaration.d.ts +31 -2
- package/dist/src/workflow-declaration.js +140 -16
- package/dist/src/workflow-discovery.d.ts +19 -0
- package/dist/src/workflow-discovery.js +36 -0
- package/dist/src/workflow-refusals.d.ts +6 -0
- package/dist/src/workflow-refusals.js +42 -0
- package/dist/src/workflow-schedule.d.ts +22 -0
- package/dist/src/workflow-schedule.js +53 -0
- package/dist/src/workflows.d.ts +12 -1
- package/dist/src/workflows.js +9 -0
- package/dist/tools/admit-workflow.d.ts +14 -0
- package/dist/tools/admit-workflow.js +107 -0
- package/dist/tools/doctor-adoption.d.ts +1 -0
- package/dist/tools/doctor-adoption.js +11 -0
- package/dist/tools/project-configuration.d.ts +4 -0
- package/dist/tools/project-configuration.js +47 -0
- package/dist/tools/self-host-processes.d.ts +1 -0
- package/dist/tools/self-host-processes.js +33 -22
- package/dist/tools/self-host-project.d.ts +21 -0
- package/dist/tools/self-host-project.js +224 -0
- package/dist/tools/self-host-runtime.d.ts +16 -0
- package/dist/tools/self-host-runtime.js +60 -0
- package/dist/tools/self-host-workflows.d.ts +1 -0
- package/dist/tools/self-host-workflows.js +36 -1
- package/dist/tools/self-host.js +106 -135
- package/dist/tools/workflow-bundle-dependencies.d.ts +5 -0
- package/dist/tools/workflow-bundle-dependencies.js +93 -0
- package/dist/tools/workflow-bundles.d.ts +20 -0
- package/dist/tools/workflow-bundles.js +173 -0
- package/dist/tools/workflow-candidate-counts.d.ts +12 -0
- package/dist/tools/workflow-candidate-counts.js +266 -0
- package/dist/tools/workflow-candidate-fields.d.ts +5 -0
- package/dist/tools/workflow-candidate-fields.js +24 -0
- package/dist/tools/workflow-candidate-helper-counts.d.ts +10 -0
- package/dist/tools/workflow-candidate-helper-counts.js +129 -0
- package/dist/tools/workflow-candidate-program.d.ts +13 -0
- package/dist/tools/workflow-candidate-program.js +106 -0
- package/dist/tools/workflow-candidate-source.d.ts +6 -0
- package/dist/tools/workflow-candidate-source.js +169 -0
- package/dist/tools/workflow-candidate-syntax.d.ts +21 -0
- package/dist/tools/workflow-candidate-syntax.js +263 -0
- package/dist/tools/workflow-command.d.ts +1 -0
- package/dist/tools/workflow-command.js +143 -0
- package/dist/tools/workflow-folder-capture.d.ts +18 -0
- package/dist/tools/workflow-folder-capture.js +261 -0
- package/dist/tools/workflow-folders.d.ts +49 -0
- package/dist/tools/workflow-folders.js +203 -0
- package/dist/tools/workflow-source-effects.d.ts +10 -0
- package/dist/tools/workflow-source-effects.js +213 -0
- package/dist/tools/workflow-test-hook.d.ts +7 -0
- package/dist/tools/workflow-test-hook.js +16 -0
- package/dist/tools/workflow-test-loader.d.ts +1 -0
- package/dist/tools/workflow-test-loader.js +10 -0
- package/dist/workflows/develop-prd.js +71 -3
- package/dist/workflows/develop-ticket.js +6 -1
- package/dist/workflows/ticket-prompts.js +17 -3
- package/package.json +66 -6
- package/templates/develop/README.md +78 -0
- package/templates/develop/develop/accept.ts +11 -0
- package/templates/develop/develop/flow-tests.ts +357 -0
- package/templates/develop/develop/local.ts +40 -0
- package/templates/develop/develop/option-tests.ts +174 -0
- package/templates/develop/develop/partial-choice.ts +29 -0
- package/templates/develop/develop/partial.ts +28 -0
- package/templates/develop/develop/pull.ts +152 -0
- package/templates/develop/develop/queue-partial.ts +39 -0
- package/templates/develop/develop/refusal-tests.ts +174 -0
- package/templates/develop/develop/report.ts +156 -0
- package/templates/develop/develop/round.ts +40 -0
- package/templates/develop/develop/scenarios.ts +87 -0
- package/templates/develop/develop/turn.ts +67 -0
- package/templates/develop/develop.declaration.json +24 -0
- package/templates/develop/develop.test.ts +287 -0
- package/templates/develop/develop.ts +30 -0
- package/templates/develop/template.json +153 -0
- package/dist/src/chats/analysis-batches.d.ts +0 -24
- package/dist/src/chats/analysis-batches.js +0 -83
- package/dist/src/chats/analysis-contract.d.ts +0 -80
- package/dist/src/chats/analysis-contract.js +0 -16
- package/dist/src/chats/analysis-data.d.ts +0 -3
- package/dist/src/chats/analysis-data.js +0 -85
- package/dist/src/chats/analysis-input.d.ts +0 -12
- package/dist/src/chats/analysis-input.js +0 -83
- package/dist/src/chats/analysis-links.d.ts +0 -2
- package/dist/src/chats/analysis-links.js +0 -25
- package/dist/src/chats/analysis-prompt.d.ts +0 -10
- package/dist/src/chats/analysis-prompt.js +0 -16
- package/dist/src/chats/analysis-proposal.d.ts +0 -2
- package/dist/src/chats/analysis-proposal.js +0 -32
- package/dist/src/chats/analysis-provenance.d.ts +0 -4
- package/dist/src/chats/analysis-provenance.js +0 -99
- package/dist/src/chats/analysis-shape.d.ts +0 -3
- package/dist/src/chats/analysis-shape.js +0 -228
- package/dist/src/chats/analysis.d.ts +0 -3
- package/dist/src/chats/analysis.js +0 -42
- package/dist/src/chats/corpus-input.d.ts +0 -5
- package/dist/src/chats/corpus-input.js +0 -288
- package/dist/src/chats/destination.d.ts +0 -4
- package/dist/src/chats/destination.js +0 -96
- package/dist/src/chats/import.d.ts +0 -15
- package/dist/src/chats/import.js +0 -243
- package/dist/src/chats/input-file.d.ts +0 -3
- package/dist/src/chats/input-file.js +0 -32
- package/dist/src/chats/normalize.d.ts +0 -29
- package/dist/src/chats/normalize.js +0 -155
- package/dist/src/chats/previous-import.d.ts +0 -5
- package/dist/src/chats/previous-import.js +0 -34
- package/dist/src/chats/reader.d.ts +0 -21
- package/dist/src/chats/reader.js +0 -118
- package/dist/src/chats/redact-import.d.ts +0 -14
- package/dist/src/chats/redact-import.js +0 -141
- package/dist/src/chats/redaction.d.ts +0 -41
- package/dist/src/chats/redaction.js +0 -161
- package/dist/src/chats/revision-input.d.ts +0 -3
- package/dist/src/chats/revision-input.js +0 -180
- package/dist/src/chats/revision.d.ts +0 -48
- package/dist/src/chats/revision.js +0 -94
- package/dist/src/chats/selection.d.ts +0 -25
- package/dist/src/chats/selection.js +0 -109
- package/dist/src/chats/source.d.ts +0 -23
- package/dist/src/chats/source.js +0 -135
- package/dist/src/chats/structure.d.ts +0 -57
- package/dist/src/chats/structure.js +0 -216
- package/dist/src/daemon/analysis-launch.d.ts +0 -5
- package/dist/src/daemon/analysis-launch.js +0 -81
- package/dist/src/daemon/analysis-token.d.ts +0 -6
- package/dist/src/daemon/analysis-token.js +0 -88
- package/dist/src/daemon/analysis-worker.d.ts +0 -22
- package/dist/src/daemon/analysis-worker.js +0 -219
- package/dist/src/daemon/chats-files.d.ts +0 -15
- package/dist/src/daemon/chats-files.js +0 -113
- package/dist/src/daemon/chats-worker.d.ts +0 -12
- package/dist/src/daemon/chats-worker.js +0 -200
- package/dist/workflows/analyze-chats.d.ts +0 -10
- package/dist/workflows/analyze-chats.declaration.json +0 -9
- package/dist/workflows/analyze-chats.js +0 -73
package/README.md
CHANGED
|
@@ -18,124 +18,21 @@ What works now:
|
|
|
18
18
|
|
|
19
19
|
- `cloving start` starts the service for one git repository, with the workflow host, the Claude worker and a watch page.
|
|
20
20
|
- Three workflows from one workflow file: `develop-prompt`, `develop-ticket` and `develop-issue`.
|
|
21
|
+
- `cloving start` loads the bundled `workflows/`, repository `.cloving/workflows/` and user `cloving/workflows/` folders (see "Develop a workflow").
|
|
21
22
|
- In a run of `develop-ticket` or `develop-prompt`, the operator approves or denies the work of each round. A run has a maximum of three rounds.
|
|
22
23
|
- In a run of `develop-issue`, Greptile reviews the pull request. The GitHub worker merges the pull request at the confidence score 5/5, with no human approval.
|
|
23
24
|
- Cloving develops itself through `develop-issue`, with a Greptile review and an automatic merge.
|
|
24
|
-
- The watch page shows the runs, the events and the work of the
|
|
25
|
-
- `cloving chats import -- FILE_A FILE_B` imports selected Claude Code transcripts into private runtime files without a service.
|
|
26
|
-
- `cloving chats redact IMPORT_ID` creates a redacted corpus and a separate counts manifest without a service.
|
|
25
|
+
- The watch page shows the runs, the events and the work of the worker as it occurs.
|
|
27
26
|
|
|
28
27
|
What does not work yet:
|
|
29
28
|
|
|
30
29
|
- The Claude worker is not confined. It runs as the current user.
|
|
31
30
|
- An approval does not merge the branch. You merge the branch yourself.
|
|
32
|
-
- `cloving start` loads only the workflow file `workflows/develop-ticket.ts`. A new workflow file also needs changes of the code (see "Develop a workflow").
|
|
33
31
|
- Cloving cannot make a workflow from your earlier chats yet. The issues [#42](https://github.com/cloving-ai/cloving-ai/issues/42), [#43](https://github.com/cloving-ai/cloving-ai/issues/43) and [#44](https://github.com/cloving-ai/cloving-ai/issues/44) are open for this work. Until then, the prompt "Find workflows in your chats" is a guide for a coding agent.
|
|
34
|
-
- This pre-release package is not published to npm.
|
|
35
|
-
|
|
36
|
-
### Import selected chats
|
|
37
|
-
|
|
38
|
-
Chat import, redaction and analysis file operations currently require macOS. On Linux, the chat commands return `platform-unsupported` with exit status `1` before file access. The analysis file adapter also refuses with `platform-unsupported` before reading payloads.
|
|
39
|
-
|
|
40
|
-
```sh
|
|
41
|
-
cloving chats import -- TRANSCRIPT_A.jsonl TRANSCRIPT_B.jsonl
|
|
42
|
-
cloving chats import --previous-import IMPORT_ID -- TRANSCRIPT_A.jsonl TRANSCRIPT_B.jsonl
|
|
43
|
-
cloving chats import --root PROJECTS_A --root PROJECTS_B --exclude-project project-skip -- TRANSCRIPT_A.jsonl
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Select files positionally, projects folders with repeatable `--root`, or both. Relative paths use the current directory. At least one file or root is required. After `--`, every argument is a literal file path, including names that look like options. The command does not expand globs; your shell can expand an unquoted glob. A source must be a regular file of at most 10 MiB (10,485,760 bytes). Symbolic links are refused. Sources keep their original bytes.
|
|
47
|
-
|
|
48
|
-
Each root explicitly selects a projects folder. Claude Code normally stores projects below its configuration folder, whose default name is `.claude` below the home folder; `CLAUDE_CONFIG_DIR` can change that location. Cloving never discovers a root from configuration or reads settings to expand your selection. This layout does not guarantee supported transcript formats or complete history. [Claude Code session storage](https://code.claude.com/docs/en/sessions#where-transcripts-are-stored) and [subagent storage](https://code.claude.com/docs/en/sub-agents) describe the source layout.
|
|
49
|
-
|
|
50
|
-
`--exclude-project NAME` matches a direct project folder name exactly, preserving case. Repeat it for several names. Exclusions require roots and never affect positional files. Names cannot contain separators, parent-directory markers or glob patterns. An excluded subtree produces one `project-excluded` entry; its children are never listed or opened.
|
|
51
|
-
|
|
52
|
-
Positional files retain their order and repetitions and come before scanned files. Roots sort by canonical path, retaining repeated selections with stable ties. Entries sort by relative path using JavaScript string order. Scans select regular files ending in lowercase `.jsonl`; other filenames produce `unsupported-extension` without a content read. Root children have depth one. Session files at depth two and subagent files at depth four fit the limit. Folders at depth four are pending with `depth-limit` and disclose no descendant inventory.
|
|
53
|
-
|
|
54
|
-
Across all roots, at most 100 eligible scanned candidates receive a source-read attempt. A failed open, oversized file, invalid encoding or unsupported transcript still consumes a slot. Repeated roots and overlapping selections consume separate slots; positional files are outside this budget. All metadata candidates are ordered before slots are allocated. Later candidates are pending with `file-limit` and are never opened. Select a pending file explicitly in a later import. The supported macOS scanner refuses links in roots, ancestors and entries, canonical containment escapes, Git repository roots (including worktrees and bare repositories), and source or ancestor identity changes before reading bytes. Inaccessible directories produce unreadable subtree entries. Directory enumeration also refuses raced links; an unavailable kernel protection fails closed.
|
|
55
|
-
|
|
56
|
-
Each import accepts at most 50 MiB (52,428,800 bytes) of normalized corpus JSONL, measured as UTF-8 including newline separators. Repeated selections count toward this total. This bounds the message content retained for structure analysis. Exceeding the limit returns `output-failed` with exit code `1`, leaves no completed coverage manifest and may leave partial data files. Select fewer sources for each import; copy and conflict comparisons apply within each import.
|
|
57
|
-
|
|
58
|
-
The supported Claude Code JSONL subset has `user` and `assistant` envelopes with a matching `message.role`. Content can be a string or an ordered array of `text`, `tool_use` and `tool_result` blocks. Records preserve the role, timestamp, session ID, tool fields and original block positions. Missing timestamps and session IDs become `null`. Thinking blocks and non-message metadata are omitted and counted. Unknown message shapes or blocks refuse the entire source. Claude Code's internal transcript format can change between versions.
|
|
59
|
-
|
|
60
|
-
Each import creates `chats/IMPORT_ID/corpus.jsonl`, `structure.json` and `coverage.json` below the resolved Cloving runtime directory. Selection uses a nonempty `CLOVING_RUNTIME_DIR`, then `XDG_RUNTIME_DIR/cloving`, then `XDG_STATE_HOME/cloving/run`, then `HOME/.local/state/cloving/run`. Relative settings resolve against the current directory. Destinations inside a Git repository are refused, including through symbolic links. New directories have mode `0700`; files have mode `0600`. These permissions add no encryption or redaction.
|
|
61
|
-
|
|
62
|
-
New corpus records have `schemaVersion: 2` and a `sourceId` of `sha256:HEX:OFFSET`. The digest covers the exact source bytes. The offset is the zero-based byte position of the source line. `text` joins the message's text blocks with a newline; `blocks` retains each supported block's `position`. Tool result content is copied as JSON data. Positional-only imports retain coverage version 1 with its ordered `files` array. Imports containing roots write coverage version 2 with ordered `roots`, `exclusions`, fixed `limits` and ordered `entries`. Each entry identifies its zero-based positional or sorted-root selection, file or subtree kind, resolved path, status, fixed reason code, digest when available, record count and omission counts (`metadataRecords` and `thinkingBlocks`). Excluded and pending subtree entries count only themselves. Scanned oversized files are `unsupported` with `file-too-large`; positional oversized files retain `too-large`, including mixed imports. Refused sources contribute no records. Included sources also record `byteSize`, measured from the exact hashed bytes. Both coverage versions accept legacy entries without sizes. The final coverage manifest appears only after the corpus, structure and optional revision files close successfully. Output failure leaves no completed coverage manifest; partial data files may remain.
|
|
63
|
-
|
|
64
|
-
Use `--previous-import IMPORT_ID` before `--` to compare with one earlier import in the resolved runtime chats folder. It accepts one UUID v4 ID, using the same syntax as redaction. Missing, repeated and malformed values are usage errors (`missing-value`, `repeated-previous-import` and `invalid-import-id`). At least one current file or root is still required. The earlier coverage manifest must be valid and carry the same ID; completed partial imports are allowed. Existing revisions must also be valid with a positive safe revision number that can be incremented. Missing coverage, invalid metadata or linked inputs return `previous-import-refused`, exit `1`, a null import ID and zero counts before selection, source reads or new output.
|
|
65
|
-
|
|
66
|
-
Comparison writes private `revision.json` with `schemaVersion: 1`, `importId`, `previousImportId`, `evidenceRevision`, `counts` and ordered `entries`. An earlier import without a revision starts revision `1`; otherwise its revision increases by one. Each entry stores `path`, `root` (null for positional selections), `before` and `after` evidence, one `status` and one fixed `reason`. Absent evidence is null; present evidence contains a nullable digest and optional byte size. No message text is stored. Counts always include `new`, `missing`, `unchanged`, `changed`, `truncated` and `extended`, including zeros.
|
|
67
|
-
|
|
68
|
-
Selections match only by stored source path and selected root path, resolving each manifest's root indexes independently. Repeated keys pair in order. Current entries come first, followed by unmatched earlier entries in their earlier order. New selections are `new`; absent or currently refused selections are `missing`. Equal non-null digests are `unchanged`. An unavailable earlier digest or size makes differing evidence `changed`. Fewer bytes are `truncated`; more bytes are `extended` only when SHA-256 of exactly the earlier byte count equals the earlier digest. Other differences are `changed`. Prefixes use the same bounded source read; no source is reopened. Earlier entries never select sources or invent subtree descendants. Missing or truncated evidence forces `import-partial` and exit `1`. Revisions do not merge corpora, establish complete history or invoke later analysis. Without the option, no revision file is created and the summary retains its existing shape.
|
|
69
|
-
|
|
70
|
-
Version 2 preserves nonempty envelope `uuid` strings exactly, with `null` for unknown identity. It preserves boolean `isSidechain` flags, otherwise `null`. A nonempty `parentUuid` string is an observed link and explicit `null` is an observed root. Absent, empty or invalid parents are omitted and remain unknown. Import retains all supported branches and side chains and never uses API message IDs, paths, content or neighbors to infer identity.
|
|
71
|
-
|
|
72
|
-
The version 1 structure sidecar references each occurrence by zero-based `recordIndex` and `sourceId`, including repeated selections with the same source ID. It contains no message content or source paths. Known identity matches across the import mark copies of the earliest occurrence with equal role, text and blocks, ignoring object member order while preserving array order and exact JSON number tokens. All records stay in the corpus. Different content for one identity produces a conflict group containing every variant and its copies; no variant wins.
|
|
73
|
-
|
|
74
|
-
Structure groups nodes and observed links within known sessions. Null and empty session IDs remain separate, unlinked occurrences. Links with conflicting observed parents or unknown child identity are marked ambiguous. Forks require multiple distinct known child IDs through unambiguous links. Missing parent targets are counted once per session and target ID, separately from unknown parent fields. Cyclic groups include all observed links, including ambiguous links; their IDs form strongly connected groups in occurrence order. Counts include records, known sessions, forks, copies, conflict groups, unknown identities and missing parent targets, including zeros. `historyCompleteness` is always `"unknown"`: selected history and even an explicit root cannot establish complete history. Omitted metadata parents remain unresolved.
|
|
75
|
-
|
|
76
|
-
Console output contains counts, fixed codes and an opaque import ID. Source paths, exclusion names, event and session IDs, and transcript text stay in the private files. Root imports add `excluded` and `pending` entry totals. `refused` counts unreadable, unsupported and positional oversized entries. Exit code `0` and `import-complete` mean no refused or pending entries and, when comparing revisions, no missing or truncated evidence; exclusions alone permit success without comparison. Code `1` and `import-partial` mean refused, pending, missing or truncated entries when output completes. Output failures return code `1`, a null import ID and zeroed counts. Code `2` means invalid usage, before source or destination access. Importing performs only structural comparison; it makes no model or network call and does not execute transcript instructions. Full quality admission and release qualification remain pending.
|
|
77
|
-
|
|
78
|
-
### Protected chat analysis files
|
|
79
|
-
|
|
80
|
-
The daemon provides `chats` capability methods `read-analysis` and `save-analysis` only to the registered `analyze-chats` workflow. A declaration must explicitly name `chats`; existing workflows retain their capabilities. Start one analysis with `cloving run analyze-chats --input input.json`, where the file contains exactly `{ "importId": "11111111-1111-4111-8111-111111111111" }` using your redacted import ID. The declared workflow charges three effect units for sequential read, one model turn and save. No evidence path is accepted.
|
|
81
|
-
|
|
82
|
-
`read-analysis` takes `{ importId }`, with the same lowercase UUID v4 syntax as redaction and the import ID as its protected target. It reads only `redaction.json`, `redacted.jsonl` and `structure.json` from the resolved runtime import directory. Its completed result contains `{ snapshot }`, whose `importId`, `redaction`, `corpus` and `structure` fields are strings. Keep these strings in private durable state; parsing and serializing corpus numbers can lose their exact tokens. Original corpus, coverage, revision and transcript content are never analysis inputs.
|
|
83
|
-
|
|
84
|
-
Each evidence file is bounded before retention. The redacted corpus, serialized snapshot, complete escaped model document (`{ prompt: analysisModelInput(snapshot) }`) and final serialized save payload must each fit 65,536 UTF-8 bytes. The model-input formatter includes instructions and the full sidecar. Overflow returns `analysis-too-large`; transport limits are unchanged. Missing redaction manifest or corpus returns `redaction-required`, malformed nonempty import IDs return `invalid-import-id`, and invalid or unsafe evidence returns `analysis-input-refused`. Manifest counts and identity fields must match the corpus. Sidecar validation retains original copy/conflict classes, using class markers rather than redacted content to check structure consistency.
|
|
85
|
-
|
|
86
|
-
`save-analysis` takes `{ importId, cycleId, snapshot, reply }`. The reply is JSON text and validation uses the captured snapshot even if the files later change. Invalid JSON returns `analysis-invalid-json`; candidate refusal retains the validator's unique codes in declaration order. Accepted output writes and closes private `proposal-CYCLE_ID.txt` before creating `analysis-CYCLE_ID.json` as the completion marker. The JSON is the complete validator result, including every citation, counterevidence item and limitation. The deterministic plain-text proposal is at most 4,000 characters. Matching duplicate saves retain bytes and receipt identity; a matching proposal without a marker may finish. Conflicting or incomplete existing outputs remain unchanged and return `analysis-output-failed`. A proposal alone is not completed analysis. New files have mode `0600`; destinations and linked files use the import protections.
|
|
87
|
-
|
|
88
|
-
The pure batch preparer validates a captured snapshot, selects the longest consecutive whole-record prefix within 65,536-byte encoded launch and adapter documents, and refuses a fifth batch. It preserves record bytes, global occurrence indexes and captured copy/conflict classes, includes the earlier result as data, and measures the fixed instruction and prompt encoding shared with the restricted launcher. Raw metadata remains bounded at 65,536 bytes and the input corpus at 52,428,800 bytes. Workflow integration, streaming capture, cumulative merge, immutable coverage checkpoints, batch save bounds and restart across batches remain pending; the running workflow retains its single-snapshot limits above.
|
|
89
|
-
|
|
90
|
-
Analysis reuse after an evidence revision is unavailable. `earlierAnalysisId` is not a supported analysis input, and `analysis:IMPORT_ID:CYCLE_ID` identifies a saved result without establishing checkpoint coverage. Import revisions classify source changes but do not map analysis citations or transfer analyzed record ranges. Reuse requires an implemented and verified batch pipeline with result merging, committed complete or partial coverage checkpoints, replay binding and durable question/answer follow-up. The pure batch preparer and saved validator results do not satisfy that prerequisite.
|
|
91
|
-
|
|
92
|
-
A candidate with `openQuestion` currently returns `analysis-input-refused` from `save-analysis`, reconciliation and stop without creating output. The pure question validator is implemented; private drafts, durable analysis questions, expiry and answer follow-up are pending. The workflow still uses one turn and three effect units.
|
|
93
|
-
|
|
94
|
-
Success returns `{ kind: "handoff_requested", cycleId, promotionId }`, where `promotionId` is an opaque analysis reference. It grants no execution or publication authority. The file adapter performs no git work, network request or model call. Redaction can leave private values, and validator acceptance checks shape and cited occurrences rather than factual accuracy. History completeness remains unknown and independent observations remain unestablished.
|
|
95
|
-
|
|
96
|
-
The analysis turn always uses the `claude` executable on the search path, including with `CLOVING_WORKER=codex`. Development workflows keep their configured worker. Analysis requires Claude Code major version 2, at least 2.1.302, with every required launch flag in its help text; an unavailable or unsupported CLI returns `analysis-worker-unavailable`. It accepts no `analyze-chats` profile or development-command override.
|
|
97
|
-
|
|
98
|
-
To configure subscription login:
|
|
99
|
-
|
|
100
|
-
1. Run `claude setup-token` once and store the token in your secret manager.
|
|
101
|
-
2. Set `CLOVING_ANALYSIS_TOKEN_COMMAND` before starting the daemon. Its value is a JSON array of one or more nonempty strings naming a program and its arguments, for example `CLOVING_ANALYSIS_TOKEN_COMMAND='["op","read","op://Development/Claude Analysis/token"]' cloving start`. The command must print only the stored token on stdout.
|
|
102
|
-
3. Start an `analyze-chats` run. Immediately before each new turn, Cloving runs the command without a shell, with a 10-second deadline and a 4,096-byte limit shared by stdout and stderr. After removing one final line break, stdout must contain 1–512 printable ASCII characters without spaces or control characters. An unset or malformed setting, command failure, timeout, excessive output or invalid token blocks with `analysis-worker-unavailable` and a fixed cause category.
|
|
103
|
-
|
|
104
|
-
The fetched token goes only to the analysis process as `CLAUDE_CODE_OAUTH_TOKEN`. Cloving does not put it in its environment, development workers, logs or stored results, and does not fetch it for CLI probes or completed-turn replay. Analysis excludes `ANTHROPIC_API_KEY`, including a key present in the daemon environment. Cloving does not read the macOS keychain.
|
|
105
|
-
|
|
106
|
-
The fixed launch removes built-in tools with `--tools ""` and denies MCP tools separately, with an empty strict MCP configuration. Safe and restricted modes disable discovery; skills, hooks, project instructions, plugins, session persistence and Chrome integration are disabled. The command runs once with a five-dollar budget in an empty private directory and uses a separate fixed system instruction and encoded evidence input. Only this turn contacts the model. See the [Claude CLI controls](https://code.claude.com/docs/en/cli-reference) and [subscription authentication](https://code.claude.com/docs/en/authentication).
|
|
107
|
-
|
|
108
|
-
Replies of at most 16,000 UTF-8 bytes are retained completely for validation. Larger replies return `analysis-reply-too-large`; unsuccessful turns return `analysis-turn-failed`. Both retain available usage. A completed private worker result is written before its report, so durable replay can reuse it. An interrupted or unknown completion never starts a replacement turn. Analysis creates no raw stdout/stderr turn logs or generic text summary. Worker records and accepted outputs remain private; operational views expose codes, counts, usage and opaque identities. Temporary CLI data is removed when the turn completes. Acceptance grants no execution, publication or workflow-generation authority.
|
|
109
|
-
|
|
110
|
-
### Redact an imported corpus
|
|
111
|
-
|
|
112
|
-
```sh
|
|
113
|
-
cloving chats redact IMPORT_ID
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Use the UUID v4 import ID printed by `chats import`. This command uses the same runtime directory selection and needs no service. It accepts version 1 and version 2 corpus records and coverage manifests. It validates coverage without reopening transcript paths. Original corpus, coverage and structure files remain unchanged.
|
|
117
|
-
|
|
118
|
-
The command writes `redacted.jsonl` and `redaction.json` beside the originals. New files have mode `0600`. Existing outputs are refused. The manifest records rule version `1`, record count, category counts (including zeros), and total replacements. It contains no matched values or replacement map. The manifest is written only after the redacted corpus closes successfully. A failure can leave an incomplete redacted corpus; there is no restart recovery.
|
|
119
|
-
|
|
120
|
-
`HOME` supplies the home folder. The rules replace home paths (including tilde forms), e-mail addresses, supported GitHub, Anthropic and OpenAI tokens, complete private-key blocks, and URLs with user information. `CLOVING_PRIVATE_TEXTS` adds literal texts with case ignored. For this command, an unset setting or `[]` adds no texts. Malformed lists, non-string entries and blank entries are refused. The issue-posting workflows still require a nonempty private-text list.
|
|
121
|
-
|
|
122
|
-
Redaction inspects message text, text blocks and nested tool input and result strings, including member names. Overlapping matches count once, with priority given to private keys, credential URLs, home paths, tokens, e-mail addresses, then private texts. Both stored copies of message text count. Source references, schema versions, identity fields, parent-field presence, record order and exact number tokens remain intact. The structure sidecar is private metadata outside redaction scope and is not a redacted export. Member-name collisions are refused.
|
|
123
|
-
|
|
124
|
-
Console output contains only fixed codes, counts and the import ID. Exit code `0` means success, `1` means processing failure, and `2` means invalid usage. Originals and metadata remain outside redaction scope. Both originals and outputs remain private runtime data. Redaction makes no model or network call and does not execute transcript instructions. It neither guarantees removal of every private value nor authorizes later model use.
|
|
125
|
-
|
|
126
|
-
### Validate process analysis data
|
|
127
|
-
|
|
128
|
-
The pure `validateAnalysis(candidate, { records, structure })` function in `src/chats/analysis.ts` validates one version 1 process candidate against an ordered redacted corpus and its matching pre-redaction, in-memory `ImportStructure`. The evidence object keeps the API within the repository's two-parameter quality limit. Stored sidecar envelopes are not this input type. This pure validator makes no model call.
|
|
129
|
-
|
|
130
|
-
Candidates declare IDs and descriptions for inputs, ordered stages, branches, bounded loops, artifacts, stopping conditions, gates and non-gates. Branch links use `sourceStageId` and `targetStageId`; loops use ordered `stageIds` and `exhaustedStoppingConditionId`. Stages, gates and loops declare a claim `kind`, `sourceRefs` and `counterevidenceRefs`. References identify exact occurrences by `recordIndex` and `sourceId`. Copies add no support; observed repetition needs two directly cited original source IDs. Routine repair, accidental interruption and tool-imposed confirmation require matching non-gate classifications.
|
|
131
|
-
|
|
132
|
-
Version 1 also accepts an optional `openQuestion` object containing only `claimId`, `description`, `sourceRefs` and `choices`. It names a stage, gate or loop claim and cites distinct known occurrences from that claim's support and counterevidence, with no unrelated references. Two to four choices each contain only a unique nonempty `id` and an inert `description`. Malformed or multiple questions return `invalid-question`; absent claims or missing declared conflict return `question-without-conflict`. Missing and unknown sources retain their existing codes, as do recursive authority fields. Validation checks the declared relations and makes no claim that the prose describes a real conflict. A validated question remains unresolved analysis data.
|
|
133
|
-
|
|
134
|
-
Acceptance returns the candidate plus limitations preserving conflict groups, unknown history and unestablished independence. Refusal returns only fixed, unique codes in declaration order. Candidate fields are closed; authority and executable fields are refused, and descriptions remain inert. Validation does not certify redaction, evidence authenticity, inference quality, publication safety or permission for any action. Evidence content and exact numeric tokens remain unchanged. Full product admission remains pending CLV-002 through CLV-004.
|
|
135
32
|
|
|
136
33
|
## Install
|
|
137
34
|
|
|
138
|
-
Cloving supports macOS and Linux on arm64 and x64, with the Node and npm ranges in `package.json`. Workflow admission
|
|
35
|
+
Cloving supports macOS and Linux on arm64 and x64, with the Node and npm ranges in `package.json`. Workflow admission accepts the same Node range: Node 24.22.1 or a later Node 24 version, and Node 26.9.0 or a later Node 26 version. CI runs the developer gate and packed-install checks on macOS 15 arm64 and Ubuntu 24.04 x64 with Node 26.9.0 and npm 11.19.1. CI also runs the Ubuntu checks with Node 24.22.1. The `quality_linux` check passes only when the Ubuntu checks pass on both Node versions. Windows is unsupported.
|
|
139
36
|
|
|
140
37
|
A run needs these tools:
|
|
141
38
|
|
|
@@ -143,14 +40,14 @@ A run needs these tools:
|
|
|
143
40
|
- The `claude` command of Claude Code on the search path. The Claude worker runs it as the current user.
|
|
144
41
|
- For `develop-issue` only: the `gh` command with a login, and a GitHub repository that Greptile reviews. The worker uses the `gh` login and the git credentials of the current user.
|
|
145
42
|
|
|
146
|
-
|
|
43
|
+
Install the executable from npm:
|
|
147
44
|
|
|
148
45
|
```sh
|
|
149
46
|
npm install --global cloving --ignore-scripts
|
|
150
47
|
cloving --version
|
|
151
48
|
```
|
|
152
49
|
|
|
153
|
-
This package replaces the older Cloving CLI under the same npm name. The
|
|
50
|
+
This package replaces the older Cloving CLI under the same npm name. The installed package includes startup, the watch command, bundled workflows and the SDK compiler integration. It starts without a source checkout or development dependencies, using the installed runtime dependencies. Installation runs no scripts and starts no service.
|
|
154
51
|
|
|
155
52
|
For checkout development, get the code and install the dependencies:
|
|
156
53
|
|
|
@@ -164,7 +61,7 @@ The runtime dependencies include the Workflow SDK (`workflow`), its directive co
|
|
|
164
61
|
|
|
165
62
|
### The `cloving` command
|
|
166
63
|
|
|
167
|
-
For checkout development, put a launcher for `src/cli.ts` in a directory of the search path. The launcher must use
|
|
64
|
+
For checkout development, put a launcher for `src/cli.ts` in a directory of the search path. The launcher must use a Node version in the supported range. This example is for a checkout in `~/cloving` and a launcher in `~/.local/bin`:
|
|
168
65
|
|
|
169
66
|
```sh
|
|
170
67
|
mkdir -p ~/.local/bin
|
|
@@ -197,6 +94,48 @@ The installed executable includes JavaScript for startup, the watch command and
|
|
|
197
94
|
|
|
198
95
|
For a project-local tarball installation, use `./node_modules/.bin/cloving` wherever the examples below say `cloving`, or add your project's `node_modules/.bin` to the search path. After a build, `npm link` also supplies the executable.
|
|
199
96
|
|
|
97
|
+
### Repository settings
|
|
98
|
+
|
|
99
|
+
`cloving start` reads `.cloving/config.json` in the selected repository and a private file at `<config>/cloving/projects/<name>-<id>.json`. The name and ID match the repository's runtime directory. `<config>` is an absolute `XDG_CONFIG_HOME`, or `~/.config` otherwise. Missing files keep the existing defaults. Restart after changing a file.
|
|
100
|
+
|
|
101
|
+
Each file requires `"version": 1`. Environment settings override private settings, which override project settings, which override built-in defaults. The start guide names each configured field's source: `environment`, `private` or `project`.
|
|
102
|
+
|
|
103
|
+
The project file supports `checks.beforeMerge`, `checks.required`, `tickets.command`, `tickets.nextCommand`, `queue.everySeconds` and `queue.lanes`. The private file supports these same fields plus `harness.name`, `harness.kind`, `harness.path`, `harness.command`, `harness.turn`, `harness.output`, `profiles`, `fallbackModels`, `tokenPrices`, `privateTexts`, `notify.command`, `watch.port`, `watch.actions`, `selfUpdate.everySeconds`, `github.command` and `workflows.folder`. Commands are arrays of arguments; seconds, lanes and ports are numbers. A watch port can also be `"off"`; watch actions are `"on"` or `"off"`.
|
|
104
|
+
|
|
105
|
+
For example, a shared `.cloving/config.json` can contain:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"version": 1,
|
|
110
|
+
"checks": { "beforeMerge": ["npm", "test"] }
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The private file can contain `{"version": 1, "privateTexts": ["acme"]}`. With `CLOVING_CHECK_COMMAND='["make", "check"]'`, the environment command takes priority. `harness.command` belongs to `harness.name`: `CLOVING_CLAUDE_COMMAND` overrides it for Claude, `CLOVING_CODEX_COMMAND` for Codex and `CLOVING_OPENCODE_COMMAND` for opencode. Without a saved command, `harness.path` replaces the program in that worker's default command. `harness.kind` accepts `file` or `shell`; shell harnesses run through `SHELL -l -i -c`, with command parts passed as separate arguments. `profiles` contains the profile map directly; `CLOVING_PROFILES` still selects a separate profiles file and overrides that map.
|
|
115
|
+
|
|
116
|
+
Read and change settings with the configuration commands:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
cloving config list
|
|
120
|
+
cloving config get checks.beforeMerge --json
|
|
121
|
+
cloving config set checks.beforeMerge '["npm","test"]'
|
|
122
|
+
cloving config set privateTexts '["acme"]'
|
|
123
|
+
cloving config set queue.lanes 2 --private
|
|
124
|
+
cloving config unset queue.lanes --private
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
All four commands accept `--json` and `--repository <directory>`. Use the setting names listed above; `profiles` and `tokenPrices` are whole JSON maps, and dictionary subpaths are unknown settings. Values are parsed as JSON, falling back to text. Changes are validated before writing and go to the field's default file; `--private` writes an override in the private file. `list` and `get` show each value's source, including `default`, and all hidden file values. JSON results have `schemaVersion: 1`; list returns a `settings` array, get returns `field`, `value`, `source` and `hidden`. A default `null` means no configured value. Mutation results include `field`, `scope`, `action` and `notice`; notice is null when no service is running.
|
|
128
|
+
|
|
129
|
+
Each change holds a file lock from read through atomic replacement, keeping the previous bytes as `<file>.bak`. A first write needs no backup. Private files, backups and temporary files have mode 0600. Locks from stopped processes are reclaimed; a live lock waits at most ten seconds, then exits with `settings-locked`. Invalid settings and unknown fields exit with status 2 without changing a file. For a running service, the result explains whether to restart to use the new value or whether an environment or private source will keep hiding it after a restart. Settings do not reload automatically.
|
|
130
|
+
|
|
131
|
+
Changing or unsetting `harness.name` clears kind, path and command. Changing or unsetting kind clears path and command; changing or unsetting path clears command. This keeps the previous harness's configuration from carrying into a new selection.
|
|
132
|
+
|
|
133
|
+
The package includes [the project schema](src/project-config.schema.json) and [the private schema](src/private-config.schema.json), also installed under `dist/src/`. Unknown fields fail with `unknown-setting: <field>`, invalid values with `invalid-setting: <field>`, and unsupported or missing versions with `unsupported-config-version: version`. Private fields in the project file fail with `private-setting-in-project-file: <field>`. Files cannot set runtime directories, ownership leases, host bounds or the watch repository; those remain environment settings.
|
|
134
|
+
|
|
135
|
+
Run `cloving doctor` from the repository before starting the service. It lists each requirement with a fixed code, `ok`, `fail` or `skip`, and a fixing command. Required failures and skipped prerequisites exit with status 1. `cloving doctor --json` prints the same checks as `{ "version": 1, "checks": [...] }` for setup agents.
|
|
136
|
+
|
|
137
|
+
Doctor probes harness versions and effective executable paths without running a turn. Shell scripts and simple `node script` wrappers receive `--version` from the repository folder; unsupported launcher arguments fail the probe. GitHub login and reads use the origin's host, including GitHub Enterprise. GitHub calls are reads limited to ten seconds each; a service probe waits at most five seconds. Recent Greptile activity does not establish that a usable review is available, and missing activity does not prove the app is absent.
|
|
138
|
+
|
|
200
139
|
## First run
|
|
201
140
|
|
|
202
141
|
This first run uses `develop-prompt` on a test repository. It needs no ticket command and no GitHub repository.
|
|
@@ -255,11 +194,13 @@ This first run uses `develop-prompt` on a test repository. It needs no ticket co
|
|
|
255
194
|
|
|
256
195
|
`cloving start` starts one Cloving service for the git repository of the current directory, with the workflow host, the Claude worker and the watch page. `--repository` selects a different repository. Stop the service with Control-C. A stop of the service stops a turn in work; submit that ticket again.
|
|
257
196
|
|
|
258
|
-
|
|
197
|
+
Each repository has its own default runtime directory: `<root>/cloving/<name>-<id>/run`. The root is `XDG_RUNTIME_DIR`, then `XDG_STATE_HOME`, then `~/.local/state`. The name keeps only letters, digits, `-` and `_` from the git top-level folder name, up to 24 characters. The ID is the first eight hexadecimal characters of the SHA-256 digest of that folder's real path. Commands in the repository find its service automatically; outside it, use `--repository <directory>` or `CLOVING_RUNTIME_DIR`. The start guide prints commands with the service repository already selected. `CLOVING_RUNTIME_DIR` overrides both the recorded adoption and the default for clients and start.
|
|
198
|
+
|
|
199
|
+
On start, Cloving keeps the old default runtime path only when all retained workflow bundles beside it validate for this repository. It records that choice under `<state>/cloving/adopted-runtime/<name>-<id>.json`, where state is `XDG_STATE_HOME` or `~/.local/state`. Clients reuse the record. With no bundles, foreign or mixed repositories, or damaged bundles, start uses the new default and prints a note with the old path. No state moves. The directory above the runtime directory still holds the authority state, Local World data, turn records and git work trees. Set a different `CLOVING_WATCH_PORT` for a second service.
|
|
259
200
|
|
|
260
|
-
`cloving watch` serves a local page on `http://127.0.0.1:3663/` (`--port` selects a different port). The page shows the runs, the events, the work of the
|
|
201
|
+
`cloving watch` serves a local page on `http://127.0.0.1:3663/` (`--port` selects a different port). The page shows the runs, the events, the work of the worker as it occurs, and each pending decision with its approve and deny commands. `cloving start` starts the page too; `CLOVING_WATCH_PORT` selects its port, and the value `off` starts none.
|
|
261
202
|
|
|
262
|
-
Each workflow board with supported inputs has **+ Add a card** at the bottom of its first column, including an empty board. It opens an inline form: **Issue number** for `develop-issue`, **Ticket ID** for `develop-ticket`, **Idea** for `develop-prd` and **Task** for `develop-prompt`. Declared workflows
|
|
203
|
+
Each workflow board with supported inputs has **+ Add a card** at the bottom of its first column, including an empty board. It opens an inline form: **Issue number** for `develop-issue`, **Ticket ID** for `develop-ticket`, **Idea** for `develop-prd` and **Task** for `develop-prompt`. Declared workflows use the string fields in their bundled declaration. Unsupported inputs have no control. Select **Add card**, press Enter in a one-line field, or Command/Control+Enter in a text area to send. **Cancel** and Esc close the form. The service reply shows the admitted run ID or a refusal code; admitted cards arrive through the existing live stream. For `workflow-active`, add `cloving: ready` to the issue to queue it.
|
|
263
204
|
|
|
264
205
|
The form sends the same run request as the CLI and retains its random command ID for retries, including a lost reply. Retry the same form to avoid duplicate runs. The write endpoint accepts at most 16 KiB of JSON, requires the page's random token, matching Origin and a loopback/localhost Host with the bound port, and allows no cross-origin preflight. `CLOVING_WATCH_ACTIONS=off` removes both the form control and the endpoint; actions are enabled by default. The **Start a run** section remains available with prompts to copy into agents. Decisions and other run actions still use their displayed CLI commands.
|
|
265
206
|
|
|
@@ -269,9 +210,10 @@ The form sends the same run request as the CLI and retains its random command ID
|
|
|
269
210
|
- `CLOVING_TICKET_COMMAND`: the ticket command (see "Daily development").
|
|
270
211
|
- `CLOVING_NOTIFY_COMMAND`: a JSON list naming a command to notify the operator about pending decisions, blocked runs and failed runs. It has no default.
|
|
271
212
|
- `CLOVING_NEXT_COMMAND`: a JSON list of a command and its arguments that prints the next ready ticket ID for `--next`. It has no default.
|
|
272
|
-
- `CLOVING_ANALYSIS_TOKEN_COMMAND`: a JSON command array that prints the subscription token just before an analysis turn (see "Protected chat analysis files"). It has no default.
|
|
273
213
|
- `CLOVING_CLAUDE_COMMAND`: a replacement of the `claude` command of the worker.
|
|
274
|
-
- `CLOVING_PROFILES`: a JSON file of named
|
|
214
|
+
- `CLOVING_PROFILES`: a JSON file of named turn profiles, read once when the service starts.
|
|
215
|
+
- `CLOVING_COMMANDS`: a JSON object mapping command names to argument arrays, such as `{"review-requests":["/opt/cloving/scan-reviews.mjs"]}`. Names contain 1–64 ASCII letters, digits or hyphens; the first argument must be an absolute executable path. Unset means no commands. Invalid values stop startup with `invalid-commands`.
|
|
216
|
+
- `CLOVING_COMMAND_SECONDS`: a whole number from 1 to 3600, default 60. Invalid values stop startup with `invalid-command-seconds`. A command exceeding the deadline fails with `command-timeout`.
|
|
275
217
|
- `CLOVING_WATCH_ACTIONS`: `off` disables starting runs from the board; default `on`.
|
|
276
218
|
- `CLOVING_WATCH_PORT`: the port of the watch page, or `off`.
|
|
277
219
|
- `CLOVING_WATCH_REPOSITORY`: repository context for a separate watch process. `start` sets this from its service repository. The watch process uses its local origin and has no current-directory default.
|
|
@@ -280,7 +222,56 @@ The form sends the same run request as the CLI and retains its random command ID
|
|
|
280
222
|
|
|
281
223
|
A command setting is a JSON list of texts, for example `CLOVING_CHECK_COMMAND='["npm", "test"]'`.
|
|
282
224
|
|
|
283
|
-
The notify command receives no added argument. Its standard input receives one JSON object with `event`, `runId`, `workflowId`, `subject` and `body`. The body contains the decision or outcome text already shown by `cloving questions` or `cloving status`, with complete response commands, up to 4000 characters. The daemon checks every 15 seconds
|
|
225
|
+
The notify command receives no added argument. Its standard input receives one JSON object with `event`, `runId`, `workflowId`, `subject` and `body`. The body contains the decision or outcome text already shown by `cloving questions` or `cloving status`, with complete response commands, up to 4000 characters. The daemon checks every 15 seconds. The first check with no notification record only records existing events.
|
|
226
|
+
|
|
227
|
+
The private `notify-events.json` journal records a notification as `pending` before calling the command and as `sent` after exit code 0. A failed command retries the same message after 1, 5, 15 and 60 minutes, with five attempts in total and a 30-second limit per attempt. The journal records attempt times and fixed failure reasons without command arguments or output. Pending and retryable failed entries resume after restart. Exhausted delivery logs one line naming its key. Old key-only records migrate as sent without sending them again.
|
|
228
|
+
|
|
229
|
+
Journal updates use a temporary file and rename. Sent and exhausted failed delivery details expire after seven days; their keys remain to prevent later duplicate delivery. Unfinished deliveries remain until resolved. A crash after command success but before recording that success can repeat delivery. Decisions opened and closed between checks can be missed.
|
|
230
|
+
|
|
231
|
+
### Answer decisions from a phone
|
|
232
|
+
|
|
233
|
+
Cloving can send its pending decisions to the Cloving app on your phone, and the phone can answer them. `cloving start` runs the phone relay as a child process. `CLOVING_PHONE_RELAY` with the value `off` starts no relay. `CLOVING_RELAY_ORIGIN` selects the relay server; the default is `https://cloving.ai`.
|
|
234
|
+
|
|
235
|
+
To connect a phone:
|
|
236
|
+
|
|
237
|
+
1. On the watch page, click **Connect a phone**. The page shows a QR code for 10 minutes.
|
|
238
|
+
2. Scan the code with the camera of the phone, or with **Pair a computer** in the app. Sign in to cloving.ai in the app when it asks.
|
|
239
|
+
3. In the app, click Connect. The watch page and the app show the name of the phone and the same 6-digit code.
|
|
240
|
+
4. Click **Accept** on the watch page only when the two codes are the same. The app checks the signed receipt of the computer, and the page shows **Connected**.
|
|
241
|
+
|
|
242
|
+
After that, each new decision goes to the connected phones. Answer a question with text. Approve or deny an approval with Face ID, a fingerprint or the screen lock of the phone; the phone signs approvals with a key for approvals only. Before the relay sends a decision, it checks the signature, the device, the age of the answer (10 minutes at most) and the prompt that the phone saw. The decision records the actor `device:<device ID>`, and `cloving status <run-id>` shows it. When a decision closes on the computer or expires, the phone removes it.
|
|
243
|
+
|
|
244
|
+
`cloving phone list` lists the phones. `cloving phone remove <device-id>` removes one phone. `cloving phone reset` removes the registration with cloving.ai and all phones. A removed phone gets no more decisions, and the relay refuses its answers.
|
|
245
|
+
|
|
246
|
+
What cloving.ai sees: the names of the computer and of each phone, the public keys, the times of the messages, the sizes of the sealed messages (padded to 1, 4, 16 or 64 KiB) and opaque IDs. The decisions, the prompts and the answers are end-to-end encrypted between this computer and your phones (HPKE with P-256 keys that stay on the devices), and each message is signed. cloving.ai cannot read or change them.
|
|
247
|
+
|
|
248
|
+
The questions of `analyze-chats` are private. Their full text goes only to your own paired phones, end-to-end encrypted, and the app shows it only after it opens the message. The preview for the lock screen keeps a fixed text.
|
|
249
|
+
|
|
250
|
+
### Daily loop facts
|
|
251
|
+
|
|
252
|
+
Run `cloving report --since 24 --json` for a versioned JSON report, or omit `--json` for text. `--since` accepts whole hours from 1 to 168 and defaults to 24. The window includes its start and excludes its end; the command captures the end time once. It reads the daemon's committed events and individual run status through the control socket. Set `CLOVING_WATCH_REPOSITORY` to the service repository to enable GitHub reads; its local origin supplies the host, owner and repository. There is no current-directory fallback. `CLOVING_GH_COMMAND` selects the GitHub command as a JSON argument array, defaulting to `gh`.
|
|
253
|
+
|
|
254
|
+
This is the first part of the daily report. The implemented values are:
|
|
255
|
+
|
|
256
|
+
| JSON value | Meaning |
|
|
257
|
+
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
258
|
+
| `metrics.tickets.completed` | Currently closed GitHub issues whose state reason is completed and whose latest closure falls in the window. Pull requests and issues closed for another reason are excluded. This is a current snapshot: reopening an issue removes that closure from these figures. |
|
|
259
|
+
| `metrics.pullRequests.merged` | Pull requests with a GitHub merge timestamp in the window. Closed, unmerged pull requests are excluded. |
|
|
260
|
+
| `metrics.runs.completed` | Terminal `run.completed` events with no outcome or with `handoff_requested`, excluding partial ticket handoffs. |
|
|
261
|
+
| `metrics.runs.partial` | Handoffs explicitly marked with partial ticket status. |
|
|
262
|
+
| `metrics.runs.blocked` | Terminal `run.completed` events with the blocked outcome. |
|
|
263
|
+
| `metrics.runs.failed` | Terminal `run.failed` events. |
|
|
264
|
+
| `metrics.runs.cancelled` | Terminal `run.cancelled` events. |
|
|
265
|
+
| `metrics.cost.medianRunUsd` | Median lifetime actual turn cost among runs ending in the window. Any unknown run cost makes this value unavailable. |
|
|
266
|
+
| `runCosts` | Each ended run's outcome, end time, actual total USD cost, individual turn operation IDs and actual prices, plus the historical daily median run-cost baseline. All of a run's turn cost is attributed to its end time, including turns before the window. Estimates never replace unknown actual prices. An observed run with no worker turns and settled effects has cost zero. A cancelled run that is still draining, or any unresolved effect, leaves its total unavailable. Active runs are excluded. |
|
|
267
|
+
|
|
268
|
+
Every metric has `value`, `comparisonValue`, `baseline` and `evidence`. Counts retain their requested-window totals in `value`; `comparisonValue` is their equivalent per 24 hours. Costs are not scaled. The baseline uses the seven consecutive 24-hour periods immediately before the window, anchored at the same time of day in UTC. It contains the median and the band from median minus three times the median absolute deviation to median plus three times that deviation. Only values strictly outside the band receive `mark: "outside"`. Individual run costs use the band of daily median run costs. With fewer than five complete observed days, `history` is `short`, the median and band are null, and there is no mark. Empty observed days count as zero for counts; days with no cost sample or with an unknown cost do not establish a daily cost median. An unrepresentable numeric band has unavailable history and no mark. Daemon history starts at its earliest retained event; GitHub history starts at repository creation.
|
|
269
|
+
|
|
270
|
+
Evidence includes the contributing run IDs and event sequences, issue numbers or pull request numbers. Baseline evidence is separate from current-window evidence. Run cost details also expose each turn's operation ID. The daemon event read is pinned to the first page's latest sequence; later appended events are excluded. Run status and GitHub sources are separate reads, so the report is not an atomic cross-source snapshot.
|
|
271
|
+
|
|
272
|
+
Each GitHub read uses the existing retry rules: transient network errors and HTTP 500, 502, 503 and 504 receive up to three attempts, with growing waits of 30 and 60 seconds. Read failure, invalid data, an unknown actual price or missing repository context is represented as `unavailable` with a null metric value, rather than zero. A failed source does not hide readable sources. `sources` shows which reads succeeded; the command exits zero when it produces a report, including one with unavailable sources. Invalid command arguments exit two.
|
|
273
|
+
|
|
274
|
+
The `coverage` object explicitly marks the remaining facts unavailable: immutable ticket closure and reopening history, fix rounds per merged pull request, ready-to-merge lead time, phase durations, lane utilization and ready-ticket idle time, peak active runs, skips and unsuccessful streaks, first-attempt main CI passes, flaky failures and test names, reverted work, review scores, CI runner/OS utilization and limits, time-outs, reruns and runner waits, completed-ticket costs across attempts, and operator merges, reruns, labels and restarts. These facts are not inferred from the implemented counts. The report performs no model call, writes no state and changes no queue.
|
|
284
275
|
|
|
285
276
|
## Daily development
|
|
286
277
|
|
|
@@ -289,7 +280,7 @@ The notify command receives no added argument. Its standard input receives one J
|
|
|
289
280
|
- `develop-prompt` takes the text of a task. Use it for a small task with no ticket, or for a first try in a new repository.
|
|
290
281
|
- `develop-ticket` takes a ticket ID and reads the ticket itself. Use it for the normal work on a ticket, when you want to read each diff and merge the branch yourself.
|
|
291
282
|
- `develop-issue` takes a ticket ID too, and sends the work through a GitHub pull request. Use it for a small ticket with clear criteria, in a GitHub repository that Greptile reviews and that has a check command. It is the workflow of the continuous process.
|
|
292
|
-
- `develop-prd` takes an idea, researches the code and web, reviews a draft PRD, and posts it as a GitHub issue. Set `CLOVING_PRIVATE_TEXTS` to a nonempty JSON list before starting the daemon. A refused post preserves the worktree and draft.
|
|
283
|
+
- `develop-prd` takes an idea, researches the code and web, reviews a draft PRD, and posts it as a GitHub issue. It then hands the issue number to `develop-issue` to build automatically. Set `CLOVING_PRIVATE_TEXTS` to a nonempty JSON list before starting the daemon. A refused post preserves the worktree and draft. If the handoff is refused, it adds `cloving: ready` so the issue queue can build it later. The PRD run retains `issue-N` as its promotion, shows the refusal code in its outcome reason, and removes its worktree after posting. If the fallback label fails, status also shows `issueUpdate: failed`; inspect the GitHub operation and queue the issue after resolving the failure.
|
|
293
284
|
|
|
294
285
|
```sh
|
|
295
286
|
cloving start # terminal 1, in the repository
|
|
@@ -308,6 +299,8 @@ The rule: read the diff before you approve.
|
|
|
308
299
|
|
|
309
300
|
Claude turns have web research tools by default, an empty GitHub CLI configuration folder, and no GitHub token environment variables. This is not a sandbox: a turn can still find other credentials. The PRD refusal rules catch the listed private text and common sensitive forms; unlisted private names can pass. A `post-uncertain` result requires checking GitHub before trying another run.
|
|
310
301
|
|
|
302
|
+
`cloving status RUN_ID --json` includes `run.input`, and `cloving status --json` includes `input` on each listed run. It is the normalized submission admitted with the run, such as `{"ticket":"297"}` for `develop-issue` or the submitted fields of a declared workflow. It survives service restarts and excludes resolver-added ticket text and repository paths. Older runs without a stored submission show `null`. The text status of one run shows `Input:` as compact JSON, limited to the first 200 characters with `…` appended when longer. Terminal control and hidden format characters are escaped.
|
|
303
|
+
|
|
311
304
|
### The ticket command
|
|
312
305
|
|
|
313
306
|
`CLOVING_TICKET_COMMAND` (a JSON list of texts) names the ticket command. The contract of a ticket command:
|
|
@@ -337,11 +330,31 @@ CLOVING_TICKET_COMMAND='["github-ticket"]' cloving start
|
|
|
337
330
|
|
|
338
331
|
`cloving run develop-issue --next` and `cloving run develop-ticket --next` use the reserved ticket ID `next`, just like `--ticket next`. Set `CLOVING_NEXT_COMMAND` to a JSON list naming your selection command and its arguments. The daemon adds no argument. The command chooses a ready ticket in your tracker and prints its ID; the daemon trims it and reads the ticket with `CLOVING_TICKET_COMMAND`. Both commands share the resolver's 30-second deadline. Selection logic stays in your command, and each invocation submits only one run.
|
|
339
332
|
|
|
333
|
+
Set `CLOVING_QUEUE_SECONDS` to a whole number from 10 to 3600 to poll for ready tickets. It requires both ticket commands. The queue starts the declared workflow with `"queue": true`; when none declares it, the bundled `develop-issue` takes the queue. `CLOVING_QUEUE_LANES` sets the number of simultaneous runs, from 1 to 4, and requires `CLOVING_QUEUE_SECONDS`; its default is 1. For example, `CLOVING_QUEUE_LANES=4` admits at most one new ticket each cycle until four lanes are busy. Each run has its own branch and work tree. Manual submissions to the queue workflow share this limit and receive `workflow-active` when all lanes are busy. A ticket already being developed receives `ticket-active`. After a restart that moves the queue to another workflow, active runs of earlier queue workflows still occupy lanes and exclude their tickets.
|
|
334
|
+
|
|
335
|
+
The optional declaration field `queue` is a boolean. `"queue": true` requires a field named `ticket` with `"type": "ticket"` (`queue-needs-ticket-field`), and every other input field must be optional (`queue-input-incomplete`). Two queue declarations stop startup with `queue-workflow-conflict`. `cloving doctor` requires `next-command` and `queue-interval` whenever a declared workflow takes the queue.
|
|
336
|
+
|
|
337
|
+
Before enabling more than one lane, update the next-ticket command to honor `CLOVING_SKIP_TICKETS`. Cloving sets this environment variable to space-separated IDs of active tickets and tickets waiting for dependencies. The command must never print an ID from that list. It should print nothing when no eligible ticket remains. The service also checks selected tickets before admission.
|
|
338
|
+
|
|
339
|
+
In the ticket text returned by `CLOVING_TICKET_COMMAND`, each `#N` in a sentence starting with **Needs** inside `## Dependencies` is a hard dependency. For example, `Needs #123 and #124.` waits until both GitHub issues close. Other references and sections do not create dependencies. Cloving reads dependency state with `CLOVING_GH_COMMAND` (default `gh`); an unavailable or invalid response prevents admission. An open dependency returns `ticket-dependency-open` and puts the ticket in the skip list. Waiting tickets are checked again each cycle with a free lane, so a closed dependency makes its ticket eligible again.
|
|
340
|
+
|
|
341
|
+
`cloving status` and watch display busy lanes and skipped tickets with their reasons. A pending self-update holds new admissions while active lanes drain. Three unsuccessful runs in a row, counted in the order they finish, stop the queue; a successful handoff clears the streak. Ordinary restarts preserve it. After inspecting and reconciling the failures, start with `--reset-queue` to clear the streak. A conflict caused by an earlier lane's merge returns its diagnostics to a fix round. Lanes do not add CI runners or parallelize turns within one run.
|
|
342
|
+
|
|
340
343
|
An empty selection gives `no-ready-ticket`; a failed command gives `next-ticket-not-available`. Missing configuration gives `next-source-not-configured`. Multiple IDs, an invalid ID, or `next` as the selected ID give `invalid-ticket-id`. Use only one of `--next`, `--ticket`, `--prompt` and `--input`.
|
|
341
344
|
|
|
345
|
+
A saved harness named `grok` runs `grok --output-format streaming-messages-json --always-approve -p <turn text>` in the work tree. `CLOVING_GROK_COMMAND` replaces its command; the turn text follows `-p` or `--single` as one argument, or is appended if neither flag is present. Grok runs without approval prompts and has the same GitHub credential isolation as the other workers. Completion requires exit status 0 and a terminal result with `subtype: "success"` and `is_error: false`. The terminal result supplies the report and ticket status. Grok capacity errors are not retried.
|
|
346
|
+
|
|
347
|
+
Grok token counts come only from the terminal result, including its subagents. Input includes uncached input and cache creation; cache reads and output remain separate. Missing, all-zero or stderr-warned incomplete usage shows `Token usage not known`. An estimate requires complete usage attributed to exactly one model with a configured price. Mixed models and missing prices show `Price not known`. Grok uses token-based estimates and ignores its projected cost field. No Grok model prices are built in.
|
|
348
|
+
|
|
342
349
|
### Review rounds of `develop-ticket` and `develop-prompt`
|
|
343
350
|
|
|
344
|
-
Worker turns retry an exact Codex capacity refusal or a Claude overload after 60 seconds, then 180 seconds
|
|
351
|
+
Worker turns retry an exact Codex capacity refusal or a Claude overload after 60 seconds, then 180 seconds. Claude worker turns also retry a failed result whose text starts with `API Error: Can't reach the API server`: the first retry waits 10 minutes and the second waits 40 minutes. The retry number selects the wait for its kind, with at most three attempts across both kinds. Other failures are not retried. Each wait starts after the failed attempt ends. Stopping during a wait returns `stopped`; a daemon restart fails the recorded operation for existing recovery.
|
|
352
|
+
|
|
353
|
+
Each retry keeps the work tree, profile, budget and limits, and starts a new session with a continuation line before the full prompt. After any network failure, every later attempt gets the network continuation, which asks it to check earlier commits, pushes, review comments and messages, avoid repeating completed writes, and stop to report a write whose result is unknown. It also gets fresh git evidence for commits since this turn's first attempt. Cloving fetches the branch from `origin` and marks each commit `pushed`, `not pushed` or `unknown`; an absent origin branch means `not pushed`. Each git command has a 30-second limit and is cancelled by drop. Failed reads give explicit unknown evidence.
|
|
354
|
+
|
|
355
|
+
Before a retry, Cloving checks the work tree's git index lock, including linked work trees. It never removes a lock: `worktree-index-locked` records its path and tells the operator to remove it when no git process uses the work tree. An unreadable index path gives `worktree-index-unknown`. Neither failure starts another attempt. The step keeps one operation identity and sums usage across attempts. Private turn journals, status text and JSON, and watch retain each attempt's model, token counts, cost estimate and `retryKind` (`capacity`, `network`, or `null` for other results). Older attempts without the field show no kind. Attempt kinds appear after the turn ends.
|
|
356
|
+
|
|
357
|
+
`CLOVING_FALLBACK_MODELS` accepts a JSON array of at most three nonempty model names, for example `'["gpt-6-astra"]'`. Without the setting or with `[]`, every attempt keeps the main model. The first attempt and first retry use the main model; each later attempt uses the next configured fallback within the existing attempt limit. With three attempts, only the first fallback can be used. The worker command must explicitly name a model with `-m NAME`, `--model NAME` or `--model=NAME`; otherwise it stays unchanged and the private journal records `fallback-model-unavailable`. Fallbacks apply only when the preceding attempt ended with a capacity refusal. A network retry keeps the model of the configured command. A fallback model can have a higher price. Each attempt's estimate uses its own model's price; an unknown price or missing token count leaves the total estimate unknown. Malformed settings refuse daemon start with `invalid-fallback-models`.
|
|
345
358
|
|
|
346
359
|
A run of `develop-ticket` or `develop-prompt` has one to three review rounds. Each round does one Claude Code turn in the work tree of the run, on the branch `cloving/<ticket id>-…`, and then opens one approval. The first round makes the work tree. The approval text has the round, the ticket ID, the branch, the commits, the changed files and the report of the worker. Read the diff before you decide. The decision has these results:
|
|
347
360
|
|
|
@@ -355,14 +368,44 @@ Single-run status (`cloving status RUN_ID`, also with `--json`) and watch show e
|
|
|
355
368
|
|
|
356
369
|
The built-in table uses standard short-context prices read on **2026-10-18** from the [OpenAI pricing page](https://openai.com/api/pricing/), in USD per million tokens:
|
|
357
370
|
|
|
358
|
-
| Model
|
|
359
|
-
|
|
|
360
|
-
| `gpt-6.1-sol` |
|
|
361
|
-
| `gpt-6-astra` | 10.00 |
|
|
371
|
+
| Model | Input | Cached input | Output |
|
|
372
|
+
| ------------- | ----: | -----------: | -----: |
|
|
373
|
+
| `gpt-6.1-sol` | 2.00 | 0.10 | 10.00 |
|
|
374
|
+
| `gpt-6-astra` | 10.00 | 1.00 | 50.00 |
|
|
362
375
|
|
|
363
376
|
`CLOVING_TOKEN_PRICES` accepts a JSON object mapping model names to non-negative finite `input`, `cachedInput` and `output` prices per million tokens. Its entries override matching built-in entries and retain the others. Estimates use the reported model or the worker command's model argument, with `estimatedCostUsd` totals only when every ended turn has an estimate. There is no live price lookup; estimates do not change budgets or limits.
|
|
364
377
|
|
|
365
|
-
|
|
378
|
+
Each turn selects its harness in this order: profile `harness`, `CLOVING_WORKER`, private `harness.name`, then `claude`. `CLOVING_WORKER` changes the default for one start; a profile can still override it. Native workers are `claude`, `codex`, `cursor-agent`, `gemini`, `grok` and `opencode`. Other harnesses need a private `harness.turn` command form; without one, startup stops with `harness-no-worker` and its name. Private command, path and kind settings apply only to the matching harness. Status, its JSON and watch show each turn's harness.
|
|
379
|
+
|
|
380
|
+
For another harness, set its name and one-shot command in the private file:
|
|
381
|
+
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"version": 1,
|
|
385
|
+
"harness": {
|
|
386
|
+
"name": "mytool",
|
|
387
|
+
"kind": "file",
|
|
388
|
+
"turn": ["mytool", "--print", "{prompt}"],
|
|
389
|
+
"output": "text"
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
`turn` must be a nonempty list of strings. Its program must be nonempty and cannot be `{prompt}`. One later argument can be exactly `{prompt}`; it receives the turn text as a literal argument. Without that argument, the text goes to standard input. `harness.command` takes precedence over `turn`; otherwise `harness.path`, when set, replaces the program in `turn`. These private values apply only to the selected private harness. The command runs in the worktree, using the login shell only for kind `shell`.
|
|
395
|
+
|
|
396
|
+
`output` accepts only `text` and defaults to it. The generic worker retains the end of standard output as the report within the normal turn result limits, preserving the ticket status parsed from the full output separately. A nonzero exit fails the turn and reports the end of standard error. Cost and token counts remain null; status shows “Price not known” and “Token usage not known”. `cloving doctor` probes the program without running a turn and recommends `cloving config set harness.turn '[...]'` when its command form is missing.
|
|
397
|
+
|
|
398
|
+
Cursor Agent runs `cursor-agent -p --output-format stream-json --force` in the run's work tree. Turn text below 64 KiB in UTF-8 is its final argument; larger text goes through standard input to stay below operating-system argument limits. It uses the last assistant text as the report and requires exit status 0 and a terminal result with `subtype: "success"` and `is_error: false`. File and command tools appear in watch activity. Error events and incomplete streams fail the turn. Capacity errors do not retry. GitHub token variables are removed and each turn gets an empty temporary GitHub configuration directory.
|
|
399
|
+
|
|
400
|
+
The [Cursor output reference](https://cursor.com/docs/cli/reference/output-format) defines the older stream used by version 2026.01.28 without counts. These runs show `Token usage not known`. For newer terminal `usage` objects, Cloving accepts only four nonnegative safe integer fields: `inputTokens`, `outputTokens`, `cacheReadTokens` and `cacheWriteTokens`. Cursor's [published cache explanation](https://forum.cursor.com/t/discrepancies-between-cursor-cli-usage-and-billing/164471/7) defines uncached input separately from reads and writes, with counts covering the whole turn; Cloving retains each category separately, including optional `cache_creation_input_tokens` in JSON. Other shapes remain unknown. No Cursor prices are built in. A configured price produces an estimate when cache writes are zero; positive writes leave the estimate unknown because the price table has no cache-write rate. Cursor's [headless cost explanation](https://forum.cursor.com/t/cost-information-in-cursor-cli-headless-mode/164583/7) distinguishes CLI counts from billed charges.
|
|
401
|
+
|
|
402
|
+
With `codex`, `CLOVING_CODEX_COMMAND` replaces the native Codex command and must still emit native Codex events. A shared-format wrapper uses `CLOVING_CLAUDE_COMMAND` with the `claude` worker selection. Its last JSONL result supplies `total_cost_usd` and `usage.input_tokens`, `usage.cache_read_input_tokens` and `usage.output_tokens`, including on errors. Report only the usage and price of that command call; the wrapper must normalize resumed sessions or multi-input accounting. Cloving does not infer the output format from the executable or model name or reconstruct missing usage from old logs.
|
|
403
|
+
|
|
404
|
+
With `gemini`, the default command is `gemini --output-format stream-json --approval-mode yolo -p <turn text>`, with no approval prompts. `CLOVING_GEMINI_COMMAND` replaces the command and fixed arguments; Cloving appends `-p` and the turn text. Turn text of 64 KiB or more in UTF-8 goes through standard input without `-p`, to stay below operating-system argument limits; Gemini runs headless because its input is not a terminal. The [Gemini headless stream](https://geminicli.com/docs/cli/headless/) supplies the last assistant message as the report. Completion requires exit status 0, a successful result event and no event with error severity. Gemini capacity errors are not retried. Status retains each model's token counts. Input includes cached tokens; estimates price uncached input and cache reads separately at each model's configured price, then sum them. Missing counts show `Token usage not known`; a missing price for any model makes the estimate unknown. There are no built-in Gemini prices; add them through `tokenPrices` or `CLOVING_TOKEN_PRICES`. Gemini turns use an empty GitHub configuration directory and receive no GitHub token variables.
|
|
405
|
+
|
|
406
|
+
With `opencode`, the default is `opencode run --format json "<turn text>"` in the work tree. `CLOVING_OPENCODE_COMMAND` replaces the command prefix; Cloving appends the turn text as one argument. Model options use `provider/model`. Cloving sets `OPENCODE_PERMISSION='{"*":"allow"}'` for unattended edits and shell commands under default OpenCode permissions. Explicit project or agent permission rules may still take precedence; `opencode run` auto-rejects any remaining approval requests. Cloving uses the same empty GitHub configuration folder and stripped GitHub token variables as the other workers. See [CLI configuration](https://opencode.ai/docs/cli/#environment-variables) and [permissions](https://opencode.ai/docs/permissions/).
|
|
407
|
+
|
|
408
|
+
The opencode worker requires exit status 0, no `error` event and a last `step_finish` with `part.reason: "stop"`. Its last assistant `text` is the report, including the ticket status line. Counts on `step_finish` describe individual steps, so Cloving sums them; output includes both output and reasoning tokens. Input and cache read counts remain separate. The two-step fixture in `test/opencode-fixture.ts` follows the [v1.3.17 JSON event emitter](https://github.com/anomalyco/opencode/blob/v1.3.17/packages/opencode/src/cli/cmd/run.ts), [step processing](https://github.com/anomalyco/opencode/blob/v1.3.17/packages/opencode/src/session/processor.ts) and [usage normalization](https://github.com/anomalyco/opencode/blob/v1.3.17/packages/opencode/src/session/index.ts). This version's normal events omit the model; the command's model option supplies the fallback. A reported model takes priority when present. Missing counts show `Token usage not known`; without a matching price entry the cost is `Price not known`. No opencode prices are added, and opencode capacity errors are not retried.
|
|
366
409
|
|
|
367
410
|
Set `CLOVING_PROFILES` to a file such as:
|
|
368
411
|
|
|
@@ -370,6 +413,7 @@ Set `CLOVING_PROFILES` to a file such as:
|
|
|
370
413
|
{
|
|
371
414
|
"profiles": {
|
|
372
415
|
"checker": {
|
|
416
|
+
"harness": "claude",
|
|
373
417
|
"env": { "REVIEW_ONLY": "1", "TURN_NAME": "{turn}" },
|
|
374
418
|
"allowedTools": ["Read", "Grep", "Skill", "Agent"],
|
|
375
419
|
"maxBudgetUsd": 15,
|
|
@@ -380,11 +424,11 @@ Set `CLOVING_PROFILES` to a file such as:
|
|
|
380
424
|
}
|
|
381
425
|
```
|
|
382
426
|
|
|
383
|
-
A workflow selects a profile with a `protectedEffect` request for capability `claude` and method `run-turn:checker` or `run-turn:fixer`. Profile names contain 1–119 ASCII letters, digits, hyphens or underscores. All fields are optional
|
|
427
|
+
A workflow selects a profile with a `protectedEffect` request for capability `claude` and method `run-turn:checker` or `run-turn:fixer`. Profile names contain 1–119 ASCII letters, digits, hyphens or underscores. All fields are optional. `harness`, `env` and `workflows` work with each harness. Native workers also accept `model`; a generic worker refuses it with `profile-model-not-supported`. `model` replaces existing `-m`, `--model` or `--model=` options, or adds one model option. Only `allowedTools`, `permissionMode` and `maxBudgetUsd` require Claude; using them with another harness gives `profile-needs-claude`. Unspecified fields keep the selected worker's defaults. Flags after `--` remain unchanged.
|
|
384
428
|
|
|
385
|
-
`workflows` restricts a profile to the service's registered workflow IDs; omitting it allows every workflow. Each turn receives its run ID and journal operation key joined with a hyphen in `CLOVING_TURN_ID`. That value replaces each `{turn}` in profile environment values. Variables added by one profile do not carry into the next turn. The four GitHub token variables and `GH_CONFIG_DIR` are forbidden in profiles; turns retain an empty GitHub configuration directory. Invalid files stop startup with `invalid-profiles`. Turns refuse unknown profiles with `unknown-profile
|
|
429
|
+
`workflows` restricts a profile to the service's registered workflow IDs; omitting it allows every workflow. Each turn receives its run ID and journal operation key joined with a hyphen in `CLOVING_TURN_ID`. That value replaces each `{turn}` in profile environment values. Variables added by one profile do not carry into the next turn. The four GitHub token variables and `GH_CONFIG_DIR` are forbidden in profiles; turns retain an empty GitHub configuration directory. Invalid files stop startup with `invalid-profiles`. Turns refuse unknown profiles with `unknown-profile` and disallowed workflows with `profile-not-allowed`.
|
|
386
430
|
|
|
387
|
-
To run a Claude or
|
|
431
|
+
To run a Claude, Codex or opencode turn on an existing branch, a custom workflow sets
|
|
388
432
|
the effect target to `branch:feature-search`. The service finds that branch's
|
|
389
433
|
linked work tree in its own repository. The main checkout is excluded. Before
|
|
390
434
|
each turn, the work tree must be clean, including untracked files, submodule
|
|
@@ -424,9 +468,17 @@ Merged runs retain `handoff_requested` with `ticketStatus: complete` or `partial
|
|
|
424
468
|
|
|
425
469
|
To resume a queue stopped at this limit, stop the service with Control-C, inspect the failures and reconcile the affected issues, then run `cloving start --reset-queue` with the same repository, runtime and queue settings. This clears the failure streak once and preserves tracked commands. Ordinary restarts, including automatic self-updates, preserve the count. Resetting the queue does not change issue labels or retry blocked issues.
|
|
426
470
|
|
|
427
|
-
At the confidence score 5/5, the worker merges the pull request with a merge commit, with no human approval. Then it removes the remote branch, the work tree and the local branch. With a lower score, the Claude worker gets the summary and the findings of the review for one more round. After three such rounds, or after 60 minutes with no review, the run stops and the pull request stays open.
|
|
471
|
+
At the confidence score 5/5, the worker runs the local merge check and waits for CI of the exact head it will merge, including an updated head pushed after merging the default branch. When CI passes, it merges the pull request with a merge commit, with no human approval. Then it removes the remote branch, the work tree and the local branch. With a lower score, the Claude worker gets the summary and the findings of the review for one more round. After three such rounds, or after 60 minutes with no review, the run stops and the pull request stays open.
|
|
472
|
+
|
|
473
|
+
Set `CLOVING_REQUIRED_CHECKS` to a JSON list of check run names, such as `["quality", "quality_linux"]`, to require those checks. Without the setting, all GitHub Actions checks on the head commit are required. The newest run of each name decides; `success`, `neutral` and `skipped` pass. With no setting and no Actions checks after a 60-second grace period, a repository without CI can merge. A configured missing check keeps waiting. CI is polled every 30 seconds with a separate 90-minute limit. A timeout returns `ci-pending` and blocks without a fix round.
|
|
474
|
+
|
|
475
|
+
When a GitHub Actions check fails in its first workflow attempt, Cloving requests one rerun of the failed jobs and keeps waiting within the same 90-minute CI limit. The operation journal retains the request before sending it, the first failure output and the original deadline, so a daemon restart cannot request another rerun. Status and watch show “CI rerun after a failed check”. A failed rerun request reports a transient GitHub error.
|
|
428
476
|
|
|
429
|
-
|
|
477
|
+
A second failure returns `ci-failed` with the outputs of both attempts, including failed check names, failed steps and failed log tails, with private texts hidden and at most 6,000 characters in total. Checks outside GitHub Actions or without an Actions run link retain the immediate failure behavior. The failure starts a worker fix round that must repair CI, including failures the pull request did not cause, and commit the fix. The new commit receives a new Greptile review and CI gate. Review, local check, conflict and CI repairs share the existing limit of three fix rounds.
|
|
478
|
+
|
|
479
|
+
The watch board keeps blocked `develop-issue` runs in `Stopped`. A validated local merge report shows `PR merged · #N`. A later completed run for the same established GitHub issue must have its own validated merge report to show `Ticket completed by later run`. The original and later run have separate links. Either local proof prevents a GitHub read. These marks preserve the recorded run outcome.
|
|
480
|
+
|
|
481
|
+
Otherwise, the board reads the recorded pull request with `CLOVING_GH_COMMAND` (default `gh`). It validates the PR and issue identities against the configured repository's local origin. The first queued or active read shows `Checking PR`. A confirmed merge shows `PR merged · #N`; a failed read shows `PR status unavailable`. An unmerged PR shows `Needs attention`. A separate `Stale` mark appears when the last successful unmerged observation is older than 120 seconds, including after a failed refresh. A successful refresh resets its age. Confirmed merges never become stale.
|
|
430
482
|
|
|
431
483
|
Reads run in the background, one at a time, with a five-second limit. Board loads can refresh unresolved PRs at least 60 seconds after the previous attempt started. Confirmed merges remain cached until the watch process stops. A later normal page load shows each result.
|
|
432
484
|
|
|
@@ -466,7 +518,7 @@ Set up Cloving for my repository REPOSITORY_PATH. Ask me for each value that you
|
|
|
466
518
|
|
|
467
519
|
1. Check the platform: macOS or Linux on arm64 or x64, with the Node and npm versions of "engines" in package.json of Cloving. Check that git and the claude command work. If I want develop-issue, check that gh has a login and that Greptile reviews the GitHub repository.
|
|
468
520
|
2. If I have no Cloving checkout, clone https://github.com/cloving-ai/cloving-ai.git to ~/cloving. In the checkout, run: npm ci --ignore-scripts
|
|
469
|
-
3. Write a launcher ~/.local/bin/cloving that runs src/cli.ts of the checkout with the correct node program, and make it executable. Do not use npm link
|
|
521
|
+
3. Write a launcher ~/.local/bin/cloving that runs src/cli.ts of the checkout with the correct node program, and make it executable. Do not use npm link. Make sure that ~/.local/bin is on the search path and that "cloving --version" works.
|
|
470
522
|
4. Ask me which tracker has my tickets. Write a ticket command for that tracker in ~/.local/bin. The command gets a ticket ID as its last argument and prints the text of the ticket. For develop-issue, the first line is "# " and the title, and one line is "GitHub issue: OWNER/REPOSITORY#NUMBER". Test the command with one ticket ID, and show me the output.
|
|
471
523
|
5. Start the service in a terminal session that stays open, for example a tmux session. In that session, in my repository, run: CLOVING_TICKET_COMMAND='["TICKET_COMMAND"]' cloving start
|
|
472
524
|
For develop-issue, also set CLOVING_CHECK_COMMAND to the checks of my repository. Give me the address of the watch page.
|
|
@@ -556,14 +608,24 @@ Steps:
|
|
|
556
608
|
|
|
557
609
|
## Develop a workflow
|
|
558
610
|
|
|
611
|
+
`cloving start` loads workflows from three folders:
|
|
612
|
+
|
|
613
|
+
- The bundled `workflows/` folder in the Cloving package. Startup compiles each TypeScript file in this folder.
|
|
614
|
+
- The repository `.cloving/workflows/` folder.
|
|
615
|
+
- The user `cloving/workflows/` folder under an absolute `XDG_CONFIG_HOME`, otherwise under `~/.config`. An unset or relative `XDG_CONFIG_HOME` uses that default.
|
|
616
|
+
|
|
617
|
+
The private setting `workflows.folder` replaces the user folder with an absolute folder path, for example a private git repository of your own workflows. Set it with `cloving config set workflows.folder <path>`. Cloving reads the folder where it is. The folder itself must not be a symbolic link; a link in the path above it is allowed. `cloving workflows list` shows its workflows with the scope `user`.
|
|
618
|
+
|
|
559
619
|
A workflow is a TypeScript file in the form of `workflows/develop-ticket.ts`:
|
|
560
620
|
|
|
561
621
|
- An exported async function with the directive `"use workflow"` as its first statement. The directive needs the compiler of the Workflow SDK, not only TypeScript.
|
|
562
|
-
- The first argument is the input of the run. A resolver of the daemon makes it, for example `cycleId`, `ticketId`, `ticket` and `repository`.
|
|
563
|
-
- The step helpers come from the subpaths of `cloving/workflows`: `protectedEffect` from `cloving/workflows/steps/protected-effect`, `decisionGate` from `cloving/workflows/steps/decision-gate`
|
|
622
|
+
- The first argument is the input of the run. A resolver of the daemon makes it, for example `cycleId`, `ticketId`, `ticket` and `repository`. With `decisionGate`, the host calls the function again with the decision (`gateId`, `decision` and `answer`) as the second argument. The function must examine that argument before it asks for an effect, as `decided()` in `workflows/develop-ticket.ts` does. With `waitForDecision`, the decision returns to the same call.
|
|
623
|
+
- The step helpers come from the subpaths of `cloving/workflows`: `protectedEffect` from `cloving/workflows/steps/protected-effect`, `decisionGate` from `cloving/workflows/steps/decision-gate`, `waitForDecision` from `cloving/workflows/steps/wait-for-decision`, `loadCycle` from `cloving/workflows/steps/load-cycle`, `handoff` from `cloving/workflows/steps/handoff`, `waitForRun` from `cloving/workflows/steps/wait-for-run` and `runCommand` from `cloving/workflows/steps/run-command`. The type `CycleOutcome` comes from `cloving/workflows`. An import of `cloving/workflows` starts no service.
|
|
564
624
|
- `protectedEffect` asks the daemon for one effect of a capability, for example a Claude turn. `decisionGate` opens a decision for a person.
|
|
565
625
|
- The function returns a `CycleOutcome`: `waiting_for_decision`, `handoff_requested` or `blocked`.
|
|
566
626
|
|
|
627
|
+
For `handoff_requested`, the daemon retains the workflow's `promotionId` in the completion event. An optional bounded `reason` records a handoff refusal while preserving the promotion. `cloving status <runId>` shows the promotion as `Promotion:` and the reason beside the outcome; JSON status includes `run.outcome.promotionId` and `run.outcome.reason`. Watch retains the promotion in the run model and completed-run details. This identifier is an opaque reference and grants no authority.
|
|
628
|
+
|
|
567
629
|
```ts
|
|
568
630
|
import type { CycleOutcome } from "cloving/workflows";
|
|
569
631
|
import { decisionGate } from "cloving/workflows/steps/decision-gate";
|
|
@@ -628,37 +690,391 @@ export async function developTicket(
|
|
|
628
690
|
|
|
629
691
|
In this example, an approval completes the run and a denial ends it. `workflows/develop-ticket.ts` also starts one more round after a denial with a feedback text.
|
|
630
692
|
|
|
693
|
+
New workflows can await a decision in the same call:
|
|
694
|
+
|
|
695
|
+
```ts
|
|
696
|
+
import { waitForDecision } from "cloving/workflows/steps/wait-for-decision";
|
|
697
|
+
|
|
698
|
+
export async function review(input: { cycleId: string }) {
|
|
699
|
+
"use workflow";
|
|
700
|
+
const decision = await waitForDecision({
|
|
701
|
+
gateId: "review-1",
|
|
702
|
+
kind: "approval",
|
|
703
|
+
prompt: "Approve this change?",
|
|
704
|
+
});
|
|
705
|
+
return {
|
|
706
|
+
kind: "handoff_requested",
|
|
707
|
+
cycleId: input.cycleId,
|
|
708
|
+
promotionId: decision.decision,
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
`waitForDecision` accepts the same request as `decisionGate` and returns `{ decision, answer }`. The decision is `approved`, `denied`, `answered` or `expired`. An optional `expiresAt` resumes the wait with `expired` and `answer: null`. The SDK hook survives daemon restarts and replay. Gate IDs belong to a run, so different runs can use the same gate ID. The existing questions, decision commands and binding values apply; dropping the run cancels the wait. `waitForDecision` is also exported by `cloving/workflows`.
|
|
714
|
+
|
|
715
|
+
Use `sleep({ seconds })` inside a workflow to pause between short outside-state checks. Whole seconds from 1 through 86400 are accepted; other requests reject with `invalid-sleep-seconds` before any wait. Import it from `cloving/workflows` or `cloving/workflows/steps/sleep`. It uses the SDK workflow timer and consumes no protected effect units or slots.
|
|
716
|
+
|
|
717
|
+
For example, configure an operator-supplied `approval-status` command to make one read, print `{"ready":false}` or `{"ready":true}`, and exit. Cloving does not ship this command. Set `CLOVING_COMMAND_SECONDS=10` to bound each command; this setting applies to all configured commands. The following candidate declaration permits at most 30 command units:
|
|
718
|
+
|
|
719
|
+
```json
|
|
720
|
+
{
|
|
721
|
+
"workflow": {
|
|
722
|
+
"workflowId": "approval",
|
|
723
|
+
"effectLimit": 30,
|
|
724
|
+
"capabilities": [
|
|
725
|
+
"command"
|
|
726
|
+
],
|
|
727
|
+
"commands": [
|
|
728
|
+
"approval-status"
|
|
729
|
+
],
|
|
730
|
+
"input": {
|
|
731
|
+
"maximumBytes": 1024,
|
|
732
|
+
"fields": {
|
|
733
|
+
"request": {
|
|
734
|
+
"type": "string"
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
"methods": {
|
|
740
|
+
"command": [
|
|
741
|
+
"run:approval-status"
|
|
742
|
+
]
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
The workflow checks immediately, then sleeps 60 seconds only between pending checks, with at most 30 checks and 29 sleeps:
|
|
748
|
+
|
|
749
|
+
```ts
|
|
750
|
+
import { runCommand, sleep } from "cloving/workflows";
|
|
751
|
+
|
|
752
|
+
export async function approval(input: {
|
|
753
|
+
cycleId: string;
|
|
754
|
+
input: { request: string };
|
|
755
|
+
}) {
|
|
756
|
+
"use workflow";
|
|
757
|
+
for (let round = 1; round <= 30; round += 1) {
|
|
758
|
+
const result = await runCommand({
|
|
759
|
+
scope: { phase: "approval", operation: "check", round },
|
|
760
|
+
name: "approval-status",
|
|
761
|
+
input: { request: input.input.request },
|
|
762
|
+
});
|
|
763
|
+
if (result.kind !== "completed")
|
|
764
|
+
return blocked(input.cycleId, result.reason ?? result.kind);
|
|
765
|
+
let response: unknown = null;
|
|
766
|
+
try {
|
|
767
|
+
if (result.result !== null) response = JSON.parse(result.result);
|
|
768
|
+
} catch {
|
|
769
|
+
response = null;
|
|
770
|
+
}
|
|
771
|
+
if (response === null || typeof response !== "object" || Array.isArray(response)
|
|
772
|
+
|| !("ready" in response) || typeof response.ready !== "boolean")
|
|
773
|
+
return blocked(input.cycleId, "external-condition-invalid-result");
|
|
774
|
+
if (response.ready)
|
|
775
|
+
return { kind: "handoff_requested", cycleId: input.cycleId, promotionId: "external-condition-ready" };
|
|
776
|
+
if (round < 30) await sleep({ seconds: 60 });
|
|
777
|
+
}
|
|
778
|
+
return blocked(input.cycleId, "external-condition-timeout");
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function blocked(cycleId: string, reason: string) {
|
|
782
|
+
return { kind: "blocked", cycleId, reason, evidenceId: "approval-status" };
|
|
783
|
+
}
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
Each completed command releases its effect slot before sleeping. Only boolean `false` means pending; every non-completed command stops with its reason or kind, and missing, malformed or wrongly shaped output stops with `external-condition-invalid-result`. Thirty pending checks return `external-condition-timeout`. The bound limits attempts; command durations and capacity waits add elapsed time. An overdue SDK timer resumes the next check without scheduling catch-up checks.
|
|
787
|
+
|
|
788
|
+
A recorded SDK timer retains its wake time through replay. Combined command recovery, four-waiter capacity release, cancellation and installed polling restart qualification remain acceptance prerequisites. The source analyzer estimates one command call for this early-exit loop; the runtime enforces the full budget. Observed approval is data: subsequent effects still need their own authorization and enough budget. Adding a sleep cannot shorten a command that already polls internally.
|
|
789
|
+
|
|
790
|
+
Use `reviewByPerson` to request approval of an immutable commit or draft:
|
|
791
|
+
|
|
792
|
+
```ts
|
|
793
|
+
import { reviewByPerson } from "cloving/workflows/steps/review-by-person";
|
|
794
|
+
|
|
795
|
+
const review = await reviewByPerson({
|
|
796
|
+
subject: { draft: "The proposed issue text." },
|
|
797
|
+
});
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
Call it inside a workflow. The result is `{ accepted, feedback, commit, subjectDigest }`; only an approval sets `accepted` to `true`. Feedback contains the decision's answer, or an empty string. A commit subject is `{ commit, target }`, where `commit` is the full 40 or 64 character lowercase commit ID and `target` identifies the run's work tree. To review an existing pull, supply its `commit` together with the run target. A draft subject is `{ draft, commit? }`, where the optional commit identifies its base; the result's `commit` is always `null` for a draft. Drafts must contain text, and commit subjects need a nonempty target.
|
|
801
|
+
|
|
802
|
+
The SHA-256 `subjectDigest` identifies the exact subject, including a draft's text and optional base commit. A changed subject opens a different approval. Repeating the same subject within a run reuses its recorded decision. The helper returns that decision in the original call through `waitForDecision`, and is also exported by `cloving/workflows`. Workflow tests supply its decisions through the fake bridge's `decisions` queue; person reviews consume no protected effect budget.
|
|
803
|
+
|
|
804
|
+
For a Greptile review of a pull request, use the same result contract:
|
|
805
|
+
|
|
806
|
+
```ts
|
|
807
|
+
import { reviewByAgent } from "cloving/workflows/steps/review-by-agent";
|
|
808
|
+
|
|
809
|
+
const subject = { number: pull.number, commit: pull.commit, target: runTarget };
|
|
810
|
+
const review = await reviewByAgent({ subject: subject, reviewer: { kind: "greptile" } });
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
Replacing that call with `reviewByPerson({ subject })` preserves the result type and subject digest. Greptile needs the pull number and immutable commit; drafts can use person or turn review. Only a score of 5/5 sets `accepted: true`. Feedback includes the summary and findings with their file locations. Responses must name the requested pull and commit; malformed or unrelated results reject with `invalid-review-result`. Failed effects reject with the effect's reason.
|
|
814
|
+
|
|
815
|
+
`reviewByAgent` uses the existing GitHub `wait-review` worker method and one protected effect unit per distinct subject. Its candidate declaration must include the `github` capability and `methods: { "github": ["wait-review"] }` with a sufficient effect limit. The broker still controls authority and budgets. Both the subpath above and `cloving/workflows` export the helper. Fake scenarios provide worker results in `effects["github wait-review"]`. A turn reviewer uses the same subject and result:
|
|
816
|
+
|
|
817
|
+
```ts
|
|
818
|
+
const review = await reviewByAgent({
|
|
819
|
+
subject: subject,
|
|
820
|
+
reviewer: { kind: "turn", profile: "checker" },
|
|
821
|
+
});
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
The named profile runs through the existing `claude` capability and `run-turn:checker` method, using that profile's configured harness and restrictions. The declaration must name that exact method and capability; the helper consumes one protected effect unit. The workflow call must contain a literal profile name. Commit reviews use the subject's run target and ask the reviewer to inspect that exact commit. Draft reviews use a separate work tree identified by the draft's subject digest. Repeating a subject with the same profile reuses its operation; changing the subject or profile starts a new review operation.
|
|
825
|
+
|
|
826
|
+
The report must end with `Review verdict: accept` or `Review verdict: change`. The worker records this verdict separately before truncating the report. Only `accept` sets `accepted: true`; feedback is the report text before the verdict, within the worker's report limits. A missing final verdict returns `accepted: false` and `The review turn has no verdict line.` A failed turn rejects with its effect reason. Fake scenarios provide `{ summary, reviewVerdict }` worker results through `effects["claude run-turn:checker"]`, with `reviewVerdict` set to `"accept"`, `"change"` or `null`.
|
|
827
|
+
|
|
828
|
+
Use `mergeWhenGreen` to merge a pull request when its checks pass:
|
|
829
|
+
|
|
830
|
+
```ts
|
|
831
|
+
import { mergeWhenGreen } from "cloving/workflows/steps/merge-when-green";
|
|
832
|
+
|
|
833
|
+
const pull = { number: opened.number, url: opened.url, commit: opened.commit, target: runTarget };
|
|
834
|
+
const merge = await mergeWhenGreen({ pull: pull, check: "beforeMerge" });
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
`check: "beforeMerge"` runs the `checks.beforeMerge` command of the settings before the merge. When that setting is missing, the result is `{ merged: false, reason: "check-not-configured", output: "" }`, and the pull request stays open. `check: null` merges with no local check, also after an update from the default branch. A missing setting never turns the check off. In both cases, CI must pass on the updated head. The review of the pull commit stays valid when the step updates the branch from the default branch.
|
|
838
|
+
|
|
839
|
+
The result is `{ merged, reason, output }`. After a merge, `merged` is `true` and `reason` is `null`. For `check-failed`, `merge-conflict` and `ci-failed`, `output` holds the check, conflict or CI output for a fix turn. `ci-pending` means that CI did not finish within the wait limit. To try the merge again, call the step with a higher `round`, for example `{ pull: pull, check: "beforeMerge", round: 2 }`. `round` is a positive integer and defaults to 1. A call with the same pull, commit and round uses the same operation, so a resumed workflow does not merge twice. Other failures reject with the effect reason, and a malformed result rejects with `invalid-merge-result`. An invalid `pull` or `check` rejects with `invalid-merge-request` before any request.
|
|
840
|
+
|
|
841
|
+
Replace that call with `mergeByPerson({ pull: pull, check: "beforeMerge" })` to let a person decide when the merge starts. The step opens an approval for the pull number and commit. After an approval, it merges with the same check rules and `round`. After a denial, the result is `{ merged: false, reason: "not-approved", output }`, where `output` is the answer of the person, and no merge starts. The approval belongs to the pull commit, so a retry with a higher `round` does not ask again.
|
|
842
|
+
|
|
843
|
+
Both steps use the existing GitHub `merge` worker method and one protected effect unit. The candidate declaration must include the `github` capability and `methods: { "github": ["merge"] }`. The step subpaths and `cloving/workflows` export the step functions. Import the types `PullRequest`, `MergeRequest` and `MergeResult` from `cloving/workflows`. Fake scenarios give worker results in `effects["github merge"]` and person decisions in `decisions`.
|
|
844
|
+
|
|
631
845
|
The workflow tests run the workflow function with a fake step bridge (`test/develop-workflow-fixture.ts`). The fixture resolves `cloving/workflows` to the files of `src`, records each effect request and gate request, and gives the results of a scenario. `test/develop-ticket-workflow.test.ts` and `test/develop-issue-workflow.test.ts` use it.
|
|
632
846
|
|
|
633
|
-
`cloving
|
|
847
|
+
Use `cloving workflows list` to inspect bundled, project and user workflows without a service. `--json` prints an array with `scope`, `workflowId`, `file`, `admitted`, `codes` and each valid parsed declaration. Invalid declarations have a null ID. Inspection uses the same folder admission and compiler as startup in a disposable project directory.
|
|
848
|
+
|
|
849
|
+
Put tests beside each folder workflow as `<name>.test.ts`, then run `cloving workflows test <workflow-id>`. Omit the ID to run all available project and user workflow tests. Both commands accept `--repository <directory>`. The Node test runner prints its results and supplies the exit status. Unknown IDs report `unknown-workflow`; known workflows without tests report `workflow-test-missing`. Neither needs a running service.
|
|
850
|
+
|
|
851
|
+
The package contains workflow templates. `cloving templates list` shows each template with its summary and its number of decision points, and `cloving templates show <name>` shows its decision points and its workflow files. With `--json`, `list` prints an array of `{ name, summary, points }`, and `show` prints `{ template, files }`, where `template` is the content of `template.json`.
|
|
852
|
+
|
|
853
|
+
`cloving templates copy <name>` writes the workflow file, its helper folder, its declaration and its test into `.cloving/workflows/` of the current repository (or of `--repository <directory>`), and prints the decision points and the next command `cloving workflows test <workflow-id>`. The declaration of the copy names its template in the field `template`. `--id <workflow-id>` gives the copy another workflow ID: the command renames the files, the helper folder, the workflow ID and the exported function, and changes the imports and the test references. The ID uses lowercase letters, digits and single hyphens and starts with a letter; another ID fails with `invalid-workflow-id`. A copy writes no file when a workflow file or folder of that ID is already present, and fails with `workflow-exists`. The command writes the files into a temporary folder first, and then puts each file in place; after a failure, it removes the files that it put in place. When two workflows of the project and user folders have `"queue": true` after a copy, the command prints `queue-workflow-conflict`; select "a person" for the select point of one copy.
|
|
634
854
|
|
|
635
|
-
|
|
855
|
+
Tests can import `runScenario` from `cloving/workflows/testing`:
|
|
856
|
+
|
|
857
|
+
```typescript
|
|
858
|
+
import assert from "node:assert/strict";
|
|
859
|
+
import { test } from "node:test";
|
|
860
|
+
import { runScenario } from "cloving/workflows/testing";
|
|
861
|
+
|
|
862
|
+
await test("develop uses the fake worker result", async () => {
|
|
863
|
+
const turn = {
|
|
864
|
+
kind: "completed" as const,
|
|
865
|
+
operationId: "turn-1",
|
|
866
|
+
receipt: null,
|
|
867
|
+
result: "fake result",
|
|
868
|
+
};
|
|
869
|
+
const result = await runScenario({
|
|
870
|
+
workflow: new URL("./develop.ts", import.meta.url).href,
|
|
871
|
+
exportName: "develop",
|
|
872
|
+
input: {},
|
|
873
|
+
turn,
|
|
874
|
+
});
|
|
875
|
+
assert.deepEqual(result.outcome, turn);
|
|
876
|
+
assert.equal(result.requests.length, 1);
|
|
877
|
+
});
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
Each scenario runs in an isolated Node process. `effects` maps `"<capability> <method>"` to result queues; `gate` supplies a decision-gate result, `effects.handoff` supplies handoff results, `waits` supplies run-wait outcomes, `decisions` supplies `{ decision, answer }` results for `waitForDecision` in call order, and `decision` supplies the workflow's second argument. A missing `waitForDecision` result throws `no-scenario-result`. Sleep requests are recorded in order and complete immediately in both testing APIs, without consuming the simulated effect budget. An optional `effectLimit` simulates a budget and adds `spent` to the result. Missing effect or gate results return `unavailable`. For tests that import the workflow directly, `using bridge = fakeStepBridge(scenario)` installs the same fake bridge until disposal and exposes `requests` and `spent`. It refuses to replace an active host bridge. The test command resolves public Cloving imports from its own package, including a global installation, without adding a package to the consumer repository.
|
|
881
|
+
|
|
882
|
+
The four built-in registrations (`develop-ticket`, `develop-issue`, `develop-prd` and `develop-prompt`) keep their existing input contracts and resolvers.
|
|
883
|
+
|
|
884
|
+
For a repository or user folder workflow, use one `<name>.ts` file with one exported workflow function. Put one `<name>.declaration.json` file beside it. Function names and workflow IDs must be unique across the folders and bundled workflows.
|
|
885
|
+
|
|
886
|
+
Discovery considers files in the folder root and ignores `.d.ts` and `.test.ts` files. Put local TypeScript helpers in subfolders: for example, `develop.ts` can import `./develop/prompts.ts`. Helpers can import further `.ts` files through `./` paths within those subfolders and need no workflow function or declaration. Imports with parent traversal, absolute paths or a `node_modules` component are refused, as are package imports outside the public `cloving/workflows` modules.
|
|
887
|
+
|
|
888
|
+
Every imported helper must be a regular file, with no links in its path. Its imports and effects receive the same checks as the workflow, and their combined effects count against the workflow's declared limit. Startup captures and retains all imported helper bytes in the workflow generation. Editing a helper changes the digest for new runs; existing runs keep their earlier helpers after restart.
|
|
889
|
+
|
|
890
|
+
The declaration has two fields: `workflow` contains the workflow declaration, and `methods` maps each declared capability to its permitted methods. Each list must contain at least one nonblank method name and no duplicates. Do not add method keys for undeclared capabilities. The `isCandidateDeclaration` check in `src/workflow-candidate.ts` validates this form:
|
|
636
891
|
|
|
637
892
|
```json
|
|
638
893
|
{
|
|
639
|
-
"
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
"
|
|
644
|
-
|
|
645
|
-
"
|
|
646
|
-
|
|
894
|
+
"workflow": {
|
|
895
|
+
"workflowId": "review-run",
|
|
896
|
+
"effectLimit": 14,
|
|
897
|
+
"capabilities": ["claude"],
|
|
898
|
+
"input": {
|
|
899
|
+
"maximumBytes": 65536,
|
|
900
|
+
"fields": {
|
|
901
|
+
"prompt": { "type": "string" },
|
|
902
|
+
"rounds": {
|
|
903
|
+
"type": "integer",
|
|
904
|
+
"minimum": 1,
|
|
905
|
+
"maximum": 10,
|
|
906
|
+
"optional": true
|
|
907
|
+
}
|
|
908
|
+
}
|
|
647
909
|
}
|
|
910
|
+
},
|
|
911
|
+
"methods": {
|
|
912
|
+
"claude": ["run-turn"]
|
|
648
913
|
}
|
|
649
914
|
}
|
|
650
915
|
```
|
|
651
916
|
|
|
652
|
-
The ID accepts 1–64 letters, digits or hyphens. The effect limit is 1–100; capabilities
|
|
917
|
+
The ID accepts 1–64 letters, digits or hyphens. The effect limit is 1–100; capabilities contain one or more of `claude`, `github` and `command`. The input byte limit is 1–1048576, subject to the service's own limit. Fields accept nonempty strings, integers with optional inclusive bounds, booleans, or tickets. Fields are required unless `optional` is `true`; unknown fields are rejected. Bounds apply only to integers.
|
|
918
|
+
|
|
919
|
+
A declaration can have one `"ticket": { "type": "ticket" }` field, named `ticket`. Submit its ID as a string. When it is the only required field, use `cloving run <workflow-id> --ticket 312` or `--next`. You can also use `--input ticket.json` with `{"ticket":"312"}`; include every other required field in that file. At admission, the daemon reads the configured ticket command and replaces the ID with `{ "id": "312", "text": "# ..." }` inside the workflow's `input`. `--next` selects the ID through the configured next command. Missing sources keep the `ticket-source-not-configured` and `next-source-not-configured` refusals. The `Needs #N` dependency rule and `ticket-active` refusal apply as for `develop-issue`. Active tickets are checked within each workflow, so different workflows can work on the same ticket. Doctor requires `ticket-command` for these declarations, and the watch form labels the field **Ticket ID**.
|
|
920
|
+
|
|
921
|
+
Optional `commands` lists distinct names from `CLOVING_COMMANDS`; executable paths and arguments belong only in the operator setting. The `command` capability requires at least one name. Registration refuses an unknown name with `command-unknown`. The execution pin retains these names with each run, and authority refuses `command` effects whose `run:<name>` method is absent from that run's pinned list with `command-not-declared`. Reordering names preserves the pin; changing names changes it. Omitted and empty lists preserve legacy pins.
|
|
922
|
+
|
|
923
|
+
Optional `commandWaits` currently supports declaration validation and execution pinning only; the polling helper and separate time allowance are not implemented yet. It maps names of 1–64 ASCII letters, digits or hyphens to entries containing exactly `command`, `maximumWaitSeconds` and `pollEverySeconds`. The command must be declared in `commands`, configured in `CLOVING_COMMANDS` and permitted by the `command` capability. Maximum wait is an integer from 60 through 2,592,000 seconds; polling interval is an integer from 60 through 86,400 seconds and cannot exceed that maximum. For example, `"commandWaits": { "team-review": { "command": "review-state", "maximumWaitSeconds": 2592000, "pollEverySeconds": 3600 } }` pins that named permission. Reordering entries preserves the execution pin; changing a name, command, duration or interval changes it and refuses incompatible resumption. Legacy runs retain their original pin without wait permission. Ordinary `runCommand` calls still spend effect units, including calls to a command named in this map. Operators must choose read-only, repeat-safe checks; command execution does not enforce read-only access.
|
|
924
|
+
|
|
925
|
+
Internal command delivery supports an earlier absolute deadline in preparation for polling waits. An attempt uses the shorter of `CLOVING_COMMAND_SECONDS` and the remaining milliseconds, refuses to spawn at or after expiry, and reports `command-wait-timeout` if it settles too late. A recorded result still replays after expiry; an unfinished journaled command retains `command-interrupted` recovery. This internal cap is not exposed through workflow requests and supplies no budget exemption. Durable polling admission, scheduling and deadline enforcement at authority commit remain pending.
|
|
926
|
+
|
|
927
|
+
Use `runCommand` inside a workflow to request a named command:
|
|
928
|
+
|
|
929
|
+
```typescript
|
|
930
|
+
import { runCommand } from "cloving/workflows/steps/run-command";
|
|
931
|
+
|
|
932
|
+
const result = await runCommand({
|
|
933
|
+
scope: { phase: "scan", operation: "reviews", round: 1 },
|
|
934
|
+
name: "review-requests",
|
|
935
|
+
input: {},
|
|
936
|
+
});
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
The helper is also exported from `cloving/workflows`. It accepts serializable JSON input, sends its JSON text through `protectedEffect`'s host bridge with capability `command`, method `run:<name>`, target `<name>` and one effect unit, and returns the same `ProtectedResult` union. A completed result's `result` field contains the stored output text. Refusals retain their fixed reason, including `command-not-declared`; without a host it returns `unavailable` with `cloving-host-unavailable`. The scope identifies the logical operation using the same rules as `protectedEffect`; it grants no authority. Candidate admission requires a literal command name and counts each call against the effect limit.
|
|
940
|
+
|
|
941
|
+
The daemon starts the operator command without a shell in the repository folder and writes the JSON input to stdin. It uses the Claude turn environment, including its GitHub credential isolation, plus `CLOVING_RUN_ID` and `CLOVING_OPERATION_ID`. Stdout is limited to 1 MiB of bytes and decoded as UTF-8 text; invalid byte sequences become replacement characters. Stderr is limited to 64 KiB and retained only in the effect journal as `command.stderr.log`. Exceeding either output limit fails with `command-output-limit`; a nonzero exit fails with `command-exit`, with its exit status retained in the journal and status display. An unavailable command name fails with `command-unknown`.
|
|
942
|
+
|
|
943
|
+
The journal records a claim before starting the process. Completed effects replay their stored result after restart. A claimed effect without a result fails with `command-interrupted` after restart and never starts again, including when the earlier process may have written data. Each logical operation starts at most one command; use a new operation scope for a new invocation. The watch run page and `cloving status <run>` show each command name, state, exit status and duration.
|
|
944
|
+
|
|
945
|
+
Optional `handoffs` lists distinct registered workflow IDs. Registration refuses unknown targets. The execution pin retains these permissions with each run: target order does not affect the pin, while adding or removing a target changes it. Omitted and empty lists keep existing pins. Stored permissions survive restart and retained workflow versions. The built-in `develop-prd` declares `develop-issue` as its only handoff target and calls it after creating an issue; the other built-in workflows declare none. A refused PRD handoff adds `cloving: ready` for the queue and records the refusal in the PRD run's outcome.
|
|
946
|
+
|
|
947
|
+
Use `handoff` inside a workflow to submit input to a declared target:
|
|
948
|
+
|
|
949
|
+
```typescript
|
|
950
|
+
import { handoff } from "cloving/workflows/steps/handoff";
|
|
951
|
+
|
|
952
|
+
const runId = await handoff({
|
|
953
|
+
workflowId: "develop-issue",
|
|
954
|
+
input: { ticket: "CLV-123" },
|
|
955
|
+
});
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
The step returns the target run ID after admission through the operator submission path. It spends one protected-effect unit. The caller’s pinned `handoffs` list must include the target; otherwise the step fails with `handoff-not-declared`. The target validates and resolves the input through its normal submission contract. Refusal fails with `handoff-input-refused` and admits no target run. If the target returns `workflow-active`, the worker makes at most ten attempts, one second apart, then fails with `handoff-start-timeout`. The worker retains the active-target refusal count across restarts. Replays reuse command ID `handoff:<operation ID>` and return the same run ID; separate step calls have distinct operation IDs. After a lost submission reply, recovery replays that same command ID only while the source run, lease, budget and workflow pin still permit dispatch. Status checks and stop never start a target run.
|
|
959
|
+
|
|
960
|
+
Hand-offs record `parentRunId` and `chainId` (the first run's ID) in the new run's admission. A chain permits at most five runs, including the first; attempting a sixth fails with `chain-limit` and starts no run. This fixed limit follows ancestry, so separate children of the same parent do not consume each other's chain length. The records and limit survive service restarts. `cloving status` shows the parent, chain and child run IDs in text and JSON; watch cards link to parent and child runs across workflows.
|
|
961
|
+
|
|
962
|
+
Use `waitForRun` to await the outcome of a run that the current run started with `handoff`:
|
|
963
|
+
|
|
964
|
+
```typescript
|
|
965
|
+
import { handoff, waitForRun } from "cloving/workflows";
|
|
966
|
+
|
|
967
|
+
const runId = await handoff({ workflowId: "size-ticket", input: {} });
|
|
968
|
+
const outcome = await waitForRun({ runId, maximumWaitSeconds: 60 });
|
|
969
|
+
if (
|
|
970
|
+
outcome.kind === "handoff_requested" &&
|
|
971
|
+
outcome.promotionId === "size-small"
|
|
972
|
+
)
|
|
973
|
+
await handoff({ workflowId: "take-ticket", input: {} });
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
`maximumWaitSeconds` must be an integer from 1 to 86400. The result contains `kind`, `reason` and `promotionId`; absent reason or promotion values are `null`. A wait for an unknown run, a run started by another run, or an invalid bound fails with `run-wait-refused`. Expiry fails with `run-wait-timeout`. Waiting spends no effect units. Several handoffs to a workflow without `when-idle` admission create distinct child runs that can each be awaited.
|
|
977
|
+
|
|
978
|
+
The compiled SDK step uses durable retries and its original start time, so restarting the daemon preserves the deadline, includes downtime in the wait and replays the same terminal outcome. `cloving status` shows condition `waiting-run` and the awaited run ID (`awaitedRunId` in JSON); watch cards show a link labeled “Waiting for run”.
|
|
979
|
+
|
|
980
|
+
Optional `stages` contains 1–12 entries in board order. Each entry has a unique `name` (1–64 ASCII letters, digits or hyphens), a unique `label` (1–40 printable characters on one line, without outer spaces), and any combination of `operations`, `gateKinds` and `gateIds`. At least one selector array must be nonempty; the others may be omitted or empty. Operations match the `scope.operation` passed to `protectedEffect`; gate IDs match the `gateId` recorded when a gate opens. Both use 1–128 ASCII letters, digits or `._:/-`. Gate kinds are `approval` and `question`. Selectors cannot repeat within an array or belong to two stages of the same selector type within one workflow. Different selector types may share text, and different workflows may reuse IDs. Unknown fields and labels `Other`, `Done` and `Stopped` are rejected.
|
|
981
|
+
|
|
982
|
+
For declared stages, the board and its `data.json` response use the latest committed `effect.requested` or `gate.opened` sequence, preserving that event's start time. Effect requests select by operation. Gate openings first search every stage for an exact, case-sensitive gate ID match, regardless of declaration order; without one, they fall back to the gate kind. Unmatched events and runs without a relevant event use Other. Unused valid IDs are allowed. Decisions and worker output do not advance a card; later requests or gate openings can move it back. Declared labels are followed by Other, Done and Stopped. Existing terminal retention rules take precedence. Unstaged workflows and `develop-issue` keep their existing columns and placement. Declarations load at service startup; stages provide display metadata and grant no capabilities.
|
|
983
|
+
|
|
984
|
+
For example, a `review-flow` declaration can distinguish successive approval gates without another workflow or handoff step:
|
|
985
|
+
|
|
986
|
+
```json
|
|
987
|
+
{
|
|
988
|
+
"stages": [
|
|
989
|
+
{
|
|
990
|
+
"name": "your-review",
|
|
991
|
+
"label": "Your review",
|
|
992
|
+
"gateIds": ["your-review"]
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
"name": "team-review",
|
|
996
|
+
"label": "Team review",
|
|
997
|
+
"gateIds": ["team-review"]
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
"name": "ready-to-merge",
|
|
1001
|
+
"label": "Ready to merge",
|
|
1002
|
+
"gateIds": ["merge-review"]
|
|
1003
|
+
}
|
|
1004
|
+
]
|
|
1005
|
+
}
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
Each committed opening moves the run's single card to the matching column. Completion with outcome kind `handoff_requested` places it in Done; failure, cancellation or completed outcome `blocked` places it in Stopped. Those terminal columns retain the twenty newest cards within the inclusive past seven days. Other completed outcomes have no terminal card. Done is reserved and cannot be declared as a stage.
|
|
653
1009
|
|
|
654
1010
|
After startup, `cloving status` lists the declared ID. Submit JSON with `cloving run review-run --input input.json`. The function receives `{ cycleId, repository, input }`, where `cycleId` is `task-` followed by 12 characters derived from the command ID, `repository` is the service repository, and `input` retains the checked JSON values. Invalid input is rejected before a run starts. Invalid declarations or duplicate workflow IDs or function names stop startup.
|
|
655
1011
|
|
|
1012
|
+
A declared workflow can include an interval schedule:
|
|
1013
|
+
|
|
1014
|
+
```json
|
|
1015
|
+
"schedule": { "everySeconds": 300, "input": {} }
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
`everySeconds` must be a whole number from 60 through 86400, and `input` must satisfy the workflow's submission contract, including its byte limit. The resolved input, including the daemon's cycle and repository fields, must also fit the service's 262144-byte document limit. Set `CLOVING_SCHEDULES` to a JSON object such as `{"review-queue":{"everySeconds":600,"input":{}}}` to replace a declaration's schedule or schedule an otherwise unscheduled declared workflow. Only registered declared workflow IDs are accepted. Invalid settings fail startup with `invalid-schedules`, `unknown-schedule-workflow`, `invalid-schedule-interval` or `invalid-schedule-input`. Invalid declaration files retain the `invalid-workflow-declaration` refusal.
|
|
1019
|
+
|
|
1020
|
+
The first run starts after one interval. Slots retain increasing numbers in private Cloving state (`state/schedules.json`); each submission uses the operator command ID `schedule:<workflowId>:<slot>`. Restart preserves the next boundary and retries an unacknowledged submission with its saved input and command ID. Missed intervals coalesce into the latest due slot rather than a burst of catch-up runs. Changing the interval starts a fresh interval while retaining slot numbers and any active run.
|
|
1021
|
+
|
|
1022
|
+
An earlier scheduled run blocks later scheduled slots while queued, running, waiting for a decision or paused. Each overlap records a `schedule.skipped` event on that earlier run, with `workflowId` and `slot`, without changing its status or observation. Manual runs remain independent. The `develop-issue` queue continues to use `CLOVING_QUEUE_SECONDS`.
|
|
1023
|
+
|
|
1024
|
+
This first scheduling slice does not yet implement schedule pause/resume commands, stopping and notification after repeated failures, or schedule displays and scheduled-run marks in status and watch. Schedule settings load at startup. A scheduler transport or journal failure stops scheduling and writes a diagnostic; reconcile the state and restart to retry a pending slot. Full product admission remains subject to the external verification policy.
|
|
1025
|
+
|
|
1026
|
+
Startup retains each complete compiled project under `workflow-bundles` in private daemon state, beside the generated `project` directory. Each content-addressed snapshot includes workflow sources, declarations, registration manifests, all compiler artifacts, and the installed dependency graph and Node identity. Snapshots bind to the canonical repository and survive later source edits or removal. Storage lookup validates every captured file and returns `pinned-workflow-unavailable` for missing or changed bytes; startup refuses reuse of a matching damaged snapshot without repairing it. No retained generation is deleted automatically. Before Local World starts, the daemon loads the retained generations needed by unfinished runs. Their starts, replays and continuations use the recorded bundle, including after workflow removal; new submissions use current registrations. Duplicate answers and replay preserve effect receipts and spending.
|
|
1027
|
+
|
|
1028
|
+
### Project and user workflows
|
|
1029
|
+
|
|
1030
|
+
At daemon startup, Cloving also checks these folders on both macOS and Linux:
|
|
1031
|
+
|
|
1032
|
+
- Project: `.cloving/workflows` under the repository selected by `cloving start --repository REPOSITORY` (the current repository by default).
|
|
1033
|
+
- User: `$XDG_CONFIG_HOME/cloving/workflows` when `XDG_CONFIG_HOME` is absolute; otherwise `$HOME/.config/cloving/workflows`. macOS uses these same XDG rules.
|
|
1034
|
+
|
|
1035
|
+
Missing folders are left absent. Discovery reads top-level `.ts` files, excluding `.d.ts`, and requires a matching `<name>.declaration.json`. Unlike the bundled declaration above, an operator declaration wraps the workflow metadata and lists the effect methods it may request:
|
|
1036
|
+
|
|
1037
|
+
```json
|
|
1038
|
+
{
|
|
1039
|
+
"workflow": {
|
|
1040
|
+
"workflowId": "project-review",
|
|
1041
|
+
"effectLimit": 1,
|
|
1042
|
+
"capabilities": ["claude"],
|
|
1043
|
+
"input": {
|
|
1044
|
+
"maximumBytes": 1024,
|
|
1045
|
+
"fields": { "task": { "type": "string" } }
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
"methods": { "claude": ["run-turn"] }
|
|
1049
|
+
}
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
Use one exported async workflow function with `"use workflow"` and the public `cloving/workflows` helpers. Each capability requires a nonempty list of distinct method names. Declaration, source and actual SDK compilation must pass admission before registration. Declaring a method grants no execution authority; the broker still enforces each run's capabilities and budget. Submit inputs with `cloving run project-review --input input.json`.
|
|
1053
|
+
|
|
1054
|
+
Admitted project and user workflows appear beside the bundled registrations. A refused neighbor does not stop valid workflows from loading. `cloving status --json` exposes `workflowRefusals`; status and watch show each refusal's scope, safe file label and codes without its source text. These codes identify the failed check:
|
|
1055
|
+
|
|
1056
|
+
| Codes | Meaning |
|
|
1057
|
+
| --- | --- |
|
|
1058
|
+
| `invalid-declaration` | Missing, malformed or invalid declaration. |
|
|
1059
|
+
| `forbidden-import`, `missing-export` | Import outside the public helper surface, or an unavailable named export. |
|
|
1060
|
+
| `unsupported-source`, `dynamic-effect`, `direct-effect` | Unsupported source form, a nonliteral effect request, or a direct effect outside helpers. |
|
|
1061
|
+
| `undeclared-effect`, `effect-limit` | Requested method absent from the declaration, or statically known work exceeding the limit. |
|
|
1062
|
+
| `workflow-conflict`, `bundled-name-reserved` | Conflicting basename, workflow ID or function name across operator files, or collision with a bundled registration. |
|
|
1063
|
+
| `unsafe-workflow-path`, `workflow-read-failed`, `workflow-source-changed` | Unsafe file or folder path, unreadable input, or input changed during capture. |
|
|
1064
|
+
| `compile-failed` | Candidate analysis or SDK compilation could not complete. |
|
|
1065
|
+
|
|
1066
|
+
Project and user files form one conflict set; neither scope overrides the other. Sources and declarations are captured at startup. Edits take effect on the next start, when new runs use the newly admitted version. A restored question retains its original prompt and decision identity. After a source edit and a restart, unfinished runs continue on the retained generation that they started with, as the paragraph on `workflow-bundles` above describes, and new runs use the newly admitted version.
|
|
1067
|
+
|
|
1068
|
+
Every operator run binds to the daemon's canonical repository, including workflows loaded from the user folder. Its top-level `repository` input comes from the daemon; an input field also named `repository` remains ordinary submitted data under `input.repository`. A repository alias resolves to the same canonical binding. Retained snapshots are repository-bound and cannot be reused for another repository.
|
|
1069
|
+
|
|
656
1070
|
Candidate policy checking is available internally through `checkWorkflowCandidate` in `src/workflow-candidate.ts`. It takes serializable analysis facts, an export snapshot and a candidate declaration containing `workflow` and `methods`. Each declared capability requires a nonempty list of distinct method names. The existing workflow sidecar format is unchanged. Results are sorted, unique refusal codes; an empty list grants no admission or execution authority.
|
|
657
1071
|
|
|
658
|
-
The caller supplies imported symbol names, literal effect pairs (`null` represents a dynamic value), direct-effect and unsupported-form findings, and known call and unit totals. Unknown portions of a total still require runtime budget enforcement. This pure checker does not analyze source or verify these facts.
|
|
1072
|
+
The caller supplies imported symbol names, literal effect pairs (`null` represents a dynamic value), direct-effect and unsupported-form findings, and known call and unit totals. Unknown portions of a total still require runtime budget enforcement. This pure checker does not analyze source or verify these facts. The installed admission adapter in `tools/admit-workflow.ts` derives those facts from source and performs SDK compilation; the pure checker alone cannot admit or authorize a run.
|
|
659
1073
|
|
|
660
1074
|
## Development of Cloving
|
|
661
1075
|
|
|
1076
|
+
The shell tests require both zsh at `/bin/zsh` and Bash at `/bin/bash`. macOS includes them. Linux contributors and CI runners must install both before setup; on Ubuntu, install the `zsh` and `bash` system packages. The fixtures exercise zsh 5.9 and Bash 3.2 or 5.2, including their real startup files; neither shell's tests are optional. `npm run check:setup` checks that both executables run and reports a missing prerequisite before compilation.
|
|
1077
|
+
|
|
662
1078
|
```sh
|
|
663
1079
|
npm ci --ignore-scripts
|
|
664
1080
|
npm run check:setup
|
|
@@ -667,7 +1083,9 @@ node dist/src/cli.js --help
|
|
|
667
1083
|
npm pack --ignore-scripts
|
|
668
1084
|
```
|
|
669
1085
|
|
|
670
|
-
|
|
1086
|
+
After a merge or branch update changes `package.json` or `package-lock.json`, rerun `npm ci --ignore-scripts` in the worktree before running checks. Copied `node_modules` can still contain the previous versions; the developer gate rejects a mismatch with the lockfile.
|
|
1087
|
+
|
|
1088
|
+
`npm pack` prints the file name of the tarball. Install it into a separate project with `npm install --ignore-scripts --omit=dev /absolute/path/to/TARBALL`, then run `./node_modules/.bin/cloving --version` or `./node_modules/.bin/cloving start`. The package includes the emitted CLI, start program, compiler and bundled workflows; startup uses the installed runtime dependencies. The package has no installation hooks. An import of `cloving/workflows` gives the step helpers `loadCycle`, `protectedEffect`, `decisionGate`, `waitForDecision`, `handoff`, `waitForRun` and `runCommand`, and starts no service.
|
|
671
1089
|
|
|
672
1090
|
`npm run check` and `npm run test:concurrency` currently reject admission because the complete quality policy is not implemented. Setup checks are not product acceptance. State and retained releases will live outside the package installation; installing or updating the package does not activate a daemon or migrate state.
|
|
673
1091
|
|
|
@@ -675,9 +1093,31 @@ Run `npm run test:scale` on an otherwise idle machine to measure authority openi
|
|
|
675
1093
|
|
|
676
1094
|
`npm run check:quality` checks the npm manifest, lock and installed versions, regenerates TypeScript output in memory, inventories first-party files, and runs strict compilation, the proposed module catalog, workflow/effect boundaries, suppression and test-integrity checks, formatting, typed lint, structural checks, clone detection and the regular local test suite. Production modules require at least 90% line, statement and function coverage and 85% branch coverage per file. Coverage uses native V8 ranges mapped to the syntax tree of the exact Node-stripped source; unimported production files fail. Class and named object types allow at most 24 callable members; interfaces allow 10. Clone checks support ordinary TypeScript and JavaScript; JSX is rejected until separately qualified. `npm run check:lint`, `npm run check:format` and `npm run test:quality` run individual development checks.
|
|
677
1095
|
|
|
1096
|
+
The gate uses the committed `quality/test-durations.json` record to start files with unknown durations first in path order, then recorded files from longest to shortest, breaking ties by path. Stale entries have no effect. The launcher passes the ordered list directly to Node’s programmatic test runner because CLI discovery sorts positional paths. The test inventory and sorted report stay the same. Run `npm run record:test-durations` to refresh the record after adding or changing tests, then commit the JSON. This runs the regular suite with the gate's worker count, timeout settings, isolated environment and V8 coverage, sums each file's top-level test and suite durations in seconds, and writes entries in path order after a successful complete run. Nested tests are not counted twice. Temporary coverage stays outside the repository and is removed afterward. The command accepts the same concurrency and time-factor settings as the gate.
|
|
1097
|
+
|
|
1098
|
+
The quality report includes elapsed time and the effective limit in milliseconds for each bounded command: npm inventory, clone detection, tests and coverage conversion. Commands using more than 80 percent of their limit produce a warning in the JSON report and on stderr after the report. Failed checks print the collected timings and warnings with the original error and a failing exit status. Warnings preserve a passing result. If your change made the check slower, address the warning in the same ticket.
|
|
1099
|
+
|
|
678
1100
|
The watch scrolling tests launch an isolated headless Chrome process at desktop and phone widths. They require Chrome on the test machine. Run them with `node --test test/watch-scrolling.test.ts`. The fixture serves bundled Preact and board data over loopback and blocks external page resources.
|
|
679
1101
|
|
|
680
|
-
|
|
1102
|
+
To check the 390 px expansion race, run both phone scrolling scenarios twenty times with the CI timing factor:
|
|
1103
|
+
|
|
1104
|
+
```sh
|
|
1105
|
+
for run in $(seq 1 20); do
|
|
1106
|
+
CLOVING_CHECK_TIME_FACTOR=3 node --test --test-name-pattern='CLV-(134|196).*390px$' test/watch-scrolling.test.ts || exit 1
|
|
1107
|
+
done
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
The expansion helper waits for two animation frames before horizontal input. Resolving in the first callback can send the gesture before the widened Done column renders, leaving the board at its old horizontal scroll limit even after scrolling settles. The regression delays that rendering pass with `testMilliseconds(250)` and keeps the full card visibility assertion. A failure reports the card, column and board rectangles, both scroll positions and the viewport; compare the card's right edge with the board's right edge to distinguish horizontal clipping from an incomplete vertical scroll.
|
|
1111
|
+
|
|
1112
|
+
Before a ticket report, commit your work and run `npm run check:clean`. It refuses uncommitted changes, archives the committed tree of `HEAD` into a temporary directory, copies the worktree's `node_modules`, and runs `node tools/check-quality.ts` there with the same environment and exit code. Ignored build output such as `dist/` is absent, so tests that rely on stale local files fail. The temporary copy is removed after success, failure or SIGINT/SIGTERM interruption.
|
|
1113
|
+
|
|
1114
|
+
After merging dependency changes, run `npm ci --ignore-scripts` in the worktree before checking it. Copied dependencies can belong to an earlier lockfile; `check:clean` copies them unchanged. An `installed-lock-mismatch` requires refreshing that installation and rerunning the checks. If existing `dist/` output produces `generated-output-mismatch`, regenerate it with `npm run build` before rerunning `check:quality`.
|
|
1115
|
+
|
|
1116
|
+
Set `CLOVING_CHECK_TIME_FACTOR` to a whole number from 1 to 5 (default 1) to multiply the developer gate test and coverage command time limits, the inner hook check limit (15 seconds by default), and the installed hook limit (30 seconds by default). With factor 3, the hook limits are 45 and 90 seconds, respectively.
|
|
1117
|
+
|
|
1118
|
+
Set `CLOVING_TEST_CONCURRENCY` to a whole number from 1 to 8 to override the developer gate's test worker count. Without the setting, the gate uses one fewer worker than the available cores, with a minimum of 2 and a maximum of 4. macOS CI sets it to 3 and Ubuntu CI sets it to 4. Pull requests from branches of this repository run both jobs on local runners with the label `cloving-ci` (macOS arm64 and Linux arm64) with 8 workers; the local macOS runner accepts the macOS version of its machine. Invalid values fail with an error.
|
|
1119
|
+
|
|
1120
|
+
Tests that stop a child process should await `stopChild` from `test/stop-child.ts`. Use `stopChildGroup` for a detached process group, including descendants whose leader has exited. Both helpers send `SIGTERM`, wait up to five seconds times `CLOVING_CHECK_TIME_FACTOR` for exit, then escalate to `SIGKILL`. When a test requires a hard crash or an unresponsive child, start that child with `NODE_V8_COVERAGE` omitted so a kill cannot interrupt its coverage recording. Keep coverage enabled for ordinary child runs and recovery runs.
|
|
681
1121
|
|
|
682
1122
|
The catalog and checker configuration are local proposals. The bounded SDK artifact, dependency-surface, production-coverage and mandatory-test inventory components have local qualification fixtures. Protected activation of their records, verifier execution and hooks remain pending. Dependency snapshots and supplied review records cannot activate a candidate. The local quality command cannot accept a product candidate. Static workflow checks are not isolation or replay proof. Deterministic sequencing fixtures qualify the test mechanism; they do not prove ownership or storage concurrency in the service.
|
|
683
1123
|
|