bootproof 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +840 -152
  2. package/dist/agent-plan.d.ts +44 -0
  3. package/dist/agent-plan.js +826 -0
  4. package/dist/agent-run.d.ts +117 -0
  5. package/dist/agent-run.js +459 -0
  6. package/dist/ai-repair.d.ts +58 -0
  7. package/dist/ai-repair.js +380 -0
  8. package/dist/cli.js +730 -46
  9. package/dist/diagnosis.js +101 -16
  10. package/dist/diff.d.ts +29 -0
  11. package/dist/diff.js +569 -0
  12. package/dist/exec.d.ts +30 -2
  13. package/dist/exec.js +329 -51
  14. package/dist/external-health.d.ts +16 -0
  15. package/dist/external-health.js +214 -0
  16. package/dist/infer.js +238 -39
  17. package/dist/plan.js +2 -0
  18. package/dist/proof.d.ts +78 -2
  19. package/dist/proof.js +265 -12
  20. package/dist/receipt.d.ts +52 -0
  21. package/dist/receipt.js +356 -0
  22. package/dist/redact.d.ts +4 -0
  23. package/dist/redact.js +86 -2
  24. package/dist/registry.d.ts +82 -30
  25. package/dist/registry.js +355 -53
  26. package/dist/remote.js +3 -3
  27. package/dist/repair-playbooks.d.ts +24 -0
  28. package/dist/repair-playbooks.js +593 -0
  29. package/dist/repair-safety.d.ts +130 -0
  30. package/dist/repair-safety.js +766 -0
  31. package/dist/repair.d.ts +43 -11
  32. package/dist/repair.js +716 -7
  33. package/dist/run.d.ts +3 -0
  34. package/dist/run.js +218 -41
  35. package/dist/sbom.d.ts +22 -0
  36. package/dist/sbom.js +99 -0
  37. package/dist/taxonomy.d.ts +8 -3
  38. package/dist/taxonomy.js +404 -8
  39. package/dist/types.d.ts +40 -1
  40. package/docs/AGENT_IN_THE_LOOP.md +171 -0
  41. package/docs/AGENT_RUN_RECEIPTS.md +38 -0
  42. package/docs/CI_ACTION.md +67 -2
  43. package/docs/DETERMINISTIC_REPAIR_SAFETY_MODEL.md +705 -0
  44. package/docs/FAILURE_TAXONOMY.md +28 -1
  45. package/docs/HONESTY_CONTRACT.md +34 -12
  46. package/docs/LAUNCH_PLAYBOOK.md +232 -0
  47. package/docs/REAL_WORLD_FIXTURES.md +105 -0
  48. package/docs/REGISTRY.md +48 -28
  49. package/docs/REPAIR_RECEIPT.md +54 -8
  50. package/docs/agent-loop-gap-analysis.md +188 -0
  51. package/docs/examples/registry-seeds/advertised-port-mismatch.json +28 -0
  52. package/docs/examples/registry-seeds/airbyte-abctl-external-orchestrator.json +36 -0
  53. package/docs/examples/registry-seeds/go-ollama-service.json +36 -0
  54. package/docs/examples/registry-seeds/laravel-vite-sqlite.json +36 -0
  55. package/docs/examples/registry-seeds/monorepo-ambiguous-health.json +29 -0
  56. package/docs/examples/registry-seeds/php-composer.json +33 -0
  57. package/docs/examples/registry-seeds/rails-bundler.json +32 -0
  58. package/docs/examples/registry-seeds/sentry-devenv-direnv.json +41 -0
  59. package/docs/schemas/action-verdict-v1.schema.json +64 -0
  60. package/docs/schemas/agent-plan-v1.schema.json +148 -0
  61. package/docs/schemas/agent-run-receipts-v1.schema.json +192 -0
  62. package/docs/schemas/ai-repair-suggestion-v1.schema.json +70 -0
  63. package/docs/schemas/ci-context-v1.schema.json +63 -0
  64. package/docs/schemas/diff-result-v1.schema.json +66 -0
  65. package/docs/schemas/federated-receipt-v1.schema.json +51 -0
  66. package/docs/schemas/registry-entry-v1.schema.json +95 -0
  67. package/docs/schemas/registry-seed-example-v1.schema.json +102 -0
  68. package/docs/schemas/repair-action-v1.schema.json +136 -0
  69. package/docs/schemas/repair-receipt-v1.schema.json +221 -0
  70. package/package.json +10 -6
package/dist/diagnosis.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { classifyFailure, extractMissingEnvNames, safeLocalEnvValue } from "./taxonomy.js";
1
2
  function packageManagerMismatch(evidence, inference) {
2
3
  const expected = evidence?.match(/expected version:\s*([^\n]+)/i)?.[1]?.trim() ?? inference?.packageManagerVersion ?? "the declared version";
3
4
  const actual = evidence?.match(/Got:\s*([^\n]+)/i)?.[1]?.trim() ?? "a different version";
@@ -15,7 +16,48 @@ function packageManagerMismatch(evidence, inference) {
15
16
  };
16
17
  }
17
18
  export function diagnoseFailure(failureClass, evidence, explanation, inference) {
19
+ const classified = evidence ? classifyFailure(evidence) : null;
20
+ const precise = classified?.class === failureClass ? classified : null;
21
+ const preciseFailure = (whyRefused, fallbackSafeNextStep) => ({
22
+ whatHappened: precise?.explanation ?? explanation,
23
+ whyRefused,
24
+ safeNextStep: precise?.safeNextStep ?? fallbackSafeNextStep,
25
+ });
18
26
  switch (failureClass) {
27
+ case "missing_ruby_version":
28
+ return preciseFailure("BootProof cannot execute the repository with a Ruby version that is not installed.", "Install the repository-required Ruby version with rbenv, then rerun BootProof.");
29
+ case "missing_php_runtime":
30
+ return preciseFailure("BootProof cannot execute the repository without a supported PHP runtime.", "Install a PHP version supported by the repository, then rerun BootProof.");
31
+ case "missing_composer":
32
+ return preciseFailure("BootProof cannot install the repository's PHP dependencies without Composer.", "Install Composer using its documented installation method, then rerun BootProof.");
33
+ case "unsupported_php_version_for_composer_lock":
34
+ return preciseFailure("Composer refused the locked dependency set because the available PHP version is unsupported.", "Use a PHP version compatible with composer.lock, then rerun composer install.");
35
+ case "missing_php_vendor_autoload":
36
+ return preciseFailure("The PHP dependency autoloader is absent, so the application cannot start reliably.", "Resolve any PHP or Composer version issue first, then run composer install to generate vendor/autoload.php.");
37
+ case "laravel_sqlite_database_missing":
38
+ return preciseFailure("Laravel cannot use the configured SQLite connection because its local database file is absent.", "After review, create database/database.sqlite and run php artisan migrate. Both steps mutate local state and require explicit approval.");
39
+ case "laravel_migrations_required":
40
+ return preciseFailure("Laravel started, but its required database tables are absent.", "Run php artisan migrate only after explicit approval because it mutates local database state.");
41
+ case "missing_build_tool":
42
+ return preciseFailure("The dependency installation cannot complete without the required native build tool.", "Install the reported build tool, then rerun BootProof.");
43
+ case "native_extension_compile_failed":
44
+ return preciseFailure("A required gem did not install, so the application cannot be started reliably.", "Inspect the preserved compiler output, install the required native dependencies, then rerun BootProof.");
45
+ case "missing_database_config":
46
+ return preciseFailure("The application has no usable database configuration, so a verified boot is impossible.", "Create config/database.yml from the repository's documented example, review it, then rerun BootProof.");
47
+ case "missing_required_config":
48
+ return preciseFailure("A repository-required configuration file is absent.", "Restore the reported file from the repository's documented example, review it, then rerun BootProof.");
49
+ case "postgres_unavailable":
50
+ return preciseFailure("The application could not reach the PostgreSQL service required for boot.", "Start PostgreSQL, verify its configured host and port, then rerun BootProof.");
51
+ case "postgres_role_missing":
52
+ return preciseFailure("PostgreSQL was reachable, but the configured role does not exist.", "Create the reported PostgreSQL role or configure an existing role, then rerun BootProof.");
53
+ case "database_schema_missing":
54
+ return preciseFailure("The database is reachable but its required schema is absent or incomplete.", "Run the repository's documented migration or database setup command, then rerun BootProof.");
55
+ case "unsupported_database_version":
56
+ return preciseFailure("The installed database version does not satisfy the repository's requirement.", "Install or select a supported PostgreSQL version, then rerun BootProof.");
57
+ case "unsupported_database_config":
58
+ return preciseFailure("The repository's database configuration contains names the application does not support.", "Update config/database.yml to use only supported database names, then rerun BootProof.");
59
+ case "redis_unavailable":
60
+ return preciseFailure("The application could not reach the Redis service required for boot.", "Start Redis, verify its configured host and port, then rerun BootProof.");
19
61
  case "package_manager_version_mismatch":
20
62
  return packageManagerMismatch(evidence, inference);
21
63
  case "dependency_install_skipped":
@@ -30,12 +72,38 @@ export function diagnoseFailure(failureClass, evidence, explanation, inference)
30
72
  whyRefused: "BootProof cannot yet orchestrate that multi-step application safely enough to claim a verified boot.",
31
73
  safeNextStep: "Review the detected setup and service commands, complete the repository's documented initialization, then rerun when orchestration support is available.",
32
74
  };
75
+ case "laravel_vite_ci_hmr_blocked":
76
+ return preciseFailure("Laravel's Vite integration blocked the HMR asset server in CI. That asset server is not proof that the Laravel application booted.", "For local verification: rerun with LARAVEL_BYPASS_ENV_CHECK=1 only if intentionally testing the Vite dev server. For CI verification: use production asset build instead of Vite HMR. For Laravel app verification: run the Laravel app server, not only the Vite asset server.");
33
77
  case "orchestration_not_supported":
34
78
  return {
35
79
  whatHappened: explanation,
36
80
  whyRefused: "The detected application requires backend/frontend or repository-specific orchestration that BootProof cannot yet execute safely.",
37
81
  safeNextStep: "Use the repository's documented runbook. Treat this attestation as diagnosis only, not proof of a localhost boot.",
38
82
  };
83
+ case "go_service_orchestration_not_supported":
84
+ return {
85
+ whatHappened: explanation,
86
+ whyRefused: "A Go service was detected, but repository evidence did not establish one safe runnable service command and health contract.",
87
+ safeNextStep: "Use the repository's documented service command, or rerun with an explicit reviewed --command and health port. Treat this attestation as diagnosis only.",
88
+ };
89
+ case "host_execution_not_acknowledged":
90
+ return {
91
+ whatHappened: explanation,
92
+ whyRefused: "Host execution was selected without the required explicit acknowledgement.",
93
+ safeNextStep: "Review the inferred commands, then rerun with --provider local --unsafe-local only if you accept host execution.",
94
+ };
95
+ case "auth_required":
96
+ return {
97
+ whatHappened: explanation,
98
+ whyRefused: "The external endpoint requires authentication, so its response is not sufficient public health evidence.",
99
+ safeNextStep: "Provide a separate unauthenticated health endpoint, or verify authentication manually without exposing credentials to BootProof.",
100
+ };
101
+ case "external_health_unreachable":
102
+ return {
103
+ whatHappened: explanation,
104
+ whyRefused: "BootProof did not observe a successful HTTP response from the externally managed service.",
105
+ safeNextStep: "Confirm the external service and endpoint are reachable, then rerun the external health verification.",
106
+ };
39
107
  case "workspace_ambiguous":
40
108
  if (/multiple workspaces in parallel|starts multiple workspaces in parallel/i.test(explanation)) {
41
109
  return {
@@ -61,12 +129,20 @@ export function diagnoseFailure(failureClass, evidence, explanation, inference)
61
129
  whyRefused: "A responding server is not a verified healthy boot when the observed response is a server error.",
62
130
  safeNextStep: "Inspect the application logs and failing health route, fix the server error, then rerun BootProof.",
63
131
  };
132
+ case "health_preoccupied":
133
+ return {
134
+ whatHappened: explanation,
135
+ whyRefused: "The healthy response existed before BootProof started the application, so BootProof cannot attribute it to this repository.",
136
+ safeNextStep: "Stop the existing service and rerun BootProof, or verify the already-running service explicitly with bootproof verify-url <url>.",
137
+ };
64
138
  case "health_check_timeout":
65
139
  return {
66
140
  whatHappened: "No successful HTTP response was observed before the health timeout.",
67
141
  whyRefused: "A running process alone is not proof that the application became reachable and healthy.",
68
142
  safeNextStep: "Check the reported health candidates and application logs, then rerun with the correct port or a longer --timeout if justified.",
69
143
  };
144
+ case "health_candidate_port_mismatch":
145
+ return preciseFailure("The supervised process advertised a different port from the inferred application health URL.", "Confirm the primary application command and intended health port. For Laravel verification, run the Laravel app server rather than only the Vite asset server.");
70
146
  case "postgres_auth_env_missing":
71
147
  return {
72
148
  whatHappened: "Postgres was reached, but authentication or database environment configuration did not match.",
@@ -91,6 +167,12 @@ export function diagnoseFailure(failureClass, evidence, explanation, inference)
91
167
  whyRefused: "BootProof cannot execute the repository's explicit run path without its declared runtime or build tool.",
92
168
  safeNextStep: "Install the required tool at a version supported by the repository, then rerun BootProof.",
93
169
  };
170
+ case "go_runtime_missing":
171
+ return preciseFailure("BootProof cannot execute the selected Go service command without the Go runtime.", "Install a Go version supported by the repository, then rerun BootProof.");
172
+ case "go_build_failed":
173
+ return preciseFailure("The selected Go service did not compile or its declared modules could not be resolved.", "Inspect the preserved Go compiler or module evidence, resolve the reported issue, then rerun BootProof.");
174
+ case "missing_project_cli":
175
+ return preciseFailure("The selected package script depends on a repository CLI that is not available in the current development environment.", "Prepare the repository's documented development environment or bootstrap services so its project CLI is available, then rerun BootProof.");
94
176
  case "runtime_engine_mismatch":
95
177
  return {
96
178
  whatHappened: "The available Node.js runtime does not satisfy the repository's declared engine requirement.",
@@ -98,6 +180,17 @@ export function diagnoseFailure(failureClass, evidence, explanation, inference)
98
180
  safeNextStep: "Switch to a compatible Node.js version, then rerun BootProof.",
99
181
  };
100
182
  case "missing_env_var":
183
+ {
184
+ const missing = extractMissingEnvNames(evidence ?? explanation);
185
+ const safeValue = missing.length === 1 ? safeLocalEnvValue(missing[0]) : null;
186
+ if (safeValue) {
187
+ return {
188
+ whatHappened: `Missing variable: ${missing[0]}. Safe local value: ${safeValue}.`,
189
+ whyRefused: "The application exited before BootProof could observe healthy HTTP behavior.",
190
+ safeNextStep: `${missing[0]}=${safeValue} bootproof up . --provider local --unsafe-local --install`,
191
+ };
192
+ }
193
+ }
101
194
  return {
102
195
  whatHappened: explanation,
103
196
  whyRefused: "BootProof will not invent secrets or write protected .env files to force startup.",
@@ -121,27 +214,19 @@ export function diagnoseFailure(failureClass, evidence, explanation, inference)
121
214
  whyRefused: "BootProof cannot trust an application boot when its declared dependency installation failed.",
122
215
  safeNextStep: "Inspect the preserved install evidence, fix the underlying package or environment problem, then rerun BootProof.",
123
216
  };
124
- case "app_exited_early":
217
+ case "app_exited_early": {
218
+ const processOutput = explanation.includes("Last process output")
219
+ ? explanation.slice(explanation.indexOf("Last process output"))
220
+ : "";
125
221
  return {
126
- whatHappened: "The application process exited before any health response was observed.",
222
+ whatHappened: processOutput
223
+ ? `The application process exited before any health response was observed.\n${processOutput}`
224
+ : "The application process exited before any health response was observed.",
127
225
  whyRefused: "No live application health signal was available to verify.",
128
226
  safeNextStep: "Inspect the preserved process output, fix the startup error, then rerun BootProof.",
129
227
  };
228
+ }
130
229
  default:
131
- if (/cloned .* but will not execute remote repository code/i.test(explanation)) {
132
- return {
133
- whatHappened: explanation,
134
- whyRefused: "A remote clone is untrusted code, and BootProof requires explicit acknowledgement before running it on the host.",
135
- safeNextStep: "Review the cloned repository, then rerun with --provider local --unsafe-local only if you accept host execution.",
136
- };
137
- }
138
- if (/Local provider runs repository code directly/i.test(explanation)) {
139
- return {
140
- whatHappened: explanation,
141
- whyRefused: "Host execution was selected without the required explicit acknowledgement.",
142
- safeNextStep: "Review the inferred commands, then rerun with --provider local --unsafe-local only if you accept host execution.",
143
- };
144
- }
145
230
  return {
146
231
  whatHappened: explanation,
147
232
  whyRefused: "BootProof did not observe enough evidence to issue a verified boot result.",
package/dist/diff.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ export type DiffRiskLevel = "low" | "medium" | "high";
2
+ export interface DiffChange {
3
+ source: string;
4
+ before: string | null;
5
+ after: string | null;
6
+ }
7
+ export interface DiffResult {
8
+ schema: "bootproof/diff-result/v1";
9
+ base: string;
10
+ head: string;
11
+ changedFiles: string[];
12
+ addedServices: string[];
13
+ removedServices: string[];
14
+ addedPorts: string[];
15
+ removedPorts: string[];
16
+ addedEnvVars: string[];
17
+ removedEnvVars: string[];
18
+ changedCommands: DiffChange[];
19
+ changedPackageManagers: DiffChange[];
20
+ riskLevel: DiffRiskLevel;
21
+ proofRequired: boolean;
22
+ suggestedReviewNotes: string[];
23
+ redactionsApplied: string[];
24
+ }
25
+ export declare function validateDiffResult(value: unknown): string[];
26
+ export declare function diffRefs(repoPath: string, options?: {
27
+ base?: string;
28
+ head?: string;
29
+ }): DiffResult;