@agilno-tech/rivet 0.1.0-alpha.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.
Files changed (283) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +59 -0
  4. package/bin/cli.js +42 -0
  5. package/dist/agents/backend-django-agent/SKILL.md +35 -0
  6. package/dist/agents/backend-nestjs-agent/SKILL.md +36 -0
  7. package/dist/agents/boss-agent/SKILL.md +28 -0
  8. package/dist/agents/bounded-worker/SKILL.md +28 -0
  9. package/dist/agents/data-performance-agent/SKILL.md +32 -0
  10. package/dist/agents/delivery-ticketing-agent/SKILL.md +36 -0
  11. package/dist/agents/devops-agent/SKILL.md +34 -0
  12. package/dist/agents/engineering-manager/SKILL.md +28 -0
  13. package/dist/agents/frontend-nextjs-agent/SKILL.md +34 -0
  14. package/dist/agents/frontend-web-agent/SKILL.md +31 -0
  15. package/dist/agents/incident-response-agent/SKILL.md +42 -0
  16. package/dist/agents/jest-agent/SKILL.md +77 -0
  17. package/dist/agents/mobile-agent/SKILL.md +32 -0
  18. package/dist/agents/playwright-agent/SKILL.md +54 -0
  19. package/dist/agents/product-design-manager/SKILL.md +27 -0
  20. package/dist/agents/qa-agent/SKILL.md +29 -0
  21. package/dist/agents/quality-manager/SKILL.md +28 -0
  22. package/dist/agents/vitest-agent/SKILL.md +77 -0
  23. package/dist/governance/pre-push-rules.md +75 -0
  24. package/dist/governance/prompt-hygiene.md +22 -0
  25. package/dist/governance/review-checklist.md +24 -0
  26. package/dist/governance/safety-and-data.md +27 -0
  27. package/dist/governance/usage-rules.md +21 -0
  28. package/dist/mandatory/address-pr-feedback/SKILL.md +196 -0
  29. package/dist/mandatory/agentic-goal/SKILL.md +42 -0
  30. package/dist/mandatory/agentic-status/SKILL.md +74 -0
  31. package/dist/mandatory/apply-design/SKILL.md +228 -0
  32. package/dist/mandatory/check-ac/SKILL.md +124 -0
  33. package/dist/mandatory/create-pr-and-commit/SKILL.md +304 -0
  34. package/dist/mandatory/design/SKILL.md +312 -0
  35. package/dist/mandatory/feature-workflow/SKILL.md +87 -0
  36. package/dist/mandatory/hotfix/SKILL.md +195 -0
  37. package/dist/mandatory/pre-push/SKILL.md +272 -0
  38. package/dist/mandatory/project-context/SKILL.md +180 -0
  39. package/dist/mandatory/release-docs/SKILL.md +191 -0
  40. package/dist/mandatory/review-pr/SKILL.md +323 -0
  41. package/dist/mandatory/security-review/SKILL.md +157 -0
  42. package/dist/skills/api-contract/SKILL.md +77 -0
  43. package/dist/skills/backend-django/SKILL.md +27 -0
  44. package/dist/skills/backend-nestjs/SKILL.md +65 -0
  45. package/dist/skills/bug-ticket-creation/SKILL.md +43 -0
  46. package/dist/skills/cloudwatch-troubleshooting/SKILL.md +73 -0
  47. package/dist/skills/database-migration/SKILL.md +97 -0
  48. package/dist/skills/debugging/SKILL.md +32 -0
  49. package/dist/skills/devops-infra/SKILL.md +26 -0
  50. package/dist/skills/documentation/SKILL.md +24 -0
  51. package/dist/skills/frontend-nextjs/SKILL.md +27 -0
  52. package/dist/skills/incident-postmortem/SKILL.md +38 -0
  53. package/dist/skills/kubernetes-troubleshooting/SKILL.md +71 -0
  54. package/dist/skills/mobile-react-native/SKILL.md +37 -0
  55. package/dist/skills/postgres-analytics/SKILL.md +29 -0
  56. package/dist/skills/product-jira-ticketing/SKILL.md +28 -0
  57. package/dist/skills/qa-bug-analysis/SKILL.md +27 -0
  58. package/dist/skills/refactoring/SKILL.md +32 -0
  59. package/dist/skills/sprint-planning/SKILL.md +32 -0
  60. package/dist/skills/testing-quality/SKILL.md +62 -0
  61. package/dist/v2/protocols/agent-orchestration.md +49 -0
  62. package/dist/v2/protocols/delivery.md +45 -0
  63. package/dist/v2/protocols/design-authority.md +29 -0
  64. package/dist/v2/protocols/goal-graph.md +44 -0
  65. package/dist/v2/protocols/pattern-first-development.md +29 -0
  66. package/dist/v2/protocols/qa-evidence.md +29 -0
  67. package/dist/v2/protocols/security.md +29 -0
  68. package/dist/v2/schemas/event.schema.json +86 -0
  69. package/dist/v2/schemas/evidence.schema.json +94 -0
  70. package/dist/v2/schemas/feature-decomposition.schema.json +48 -0
  71. package/dist/v2/schemas/feature-plan.schema.json +95 -0
  72. package/dist/v2/schemas/goal-graph.schema.json +61 -0
  73. package/dist/v2/schemas/integration.schema.json +119 -0
  74. package/dist/v2/schemas/orchestration.schema.json +69 -0
  75. package/dist/v2/schemas/project.schema.json +209 -0
  76. package/dist/v2/schemas/providers.schema.json +142 -0
  77. package/dist/v2/schemas/quality.schema.json +50 -0
  78. package/dist/v2/schemas/work-action.schema.json +44 -0
  79. package/dist/v2/schemas/work-request.schema.json +253 -0
  80. package/dist/v2/templates/evidence/qa-bundle.json +194 -0
  81. package/dist/v2/templates/github-actions/rivet-deploy.yml +98 -0
  82. package/dist/v2/templates/harness/SKILL.md +90 -0
  83. package/dist/v2/templates/project/.rivet/orchestration.yaml +42 -0
  84. package/dist/v2/templates/project/.rivet/project.yaml +14 -0
  85. package/dist/v2/templates/project/.rivet/providers.yaml +8 -0
  86. package/dist/v2/templates/project/.rivet/quality.yaml +16 -0
  87. package/package.json +58 -0
  88. package/protocols/agent-orchestration.md +49 -0
  89. package/protocols/delivery.md +45 -0
  90. package/protocols/design-authority.md +29 -0
  91. package/protocols/goal-graph.md +44 -0
  92. package/protocols/pattern-first-development.md +29 -0
  93. package/protocols/qa-evidence.md +29 -0
  94. package/protocols/security.md +29 -0
  95. package/schemas/event.schema.json +86 -0
  96. package/schemas/evidence.schema.json +94 -0
  97. package/schemas/feature-decomposition.schema.json +48 -0
  98. package/schemas/feature-plan.schema.json +95 -0
  99. package/schemas/goal-graph.schema.json +61 -0
  100. package/schemas/integration.schema.json +119 -0
  101. package/schemas/orchestration.schema.json +69 -0
  102. package/schemas/project.schema.json +209 -0
  103. package/schemas/providers.schema.json +142 -0
  104. package/schemas/quality.schema.json +50 -0
  105. package/schemas/work-action.schema.json +44 -0
  106. package/schemas/work-request.schema.json +253 -0
  107. package/src/adapters/confluence.js +138 -0
  108. package/src/adapters/contract.js +657 -0
  109. package/src/adapters/factory.js +122 -0
  110. package/src/adapters/figma.js +178 -0
  111. package/src/adapters/fixtures.js +183 -0
  112. package/src/adapters/github.js +314 -0
  113. package/src/adapters/http.js +704 -0
  114. package/src/adapters/jira.js +195 -0
  115. package/src/adapters/linear.js +171 -0
  116. package/src/adapters/node-transport.js +59 -0
  117. package/src/cli/integration-setup-prompt.js +32 -0
  118. package/src/cli/interrupt.js +25 -0
  119. package/src/cli/main.js +596 -0
  120. package/src/cli/output.js +174 -0
  121. package/src/cli/parse-args.js +246 -0
  122. package/src/cli/project-discovery.js +70 -0
  123. package/src/cli/task-confirmation.js +38 -0
  124. package/src/cli/task-presentation.js +22 -0
  125. package/src/clients/claude.js +214 -0
  126. package/src/clients/codex.js +180 -0
  127. package/src/clients/compatibility.js +23 -0
  128. package/src/clients/contract.js +224 -0
  129. package/src/clients/fake.js +170 -0
  130. package/src/clients/process-runner.js +657 -0
  131. package/src/clients/result-contract.js +78 -0
  132. package/src/commands/delivery-publish.js +80 -0
  133. package/src/commands/delivery-remote.js +230 -0
  134. package/src/commands/delivery-review-update.js +39 -0
  135. package/src/commands/delivery-tracker.js +94 -0
  136. package/src/commands/delivery-transition.js +79 -0
  137. package/src/commands/delivery.js +223 -0
  138. package/src/commands/dependency-approval.js +21 -0
  139. package/src/commands/doctor.js +191 -0
  140. package/src/commands/evidence.js +44 -0
  141. package/src/commands/feature.js +212 -0
  142. package/src/commands/goals.js +231 -0
  143. package/src/commands/human-run.js +208 -0
  144. package/src/commands/human-task.js +278 -0
  145. package/src/commands/init.js +848 -0
  146. package/src/commands/install.js +814 -0
  147. package/src/commands/integration-setup.js +65 -0
  148. package/src/commands/integrations.js +54 -0
  149. package/src/commands/models.js +158 -0
  150. package/src/commands/orchestrate.js +61 -0
  151. package/src/commands/preflight.js +125 -0
  152. package/src/commands/protocols.js +228 -0
  153. package/src/commands/repositories.js +77 -0
  154. package/src/commands/setup-remote.js +41 -0
  155. package/src/commands/setup.js +214 -0
  156. package/src/commands/status.js +224 -0
  157. package/src/commands/support.js +44 -0
  158. package/src/commands/task-completion.js +104 -0
  159. package/src/commands/uninstall.js +234 -0
  160. package/src/commands/verify.js +171 -0
  161. package/src/commands/work.js +220 -0
  162. package/src/commands/worktrees.js +52 -0
  163. package/src/config/command-readiness.js +218 -0
  164. package/src/config/commands.js +206 -0
  165. package/src/config/defaults.js +55 -0
  166. package/src/config/load.js +230 -0
  167. package/src/config/validate.js +560 -0
  168. package/src/delivery/bitbucket-review.js +123 -0
  169. package/src/delivery/branch-publication.js +23 -0
  170. package/src/delivery/contract.js +379 -0
  171. package/src/delivery/github-deployment.js +132 -0
  172. package/src/delivery/github-review.js +5 -0
  173. package/src/delivery/github.js +382 -0
  174. package/src/delivery/gitlab-review.js +5 -0
  175. package/src/delivery/gitlab.js +395 -0
  176. package/src/delivery/prepare.js +90 -0
  177. package/src/delivery/publication-process.js +33 -0
  178. package/src/delivery/publication-transport.js +112 -0
  179. package/src/delivery/review-request.js +184 -0
  180. package/src/delivery/review-update.js +74 -0
  181. package/src/delivery/service.js +400 -0
  182. package/src/delivery/store.js +76 -0
  183. package/src/delivery/tracker-target.js +51 -0
  184. package/src/delivery/tracker-transition.js +158 -0
  185. package/src/delivery/tracker.js +164 -0
  186. package/src/discovery/git.js +85 -0
  187. package/src/discovery/portable.js +108 -0
  188. package/src/discovery/project.js +345 -0
  189. package/src/discovery/tools.js +145 -0
  190. package/src/evaluations/approval.js +20 -0
  191. package/src/evaluations/cost-policy.js +20 -0
  192. package/src/evaluations/harness-attempt.js +113 -0
  193. package/src/evaluations/live-runner.js +55 -0
  194. package/src/evaluations/profile.js +28 -0
  195. package/src/evaluations/report.js +11 -0
  196. package/src/evaluations/text-attempt.js +18 -0
  197. package/src/evidence/checksum.js +87 -0
  198. package/src/evidence/collect.js +727 -0
  199. package/src/evidence/validate.js +354 -0
  200. package/src/feature/accepted-integration.js +46 -0
  201. package/src/feature/actions.js +37 -0
  202. package/src/feature/client-profile.js +56 -0
  203. package/src/feature/decomposition-contract.js +58 -0
  204. package/src/feature/host-execution.js +633 -0
  205. package/src/feature/host-lock-recovery.js +96 -0
  206. package/src/feature/host-run-lock.js +32 -0
  207. package/src/feature/local-approval.js +183 -0
  208. package/src/feature/plan-contract.js +215 -0
  209. package/src/feature/planner.js +253 -0
  210. package/src/feature/run-store.js +241 -0
  211. package/src/feature/runtime-bridge.js +840 -0
  212. package/src/feature/verification-report.js +168 -0
  213. package/src/feature/workflow.js +375 -0
  214. package/src/git/client.js +509 -0
  215. package/src/git/integration-worktree.js +153 -0
  216. package/src/git/reconcile.js +222 -0
  217. package/src/git/reservations.js +450 -0
  218. package/src/git/worktrees.js +478 -0
  219. package/src/graph/completion.js +189 -0
  220. package/src/graph/fixtures.js +116 -0
  221. package/src/graph/reducer.js +124 -0
  222. package/src/graph/scheduler.js +252 -0
  223. package/src/graph/validate.js +106 -0
  224. package/src/install/managed.js +579 -0
  225. package/src/install/project-reference.js +33 -0
  226. package/src/install/project-runtime.js +142 -0
  227. package/src/install/runtime-integrity.cjs +141 -0
  228. package/src/integrations/capabilities.js +69 -0
  229. package/src/integrations/host-observation.js +26 -0
  230. package/src/integrations/registry.js +56 -0
  231. package/src/models/delegate.js +102 -0
  232. package/src/models/profiles.js +53 -0
  233. package/src/models/protocols.js +208 -0
  234. package/src/models/registry.js +83 -0
  235. package/src/models/transport.js +59 -0
  236. package/src/policy/approvals.js +251 -0
  237. package/src/policy/authority.js +282 -0
  238. package/src/policy/budget.js +149 -0
  239. package/src/policy/command-bootstrap.js +219 -0
  240. package/src/policy/commands.js +803 -0
  241. package/src/prompts/launch-contract.js +43 -0
  242. package/src/prompts/planning-contract.js +92 -0
  243. package/src/protocols/presentation.js +53 -0
  244. package/src/protocols/project.js +289 -0
  245. package/src/quality/runner.js +497 -0
  246. package/src/quality/traceability.js +215 -0
  247. package/src/repositories/identity.js +59 -0
  248. package/src/repositories/index.js +2 -0
  249. package/src/repositories/provider.js +210 -0
  250. package/src/runtime/application.js +353 -0
  251. package/src/runtime/dependency-directory.js +33 -0
  252. package/src/runtime/harness-discovery.js +121 -0
  253. package/src/runtime/heartbeat.js +47 -0
  254. package/src/runtime/instance-store.js +112 -0
  255. package/src/runtime/orchestrator.js +1133 -0
  256. package/src/runtime/portable-dependencies.js +94 -0
  257. package/src/runtime/recovery.js +190 -0
  258. package/src/runtime/retry.js +54 -0
  259. package/src/runtime/supervisor.js +166 -0
  260. package/src/runtime/worktree-bootstrap.js +169 -0
  261. package/src/state/event-store.js +333 -0
  262. package/src/state/lock.js +266 -0
  263. package/src/state/paths.js +274 -0
  264. package/src/state/redact.js +106 -0
  265. package/src/state/snapshot-store.js +310 -0
  266. package/src/status/public/app.js +96 -0
  267. package/src/status/public/index.html +37 -0
  268. package/src/status/public/styles.css +50 -0
  269. package/src/status/server.js +323 -0
  270. package/src/status/view-model.js +356 -0
  271. package/src/support/bundle.js +195 -0
  272. package/src/support/failure-report.js +96 -0
  273. package/src/work-request/contract.js +250 -0
  274. package/src/work-request/host.js +78 -0
  275. package/src/work-request/local.js +163 -0
  276. package/src/work-request/tracker.js +105 -0
  277. package/templates/evidence/qa-bundle.json +194 -0
  278. package/templates/github-actions/rivet-deploy.yml +98 -0
  279. package/templates/harness/SKILL.md +90 -0
  280. package/templates/project/.rivet/orchestration.yaml +42 -0
  281. package/templates/project/.rivet/project.yaml +14 -0
  282. package/templates/project/.rivet/providers.yaml +8 -0
  283. package/templates/project/.rivet/quality.yaml +16 -0
@@ -0,0 +1,223 @@
1
+ import { loadProjectConfig } from '../config/load.js';
2
+ import { gitExecutable, resolveConfiguredProject } from '../cli/project-discovery.js';
3
+ import { CliError, EXIT_CODES } from '../cli/output.js';
4
+ import { runArgv } from '../discovery/tools.js';
5
+ import { createGitClient } from '../git/client.js';
6
+ import { createFeatureRunStore } from '../feature/run-store.js';
7
+ import {
8
+ deliveryRecordPaths,
9
+ listExistingFeatureRunPaths,
10
+ resolveExistingFeatureRunPaths,
11
+ } from '../state/paths.js';
12
+ import { loadDeliveryCandidate } from '../delivery/prepare.js';
13
+ import { createDeliveryStore } from '../delivery/store.js';
14
+ import { createDeliveryService, createTrustedDeliveryExecutor } from '../delivery/service.js';
15
+ import { createAuthorityEnvelope } from '../policy/authority.js';
16
+ import { createApprovalRegistry } from '../policy/approvals.js';
17
+ import { runRemoteDelivery } from './delivery-remote.js';
18
+ import { trackerTargetFromRun } from '../delivery/tracker-target.js';
19
+ import { hash } from '../delivery/contract.js';
20
+
21
+ function fail(message, code = 'INVALID_INPUT') {
22
+ throw new CliError(message, code);
23
+ }
24
+ async function selectRun(project, flags, subcommand, runner) {
25
+ if (flags.run !== undefined) {
26
+ const paths = await resolveExistingFeatureRunPaths(project, flags.run, {
27
+ runner,
28
+ });
29
+ if (paths === null) fail('The selected run was not found.', 'REPOSITORY_CONFLICT');
30
+ return paths;
31
+ }
32
+ const candidates = [];
33
+ for (const paths of await listExistingFeatureRunPaths(project, { runner })) {
34
+ const record =
35
+ subcommand !== 'prepare'
36
+ ? await createDeliveryStore(await deliveryRecordPaths(paths)).read()
37
+ : await createFeatureRunStore(paths).readOnly();
38
+ if (record && (subcommand !== 'prepare' || record.status === 'awaiting-final-approval'))
39
+ candidates.push(paths);
40
+ }
41
+ if (candidates.length !== 1)
42
+ fail('Select one verified run with --run=<id>; no unique delivery run was found.', 'REPOSITORY_CONFLICT');
43
+ return candidates[0];
44
+ }
45
+
46
+ export async function deliveryCommand(parsed, dependencies) {
47
+ const { subcommand, operands, flags } = parsed;
48
+ if (
49
+ !['prepare', 'status', 'refresh', 'publish', 'review', 'review-update', 'merge', 'deploy', 'tracker-update', 'tracker-status', 'tracker-transition', 'reconcile', 'recover'].includes(subcommand) ||
50
+ operands.length ||
51
+ Object.keys(flags).some(
52
+ (key) => !['project', 'run', 'remote', 'json', 'provider', 'method', 'title', 'body'].includes(key)
53
+ ) ||
54
+ (subcommand === 'review-update' ? typeof flags.title !== 'string' || typeof flags.body !== 'string' : flags.title !== undefined || flags.body !== undefined) ||
55
+ (flags.run !== undefined && !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(flags.run)) ||
56
+ flags.run?.length > 64 ||
57
+ (subcommand !== 'prepare' && flags.remote !== undefined) ||
58
+ (flags.provider !== undefined &&
59
+ (!['refresh', 'publish', 'review', 'review-update', 'merge', 'deploy', 'tracker-update', 'tracker-status', 'tracker-transition', 'reconcile'].includes(subcommand) ||
60
+ !/^[a-z][a-z0-9-]{0,63}$/.test(flags.provider))) ||
61
+ (flags.method !== undefined &&
62
+ (subcommand !== 'merge' || !['merge', 'squash', 'rebase'].includes(flags.method))) ||
63
+ (['publish', 'review', 'review-update', 'merge', 'deploy', 'tracker-update', 'tracker-transition'].includes(subcommand) && flags.json)
64
+ ) {
65
+ fail(
66
+ 'Use rivet delivery prepare|status|refresh|publish|review|review-update|merge|deploy|tracker-update|tracker-status|tracker-transition|reconcile|recover [--run=<id>] [--project=<path>]. Prepare accepts --remote; remote operations accept --provider; interactive merge accepts --method=merge|squash|rebase. JSON is unavailable for delivery writes.'
67
+ );
68
+ }
69
+ const project = await resolveConfiguredProject(dependencies.cwd(), flags.project, {
70
+ runner: dependencies.runGit,
71
+ env: dependencies.env,
72
+ });
73
+ const executable = await gitExecutable(dependencies.env);
74
+ const runner = (_command, args, options) => (dependencies.runGit ?? runArgv)(executable, args, options);
75
+ let result;
76
+ try {
77
+ const paths = await selectRun(project.root, flags, subcommand, runner);
78
+ const store = createDeliveryStore(await deliveryRecordPaths(paths));
79
+ if (subcommand === 'recover') {
80
+ const recovery = await store.recover();
81
+ if (flags.json) dependencies.output.json({ok: true, runId: paths.runId, recovery});
82
+ else {
83
+ dependencies.output.log(recovery.recoveredLocks.length
84
+ ? `Recovered abandoned delivery locks: ${recovery.recoveredLocks.join(', ')}.`
85
+ : 'No abandoned delivery locks needed recovery.');
86
+ dependencies.output.log('Reconcile any pending provider outcome with rivet delivery reconcile.');
87
+ }
88
+ return EXIT_CODES.SUCCESS;
89
+ }
90
+ result = await store.read();
91
+ if (subcommand === 'prepare') {
92
+ const gitClient = await createGitClient({ gitExecutable: executable });
93
+ const candidate = await loadDeliveryCandidate({
94
+ project: project.root,
95
+ runId: paths.runId,
96
+ remoteName: flags.remote,
97
+ gitClient,
98
+ runner,
99
+ });
100
+ if (result !== null) {
101
+ if (
102
+ result.candidate.localVerification.evidenceDigest !== candidate.localVerification.evidenceDigest ||
103
+ result.candidate.repository.url !== candidate.repository.url ||
104
+ result.candidate.sourceBranch !== candidate.sourceBranch ||
105
+ result.candidate.targetBranch !== candidate.targetBranch
106
+ )
107
+ fail(
108
+ 'Delivery already records a different candidate. Inspect its status before continuing.',
109
+ 'REPOSITORY_CONFLICT'
110
+ );
111
+ } else {
112
+ const service = createDeliveryService({
113
+ store,
114
+ executor: createTrustedDeliveryExecutor({
115
+ provider: candidate.repository.provider,
116
+ capabilities: [],
117
+ }),
118
+ providerId: 'repository',
119
+ subjectId: 'delivery-cli',
120
+ expectedApproverId: 'terminal-human',
121
+ authority: createAuthorityEnvelope({
122
+ actorId: 'delivery-cli',
123
+ principal: 'agent',
124
+ actions: [],
125
+ ownedPaths: [],
126
+ providers: [],
127
+ commands: [],
128
+ }),
129
+ approvalRegistry: createApprovalRegistry({
130
+ approvers: [{ id: 'terminal-human', principal: 'human' }],
131
+ }),
132
+ clock: () => new Date().toISOString(),
133
+ });
134
+ result = await service.initialize(candidate);
135
+ }
136
+ }
137
+ if (['refresh', 'publish', 'review', 'review-update', 'merge', 'deploy', 'tracker-update', 'tracker-status', 'tracker-transition', 'reconcile'].includes(subcommand)) {
138
+ result = await runRemoteDelivery({
139
+ action: subcommand,
140
+ publicationContext: {project: project.root, gitExecutable: executable},
141
+ store,
142
+ config: project.config,
143
+ reloadConfig: () => loadProjectConfig(project.root),
144
+ loadTrackerTarget: async candidate => trackerTargetFromRun(await createFeatureRunStore(paths).readOnly(), candidate),
145
+ flags,
146
+ dependencies,
147
+ validateLocal: async (expected) => {
148
+ const gitClient = await createGitClient({ gitExecutable: executable });
149
+ // Select the previously recorded repository even if another remote exists.
150
+ const { discoverRepositoryRemotes, parseRepositoryRemote } = await import('../repositories/index.js');
151
+ const remotes = await discoverRepositoryRemotes(project.root, { runner });
152
+ const remote = remotes.find((item) => {try {return hash(parseRepositoryRemote(item.url)) === hash(expected.repository);} catch {return false;}});
153
+ if (!remote) fail('The prepared repository remote is no longer configured.', 'REPOSITORY_CONFLICT');
154
+ const current = await loadDeliveryCandidate({
155
+ project: project.root,
156
+ runId: paths.runId,
157
+ remoteName: remote.name,
158
+ gitClient,
159
+ runner,
160
+ });
161
+ if (
162
+ hash(current.localVerification) !== hash(expected.localVerification) ||
163
+ hash(current.repository) !== hash(expected.repository) ||
164
+ current.sourceBranch !== expected.sourceBranch ||
165
+ current.targetBranch !== expected.targetBranch
166
+ )
167
+ fail(
168
+ 'The local candidate changed. Verify the work before preparing delivery again.',
169
+ 'REPOSITORY_CONFLICT'
170
+ );
171
+ },
172
+ });
173
+ }
174
+ if (result === null)
175
+ fail(
176
+ 'No delivery record exists. Run rivet delivery prepare after verification.',
177
+ 'REPOSITORY_CONFLICT'
178
+ );
179
+ } catch (error) {
180
+ if (error instanceof CliError) throw error;
181
+ if (error?.code === 'ERR_DELIVERY_BRANCH_NOT_PUBLISHED') fail(error.safeMessage, 'REPOSITORY_CONFLICT');
182
+ if (subcommand === 'recover') fail(
183
+ 'Delivery recovery stopped. Locks must be at least five minutes old and owned by a provably dead process on this machine. Live, foreign, unsafe or previously claimed locks require investigation; no force option is available.',
184
+ 'REPOSITORY_CONFLICT'
185
+ );
186
+ fail(
187
+ 'Delivery stopped. Check verification, configured provider access, supported delivery policy and delivery status. An uncertain operation requires delivery reconcile before retry.',
188
+ 'REPOSITORY_CONFLICT'
189
+ );
190
+ }
191
+ const incomplete = deliveryOutcomeIncomplete(subcommand, result);
192
+ if (flags.json) dependencies.output.json({ ok: !incomplete, result });
193
+ else {
194
+ if (result.trackerStatus) {
195
+ dependencies.output.log(`Tracker: ${result.trackerStatus.target.issueUrl}\nCurrent status: ${result.trackerStatus.current.name}`);
196
+ for (const choice of result.trackerStatus.destinations) dependencies.output.log(`${choice.state.name} (${choice.name}; ${choice.state.type})${choice.eligible ? '' : ' - unavailable: requires fields or screen'}`);
197
+ }
198
+ if (result.reviewUpdateNoop) dependencies.output.log('The review already has the requested title and body. No write was sent.');
199
+ if (result.trackerTransitionNoop) dependencies.output.log('The selected destination is already current. No write was sent and no transition receipt was created.');
200
+ dependencies.output.log(`Recorded delivery stage: ${result.stage}`);
201
+ dependencies.output.log(`Run: ${result.candidate.runId}`);
202
+ dependencies.output.log(`Repository: ${result.candidate.repository.url}`);
203
+ dependencies.output.log(`Commit: ${result.candidate.headSha}`);
204
+ dependencies.output.log(
205
+ incomplete
206
+ ? 'Outcome is not confirmed. Run rivet delivery reconcile; do not repeat the write.'
207
+ : 'Status records confirmed stages. Delivery writes require configured provider support and interactive approval.'
208
+ );
209
+ }
210
+ return incomplete ? EXIT_CODES.PROVIDER_UNAVAILABLE : EXIT_CODES.SUCCESS;
211
+ }
212
+
213
+ export function deliveryOutcomeIncomplete(subcommand, result) {
214
+ if (subcommand === 'review-update' && result.reviewUpdateNoop) return false;
215
+ if (subcommand === 'tracker-transition' && result.trackerTransitionNoop) return false;
216
+ if (subcommand === 'tracker-status') return false;
217
+ if (!['publish', 'review', 'review-update', 'merge', 'deploy', 'tracker-update', 'tracker-status', 'tracker-transition', 'reconcile'].includes(subcommand)) return false;
218
+ if (result.operations.some(op => ['dispatching', 'indeterminate'].includes(op.state))) return true;
219
+ if (subcommand === 'reconcile') return false;
220
+ const action = subcommand === 'publish' ? 'branch-publish' : subcommand === 'review' ? 'review-request' : subcommand;
221
+ const alreadyPublished = subcommand === 'publish' && result.observation?.publication?.remoteSha === result.candidate.headSha;
222
+ return !alreadyPublished && !result.operations.some(op => op.action === action && op.state === 'succeeded');
223
+ }
@@ -0,0 +1,21 @@
1
+ function visible(value) {
2
+ return String(value).replace(/[\u0000-\u001f\u007f-\u009f]/g, character =>
3
+ `\\u${character.codePointAt(0).toString(16).padStart(4, '0')}`);
4
+ }
5
+
6
+ export function confirmIsolatedDependencyInstall(dependencies, signal) {
7
+ return plan => {
8
+ if (plan.kind === 'portable') {
9
+ dependencies.output.log(`Prepare project dependencies in ${visible(plan.worktreePath)}?`);
10
+ dependencies.output.log(`Approved checkout: ${visible(plan.expectedBranch)} at ${visible(plan.expectedCommit)}`);
11
+ for (const input of plan.inputs) dependencies.output.log(`Input: ${visible(input.path)} sha256=${input.sha256}`);
12
+ for (const step of plan.steps) dependencies.output.log(`Command in ${visible(step.cwd)}: ${JSON.stringify(step.argv)}`);
13
+ dependencies.output.log('These exact project commands may execute dependency code. Approval is not a filesystem sandbox.');
14
+ return dependencies.confirmDependencyInstall(plan, { signal });
15
+ }
16
+ dependencies.output.log(`Install locked dependencies in ${visible(plan.worktreePath)}?`);
17
+ dependencies.output.log(`Command: ${visible(plan.executable)} ${plan.args.map(visible).join(' ')}`);
18
+ dependencies.output.log('Package installation may run scripts supplied by the project or its dependencies.');
19
+ return dependencies.confirmDependencyInstall(plan, { signal });
20
+ };
21
+ }
@@ -0,0 +1,191 @@
1
+ import { createIntegrationRegistry } from '../integrations/registry.js';
2
+ import { resolve } from 'node:path';
3
+
4
+ import { EXIT_CODES } from '../cli/output.js';
5
+ import { inspectCommandReadiness } from '../config/command-readiness.js';
6
+ import { commandToolKey, compileProjectDependencies, compileQualitySteps } from '../config/commands.js';
7
+ import { loadProjectConfig, providerCredentialStatus } from '../config/load.js';
8
+ import { discoverTools } from '../discovery/tools.js';
9
+ import { verifyCommandExecutable } from '../policy/commands.js';
10
+
11
+ function emit(output, json, payload, exitCode) {
12
+ if (json) {
13
+ output.json(payload, exitCode === EXIT_CODES.SUCCESS ? 'stdout' : 'stderr');
14
+ } else if (exitCode === EXIT_CODES.SUCCESS) {
15
+ output.log(`Doctor: ${payload.status}. ${payload.summary}`);
16
+ } else {
17
+ output.error(`Doctor: ${payload.status}. ${payload.summary}`);
18
+ for(const step of payload.checks?.commands?.steps??[]) {
19
+ if(!step.available) output.error(` ${step.id}: ${step.status}; command ${JSON.stringify(step.argv)} in ${JSON.stringify(step.cwd)}. Check the executable on PATH and the configured project command.`);
20
+ }
21
+ }
22
+ return exitCode;
23
+ }
24
+
25
+ function requiredToolReady(tool) {
26
+ return tool?.present === true
27
+ && typeof tool.version === 'string'
28
+ && tool.version.length > 0
29
+ && tool.supported === true;
30
+ }
31
+
32
+ async function boundedProbe(probe, provider, timeoutMs) {
33
+ let timer;
34
+ try {
35
+ return await Promise.race([
36
+ probe(provider),
37
+ new Promise(resolvePromise => {
38
+ timer = setTimeout(() => resolvePromise({ status: 'timeout' }), timeoutMs);
39
+ }),
40
+ ]);
41
+ } finally {
42
+ clearTimeout(timer);
43
+ }
44
+ }
45
+
46
+ async function providerChecks(config, probe, timeoutMs = 3_000) {
47
+ const checks = [];
48
+ for (const provider of config.providers.providers) {
49
+ if (provider.projectIds?.length && !provider.projectIds.includes(config.project.id)) {
50
+ checks.push({ id: provider.id, readiness: 'out-of-scope', connectivity: 'not_checked' });
51
+ continue;
52
+ }
53
+ if (provider.transport && provider.transport !== 'direct-api') {
54
+ checks.push({ id: provider.id, readiness: 'host-or-local', connectivity: 'not_checked' });
55
+ continue;
56
+ }
57
+ if (provider.mode === 'disabled') {
58
+ checks.push({ id: provider.id, readiness: 'disabled', connectivity: 'not_checked' });
59
+ continue;
60
+ }
61
+ if (typeof probe !== 'function') {
62
+ checks.push({ id: provider.id, readiness: 'configured', connectivity: 'not_checked' });
63
+ continue;
64
+ }
65
+ try {
66
+ const result = await boundedProbe(probe, {
67
+ id: provider.id,
68
+ kind: provider.kind,
69
+ endpoint: provider.endpoint,
70
+ }, timeoutMs);
71
+ const connectivity = result?.status === 'available' ? 'available'
72
+ : result?.status === 'timeout' ? 'timeout' : 'unavailable';
73
+ checks.push({ id: provider.id, readiness: connectivity === 'available' ? 'ready' : 'unavailable', connectivity });
74
+ } catch {
75
+ checks.push({ id: provider.id, readiness: 'unavailable', connectivity: 'error' });
76
+ }
77
+ }
78
+ return checks;
79
+ }
80
+
81
+ export async function diagnoseDoctor(projectRoot, dependencies = {}) {
82
+ let config;
83
+ try {
84
+ config = await (dependencies.configLoader ?? loadProjectConfig)(projectRoot, { fs: dependencies.fs });
85
+ } catch {
86
+ return {
87
+ ok: false,
88
+ status: 'fail',
89
+ exitCode: EXIT_CODES.MISSING_CONFIGURATION,
90
+ error: {
91
+ code: 'MISSING_CONFIGURATION',
92
+ exitCode: EXIT_CODES.MISSING_CONFIGURATION,
93
+ message: 'Project configuration is missing or invalid.',
94
+ },
95
+ checks: { configuration: { status: 'fail' } },
96
+ summary: 'Project configuration is missing or invalid.',
97
+ };
98
+ }
99
+ const environment = dependencies.env ?? process.env;
100
+ const packageManager = config.project.stack.packageManager;
101
+ const direct=config.project.schemaVersion===3;
102
+ const commandSteps = compileQualitySteps(config);
103
+ const dependencyPlan = compileProjectDependencies(config.project);
104
+ const executableSteps = [...commandSteps, ...(dependencyPlan?.steps ?? [])];
105
+ const managers = [...new Set([
106
+ ...(direct ? [] : [packageManager]),
107
+ ...executableSteps.map(step => direct ? step.argv[0] : step.argv[0].toLowerCase().replace(/\.(?:cmd|exe)$/, '')),
108
+ ])];
109
+ const toolDiscovery = dependencies.toolDiscovery ?? discoverTools;
110
+ const reports = direct ? [await toolDiscovery({runtimeOnly:true},{cwd:projectRoot,runner:dependencies.runner})] : await Promise.all(managers.map(manager => toolDiscovery({ packageManager: manager }, {
111
+ cwd: projectRoot,
112
+ runner: dependencies.runner,
113
+ })));
114
+ let tools = Object.freeze({
115
+ ...reports[0],
116
+ ...(!direct ? Object.fromEntries(managers.map((manager, index) => [manager, reports[index]?.[manager] ?? {}])) : {}),
117
+ });
118
+ if (typeof dependencies.resolveCommandExecutable === 'function') {
119
+ const entries = direct ? [...new Map(executableSteps.map(step => [
120
+ commandToolKey(step.argv[0], step.cwd), { runner: step.argv[0], cwd: step.cwd },
121
+ ])).entries()] : managers.map(manager => [manager, { runner: manager, cwd: '.' }]);
122
+ const resolutions = await Promise.all(entries.map(async ([key, step]) => {
123
+ let runtimeResolved = false;
124
+ try {
125
+ const executable = await dependencies.resolveCommandExecutable(step.runner, direct
126
+ ? { execution: 'argv', worktree: projectRoot, cwd: step.cwd } : {});
127
+ await verifyCommandExecutable(executable, direct ? { execution: 'argv' } : {});
128
+ runtimeResolved = true;
129
+ } catch {}
130
+ return [key, Object.freeze({ ...(tools[key] ?? {}), runtimeResolved })];
131
+ }));
132
+ tools = Object.freeze({ ...tools, ...Object.fromEntries(resolutions) });
133
+ }
134
+ const credentials = dependencies.hostReadiness === true ? [] : providerCredentialStatus(config, environment);
135
+ const providers = dependencies.hostReadiness === true ? [] : await providerChecks(
136
+ config,
137
+ dependencies.providerProbe,
138
+ dependencies.providerProbeTimeoutMs,
139
+ );
140
+ const commands = inspectCommandReadiness(projectRoot, config, { fs: dependencies.fs, tools });
141
+ const missingCredentials = credentials.filter(item => item.required && !item.present);
142
+ const unavailableProviders = providers.filter(item => ['unavailable', 'timeout', 'error'].includes(item.connectivity));
143
+ const toolsReady = requiredToolReady(tools.node)
144
+ && (direct || managers.every(manager => requiredToolReady(tools[manager]) && tools[manager].runtimeResolved !== false))
145
+ && requiredToolReady(tools.git);
146
+ const otherFailure = missingCredentials.length > 0 || unavailableProviders.length > 0 || !toolsReady;
147
+ const preparationReady = !otherFailure && commands.preparationReady === true;
148
+ const preparationRequired = preparationReady && !commands.ready;
149
+ const failed = otherFailure || (!commands.ready && !preparationReady)
150
+ || commands.dependencies?.preparationReady === false;
151
+ const exitCode = missingCredentials.length > 0 || unavailableProviders.length > 0
152
+ ? EXIT_CODES.PROVIDER_UNAVAILABLE
153
+ : failed ? EXIT_CODES.FAILED_GATE : EXIT_CODES.SUCCESS;
154
+ return {
155
+ ok: !failed,
156
+ preparationReady,
157
+ status: failed ? 'fail' : preparationRequired ? 'preparation-required' : providers.some(item => item.connectivity === 'not_checked') ? 'warn' : 'pass',
158
+ exitCode,
159
+ checks: {
160
+ configuration: { status: 'pass' },
161
+ tools,
162
+ credentials,
163
+ providers,
164
+ commands,
165
+ integrations: createIntegrationRegistry({config,projectId:config.project.id,environment,host:dependencies.integrationHost}).check(),
166
+ },
167
+ summary: failed ? 'One or more readiness checks failed.' : preparationRequired
168
+ ? 'Configured dependencies require installation in the isolated checkout after approval. Quality commands have not run; use rivet task deps once the checkout exists.'
169
+ : 'Configuration and local readiness checks completed.',
170
+ };
171
+ }
172
+
173
+ export async function doctor(parsed, dependencies = {}) {
174
+ const json = parsed.flags.json === true;
175
+ const projectRoot = resolve(parsed.flags.project ?? dependencies.cwd?.() ?? process.cwd());
176
+ try {
177
+ const result = await diagnoseDoctor(projectRoot, dependencies);
178
+ return emit(dependencies.output, json, result, result.exitCode);
179
+ } catch {
180
+ return emit(dependencies.output, json, {
181
+ ok: false,
182
+ status: 'fail',
183
+ summary: 'Doctor could not complete safely.',
184
+ error: {
185
+ code: 'INTERNAL_ERROR',
186
+ exitCode: EXIT_CODES.INTERNAL_ERROR,
187
+ message: 'Doctor could not complete safely.',
188
+ },
189
+ }, EXIT_CODES.INTERNAL_ERROR);
190
+ }
191
+ }
@@ -0,0 +1,44 @@
1
+ import { CliError, EXIT_CODES } from '../cli/output.js';
2
+ import { collectEvidenceBundle } from '../evidence/collect.js';
3
+
4
+ export async function createEvidence(input) {
5
+ const bundle = await collectEvidenceBundle(input);
6
+ return Object.freeze({
7
+ ok: true,
8
+ status: bundle.manifest.status,
9
+ bundle,
10
+ });
11
+ }
12
+
13
+ export async function evidenceCommand(parsed, dependencies = {}) {
14
+ if (!parsed || parsed.command !== 'evidence' || parsed.subcommand !== null
15
+ || !Array.isArray(parsed.operands) || parsed.operands.length !== 0
16
+ || !parsed.flags || Reflect.ownKeys(parsed.flags).some(key => key !== 'json')) {
17
+ throw new CliError('Evidence command options are invalid.', 'INVALID_INPUT');
18
+ }
19
+ const context = dependencies.evidence;
20
+ if (!context || typeof context.requestFor !== 'function') {
21
+ throw new CliError('Evidence collection is not configured.', 'MISSING_CONFIGURATION');
22
+ }
23
+ const request = await context.requestFor(Object.freeze({ command: 'evidence' }));
24
+ const result = await createEvidence(request);
25
+ if (parsed.flags.json) {
26
+ dependencies.output.json({
27
+ ok: true,
28
+ command: 'evidence',
29
+ result: {
30
+ status: result.status,
31
+ runDirectory: result.bundle.runDirectory,
32
+ manifestPath: result.bundle.manifestPath,
33
+ manifestChecksum: result.bundle.manifestChecksum,
34
+ archivePath: result.bundle.archivePath,
35
+ archiveChecksum: result.bundle.archiveChecksum,
36
+ durablyPublished: result.bundle.durablyPublished,
37
+ publication: result.bundle.publication,
38
+ },
39
+ });
40
+ } else {
41
+ dependencies.output.log('Evidence: ' + result.status + '. Archive SHA-256: ' + result.bundle.archiveChecksum + '.');
42
+ }
43
+ return EXIT_CODES.SUCCESS;
44
+ }