@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.13
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/CHANGELOG.md +5992 -82
- package/COMPARISON.md +168 -63
- package/CONTRIBUTING.md +121 -0
- package/MIGRATING-from-OpenSpec.md +238 -55
- package/QUICKSTART.md +302 -0
- package/README.md +653 -144
- package/ROADMAP.md +1035 -0
- package/SCHEMA.md +3916 -263
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +927 -0
- package/dist/cli.js +133 -21
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +204 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +309 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +74 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/closing.js +42 -0
- package/dist/commands/archive/closing.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +172 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +193 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/document/head.js +151 -0
- package/dist/commands/archive/plan/document/head.js.map +1 -0
- package/dist/commands/archive/plan/document/links.js +284 -0
- package/dist/commands/archive/plan/document/links.js.map +1 -0
- package/dist/commands/archive/plan/document/run.js +136 -0
- package/dist/commands/archive/plan/document/run.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +319 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +298 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/model/extending.js +85 -0
- package/dist/commands/archive/plan/model/extending.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +106 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +318 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +30 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +355 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +166 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +305 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +367 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +221 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +6 -8
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +86 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +65 -5
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +227 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +39 -28
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +152 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +66 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +105 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +262 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +146 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +72 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +142 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +72 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +63 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +155 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +358 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +344 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +108 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +238 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +75 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +175 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +143 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +86 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +18 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +162 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +40 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +62 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +328 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +279 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +199 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +38 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +300 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +221 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +278 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +174 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +182 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/introduced.js +157 -0
- package/dist/commands/show/introduced.js.map +1 -0
- package/dist/commands/show/marks.js +53 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/show/service.js +239 -0
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +87 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/fleet-next.js +90 -0
- package/dist/commands/status/fleet-next.js.map +1 -0
- package/dist/commands/status/json.js +47 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +209 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +115 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +269 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +325 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +113 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +98 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +318 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/exclude.js +132 -0
- package/dist/commands/subsystem/txn/exclude.js.map +1 -0
- package/dist/commands/subsystem/txn/links.js +128 -0
- package/dist/commands/subsystem/txn/links.js.map +1 -0
- package/dist/commands/subsystem/txn/report.js +174 -0
- package/dist/commands/subsystem/txn/report.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +196 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +249 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/tasks/actions.js +107 -0
- package/dist/commands/tasks/actions.js.map +1 -0
- package/dist/commands/tasks/render.js +253 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +119 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +176 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/unarchive/restore.js +254 -0
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +59 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +245 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +204 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +58 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/introduced.js +102 -0
- package/dist/commands/validate/checks/introduced.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +264 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +85 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/coverage/covers-after.js +105 -0
- package/dist/commands/validate/coverage/covers-after.js.map +1 -0
- package/dist/commands/validate/feature.js +222 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +93 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +371 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +118 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/map/attest.js +98 -0
- package/dist/commands/validate/fleet/map/attest.js.map +1 -0
- package/dist/commands/validate/fleet/map/bindings.js +107 -0
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
- package/dist/commands/validate/fleet/map/consumers.js +244 -0
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
- package/dist/commands/validate/fleet/map/isolation.js +116 -0
- package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
- package/dist/commands/validate/fleet/map/usecases.js +54 -0
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +266 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +71 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +162 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +75 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/projects.js +278 -0
- package/dist/commands/validate/fleet/views/projects.js.map +1 -0
- package/dist/commands/validate/fleet/views/root-documents.js +83 -0
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +52 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +89 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/render/repeats.js +63 -0
- package/dist/commands/validate/render/repeats.js.map +1 -0
- package/dist/commands/validate/render/rollups.js +115 -0
- package/dist/commands/validate/render/rollups.js.map +1 -0
- package/dist/commands/validate/render/text.js +199 -0
- package/dist/commands/validate/render/text.js.map +1 -0
- package/dist/commands/validate/report.js +145 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +252 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +241 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +177 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/model/diverged.js +226 -0
- package/dist/commands/validate/service/model/diverged.js.map +1 -0
- package/dist/commands/validate/service/model/grade.js +163 -0
- package/dist/commands/validate/service/model/grade.js.map +1 -0
- package/dist/commands/validate/service/service.js +310 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +279 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +148 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/service/usecases/tags.js +113 -0
- package/dist/commands/validate/service/usecases/tags.js.map +1 -0
- package/dist/commands/validate/service/usecases/usecases.js +209 -0
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/validate.js +363 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +178 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +81 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +263 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +278 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +206 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +328 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +220 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +266 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +141 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +230 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +111 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +155 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +169 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +299 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +46 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +50 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +59 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +65 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +44 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +78 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +104 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +138 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +273 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md.js +203 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +60 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +254 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +144 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +151 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +193 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +280 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +373 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +323 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +220 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +188 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +311 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +67 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +210 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +19 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +338 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +43 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +146 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +92 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +38 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +290 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +195 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +83 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/asyncapi/read.js +263 -0
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +285 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +385 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +273 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/map/extend.js +84 -0
- package/dist/core/brief/map/extend.js.map +1 -0
- package/dist/core/brief/map/owed.js +266 -0
- package/dist/core/brief/map/owed.js.map +1 -0
- package/dist/core/brief/targets.js +304 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +58 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +190 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/direction/direction.js +114 -0
- package/dist/core/c4/direction/direction.js.map +1 -0
- package/dist/core/c4/likec4.js +243 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +160 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +110 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +113 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/prefetch/fleet.js +134 -0
- package/dist/core/c4/prefetch/fleet.js.map +1 -0
- package/dist/core/c4/project/architecture.js +161 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/batch.js +140 -0
- package/dist/core/c4/project/batch.js.map +1 -0
- package/dist/core/c4/project/documents.js +89 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +219 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/project/staged.js +277 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/attested.js +0 -0
- package/dist/core/c4/resolve/attested.js.map +1 -0
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +174 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/resolve/steps.js +290 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/resolve/systems.js +154 -0
- package/dist/core/c4/resolve/systems.js.map +1 -0
- package/dist/core/c4/root-project/exclude.js +352 -0
- package/dist/core/c4/root-project/exclude.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +173 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/service-model/created/models.js +96 -0
- package/dist/core/c4/service-model/created/models.js.map +1 -0
- package/dist/core/c4/service-model/documents.js +47 -0
- package/dist/core/c4/service-model/documents.js.map +1 -0
- package/dist/core/c4/service-model/fleet/extending.js +82 -0
- package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
- package/dist/core/c4/service-model/load.js +165 -0
- package/dist/core/c4/service-model/load.js.map +1 -0
- package/dist/core/c4/service-model/renderer.js +128 -0
- package/dist/core/c4/service-model/renderer.js.map +1 -0
- package/dist/core/c4/service-model/shape.js +106 -0
- package/dist/core/c4/service-model/shape.js.map +1 -0
- package/dist/core/c4/service-model/slice.js +188 -0
- package/dist/core/c4/service-model/slice.js.map +1 -0
- package/dist/core/c4/source-mask.js +369 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +308 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +167 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +30 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +126 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/declared.js +87 -0
- package/dist/core/c4/splice/identity/declared.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +73 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/identity/existing.js +80 -0
- package/dist/core/c4/splice/identity/existing.js.map +1 -0
- package/dist/core/c4/splice/identity/interior.js +80 -0
- package/dist/core/c4/splice/identity/interior.js.map +1 -0
- package/dist/core/c4/splice/identity/owner.js +50 -0
- package/dist/core/c4/splice/identity/owner.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +337 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/model/merge.js +151 -0
- package/dist/core/c4/splice/model/merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +298 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/splice/text/spot.js +131 -0
- package/dist/core/c4/splice/text/spot.js.map +1 -0
- package/dist/core/c4/workspace.js +166 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +47 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +338 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +174 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +123 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +205 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +104 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +267 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +185 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +75 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/calls/called.js +52 -0
- package/dist/core/coherence/calls/called.js.map +1 -0
- package/dist/core/coherence/coherence.js +332 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/covers/health.js +85 -0
- package/dist/core/coherence/covers/health.js.map +1 -0
- package/dist/core/coherence/declared.js +295 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +191 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +335 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +189 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +63 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/produced.js +100 -0
- package/dist/core/coherence/events/produced.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +95 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +341 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/deployment/delta.js +191 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +230 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +237 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +204 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +312 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/docs.js +37 -85
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +238 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +139 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +355 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/reports/scan.js +242 -0
- package/dist/core/doctor/reports/scan.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +224 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +103 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +286 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +238 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +234 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +89 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +209 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +180 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +194 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms/documents.js +227 -0
- package/dist/core/explain/terms/documents.js.map +1 -0
- package/dist/core/explain/terms/done-check.js +144 -0
- package/dist/core/explain/terms/done-check.js.map +1 -0
- package/dist/core/explain/terms/entry.js +14 -0
- package/dist/core/explain/terms/entry.js.map +1 -0
- package/dist/core/explain/terms/stages.js +71 -0
- package/dist/core/explain/terms/stages.js.map +1 -0
- package/dist/core/explain/terms/tasks.js +97 -0
- package/dist/core/explain/terms/tasks.js.map +1 -0
- package/dist/core/explain/terms.js +103 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/explore/explore.js +204 -0
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +174 -146
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +208 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +246 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +176 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +210 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +109 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +73 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/{concurrency.js → kernel/concurrency.js} +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
- package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +56 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/{records.js → kernel/records.js} +11 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +66 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +118 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +265 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +212 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +73 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +324 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +207 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +142 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +174 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +301 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +136 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +239 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +54 -108
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +199 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +16 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +204 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +232 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +110 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +220 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +78 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +70 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +241 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +191 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +304 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +116 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +157 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +120 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/projection/introduced.js +14 -0
- package/dist/core/projection/introduced.js.map +1 -0
- package/dist/core/provenance/findings.js +244 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +201 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +139 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +280 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +220 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +210 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +322 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +308 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +61 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +90 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/render/body.js +258 -0
- package/dist/core/repo/tree/render/body.js.map +1 -0
- package/dist/core/repo/tree/render/projects.js +182 -0
- package/dist/core/repo/tree/render/projects.js.map +1 -0
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +42 -0
- package/dist/core/repo/tree/render/stale.js.map +1 -0
- package/dist/core/repo/tree/render/text.js +84 -0
- package/dist/core/repo/tree/render/text.js.map +1 -0
- package/dist/core/repo/tree/render/views.js +293 -0
- package/dist/core/repo/tree/render/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +255 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +146 -14
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +239 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/legacy.js +271 -0
- package/dist/core/scaffold/legacy.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +252 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +268 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +95 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +204 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot/manifest.js +188 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/snapshot/snapshot.js +196 -0
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +181 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +60 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +126 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +196 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +214 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +163 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/feature/words.js +96 -0
- package/dist/core/status/feature/words.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +216 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +302 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +216 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/tasks/packet.js +199 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +166 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +140 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +101 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/modified.js +315 -0
- package/dist/core/usecases/delta/modified.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +76 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +296 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/grade/capability-tag.js +93 -0
- package/dist/core/usecases/grade/capability-tag.js.map +1 -0
- package/dist/core/usecases/grade/feature.js +88 -0
- package/dist/core/usecases/grade/feature.js.map +1 -0
- package/dist/core/usecases/grade/requirement-tag.js +94 -0
- package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
- package/dist/core/usecases/grade/steps.js +266 -0
- package/dist/core/usecases/grade/steps.js.map +1 -0
- package/dist/core/usecases/grade/usecases.js +112 -0
- package/dist/core/usecases/grade/usecases.js.map +1 -0
- package/dist/core/usecases/operations.js +123 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/service/exclude.js +119 -0
- package/dist/core/usecases/service/exclude.js.map +1 -0
- package/dist/core/usecases/service/flows.js +164 -0
- package/dist/core/usecases/service/flows.js.map +1 -0
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +180 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +170 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +259 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +158 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/read/shape.js +169 -0
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +165 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +99 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +61 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +59 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +216 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/vocabulary/report.js +90 -0
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +232 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +336 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +18 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +74 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +132 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +31 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +73 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +8 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +92 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +134 -0
- package/examples/docs/services/order-service/asyncapi.yaml +109 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +126 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +76 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +68 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +120 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +81 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +200 -0
- package/examples/docs/services/platform/notification-service/health.yaml +40 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +97 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -295
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js +0 -320
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js +0 -317
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/asyncapi.js +0 -253
- package/dist/core/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -596
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js +0 -225
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -489
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js +0 -47
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1132
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
package/dist/core/staging.js
DELETED
|
@@ -1,890 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The commit machinery `archive` and `unarchive` share: stage every new file
|
|
3
|
-
* version beside its target, swap the staged versions in, roll back what
|
|
4
|
-
* already went in when a later step fails — plus the undo snapshot `archive`
|
|
5
|
-
* writes and `unarchive` honours. Moved here verbatim from archive.ts once
|
|
6
|
-
* unarchive became its second consumer; the MERGE computations stay with
|
|
7
|
-
* archive, which is still their only caller.
|
|
8
|
-
*/
|
|
9
|
-
import { isUtf8 } from "node:buffer";
|
|
10
|
-
import { createHash } from "node:crypto";
|
|
11
|
-
import { existsSync } from "node:fs";
|
|
12
|
-
import { link, mkdir, open, readdir, readFile, rename, rm, rmdir, unlink, writeFile } from "node:fs/promises";
|
|
13
|
-
import { hostname } from "node:os";
|
|
14
|
-
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
15
|
-
import { repoPath } from "./json.js";
|
|
16
|
-
import { resolvePortableFileInsideLexically, UnsafePathError } from "./path-safety.js";
|
|
17
|
-
// Every `isRecord` below guards a record loam itself wrote and is reading back
|
|
18
|
-
// — the lock, the snapshot manifest — whose fields are asked for on the very
|
|
19
|
-
// next line, inside a REFUSAL path. `JSON.parse` answers with any JSON value,
|
|
20
|
-
// not with an object, and that is where a thrown `TypeError` costs the most: it
|
|
21
|
-
// does not make the command fail safely, it replaces a designed refusal with
|
|
22
|
-
// `internal`.
|
|
23
|
-
import { isRecord } from "./records.js";
|
|
24
|
-
/** The one place a planned write becomes bytes. */
|
|
25
|
-
function toBytes(content) {
|
|
26
|
-
return typeof content === "string" ? Buffer.from(content, "utf8") : content;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The one place that decides whether a planned write is a CREATE.
|
|
30
|
-
*
|
|
31
|
-
* `exclusive` is a no-clobber create (link(2)), so it can only be asked of a
|
|
32
|
-
* path that does not exist yet: asking it of an overwrite would fail EEXIST on
|
|
33
|
-
* every single run. Deciding here — rather than at each of the dozen
|
|
34
|
-
* `writes.push` sites in archive and unarchive — is what keeps the two commands
|
|
35
|
-
* from disagreeing about which of their writes may safely lose a race. An
|
|
36
|
-
* OVERWRITE is not left unguarded: `swapStaged` compares the target against the
|
|
37
|
-
* pre-image immediately before the rename, so a file another writer changed
|
|
38
|
-
* under us stops the commit instead of silently burying their bytes.
|
|
39
|
-
*/
|
|
40
|
-
export function planWrite(path, content) {
|
|
41
|
-
if (content !== null && !existsSync(path))
|
|
42
|
-
return { path, content, exclusive: true };
|
|
43
|
-
return { path, content };
|
|
44
|
-
}
|
|
45
|
-
/* ------------------------------------------------------------------ */
|
|
46
|
-
/* Bytes in, text only where a parser needs it */
|
|
47
|
-
/* ------------------------------------------------------------------ */
|
|
48
|
-
/**
|
|
49
|
-
* A file loam must PARSE that does not decode as the UTF-8 text loam owns.
|
|
50
|
-
*
|
|
51
|
-
* Commands map this to `merge-failed`: nothing was written, the file is named,
|
|
52
|
-
* and re-saving it as UTF-8 makes the command work. The read-only migration
|
|
53
|
-
* ingest already refuses non-UTF-8 input outright (openspec-inventory.ts) —
|
|
54
|
-
* the destructive path has strictly more to lose, so it refuses the same way
|
|
55
|
-
* rather than substituting U+FFFD and rewriting the file over the author.
|
|
56
|
-
*
|
|
57
|
-
* `reason` says what the bytes actually ARE, because the two ways a document
|
|
58
|
-
* fails this test destroy different things: one substitutes bytes, the other
|
|
59
|
-
* erases the whole document's meaning. A reader told the wrong one goes looking
|
|
60
|
-
* in the wrong place. The advice that follows is the same either way, so it is
|
|
61
|
-
* spelled once, here.
|
|
62
|
-
*/
|
|
63
|
-
export class NotUtf8Error extends Error {
|
|
64
|
-
path;
|
|
65
|
-
name = "NotUtf8Error";
|
|
66
|
-
constructor(path, reason) {
|
|
67
|
-
super(`${path} ${reason} Nothing was written. Re-save it as UTF-8, then re-run.`);
|
|
68
|
-
this.path = path;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Decode bytes a parser is about to be handed, or refuse naming the file.
|
|
73
|
-
*
|
|
74
|
-
* Two tests, the same two the read path makes (document-bytes.ts's
|
|
75
|
-
* `decodeDocument`), because this side has strictly more to lose by missing
|
|
76
|
-
* either. `isUtf8` catches the byte sequences that cannot be UTF-8 at all —
|
|
77
|
-
* which is what a byte-order mark makes UTF-16 into. Without a BOM, UTF-16LE of
|
|
78
|
-
* ASCII is a sequence of perfectly VALID UTF-8 bytes: every other byte is NUL,
|
|
79
|
-
* so `isUtf8` says yes, the file decodes with a U+0000 between every character,
|
|
80
|
-
* and every parser loam owns reads its requirements, frontmatter and headings
|
|
81
|
-
* as absent rather than as unreadable. On the read path that is a document
|
|
82
|
-
* graded green while nothing in it was checked; here it is worse, because
|
|
83
|
-
* `archive` merges against that empty baseline and writes the result back — the
|
|
84
|
-
* author's requirements are not mis-read, they are gone, and the undo snapshot
|
|
85
|
-
* records the same emptiness as the pre-image.
|
|
86
|
-
*/
|
|
87
|
-
function decodeUtf8(bytes, path) {
|
|
88
|
-
if (!isUtf8(bytes)) {
|
|
89
|
-
throw new NotUtf8Error(path, `is not valid UTF-8 — loam parses this file as text and writes it back, which would replace every ` +
|
|
90
|
-
`undecodable byte with U+FFFD, in the living document AND in the undo snapshot.`);
|
|
91
|
-
}
|
|
92
|
-
const text = bytes.toString("utf8");
|
|
93
|
-
if (text.includes("\u0000")) {
|
|
94
|
-
throw new NotUtf8Error(path, `holds NUL characters, which no document loam owns does — almost always UTF-16 saved without a byte-order ` +
|
|
95
|
-
`mark. Its bytes are valid UTF-8, so it decodes without complaint into text whose requirements, ` +
|
|
96
|
-
`frontmatter and headings all parse as ABSENT: the merge would compute over an empty baseline and write ` +
|
|
97
|
-
`that back, taking every requirement in the file with it.`);
|
|
98
|
-
}
|
|
99
|
-
return text;
|
|
100
|
-
}
|
|
101
|
-
/** Read a file loam is going to parse. The only supported way to turn a docs file into a string. */
|
|
102
|
-
export async function readUtf8(path) {
|
|
103
|
-
return decodeUtf8(await readFile(path), path);
|
|
104
|
-
}
|
|
105
|
-
/** Same bytes? Absence is its own value: a file that is gone is not a file that is empty. */
|
|
106
|
-
function sameBytes(a, b) {
|
|
107
|
-
return a === null || b === null ? a === b : a.equals(b);
|
|
108
|
-
}
|
|
109
|
-
/* ------------------------------------------------------------------ */
|
|
110
|
-
/* Reading loam's own records back off the disk */
|
|
111
|
-
/* ------------------------------------------------------------------ */
|
|
112
|
-
/** The one spelling of a sha256 loam wrote, for reading one back out of a record. */
|
|
113
|
-
function isDigest(value) {
|
|
114
|
-
return typeof value === "string" && /^[0-9a-f]{64}$/.test(value);
|
|
115
|
-
}
|
|
116
|
-
/* ------------------------------------------------------------------ */
|
|
117
|
-
/* The docs-repo advisory lock */
|
|
118
|
-
/* ------------------------------------------------------------------ */
|
|
119
|
-
/** The lock file, in the docs repo root — one writer at a time across the whole fleet. */
|
|
120
|
-
export const DOCS_LOCK = ".loam-lock";
|
|
121
|
-
/** Another writer holds the docs repo. Commands map this to the `docs-busy` envelope code. */
|
|
122
|
-
export class DocsBusyError extends Error {
|
|
123
|
-
lockPath;
|
|
124
|
-
name = "DocsBusyError";
|
|
125
|
-
constructor(lockPath, holder) {
|
|
126
|
-
super(`another loam command is writing this docs repo (${holder}) — nothing was read or written. ` +
|
|
127
|
-
`Wait for it to finish, or remove ${lockPath} if no such command is running.`);
|
|
128
|
-
this.lockPath = lockPath;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* The target changed between the pre-image staging read and the swap.
|
|
133
|
-
*
|
|
134
|
-
* The pre-image is what a rollback restores and what `unarchive` is handed, so
|
|
135
|
-
* swapping over somebody else's newer bytes would both lose their work and
|
|
136
|
-
* arm a rollback that writes a third version nobody wrote. Commands map this to
|
|
137
|
-
* `merge-failed`: nothing of theirs went in, the docs are as the other writer
|
|
138
|
-
* left them, and re-running can work.
|
|
139
|
-
*/
|
|
140
|
-
export class StagingRaceError extends Error {
|
|
141
|
-
path;
|
|
142
|
-
name = "StagingRaceError";
|
|
143
|
-
constructor(path) {
|
|
144
|
-
super(`${path} changed while this command was planning its merge — nothing was written; re-run`);
|
|
145
|
-
this.path = path;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Take the docs repo's advisory lock for the whole plan+commit window, and
|
|
150
|
-
* return the release.
|
|
151
|
-
*
|
|
152
|
-
* Advisory, and deliberately coarse: the destructive commands write the SAME
|
|
153
|
-
* three shared files (a service spec, a service contract, the one landscape),
|
|
154
|
-
* so two of them overlapping is not a merge to be resolved but a merge one of
|
|
155
|
-
* them computed against a snapshot that no longer exists. Two archives used to
|
|
156
|
-
* plan against the same landscape bytes and the second's splice silently
|
|
157
|
-
* dropped the first's additions — exit 0 on both, and a `validate` that stayed
|
|
158
|
-
* green because the surviving document was perfectly coherent.
|
|
159
|
-
*
|
|
160
|
-
* O_EXCL create is the primitive: it is atomic on every filesystem loam runs
|
|
161
|
-
* on, needs no daemon, and leaves a file a human can read and delete. A holder
|
|
162
|
-
* that died without releasing would otherwise wedge the repo forever, so a lock
|
|
163
|
-
* naming a pid on THIS host that no longer exists is broken once and retried —
|
|
164
|
-
* two racers could both break the same stale lock, which is why the pre-image
|
|
165
|
-
* check in `swapStaged` remains the second line of defence rather than an
|
|
166
|
-
* optimisation.
|
|
167
|
-
*/
|
|
168
|
-
export async function acquireDocsLock(docsDir) {
|
|
169
|
-
const path = join(docsDir, DOCS_LOCK);
|
|
170
|
-
const payload = `${JSON.stringify({ pid: process.pid, host: hostname(), at: new Date().toISOString() })}\n`;
|
|
171
|
-
for (let attempt = 0;; attempt += 1) {
|
|
172
|
-
try {
|
|
173
|
-
await writeFile(path, payload, { flag: "wx" });
|
|
174
|
-
}
|
|
175
|
-
catch (err) {
|
|
176
|
-
if (err.code !== "EEXIST")
|
|
177
|
-
throw err;
|
|
178
|
-
if (attempt === 0 && (await breakStaleLock(path)))
|
|
179
|
-
continue;
|
|
180
|
-
throw new DocsBusyError(path, await describeLock(path));
|
|
181
|
-
}
|
|
182
|
-
let released = false;
|
|
183
|
-
return async () => {
|
|
184
|
-
if (released)
|
|
185
|
-
return;
|
|
186
|
-
released = true;
|
|
187
|
-
await quietRm(path);
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/** Remove a lock whose holder is a dead process on this same host. Returns whether it did. */
|
|
192
|
-
async function breakStaleLock(path) {
|
|
193
|
-
let holder;
|
|
194
|
-
try {
|
|
195
|
-
holder = JSON.parse(await readFile(path, "utf8"));
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
// Unreadable or not ours to interpret — treat it as held. A lock file we
|
|
199
|
-
// cannot understand is the one case where guessing is worst.
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
// A lock file holding `null` (or a bare string, or an array) is JSON this
|
|
203
|
-
// parses and nothing else understands. Treat it as held, exactly as an
|
|
204
|
-
// unparseable one is: the dereference below used to run outside the try and
|
|
205
|
-
// threw out of `acquireDocsLock`, replacing the `docs-busy` refusal — the
|
|
206
|
-
// designed answer — with an `internal` envelope.
|
|
207
|
-
if (!isRecord(holder))
|
|
208
|
-
return false;
|
|
209
|
-
if (holder.host !== hostname() || typeof holder.pid !== "number")
|
|
210
|
-
return false;
|
|
211
|
-
try {
|
|
212
|
-
// Signal 0 asks "does this pid exist" without delivering anything. EPERM
|
|
213
|
-
// means it exists and belongs to somebody else — alive either way.
|
|
214
|
-
process.kill(holder.pid, 0);
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
217
|
-
catch (err) {
|
|
218
|
-
if (err.code !== "ESRCH")
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
try {
|
|
222
|
-
await unlink(path);
|
|
223
|
-
return true;
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
async function describeLock(path) {
|
|
230
|
-
try {
|
|
231
|
-
const raw = JSON.parse(await readFile(path, "utf8"));
|
|
232
|
-
return `pid ${String(raw.pid)} on ${String(raw.host)} since ${String(raw.at)}`;
|
|
233
|
-
}
|
|
234
|
-
catch {
|
|
235
|
-
return `see ${path}`;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Write every new version to a temp file beside its target, remembering what the
|
|
240
|
-
* target said first. Nothing observable changes here: on failure the temp files
|
|
241
|
-
* are removed and the docs are exactly as they were.
|
|
242
|
-
*
|
|
243
|
-
* The pre-image is read eagerly and deliberately: it is what a rollback restores
|
|
244
|
-
* and what `unarchive` is given, so a file we cannot read is a file we cannot
|
|
245
|
-
* safely rewrite, and the merge must stop before it starts.
|
|
246
|
-
*/
|
|
247
|
-
export async function stageWrites(writes) {
|
|
248
|
-
const staged = [];
|
|
249
|
-
try {
|
|
250
|
-
for (const [i, write] of writes.entries()) {
|
|
251
|
-
// mkdir(recursive) reports the topmost directory it had to create — the
|
|
252
|
-
// handle a rollback needs to put the tree back the way it found it.
|
|
253
|
-
const createdDir = (await mkdir(dirname(write.path), { recursive: true })) ?? null;
|
|
254
|
-
const before = existsSync(write.path) ? await readFile(write.path) : null;
|
|
255
|
-
const content = toBytes(write.content);
|
|
256
|
-
let tmp = null;
|
|
257
|
-
if (content !== null) {
|
|
258
|
-
tmp = tempPath(write.path, i);
|
|
259
|
-
await writeFile(tmp, content);
|
|
260
|
-
}
|
|
261
|
-
staged.push({ write, content, tmp, before, createdDir, swapped: false });
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
catch (err) {
|
|
265
|
-
await discardStaged(staged);
|
|
266
|
-
throw err;
|
|
267
|
-
}
|
|
268
|
-
return staged;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Swap the staged versions in, one file at a time. Each swap is a rename(2)
|
|
272
|
-
* within a single directory, so a concurrent reader sees either the old bytes or
|
|
273
|
-
* the new ones — never a half-written file. Across files it is not atomic: the
|
|
274
|
-
* caller rolls back what has already gone in.
|
|
275
|
-
*
|
|
276
|
-
* Every swap re-reads its target first and compares it with the pre-image the
|
|
277
|
-
* plan was computed from — a compare-and-set on the file's whole content. It is
|
|
278
|
-
* half a CAS on purpose: read and rename cannot be one syscall, so a writer
|
|
279
|
-
* landing inside that window still wins. What it does buy is the window that
|
|
280
|
-
* actually matters — the seconds a plan spends parsing LikeC4 and merging YAML
|
|
281
|
-
* between the pre-image read and this swap — and it costs one read per file.
|
|
282
|
-
*/
|
|
283
|
-
export async function swapStaged(staged) {
|
|
284
|
-
for (const s of staged) {
|
|
285
|
-
const now = existsSync(s.write.path) ? await readFile(s.write.path) : null;
|
|
286
|
-
if (!sameBytes(now, s.before))
|
|
287
|
-
throw new StagingRaceError(s.write.path);
|
|
288
|
-
if (s.tmp === null) {
|
|
289
|
-
if (s.before !== null)
|
|
290
|
-
await unlink(s.write.path);
|
|
291
|
-
}
|
|
292
|
-
else if (s.write.exclusive === true) {
|
|
293
|
-
// link(2) is the same-filesystem, atomic no-clobber counterpart to rename:
|
|
294
|
-
// it fails with EEXIST instead of replacing a file created after planning.
|
|
295
|
-
await link(s.tmp, s.write.path);
|
|
296
|
-
s.swapped = true;
|
|
297
|
-
await unlink(s.tmp);
|
|
298
|
-
s.tmp = null;
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
await rename(s.tmp, s.write.path);
|
|
303
|
-
}
|
|
304
|
-
s.swapped = true;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Put back every file that was already swapped, newest first, from the bytes read
|
|
309
|
-
* before the swap. Returns the paths it could NOT restore — the caller must say so
|
|
310
|
-
* out loud rather than report a clean failure over a half-merged repo.
|
|
311
|
-
*
|
|
312
|
-
* A rollback only ever un-does ITS OWN write: if the file no longer holds the
|
|
313
|
-
* bytes this run put there, somebody else has written it since, and restoring
|
|
314
|
-
* the pre-image would destroy their work while reporting a clean failure. That
|
|
315
|
-
* file is left exactly as found and named in the failures, which is what turns
|
|
316
|
-
* the caller's answer from `merge-failed` ("trust the repo") into
|
|
317
|
-
* `rollback-incomplete` ("look at it by hand") — the honest answer, because the
|
|
318
|
-
* repo now holds one run's writes half-reverted next to another's.
|
|
319
|
-
*/
|
|
320
|
-
export async function rollbackStaged(staged) {
|
|
321
|
-
const failures = [];
|
|
322
|
-
for (const s of [...staged].reverse()) {
|
|
323
|
-
if (!s.swapped)
|
|
324
|
-
continue;
|
|
325
|
-
try {
|
|
326
|
-
const now = existsSync(s.write.path) ? await readFile(s.write.path) : null;
|
|
327
|
-
if (!sameBytes(now, s.content)) {
|
|
328
|
-
failures.push(`${s.write.path} (changed by another writer after this run wrote it — left as found)`);
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
if (s.before === null)
|
|
332
|
-
await rm(s.write.path, { force: true });
|
|
333
|
-
else
|
|
334
|
-
await atomicWrite(s.write.path, s.before);
|
|
335
|
-
s.swapped = false;
|
|
336
|
-
}
|
|
337
|
-
catch (err) {
|
|
338
|
-
failures.push(`${s.write.path} (${message(err)})`);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
await discardStaged(staged);
|
|
342
|
-
return failures;
|
|
343
|
-
}
|
|
344
|
-
/** Write `content` to `path` through a temp file in the same directory. */
|
|
345
|
-
async function atomicWrite(path, content) {
|
|
346
|
-
const tmp = tempPath(path, 0);
|
|
347
|
-
await writeFile(tmp, content);
|
|
348
|
-
await rename(tmp, path);
|
|
349
|
-
}
|
|
350
|
-
/** A hidden sibling of `path`: same directory, so the rename never crosses a filesystem. */
|
|
351
|
-
function tempPath(path, n) {
|
|
352
|
-
return join(dirname(path), `.${basename(path)}.loam-${process.pid}-${n}-${Date.now()}.tmp`);
|
|
353
|
-
}
|
|
354
|
-
/** The shape `tempPath` writes — what a scan for leftovers of a killed run matches on. */
|
|
355
|
-
const TEMP_FILE_RE = /^\..*\.loam-\d+-\d+-\d+\.tmp$/;
|
|
356
|
-
/**
|
|
357
|
-
* Everything staging left on disk: the temp files, and the directories it made
|
|
358
|
-
* for them.
|
|
359
|
-
*
|
|
360
|
-
* Directories go by rmdir, never by recursive remove, and the walk stops the
|
|
361
|
-
* moment one is not empty. The recursive form used to be reached for because
|
|
362
|
-
* "we created this directory, so it is ours" — but a directory created at plan
|
|
363
|
-
* time is shared by the time the rollback runs: a second archive can have put a
|
|
364
|
-
* whole feature inside `features/archive/` in between, and `rm -r` on our own
|
|
365
|
-
* created root took their work with it, silently, on OUR failure path.
|
|
366
|
-
*/
|
|
367
|
-
async function discardStaged(staged) {
|
|
368
|
-
for (const s of staged)
|
|
369
|
-
if (s.tmp !== null)
|
|
370
|
-
await quietRm(s.tmp);
|
|
371
|
-
const roots = staged.filter((s) => s.createdDir !== null).map((s) => resolve(s.createdDir));
|
|
372
|
-
for (const s of [...staged].reverse()) {
|
|
373
|
-
const root = roots.find((candidate) => contains(candidate, s.write.path));
|
|
374
|
-
if (root !== undefined)
|
|
375
|
-
await quietPruneEmptyParents(dirname(s.write.path), root);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
function contains(parent, candidate) {
|
|
379
|
-
const rel = relative(parent, resolve(candidate));
|
|
380
|
-
return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Undo the directories a write had to create, from `start` up to and including
|
|
384
|
-
* `boundary`. Remove only directories that are still EMPTY: anything else in
|
|
385
|
-
* there arrived after we made it, and belongs to whoever put it there.
|
|
386
|
-
*/
|
|
387
|
-
export async function quietPruneEmptyParents(start, boundary) {
|
|
388
|
-
let cursor = resolve(start);
|
|
389
|
-
const root = resolve(boundary);
|
|
390
|
-
while (contains(root, cursor)) {
|
|
391
|
-
try {
|
|
392
|
-
await rmdir(cursor);
|
|
393
|
-
}
|
|
394
|
-
catch {
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
if (cursor === root)
|
|
398
|
-
return;
|
|
399
|
-
const parent = dirname(cursor);
|
|
400
|
-
if (parent === cursor)
|
|
401
|
-
return;
|
|
402
|
-
cursor = parent;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
export async function quietRm(path) {
|
|
406
|
-
try {
|
|
407
|
-
await rm(path, { recursive: true, force: true });
|
|
408
|
-
}
|
|
409
|
-
catch {
|
|
410
|
-
// Best effort — a leftover temp file is noise, not corruption.
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
/** Say what the failure cost: nothing, or a repo that needs looking at by hand. */
|
|
414
|
-
export function rollbackError(err, failures) {
|
|
415
|
-
if (failures.length === 0) {
|
|
416
|
-
return new Error(`${message(err)} — the living docs were rolled back, nothing was merged`);
|
|
417
|
-
}
|
|
418
|
-
return new Error(`${message(err)} — ROLLBACK INCOMPLETE, these files may be half-merged and need checking by hand: ${failures.join(", ")}`);
|
|
419
|
-
}
|
|
420
|
-
export function message(err) {
|
|
421
|
-
return err instanceof Error ? err.message : String(err);
|
|
422
|
-
}
|
|
423
|
-
/* ------------------------------------------------------------------ */
|
|
424
|
-
/* The undo snapshot */
|
|
425
|
-
/* ------------------------------------------------------------------ */
|
|
426
|
-
/**
|
|
427
|
-
* Where archive parks the bytes it is about to overwrite, inside the feature
|
|
428
|
-
* directory so it travels with it into `features/archive/`.
|
|
429
|
-
*
|
|
430
|
-
* It exists because the merge is not invertible: a MODIFIED requirement's
|
|
431
|
-
* previous text appears nowhere in the delta, and a landscape rewritten by hand
|
|
432
|
-
* since cannot be un-rewritten by re-reading the delta either. `unarchive` puts
|
|
433
|
-
* bytes back; it does not recompute them.
|
|
434
|
-
*/
|
|
435
|
-
export const SNAPSHOT_DIR = ".loam-before";
|
|
436
|
-
export const SNAPSHOT_MANIFEST = "manifest.json";
|
|
437
|
-
/**
|
|
438
|
-
* Bumped only when the layout changes in a way `unarchive` must refuse to guess
|
|
439
|
-
* at. 2 added `before` — a version-1 snapshot records nothing about the bytes it
|
|
440
|
-
* would RESTORE, so nothing can tell an intact pre-image from an edited one, and
|
|
441
|
-
* "I put your docs back" over a silently altered pre-image is the one sentence
|
|
442
|
-
* this command must never be able to say by accident.
|
|
443
|
-
*/
|
|
444
|
-
export const SNAPSHOT_VERSION = 2;
|
|
445
|
-
/** A snapshot directory that must not be replaced, because it is the only copy of the true pre-images. */
|
|
446
|
-
export class SnapshotClobberError extends Error {
|
|
447
|
-
dir;
|
|
448
|
-
name = "SnapshotClobberError";
|
|
449
|
-
constructor(dir, detail) {
|
|
450
|
-
super(`${dir} already holds an undo snapshot that does NOT match the living docs (${detail}) — a previous archive of ` +
|
|
451
|
-
`this feature wrote to the living docs and never took them back, and these are the only copies of what they ` +
|
|
452
|
-
`said before. Overwriting them would make that merge permanent and unreportable. Run \`loam archive\` again ` +
|
|
453
|
-
`after restoring the living docs from version control, or delete ${SNAPSHOT_DIR}/ if you know the merge never landed.`);
|
|
454
|
-
this.dir = dir;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
export function snapshotDir(featureDir) {
|
|
458
|
-
return join(featureDir, SNAPSHOT_DIR);
|
|
459
|
-
}
|
|
460
|
-
export function sha256(content) {
|
|
461
|
-
return typeof content === "string"
|
|
462
|
-
? createHash("sha256").update(content, "utf8").digest("hex")
|
|
463
|
-
: createHash("sha256").update(content).digest("hex");
|
|
464
|
-
}
|
|
465
|
-
export async function writeSnapshot(featureDir, docsDir, featureId, dirName, staged) {
|
|
466
|
-
const dir = snapshotDir(featureDir);
|
|
467
|
-
await assertSnapshotReplaceable(dir, docsDir);
|
|
468
|
-
// A leftover from a rolled-back run would describe a merge that never happened.
|
|
469
|
-
await quietRm(dir);
|
|
470
|
-
const files = [];
|
|
471
|
-
for (const s of staged) {
|
|
472
|
-
const rel = repoPath(docsDir, s.write.path);
|
|
473
|
-
files.push({
|
|
474
|
-
path: rel,
|
|
475
|
-
existed: s.before !== null,
|
|
476
|
-
after: sha256(s.content ?? Buffer.alloc(0)),
|
|
477
|
-
before: s.before === null ? null : sha256(s.before),
|
|
478
|
-
});
|
|
479
|
-
if (s.before !== null) {
|
|
480
|
-
const dest = join(dir, "files", rel);
|
|
481
|
-
await mkdir(dirname(dest), { recursive: true });
|
|
482
|
-
await writeFile(dest, s.before);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
const manifest = {
|
|
486
|
-
version: SNAPSHOT_VERSION,
|
|
487
|
-
feature: featureId,
|
|
488
|
-
dirName,
|
|
489
|
-
archivedAt: new Date().toISOString(),
|
|
490
|
-
files,
|
|
491
|
-
};
|
|
492
|
-
// Manifest last: its presence is what says the pre-images beside it are complete.
|
|
493
|
-
await mkdir(dir, { recursive: true });
|
|
494
|
-
await writeFile(join(dir, SNAPSHOT_MANIFEST), JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Refuse to replace a snapshot whose pre-images are still the only record of
|
|
498
|
-
* what the living docs said.
|
|
499
|
-
*
|
|
500
|
-
* A snapshot in an ACTIVE feature directory means a previous archive of this
|
|
501
|
-
* same feature wrote it — and since a clean rollback deletes it, finding one
|
|
502
|
-
* means that run either died mid-commit or reported `rollback-incomplete`. If
|
|
503
|
-
* its pre-images still match the living docs, nothing of that run landed and
|
|
504
|
-
* the snapshot is stale bookkeeping. If they do not, the living docs carry
|
|
505
|
-
* that run's writes, these files are the only way back, and the unconditional
|
|
506
|
-
* clear turned a repairable half-merge into a permanent one — after which
|
|
507
|
-
* `unarchive` restored the half-merged text and said the docs were back.
|
|
508
|
-
*/
|
|
509
|
-
async function assertSnapshotReplaceable(dir, docsDir) {
|
|
510
|
-
const manifestPath = join(dir, SNAPSHOT_MANIFEST);
|
|
511
|
-
// The manifest is written LAST, so a `files/` tree without one is a snapshot
|
|
512
|
-
// whose run died before it could swap anything: it describes no merge.
|
|
513
|
-
if (!existsSync(manifestPath))
|
|
514
|
-
return;
|
|
515
|
-
let manifest;
|
|
516
|
-
try {
|
|
517
|
-
manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
518
|
-
}
|
|
519
|
-
catch {
|
|
520
|
-
throw new SnapshotClobberError(dir, `${SNAPSHOT_MANIFEST} cannot be read, so nothing can say whether it is stale`);
|
|
521
|
-
}
|
|
522
|
-
// The cast that used to stand here asserted the shape rather than checking
|
|
523
|
-
// it: well-formed JSON that is not an object answered `undefined` to every
|
|
524
|
-
// field below and slipped through as "a different loam", and a bare `null`
|
|
525
|
-
// threw a TypeError out of the refusal itself. Both left this directory —
|
|
526
|
-
// which may hold the only copies of what the living docs said — one step from
|
|
527
|
-
// being replaced, without the staleness question ever having been asked.
|
|
528
|
-
if (!isRecord(manifest)) {
|
|
529
|
-
throw new SnapshotClobberError(dir, `${SNAPSHOT_MANIFEST} cannot be read, so nothing can say whether it is stale`);
|
|
530
|
-
}
|
|
531
|
-
if (manifest.version !== SNAPSHOT_VERSION || !Array.isArray(manifest.files)) {
|
|
532
|
-
throw new SnapshotClobberError(dir, `it was written by a different loam and cannot be checked against the living docs`);
|
|
533
|
-
}
|
|
534
|
-
// `Array.isArray` narrows to `any[]`; naming the element type here is what
|
|
535
|
-
// keeps that `any` from spreading past the one row being read.
|
|
536
|
-
const rows = manifest.files;
|
|
537
|
-
const landed = [];
|
|
538
|
-
for (const row of rows) {
|
|
539
|
-
const entry = readSnapshotClaim(row);
|
|
540
|
-
if (entry === null) {
|
|
541
|
-
throw new SnapshotClobberError(dir, `${SNAPSHOT_MANIFEST} is malformed, so nothing can say whether it is stale`);
|
|
542
|
-
}
|
|
543
|
-
let target;
|
|
544
|
-
try {
|
|
545
|
-
// These paths are data, and they are the ones this function goes and
|
|
546
|
-
// reads. They name living documents, so containment is lexical: a docs
|
|
547
|
-
// repo may legitimately mount a service directory through a symlink, and
|
|
548
|
-
// `unarchive` resolves the same field the same way before it writes back.
|
|
549
|
-
target = resolvePortableFileInsideLexically(docsDir, entry.path, `snapshot path '${entry.path}'`);
|
|
550
|
-
}
|
|
551
|
-
catch (err) {
|
|
552
|
-
if (!(err instanceof UnsafePathError))
|
|
553
|
-
throw err;
|
|
554
|
-
throw new SnapshotClobberError(dir, `${message(err)}, so nothing can say whether it is stale`);
|
|
555
|
-
}
|
|
556
|
-
const now = existsSync(target) ? sha256(await readFile(target)) : null;
|
|
557
|
-
// `existed: false` means archive created the file, so "nothing landed"
|
|
558
|
-
// means it is still absent.
|
|
559
|
-
const untouched = entry.existed ? now === entry.before : now === null;
|
|
560
|
-
if (!untouched)
|
|
561
|
-
landed.push(entry.path);
|
|
562
|
-
}
|
|
563
|
-
if (landed.length > 0)
|
|
564
|
-
throw new SnapshotClobberError(dir, `${landed.length} file(s) already carry that merge: ${landed.join(", ")}`);
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* One manifest row read back as the record `writeSnapshot` wrote, or null when
|
|
568
|
-
* the file does not actually say.
|
|
569
|
-
*
|
|
570
|
-
* Only the three fields the staleness question consumes are checked, and every
|
|
571
|
-
* one of them is checked, because an unchecked field does not read as missing
|
|
572
|
-
* here — it reads as an ANSWER. An `existed` that is not a boolean is falsy, and
|
|
573
|
-
* a `false` claim is graded by asking whether the file is ABSENT: a manifest
|
|
574
|
-
* recording a pre-image for a file that has since been deleted therefore graded
|
|
575
|
-
* as stale bookkeeping and was cleared, taking the only copy of that pre-image
|
|
576
|
-
* with it. A `before` of the wrong type equals no digest at all, so the opposite
|
|
577
|
-
* row accuses an untouched file of carrying a merge. Both are confident answers
|
|
578
|
-
* to a question the manifest could not answer.
|
|
579
|
-
*
|
|
580
|
-
* `unarchive` asks a stricter version of the same question of the same file
|
|
581
|
-
* (`after`, `archivedAt`, the feature name, no duplicates) because it is about
|
|
582
|
-
* to write THROUGH it; that check stays where it is, with its own refusals.
|
|
583
|
-
*/
|
|
584
|
-
function readSnapshotClaim(value) {
|
|
585
|
-
if (!isRecord(value))
|
|
586
|
-
return null;
|
|
587
|
-
const { path, existed, before } = value;
|
|
588
|
-
if (typeof path !== "string" || typeof existed !== "boolean")
|
|
589
|
-
return null;
|
|
590
|
-
if (existed)
|
|
591
|
-
return isDigest(before) ? { path, existed, before } : null;
|
|
592
|
-
return before === null ? { path, existed, before } : null;
|
|
593
|
-
}
|
|
594
|
-
/* ------------------------------------------------------------------ */
|
|
595
|
-
/* The commit intent record */
|
|
596
|
-
/* ------------------------------------------------------------------ */
|
|
597
|
-
/**
|
|
598
|
-
* The journal that makes an interrupted commit detectable.
|
|
599
|
-
*
|
|
600
|
-
* `swapStaged` is N renames, and only each ONE of them is atomic. A kill
|
|
601
|
-
* between two of them leaves the living docs half-merged — and nothing could
|
|
602
|
-
* see it: `doctor` and `status` called the repo healthy, `validate --all`
|
|
603
|
-
* blamed the delta, and the next archive cleared the one snapshot that could
|
|
604
|
-
* have repaired it. So the swap loop now runs inside a record written and
|
|
605
|
-
* fsynced BEFORE the first rename and removed after the last step of the
|
|
606
|
-
* commit: its mere presence says a commit was in flight, and its per-path
|
|
607
|
-
* digests say, file by file, whether that file's rename landed.
|
|
608
|
-
*
|
|
609
|
-
* It is deliberately NOT a source of truth and never disagrees with the files:
|
|
610
|
-
* every judgement it supports is made by re-reading the living bytes and
|
|
611
|
-
* comparing digests, and every byte a repair writes comes from the snapshot
|
|
612
|
-
* and is verified against the digest recorded before the crash. Outside the
|
|
613
|
-
* commit window it does not exist.
|
|
614
|
-
*/
|
|
615
|
-
export const COMMIT_INTENT = ".loam-commit";
|
|
616
|
-
const COMMIT_INTENT_VERSION = 1;
|
|
617
|
-
/** An interrupted commit this loam must not silently write over. */
|
|
618
|
-
export class InterruptedCommitError extends Error {
|
|
619
|
-
name = "InterruptedCommitError";
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Write the intent record and get it onto the disk before the first swap.
|
|
623
|
-
*
|
|
624
|
-
* fsync, not just write: the point of the record is to survive the crash that
|
|
625
|
-
* interrupts the commit, and a record still sitting in the page cache when the
|
|
626
|
-
* machine dies describes nothing. The directory entry is fsynced too where the
|
|
627
|
-
* platform allows opening a directory — without that the file's bytes can be
|
|
628
|
-
* durable while its name is not.
|
|
629
|
-
*/
|
|
630
|
-
export async function writeCommitIntent(docsDir, spec, staged) {
|
|
631
|
-
const intent = {
|
|
632
|
-
version: COMMIT_INTENT_VERSION,
|
|
633
|
-
command: spec.command,
|
|
634
|
-
restore: spec.restore,
|
|
635
|
-
pid: process.pid,
|
|
636
|
-
host: hostname(),
|
|
637
|
-
at: new Date().toISOString(),
|
|
638
|
-
feature: spec.feature,
|
|
639
|
-
moveFrom: repoPath(docsDir, spec.moveFrom),
|
|
640
|
-
moveTo: repoPath(docsDir, spec.moveTo),
|
|
641
|
-
files: staged.map((s) => ({
|
|
642
|
-
path: repoPath(docsDir, s.write.path),
|
|
643
|
-
before: s.before === null ? null : sha256(s.before),
|
|
644
|
-
after: s.content === null ? null : sha256(s.content),
|
|
645
|
-
})),
|
|
646
|
-
};
|
|
647
|
-
const path = join(docsDir, COMMIT_INTENT);
|
|
648
|
-
const handle = await open(path, "w");
|
|
649
|
-
try {
|
|
650
|
-
await handle.writeFile(JSON.stringify(intent, null, 2) + "\n", "utf8");
|
|
651
|
-
await handle.sync();
|
|
652
|
-
}
|
|
653
|
-
finally {
|
|
654
|
-
await handle.close();
|
|
655
|
-
}
|
|
656
|
-
try {
|
|
657
|
-
const dir = await open(docsDir, "r");
|
|
658
|
-
try {
|
|
659
|
-
await dir.sync();
|
|
660
|
-
}
|
|
661
|
-
finally {
|
|
662
|
-
await dir.close();
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
catch {
|
|
666
|
-
// Not every platform lets a directory be opened; the record's own bytes are
|
|
667
|
-
// flushed either way, which is the part that carries the digests.
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
/** The commit finished (or was rolled back): there is nothing left to recover from. */
|
|
671
|
-
export async function clearCommitIntent(docsDir) {
|
|
672
|
-
await quietRm(join(docsDir, COMMIT_INTENT));
|
|
673
|
-
}
|
|
674
|
-
/** The intent record as written, or null when it is absent or unreadable. */
|
|
675
|
-
export async function readCommitIntent(docsDir) {
|
|
676
|
-
const path = join(docsDir, COMMIT_INTENT);
|
|
677
|
-
if (!existsSync(path))
|
|
678
|
-
return null;
|
|
679
|
-
let parsed;
|
|
680
|
-
try {
|
|
681
|
-
parsed = JSON.parse(await readFile(path, "utf8"));
|
|
682
|
-
}
|
|
683
|
-
catch {
|
|
684
|
-
return null;
|
|
685
|
-
}
|
|
686
|
-
const i = parsed;
|
|
687
|
-
if (i === null || typeof i !== "object")
|
|
688
|
-
return null;
|
|
689
|
-
if (i.version !== COMMIT_INTENT_VERSION)
|
|
690
|
-
return null;
|
|
691
|
-
if (i.command !== "archive" && i.command !== "unarchive")
|
|
692
|
-
return null;
|
|
693
|
-
if (i.restore !== "before" && i.restore !== "after")
|
|
694
|
-
return null;
|
|
695
|
-
if (typeof i.feature !== "string" || typeof i.moveFrom !== "string" || typeof i.moveTo !== "string")
|
|
696
|
-
return null;
|
|
697
|
-
if (!Array.isArray(i.files))
|
|
698
|
-
return null;
|
|
699
|
-
for (const f of i.files) {
|
|
700
|
-
if (typeof f?.path !== "string")
|
|
701
|
-
return null;
|
|
702
|
-
if (!isDigestOrNull(f.before) || !isDigestOrNull(f.after))
|
|
703
|
-
return null;
|
|
704
|
-
}
|
|
705
|
-
return i;
|
|
706
|
-
}
|
|
707
|
-
function isDigestOrNull(value) {
|
|
708
|
-
return value === null || isDigest(value);
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Refuse or repair before this run reads anything. Returns null when there is
|
|
712
|
-
* nothing to recover.
|
|
713
|
-
*
|
|
714
|
-
* Repair is only ever the bytes the interrupted run already had on disk: each
|
|
715
|
-
* file still in the wrong state is rewritten from the snapshot pre-image and
|
|
716
|
-
* only after that pre-image's own sha256 matches the digest the record captured
|
|
717
|
-
* before the crash. A file in NEITHER state was written by somebody else since,
|
|
718
|
-
* so this refuses and names it rather than choosing which of two truths to
|
|
719
|
-
* destroy — the same doctrine `rollbackStaged` follows when it leaves a file as
|
|
720
|
-
* found.
|
|
721
|
-
*/
|
|
722
|
-
export async function recoverInterruptedCommit(docsDir) {
|
|
723
|
-
const path = join(docsDir, COMMIT_INTENT);
|
|
724
|
-
if (!existsSync(path))
|
|
725
|
-
return null;
|
|
726
|
-
const intent = await readCommitIntent(docsDir);
|
|
727
|
-
if (intent === null) {
|
|
728
|
-
throw new InterruptedCommitError(`${repoPath(docsDir, path)} records a commit this loam cannot read, so it cannot tell a half-merged docs repo ` +
|
|
729
|
-
`from a healthy one. Check the living docs against version control, delete ${COMMIT_INTENT}, then re-run.`);
|
|
730
|
-
}
|
|
731
|
-
const moveFrom = join(docsDir, ...intent.moveFrom.split("/"));
|
|
732
|
-
const moveTo = join(docsDir, ...intent.moveTo.split("/"));
|
|
733
|
-
// The move is the commit's LAST step. If it landed, every swap before it did
|
|
734
|
-
// too, and only the record's own removal was lost.
|
|
735
|
-
if (existsSync(moveTo) && !existsSync(moveFrom)) {
|
|
736
|
-
await finishRecovery(docsDir, intent);
|
|
737
|
-
return { command: intent.command, feature: intent.feature, outcome: "completed", repaired: [] };
|
|
738
|
-
}
|
|
739
|
-
const target = (rel) => join(docsDir, ...rel.split("/"));
|
|
740
|
-
const wanted = (f) => (intent.restore === "before" ? f.before : f.after);
|
|
741
|
-
const other = (f) => (intent.restore === "before" ? f.after : f.before);
|
|
742
|
-
const pending = [];
|
|
743
|
-
const unknown = [];
|
|
744
|
-
for (const f of intent.files) {
|
|
745
|
-
const file = target(f.path);
|
|
746
|
-
const now = existsSync(file) ? sha256(await readFile(file)) : null;
|
|
747
|
-
if (now === wanted(f))
|
|
748
|
-
continue;
|
|
749
|
-
if (now === other(f))
|
|
750
|
-
pending.push(f);
|
|
751
|
-
else
|
|
752
|
-
unknown.push(f.path);
|
|
753
|
-
}
|
|
754
|
-
if (unknown.length > 0) {
|
|
755
|
-
throw new InterruptedCommitError(`a \`loam ${intent.command} ${intent.feature}\` was interrupted mid-commit (pid ${intent.pid} on ${intent.host}, ` +
|
|
756
|
-
`${intent.at}) and ${unknown.length} of the file(s) it was writing now hold bytes that are neither what it ` +
|
|
757
|
-
`found nor what it wrote — somebody edited them since: ${unknown.join(", ")}. loam will not choose between ` +
|
|
758
|
-
`those two truths. Reconcile them against version control, delete ${COMMIT_INTENT}, then re-run.`);
|
|
759
|
-
}
|
|
760
|
-
if (pending.length === 0) {
|
|
761
|
-
await finishRecovery(docsDir, intent);
|
|
762
|
-
return { command: intent.command, feature: intent.feature, outcome: "consistent", repaired: [] };
|
|
763
|
-
}
|
|
764
|
-
// The pre-images travel inside the feature directory, so they are at one of
|
|
765
|
-
// the two ends of the move depending on how far the interrupted run got.
|
|
766
|
-
const roots = [join(moveFrom, SNAPSHOT_DIR, "files"), join(moveTo, SNAPSHOT_DIR, "files")];
|
|
767
|
-
const repaired = [];
|
|
768
|
-
for (const f of pending) {
|
|
769
|
-
const file = target(f.path);
|
|
770
|
-
const want = wanted(f);
|
|
771
|
-
if (want === null) {
|
|
772
|
-
await rm(file, { force: true });
|
|
773
|
-
await quietRmdir(dirname(file));
|
|
774
|
-
repaired.push(f.path);
|
|
775
|
-
continue;
|
|
776
|
-
}
|
|
777
|
-
const source = roots.map((r) => join(r, ...f.path.split("/"))).find((p) => existsSync(p));
|
|
778
|
-
if (source === undefined) {
|
|
779
|
-
throw new InterruptedCommitError(`a \`loam ${intent.command} ${intent.feature}\` was interrupted mid-commit and ${f.path} is half-written, but ` +
|
|
780
|
-
`the pre-image that would repair it is gone. Restore ${f.path} from version control, delete ` +
|
|
781
|
-
`${COMMIT_INTENT}, then re-run.`);
|
|
782
|
-
}
|
|
783
|
-
const bytes = await readFile(source);
|
|
784
|
-
if (sha256(bytes) !== want) {
|
|
785
|
-
throw new InterruptedCommitError(`a \`loam ${intent.command} ${intent.feature}\` was interrupted mid-commit and ${f.path} is half-written, but ` +
|
|
786
|
-
`the pre-image that would repair it no longer matches the digest recorded before the commit — it has been ` +
|
|
787
|
-
`edited since. Restore ${f.path} from version control, delete ${COMMIT_INTENT}, then re-run.`);
|
|
788
|
-
}
|
|
789
|
-
await atomicWrite(file, bytes);
|
|
790
|
-
repaired.push(f.path);
|
|
791
|
-
}
|
|
792
|
-
await finishRecovery(docsDir, intent);
|
|
793
|
-
return { command: intent.command, feature: intent.feature, outcome: "repaired", repaired };
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* Drop the record, and the staged temp files the killed run could not remove.
|
|
797
|
-
*
|
|
798
|
-
* Scoped to that run's own pid and its own targets — the temp name carries both
|
|
799
|
-
* — so a leftover belonging to anything else is left for `doctor` to name.
|
|
800
|
-
* Without this a repair that put every byte back still left the docs repo
|
|
801
|
-
* dirty, which is the one thing a caller checks to decide whether it worked.
|
|
802
|
-
*/
|
|
803
|
-
async function finishRecovery(docsDir, intent) {
|
|
804
|
-
const dirs = new Set(intent.files.map((f) => dirname(join(docsDir, ...f.path.split("/")))));
|
|
805
|
-
for (const dir of dirs) {
|
|
806
|
-
let entries;
|
|
807
|
-
try {
|
|
808
|
-
entries = await readdir(dir);
|
|
809
|
-
}
|
|
810
|
-
catch {
|
|
811
|
-
continue;
|
|
812
|
-
}
|
|
813
|
-
for (const name of entries) {
|
|
814
|
-
if (!TEMP_FILE_RE.test(name) || !name.includes(`.loam-${intent.pid}-`))
|
|
815
|
-
continue;
|
|
816
|
-
await quietRm(join(dir, name));
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
await clearCommitIntent(docsDir);
|
|
820
|
-
}
|
|
821
|
-
/** Remove `dir` if it is empty. Best effort — a directory left standing is noise, not corruption. */
|
|
822
|
-
async function quietRmdir(dir) {
|
|
823
|
-
try {
|
|
824
|
-
await rmdir(dir);
|
|
825
|
-
}
|
|
826
|
-
catch {
|
|
827
|
-
// Not empty, or not ours.
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
export async function scanWritePathResidue(docsDir) {
|
|
831
|
-
const lockPath = join(docsDir, DOCS_LOCK);
|
|
832
|
-
let lock = null;
|
|
833
|
-
if (existsSync(lockPath)) {
|
|
834
|
-
lock = { path: repoPath(docsDir, lockPath), holder: await describeLock(lockPath), stale: await lockIsStale(lockPath) };
|
|
835
|
-
}
|
|
836
|
-
const intent = await readCommitIntent(docsDir);
|
|
837
|
-
return {
|
|
838
|
-
lock,
|
|
839
|
-
intent,
|
|
840
|
-
intentUnreadable: intent === null && existsSync(join(docsDir, COMMIT_INTENT)),
|
|
841
|
-
temps: (await tempLeftovers(docsDir)).map((p) => repoPath(docsDir, p)).sort(),
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
/** Does the lock name a pid on THIS host that no longer exists? Read-only twin of breakStaleLock. */
|
|
845
|
-
async function lockIsStale(path) {
|
|
846
|
-
let holder;
|
|
847
|
-
try {
|
|
848
|
-
holder = JSON.parse(await readFile(path, "utf8"));
|
|
849
|
-
}
|
|
850
|
-
catch {
|
|
851
|
-
return false;
|
|
852
|
-
}
|
|
853
|
-
// Same shape guard as `breakStaleLock`, for the same reason: this one is read
|
|
854
|
-
// by `doctor`, which must be able to describe a broken repo without becoming
|
|
855
|
-
// the next thing that breaks.
|
|
856
|
-
if (!isRecord(holder))
|
|
857
|
-
return false;
|
|
858
|
-
if (holder.host !== hostname() || typeof holder.pid !== "number")
|
|
859
|
-
return false;
|
|
860
|
-
try {
|
|
861
|
-
process.kill(holder.pid, 0);
|
|
862
|
-
return false;
|
|
863
|
-
}
|
|
864
|
-
catch (err) {
|
|
865
|
-
return err.code === "ESRCH";
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
async function tempLeftovers(dir) {
|
|
869
|
-
const out = [];
|
|
870
|
-
let entries;
|
|
871
|
-
try {
|
|
872
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
873
|
-
}
|
|
874
|
-
catch {
|
|
875
|
-
return out;
|
|
876
|
-
}
|
|
877
|
-
for (const entry of entries) {
|
|
878
|
-
// `.git` alone can hold more files than the whole docs repo, and nothing
|
|
879
|
-
// loam writes ever lands in it.
|
|
880
|
-
if (entry.name === ".git" || entry.name === "node_modules")
|
|
881
|
-
continue;
|
|
882
|
-
const path = join(dir, entry.name);
|
|
883
|
-
if (entry.isDirectory())
|
|
884
|
-
out.push(...(await tempLeftovers(path)));
|
|
885
|
-
else if (entry.isFile() && TEMP_FILE_RE.test(entry.name))
|
|
886
|
-
out.push(path);
|
|
887
|
-
}
|
|
888
|
-
return out;
|
|
889
|
-
}
|
|
890
|
-
//# sourceMappingURL=staging.js.map
|