@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
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refuse or repair an interrupted commit, before this run reads anything.
|
|
3
|
+
*
|
|
4
|
+
* Split from `./intent.ts` because this is the only module on the write path
|
|
5
|
+
* that writes bytes nobody asked for in this run. Two journal versions, two
|
|
6
|
+
* doctrines, one dispatch: a version-1 record (archive/unarchive) is repaired
|
|
7
|
+
* BACKWARD from the snapshot pre-images, each verified against the digest the
|
|
8
|
+
* record captured; a version-2 record is handed to `txn/forward.ts`, which
|
|
9
|
+
* rolls FORWARD from the staged temps under the same verification. Either
|
|
10
|
+
* way, a file in NEITHER recorded state was written by somebody else since,
|
|
11
|
+
* so recovery refuses and names it rather than choosing which of two truths
|
|
12
|
+
* to destroy.
|
|
13
|
+
*
|
|
14
|
+
* The journaled writers reach it through `openCommitWindow` below — the lock,
|
|
15
|
+
* then this — so the two refusals that can keep a commit window shut are
|
|
16
|
+
* spelled once rather than once per writer. Archive and unarchive are the
|
|
17
|
+
* exception: they call it directly, inside their plan, under a lock they took
|
|
18
|
+
* themselves, and throw its refusal to a handler that maps it.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync } from "node:fs";
|
|
21
|
+
import { readFile, rm } from "node:fs/promises";
|
|
22
|
+
import { dirname, join } from "node:path";
|
|
23
|
+
import { repoPath } from "../../envelope/json.js";
|
|
24
|
+
import { acrossRename } from "../../kernel/path-safety.js";
|
|
25
|
+
import { sha256 } from "../writes.js";
|
|
26
|
+
import { atomicWrite, quietRmdir } from "../commit.js";
|
|
27
|
+
import { PRE_IMAGE_SUFFIX, SNAPSHOT_DIR } from "../snapshot/manifest.js";
|
|
28
|
+
import { COMMIT_INTENT, InterruptedCommitError, sweepPidTemps, } from "../interrupted.js";
|
|
29
|
+
import { acquireDocsLock, acquireDocsLockWaiting, DocsBusyError, LOCK_WAIT_MS } from "../lock.js";
|
|
30
|
+
import { readTxnIntent } from "../txn/journal.js";
|
|
31
|
+
import { recoverForward } from "../txn/forward.js";
|
|
32
|
+
import { clearCommitIntent, readCommitIntent, } from "./intent.js";
|
|
33
|
+
/**
|
|
34
|
+
* Refuse or repair before this run reads anything. Returns null when there is
|
|
35
|
+
* nothing to recover.
|
|
36
|
+
*
|
|
37
|
+
* Repair is only ever the bytes the interrupted run already had on disk: each
|
|
38
|
+
* file still in the wrong state is rewritten from the snapshot pre-image and
|
|
39
|
+
* only after that pre-image's own sha256 matches the digest the record captured
|
|
40
|
+
* before the crash. A file in NEITHER state was written by somebody else since,
|
|
41
|
+
* so this refuses and names it rather than choosing which of two truths to
|
|
42
|
+
* destroy — the same doctrine `rollbackStaged` follows when it leaves a file as
|
|
43
|
+
* found.
|
|
44
|
+
*/
|
|
45
|
+
export async function recoverInterruptedCommit(docsDir) {
|
|
46
|
+
const path = join(docsDir, COMMIT_INTENT);
|
|
47
|
+
if (!existsSync(path))
|
|
48
|
+
return null;
|
|
49
|
+
const intent = await readCommitIntent(docsDir);
|
|
50
|
+
if (intent === null) {
|
|
51
|
+
// Version 2 — the smaller transaction's journal. Same file, different
|
|
52
|
+
// recovery: forward from the staged temps, in txn/forward.ts.
|
|
53
|
+
const txn = await readTxnIntent(docsDir);
|
|
54
|
+
if (txn !== null)
|
|
55
|
+
return recoverForward(docsDir, txn);
|
|
56
|
+
throw new InterruptedCommitError(`${repoPath(docsDir, path)} records a commit this loam cannot read, so it cannot tell a half-merged docs repo ` +
|
|
57
|
+
`from a healthy one. Check the living docs against version control, delete ${COMMIT_INTENT}, then re-run.`);
|
|
58
|
+
}
|
|
59
|
+
const moveFrom = join(docsDir, ...intent.moveFrom.split("/"));
|
|
60
|
+
const moveTo = join(docsDir, ...intent.moveTo.split("/"));
|
|
61
|
+
// The move is the commit's LAST step. If it landed, every swap before it did
|
|
62
|
+
// too, and only the record's own removal was lost.
|
|
63
|
+
if (existsSync(moveTo) && !existsSync(moveFrom)) {
|
|
64
|
+
await finishRecovery(docsDir, intent);
|
|
65
|
+
return { command: intent.command, feature: intent.feature, outcome: "completed", repaired: [] };
|
|
66
|
+
}
|
|
67
|
+
const target = (rel) => join(docsDir, ...rel.split("/"));
|
|
68
|
+
const wanted = (f) => (intent.restore === "before" ? f.before : f.after);
|
|
69
|
+
const other = (f) => (intent.restore === "before" ? f.after : f.before);
|
|
70
|
+
const pending = [];
|
|
71
|
+
const unknown = [];
|
|
72
|
+
for (const f of intent.files) {
|
|
73
|
+
const file = target(f.path);
|
|
74
|
+
const now = existsSync(file) ? sha256(await readFile(file)) : null;
|
|
75
|
+
if (now === wanted(f))
|
|
76
|
+
continue;
|
|
77
|
+
if (now === other(f))
|
|
78
|
+
pending.push(f);
|
|
79
|
+
else
|
|
80
|
+
unknown.push(f.path);
|
|
81
|
+
}
|
|
82
|
+
if (unknown.length > 0) {
|
|
83
|
+
throw new InterruptedCommitError(`a \`loam ${intent.command} ${intent.feature}\` was interrupted mid-commit (pid ${intent.pid} on ${intent.host}, ` +
|
|
84
|
+
`${intent.at}) and ${unknown.length} of the file(s) it was writing now hold bytes that are neither what it ` +
|
|
85
|
+
`found nor what it wrote — somebody edited them since: ${unknown.join(", ")}. loam will not choose between ` +
|
|
86
|
+
`those two truths. Reconcile them against version control, delete ${COMMIT_INTENT}, then re-run.`);
|
|
87
|
+
}
|
|
88
|
+
if (pending.length === 0) {
|
|
89
|
+
await finishRecovery(docsDir, intent);
|
|
90
|
+
return { command: intent.command, feature: intent.feature, outcome: "consistent", repaired: [] };
|
|
91
|
+
}
|
|
92
|
+
// The pre-images travel inside the feature directory, so they are at one of
|
|
93
|
+
// the two ends of the move depending on how far the interrupted run got.
|
|
94
|
+
// Deliberately NOT the snapshot's (service, artifact) re-keying: the journal
|
|
95
|
+
// and the snapshot were written seconds apart under the same docs lock, so
|
|
96
|
+
// no service directory can have moved between the journal's literal paths
|
|
97
|
+
// and the pre-image keys — one moved by hand between the crash and this
|
|
98
|
+
// repair leaves its files "in neither recorded state", which is the refusal
|
|
99
|
+
// below, not a resolution question. The one directory that DOES move between
|
|
100
|
+
// them is the commit's own, and that is the second path tried below.
|
|
101
|
+
//
|
|
102
|
+
// Both pre-image NAMES are tried, the suffixed one first: a snapshot this
|
|
103
|
+
// loam wrote stores `<path>` + PRE_IMAGE_SUFFIX (version 4), while a commit
|
|
104
|
+
// an older loam was killed in left one under the living name. Trying both is
|
|
105
|
+
// safe for the same reason the two ends are: whichever is found must still
|
|
106
|
+
// hash to the digest the journal recorded, or the repair refuses below.
|
|
107
|
+
//
|
|
108
|
+
// And both PATHS: the literal one and its image across the move. The
|
|
109
|
+
// feature's own documents are the one place the journal and the snapshot do
|
|
110
|
+
// disagree, by construction — archive journals them where it writes them,
|
|
111
|
+
// inside the feature as it stands, and the snapshot records them where the
|
|
112
|
+
// move leaves them (`../snapshot/snapshot.ts`, `movesTo`), because that is
|
|
113
|
+
// where an undo finds them. A killed archive whose relinked intent was
|
|
114
|
+
// already swapped is therefore repaired from `features/archive/<FEAT>/…`'s
|
|
115
|
+
// pre-image, and refusing it as "gone" made the retry impossible.
|
|
116
|
+
const roots = [join(moveFrom, SNAPSHOT_DIR, "files"), join(moveTo, SNAPSHOT_DIR, "files")];
|
|
117
|
+
const across = (path) => repoPath(docsDir, acrossRename(target(path), moveFrom, moveTo));
|
|
118
|
+
const names = (path) => [...new Set([path, across(path)])].flatMap((p) => [`${p}${PRE_IMAGE_SUFFIX}`, p]);
|
|
119
|
+
const repaired = [];
|
|
120
|
+
for (const f of pending) {
|
|
121
|
+
const file = target(f.path);
|
|
122
|
+
const want = wanted(f);
|
|
123
|
+
if (want === null) {
|
|
124
|
+
await rm(file, { force: true });
|
|
125
|
+
await quietRmdir(dirname(file));
|
|
126
|
+
repaired.push(f.path);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const source = roots.flatMap((r) => names(f.path).map((n) => join(r, ...n.split("/")))).find((p) => existsSync(p));
|
|
130
|
+
if (source === undefined) {
|
|
131
|
+
throw new InterruptedCommitError(`a \`loam ${intent.command} ${intent.feature}\` was interrupted mid-commit and ${f.path} is half-written, but ` +
|
|
132
|
+
`the pre-image that would repair it is gone. Restore ${f.path} from version control, delete ` +
|
|
133
|
+
`${COMMIT_INTENT}, then re-run.`);
|
|
134
|
+
}
|
|
135
|
+
const bytes = await readFile(source);
|
|
136
|
+
if (sha256(bytes) !== want) {
|
|
137
|
+
throw new InterruptedCommitError(`a \`loam ${intent.command} ${intent.feature}\` was interrupted mid-commit and ${f.path} is half-written, but ` +
|
|
138
|
+
`the pre-image that would repair it no longer matches the digest recorded before the commit — it has been ` +
|
|
139
|
+
`edited since. Restore ${f.path} from version control, delete ${COMMIT_INTENT}, then re-run.`);
|
|
140
|
+
}
|
|
141
|
+
await atomicWrite(file, bytes);
|
|
142
|
+
repaired.push(f.path);
|
|
143
|
+
}
|
|
144
|
+
await finishRecovery(docsDir, intent);
|
|
145
|
+
return { command: intent.command, feature: intent.feature, outcome: "repaired", repaired };
|
|
146
|
+
}
|
|
147
|
+
/** Drop the record, and (via the shared pid-scoped sweep) the killed run's own temps. */
|
|
148
|
+
async function finishRecovery(docsDir, intent) {
|
|
149
|
+
await sweepPidTemps(intent.files.map((f) => dirname(join(docsDir, ...f.path.split("/")))), intent.pid);
|
|
150
|
+
await clearCommitIntent(docsDir);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Open a writer's commit window: take the lock in `root` — the docs repo, or
|
|
154
|
+
* gherkin's emission root in the service repo — then refuse or repair a
|
|
155
|
+
* predecessor's interrupted commit before this run reads a byte.
|
|
156
|
+
*
|
|
157
|
+
* `waitMs` is how long to sit out a live holder before answering `docs-busy`,
|
|
158
|
+
* and 0 refuses on first contact; `acquireDocsLockWaiting` says which writers
|
|
159
|
+
* wait and why archive does not.
|
|
160
|
+
*
|
|
161
|
+
* Each writer here used to spell this sequence out by hand, and the copy in
|
|
162
|
+
* `verify` once let its recovery refusal keep the lock — one bad answer set
|
|
163
|
+
* became a wedged docs repo for everyone else. So the refusal releases HERE,
|
|
164
|
+
* before it is returned, and an error that is neither refusal releases before
|
|
165
|
+
* it propagates.
|
|
166
|
+
*/
|
|
167
|
+
export async function openCommitWindow(root, waitMs) {
|
|
168
|
+
let release;
|
|
169
|
+
try {
|
|
170
|
+
release = waitMs > 0 ? await acquireDocsLockWaiting(root, waitMs) : await acquireDocsLock(root);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
if (!(err instanceof DocsBusyError))
|
|
174
|
+
throw err;
|
|
175
|
+
return { ok: false, code: "docs-busy", message: err.message };
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
return { ok: true, recovered: await recoverInterruptedCommit(root), release };
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
await release();
|
|
182
|
+
if (!(err instanceof InterruptedCommitError))
|
|
183
|
+
throw err;
|
|
184
|
+
return { ok: false, code: "commit-interrupted", message: err.message };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Recover a predecessor's interrupted commit NOW and hand the lock straight
|
|
189
|
+
* back — for a writer whose slow half runs UNLOCKED over the very bytes a
|
|
190
|
+
* half-commit corrupts (vouch's verification, gherkin's reconcile); each says
|
|
191
|
+
* at its call site why recovery inside its own commit window comes too late.
|
|
192
|
+
* Cheap when there is nothing to do: no journal, no lock, no I/O beyond one
|
|
193
|
+
* existsSync.
|
|
194
|
+
*/
|
|
195
|
+
export async function recoverIfJournaled(root) {
|
|
196
|
+
if (!existsSync(join(root, COMMIT_INTENT)))
|
|
197
|
+
return { ok: true, recovered: null };
|
|
198
|
+
const opened = await openCommitWindow(root, LOCK_WAIT_MS);
|
|
199
|
+
if (!opened.ok)
|
|
200
|
+
return opened;
|
|
201
|
+
await opened.release();
|
|
202
|
+
return { ok: true, recovered: opened.recovered };
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=recover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recover.js","sourceRoot":"","sources":["../../../../src/core/staging/recovery/recover.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Refuse or repair an interrupted commit, before this run reads anything.\n *\n * Split from `./intent.ts` because this is the only module on the write path\n * that writes bytes nobody asked for in this run. Two journal versions, two\n * doctrines, one dispatch: a version-1 record (archive/unarchive) is repaired\n * BACKWARD from the snapshot pre-images, each verified against the digest the\n * record captured; a version-2 record is handed to `txn/forward.ts`, which\n * rolls FORWARD from the staged temps under the same verification. Either\n * way, a file in NEITHER recorded state was written by somebody else since,\n * so recovery refuses and names it rather than choosing which of two truths\n * to destroy.\n *\n * The journaled writers reach it through `openCommitWindow` below — the lock,\n * then this — so the two refusals that can keep a commit window shut are\n * spelled once rather than once per writer. Archive and unarchive are the\n * exception: they call it directly, inside their plan, under a lock they took\n * themselves, and throw its refusal to a handler that maps it.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile, rm } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { repoPath } from \"../../envelope/json.js\";\nimport { acrossRename } from \"../../kernel/path-safety.js\";\nimport { sha256 } from \"../writes.js\";\nimport { atomicWrite, quietRmdir } from \"../commit.js\";\nimport { PRE_IMAGE_SUFFIX, SNAPSHOT_DIR } from \"../snapshot/manifest.js\";\nimport {\n COMMIT_INTENT,\n type CommitRecovery,\n InterruptedCommitError,\n sweepPidTemps,\n} from \"../interrupted.js\";\nimport { acquireDocsLock, acquireDocsLockWaiting, DocsBusyError, LOCK_WAIT_MS } from \"../lock.js\";\nimport { readTxnIntent } from \"../txn/journal.js\";\nimport { recoverForward } from \"../txn/forward.js\";\nimport {\n clearCommitIntent,\n readCommitIntent,\n type CommitIntent,\n type CommitIntentFile,\n} from \"./intent.js\";\n\n/**\n * Refuse or repair before this run reads anything. Returns null when there is\n * nothing to recover.\n *\n * Repair is only ever the bytes the interrupted run already had on disk: each\n * file still in the wrong state is rewritten from the snapshot pre-image and\n * only after that pre-image's own sha256 matches the digest the record captured\n * before the crash. A file in NEITHER state was written by somebody else since,\n * so this refuses and names it rather than choosing which of two truths to\n * destroy — the same doctrine `rollbackStaged` follows when it leaves a file as\n * found.\n */\nexport async function recoverInterruptedCommit(docsDir: string): Promise<CommitRecovery | null> {\n const path = join(docsDir, COMMIT_INTENT);\n if (!existsSync(path)) return null;\n const intent = await readCommitIntent(docsDir);\n if (intent === null) {\n // Version 2 — the smaller transaction's journal. Same file, different\n // recovery: forward from the staged temps, in txn/forward.ts.\n const txn = await readTxnIntent(docsDir);\n if (txn !== null) return recoverForward(docsDir, txn);\n throw new InterruptedCommitError(\n `${repoPath(docsDir, path)} records a commit this loam cannot read, so it cannot tell a half-merged docs repo ` +\n `from a healthy one. Check the living docs against version control, delete ${COMMIT_INTENT}, then re-run.`,\n );\n }\n const moveFrom = join(docsDir, ...intent.moveFrom.split(\"/\"));\n const moveTo = join(docsDir, ...intent.moveTo.split(\"/\"));\n // The move is the commit's LAST step. If it landed, every swap before it did\n // too, and only the record's own removal was lost.\n if (existsSync(moveTo) && !existsSync(moveFrom)) {\n await finishRecovery(docsDir, intent);\n return { command: intent.command, feature: intent.feature, outcome: \"completed\", repaired: [] };\n }\n\n const target = (rel: string): string => join(docsDir, ...rel.split(\"/\"));\n const wanted = (f: CommitIntentFile): string | null => (intent.restore === \"before\" ? f.before : f.after);\n const other = (f: CommitIntentFile): string | null => (intent.restore === \"before\" ? f.after : f.before);\n const pending: CommitIntentFile[] = [];\n const unknown: string[] = [];\n for (const f of intent.files) {\n const file = target(f.path);\n const now = existsSync(file) ? sha256(await readFile(file)) : null;\n if (now === wanted(f)) continue;\n if (now === other(f)) pending.push(f);\n else unknown.push(f.path);\n }\n if (unknown.length > 0) {\n throw new InterruptedCommitError(\n `a \\`loam ${intent.command} ${intent.feature}\\` was interrupted mid-commit (pid ${intent.pid} on ${intent.host}, ` +\n `${intent.at}) and ${unknown.length} of the file(s) it was writing now hold bytes that are neither what it ` +\n `found nor what it wrote — somebody edited them since: ${unknown.join(\", \")}. loam will not choose between ` +\n `those two truths. Reconcile them against version control, delete ${COMMIT_INTENT}, then re-run.`,\n );\n }\n if (pending.length === 0) {\n await finishRecovery(docsDir, intent);\n return { command: intent.command, feature: intent.feature, outcome: \"consistent\", repaired: [] };\n }\n\n // The pre-images travel inside the feature directory, so they are at one of\n // the two ends of the move depending on how far the interrupted run got.\n // Deliberately NOT the snapshot's (service, artifact) re-keying: the journal\n // and the snapshot were written seconds apart under the same docs lock, so\n // no service directory can have moved between the journal's literal paths\n // and the pre-image keys — one moved by hand between the crash and this\n // repair leaves its files \"in neither recorded state\", which is the refusal\n // below, not a resolution question. The one directory that DOES move between\n // them is the commit's own, and that is the second path tried below.\n //\n // Both pre-image NAMES are tried, the suffixed one first: a snapshot this\n // loam wrote stores `<path>` + PRE_IMAGE_SUFFIX (version 4), while a commit\n // an older loam was killed in left one under the living name. Trying both is\n // safe for the same reason the two ends are: whichever is found must still\n // hash to the digest the journal recorded, or the repair refuses below.\n //\n // And both PATHS: the literal one and its image across the move. The\n // feature's own documents are the one place the journal and the snapshot do\n // disagree, by construction — archive journals them where it writes them,\n // inside the feature as it stands, and the snapshot records them where the\n // move leaves them (`../snapshot/snapshot.ts`, `movesTo`), because that is\n // where an undo finds them. A killed archive whose relinked intent was\n // already swapped is therefore repaired from `features/archive/<FEAT>/…`'s\n // pre-image, and refusing it as \"gone\" made the retry impossible.\n const roots = [join(moveFrom, SNAPSHOT_DIR, \"files\"), join(moveTo, SNAPSHOT_DIR, \"files\")];\n const across = (path: string): string => repoPath(docsDir, acrossRename(target(path), moveFrom, moveTo));\n const names = (path: string): string[] =>\n [...new Set([path, across(path)])].flatMap((p) => [`${p}${PRE_IMAGE_SUFFIX}`, p]);\n const repaired: string[] = [];\n for (const f of pending) {\n const file = target(f.path);\n const want = wanted(f);\n if (want === null) {\n await rm(file, { force: true });\n await quietRmdir(dirname(file));\n repaired.push(f.path);\n continue;\n }\n const source = roots.flatMap((r) => names(f.path).map((n) => join(r, ...n.split(\"/\")))).find((p) => existsSync(p));\n if (source === undefined) {\n throw new InterruptedCommitError(\n `a \\`loam ${intent.command} ${intent.feature}\\` was interrupted mid-commit and ${f.path} is half-written, but ` +\n `the pre-image that would repair it is gone. Restore ${f.path} from version control, delete ` +\n `${COMMIT_INTENT}, then re-run.`,\n );\n }\n const bytes = await readFile(source);\n if (sha256(bytes) !== want) {\n throw new InterruptedCommitError(\n `a \\`loam ${intent.command} ${intent.feature}\\` was interrupted mid-commit and ${f.path} is half-written, but ` +\n `the pre-image that would repair it no longer matches the digest recorded before the commit — it has been ` +\n `edited since. Restore ${f.path} from version control, delete ${COMMIT_INTENT}, then re-run.`,\n );\n }\n await atomicWrite(file, bytes);\n repaired.push(f.path);\n }\n await finishRecovery(docsDir, intent);\n return { command: intent.command, feature: intent.feature, outcome: \"repaired\", repaired };\n}\n\n/** Drop the record, and (via the shared pid-scoped sweep) the killed run's own temps. */\nasync function finishRecovery(docsDir: string, intent: CommitIntent): Promise<void> {\n await sweepPidTemps(\n intent.files.map((f) => dirname(join(docsDir, ...f.path.split(\"/\")))),\n intent.pid,\n );\n await clearCommitIntent(docsDir);\n}\n\n/** The lock is held and a predecessor's interrupted commit is resolved: this run may read, then write. */\nexport interface OpenCommitWindow {\n ok: true;\n recovered: CommitRecovery | null;\n /** Idempotent. The caller's one obligation, in a `finally`. */\n release: () => Promise<void>;\n}\n\n/** The window did not open. Nothing was read or written, and no lock is held. */\nexport interface CommitWindowRefusal {\n ok: false;\n code: \"docs-busy\" | \"commit-interrupted\";\n message: string;\n}\n\n/**\n * Open a writer's commit window: take the lock in `root` — the docs repo, or\n * gherkin's emission root in the service repo — then refuse or repair a\n * predecessor's interrupted commit before this run reads a byte.\n *\n * `waitMs` is how long to sit out a live holder before answering `docs-busy`,\n * and 0 refuses on first contact; `acquireDocsLockWaiting` says which writers\n * wait and why archive does not.\n *\n * Each writer here used to spell this sequence out by hand, and the copy in\n * `verify` once let its recovery refusal keep the lock — one bad answer set\n * became a wedged docs repo for everyone else. So the refusal releases HERE,\n * before it is returned, and an error that is neither refusal releases before\n * it propagates.\n */\nexport async function openCommitWindow(root: string, waitMs: number): Promise<OpenCommitWindow | CommitWindowRefusal> {\n let release: () => Promise<void>;\n try {\n release = waitMs > 0 ? await acquireDocsLockWaiting(root, waitMs) : await acquireDocsLock(root);\n } catch (err) {\n if (!(err instanceof DocsBusyError)) throw err;\n return { ok: false, code: \"docs-busy\", message: err.message };\n }\n try {\n return { ok: true, recovered: await recoverInterruptedCommit(root), release };\n } catch (err) {\n await release();\n if (!(err instanceof InterruptedCommitError)) throw err;\n return { ok: false, code: \"commit-interrupted\", message: err.message };\n }\n}\n\n/**\n * Recover a predecessor's interrupted commit NOW and hand the lock straight\n * back — for a writer whose slow half runs UNLOCKED over the very bytes a\n * half-commit corrupts (vouch's verification, gherkin's reconcile); each says\n * at its call site why recovery inside its own commit window comes too late.\n * Cheap when there is nothing to do: no journal, no lock, no I/O beyond one\n * existsSync.\n */\nexport async function recoverIfJournaled(\n root: string,\n): Promise<{ ok: true; recovered: CommitRecovery | null } | CommitWindowRefusal> {\n if (!existsSync(join(root, COMMIT_INTENT))) return { ok: true, recovered: null };\n const opened = await openCommitWindow(root, LOCK_WAIT_MS);\n if (!opened.ok) return opened;\n await opened.release();\n return { ok: true, recovered: opened.recovered };\n}\n\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything in a docs repo that only a write which did not finish can produce,
|
|
3
|
+
* gathered for `doctor` to grade.
|
|
4
|
+
*
|
|
5
|
+
* It lives beside the write path rather than in `doctor` because these are
|
|
6
|
+
* staging's own artefacts and it does not re-spell any of them: `.loam-lock`
|
|
7
|
+
* comes from `../lock.ts`, `.loam-commit` from `./intent.ts`, and the temp-file
|
|
8
|
+
* pattern from `../commit.ts`. A detector that spelled them a second time would
|
|
9
|
+
* go stale the first time any of the three changed. This module reports;
|
|
10
|
+
* `doctor` decides what the report means.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { readdir } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { repoPath } from "../../envelope/json.js";
|
|
16
|
+
import { DOCS_LOCK, lockResidue } from "../lock.js";
|
|
17
|
+
import { TEMP_FILE_RE } from "../commit.js";
|
|
18
|
+
import { COMMIT_INTENT } from "../interrupted.js";
|
|
19
|
+
import { readTxnIntent } from "../txn/journal.js";
|
|
20
|
+
import { readCommitIntent } from "./intent.js";
|
|
21
|
+
export async function scanWritePathResidue(docsDir) {
|
|
22
|
+
const lockPath = join(docsDir, DOCS_LOCK);
|
|
23
|
+
// One read decides everything about the lock. An exists-then-describe-then-
|
|
24
|
+
// grade sequence read the file up to four times, and a lock released between
|
|
25
|
+
// reads — the ordinary sub-second commit window of a healthy archive — could
|
|
26
|
+
// be reported in a state no single moment ever had.
|
|
27
|
+
const residue = await lockResidue(lockPath);
|
|
28
|
+
const lock = residue === null ? null : { path: repoPath(docsDir, lockPath), ...residue };
|
|
29
|
+
// Both journal versions wear the same filename; a version-2 record must
|
|
30
|
+
// not be graded "unreadable" merely because the version-1 reader rejects
|
|
31
|
+
// its version field.
|
|
32
|
+
const intent = (await readCommitIntent(docsDir)) ?? (await readTxnIntent(docsDir));
|
|
33
|
+
return {
|
|
34
|
+
lock,
|
|
35
|
+
intent,
|
|
36
|
+
intentUnreadable: intent === null && existsSync(join(docsDir, COMMIT_INTENT)),
|
|
37
|
+
temps: (await tempLeftovers(docsDir)).map((p) => repoPath(docsDir, p)).sort(),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function tempLeftovers(dir) {
|
|
41
|
+
const out = [];
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
// `.git` alone can hold more files than the whole docs repo, and nothing
|
|
51
|
+
// loam writes ever lands in it.
|
|
52
|
+
if (entry.name === ".git" || entry.name === "node_modules")
|
|
53
|
+
continue;
|
|
54
|
+
const path = join(dir, entry.name);
|
|
55
|
+
if (entry.isDirectory())
|
|
56
|
+
out.push(...(await tempLeftovers(path)));
|
|
57
|
+
else if (entry.isFile() && TEMP_FILE_RE.test(entry.name))
|
|
58
|
+
out.push(path);
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=residue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"residue.js","sourceRoot":"","sources":["../../../../src/core/staging/recovery/residue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAkB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAqB,MAAM,aAAa,CAAC;AAsBlE,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAe;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC1C,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,oDAAoD;IACpD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,IAAI,GACR,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;IAC9E,wEAAwE;IACxE,yEAAyE;IACzE,qBAAqB;IACrB,MAAM,MAAM,GAAG,CAAC,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IACnF,OAAO;QACL,IAAI;QACJ,MAAM;QACN,gBAAgB,EAAE,MAAM,KAAK,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAC7E,KAAK,EAAE,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;KAC9E,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAW;IACtC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,yEAAyE;QACzE,gCAAgC;QAChC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;YAAE,SAAS;QACrE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAC7D,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * Everything in a docs repo that only a write which did not finish can produce,\n * gathered for `doctor` to grade.\n *\n * It lives beside the write path rather than in `doctor` because these are\n * staging's own artefacts and it does not re-spell any of them: `.loam-lock`\n * comes from `../lock.ts`, `.loam-commit` from `./intent.ts`, and the temp-file\n * pattern from `../commit.ts`. A detector that spelled them a second time would\n * go stale the first time any of the three changed. This module reports;\n * `doctor` decides what the report means.\n */\nimport { existsSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { repoPath } from \"../../envelope/json.js\";\nimport { DOCS_LOCK, lockResidue } from \"../lock.js\";\nimport { TEMP_FILE_RE } from \"../commit.js\";\nimport { COMMIT_INTENT } from \"../interrupted.js\";\nimport { readTxnIntent, type TxnIntent } from \"../txn/journal.js\";\nimport { readCommitIntent, type CommitIntent } from \"./intent.js\";\n\n/** The four shapes an unfinished write leaves behind, each named by its cause. */\nexport interface WritePathResidue {\n /**\n * A held `.loam-lock`; whether its holder is a process that no longer exists\n * on this host; and whether the file cannot name a holder at all — in which\n * case nothing will ever release it and `stale` is meaningless.\n */\n lock: { path: string; holder: string; stale: boolean; unreadable: boolean } | null;\n /**\n * An interrupted commit's intent record — present means a commit was in\n * flight when something killed it. Either journal version: the consumer\n * tells them apart by shape (`rerun` exists only on version 2).\n */\n intent: CommitIntent | TxnIntent | null;\n /** True when `.loam-commit` exists but cannot be parsed: the worst case, because nothing can grade it. */\n intentUnreadable: boolean;\n /** Docs-repo-relative paths of orphaned `.loam-*.tmp` staging files. */\n temps: string[];\n}\n\nexport async function scanWritePathResidue(docsDir: string): Promise<WritePathResidue> {\n const lockPath = join(docsDir, DOCS_LOCK);\n // One read decides everything about the lock. An exists-then-describe-then-\n // grade sequence read the file up to four times, and a lock released between\n // reads — the ordinary sub-second commit window of a healthy archive — could\n // be reported in a state no single moment ever had.\n const residue = await lockResidue(lockPath);\n const lock: WritePathResidue[\"lock\"] =\n residue === null ? null : { path: repoPath(docsDir, lockPath), ...residue };\n // Both journal versions wear the same filename; a version-2 record must\n // not be graded \"unreadable\" merely because the version-1 reader rejects\n // its version field.\n const intent = (await readCommitIntent(docsDir)) ?? (await readTxnIntent(docsDir));\n return {\n lock,\n intent,\n intentUnreadable: intent === null && existsSync(join(docsDir, COMMIT_INTENT)),\n temps: (await tempLeftovers(docsDir)).map((p) => repoPath(docsDir, p)).sort(),\n };\n}\n\nasync function tempLeftovers(dir: string): Promise<string[]> {\n const out: string[] = [];\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return out;\n }\n for (const entry of entries) {\n // `.git` alone can hold more files than the whole docs repo, and nothing\n // loam writes ever lands in it.\n if (entry.name === \".git\" || entry.name === \"node_modules\") continue;\n const path = join(dir, entry.name);\n if (entry.isDirectory()) out.push(...(await tempLeftovers(path)));\n else if (entry.isFile() && TEMP_FILE_RE.test(entry.name)) out.push(path);\n }\n return out;\n}\n\n"]}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the undo snapshot lives and what its manifest says — the half of
|
|
3
|
+
* `./snapshot.ts` a command can know without being able to take a snapshot.
|
|
4
|
+
*
|
|
5
|
+
* Split from that module when the snapshot became a package of its own: taking
|
|
6
|
+
* a snapshot (and refusing to clobber one) is a commit, and the MCP server's
|
|
7
|
+
* read-only claim is DERIVED from the import graph — a served command whose
|
|
8
|
+
* imports reach `./snapshot.ts` is, by that test, a command that can commit.
|
|
9
|
+
* The layout below is pure naming and shape, and the readers that only need
|
|
10
|
+
* to find a snapshot, or read what one recorded, import it from here.
|
|
11
|
+
*/
|
|
12
|
+
import { isUtf8 } from "node:buffer";
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { resolvePortableFileInsideLexically } from "../../kernel/path-safety.js";
|
|
16
|
+
import { isRecord } from "../../kernel/records.js";
|
|
17
|
+
import { sha256 } from "../writes.js";
|
|
18
|
+
/** Where archive parks the pre-images, inside the feature directory so they travel with it — `./snapshot.ts`'s module doc says why. */
|
|
19
|
+
export const SNAPSHOT_DIR = ".loam-before";
|
|
20
|
+
export const SNAPSHOT_MANIFEST = "manifest.json";
|
|
21
|
+
/**
|
|
22
|
+
* Bumped only when the layout changes in a way `unarchive` must refuse to guess
|
|
23
|
+
* at. 2 added `before`: a version-1 snapshot says nothing about the bytes it
|
|
24
|
+
* would RESTORE, so an edited pre-image read back as an intact one. 3 re-keys
|
|
25
|
+
* `services/` entries by `(service, artifact)`: a literal path pins the service
|
|
26
|
+
* directory where it stood on the day of the archive, and once the subsystem
|
|
27
|
+
* tree lets it move — re-cutting is expected to be routine — an undo addressed
|
|
28
|
+
* by path restores into a location that is gone. The service id never moves.
|
|
29
|
+
* 4 stores each pre-image under its own name PLUS {@link PRE_IMAGE_SUFFIX}: a
|
|
30
|
+
* pre-image kept under its living name is a second copy of a living document
|
|
31
|
+
* that every tool reading by extension takes for one. The snapshot of a
|
|
32
|
+
* landscape is a `.likec4` inside the archived feature, so a feature with its
|
|
33
|
+
* own `likec4.config.json` — or rendered alone, `likec4 start
|
|
34
|
+
* features/archive/<FEAT>`, as the brief recommends — loaded the map twice
|
|
35
|
+
* beside the delta and failed on duplicate elements, and a fleet project that
|
|
36
|
+
* did not exclude `features/` failed the same way.
|
|
37
|
+
*/
|
|
38
|
+
export const SNAPSHOT_VERSION = 4;
|
|
39
|
+
/**
|
|
40
|
+
* Version 2 is still READ, forever: version-2 snapshots already sit inside
|
|
41
|
+
* archived features, which carry them across loam upgrades, and each must keep
|
|
42
|
+
* restoring — and keep being graded for staleness by a retried archive — under
|
|
43
|
+
* its own literal-path rules. Only version 1 is refused (no restore digests).
|
|
44
|
+
*/
|
|
45
|
+
export const SNAPSHOT_COMPAT_VERSION = 2;
|
|
46
|
+
/**
|
|
47
|
+
* Version 3 is read forever for the same reason, under its own rules: rows
|
|
48
|
+
* keyed by `(service, artifact)` like version 4's, pre-images stored under
|
|
49
|
+
* their living names. A version-3 snapshot keeps its layout — it is restored
|
|
50
|
+
* from where it was written, never migrated, because rewriting a snapshot is
|
|
51
|
+
* rewriting the only copy of what the living docs once said.
|
|
52
|
+
*/
|
|
53
|
+
const SNAPSHOT_KEYED_VERSION = 3;
|
|
54
|
+
/** Appended to every pre-image a version-4 snapshot stores; see {@link SNAPSHOT_VERSION}. */
|
|
55
|
+
export const PRE_IMAGE_SUFFIX = ".before";
|
|
56
|
+
/** A manifest version this loam reads: the one it writes and the two it reads forever. */
|
|
57
|
+
export function isReadableSnapshotVersion(version) {
|
|
58
|
+
return version === SNAPSHOT_COMPAT_VERSION || version === SNAPSHOT_KEYED_VERSION || version === 4;
|
|
59
|
+
}
|
|
60
|
+
/** Does a manifest of this version key its `services/` rows by `(service, artifact)`? Version 3 on. */
|
|
61
|
+
export function keysServiceRows(version) {
|
|
62
|
+
return version >= SNAPSHOT_KEYED_VERSION;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Where a row's pre-image sits inside `files/`, forward slashes, under the
|
|
66
|
+
* layout the manifest's VERSION names — never the layout this loam would
|
|
67
|
+
* write, since a snapshot is read where it was written. Suffixed from version
|
|
68
|
+
* 4 on.
|
|
69
|
+
*/
|
|
70
|
+
export function preImageRel(version, path) {
|
|
71
|
+
return version >= 4 ? `${path}${PRE_IMAGE_SUFFIX}` : path;
|
|
72
|
+
}
|
|
73
|
+
export function snapshotDir(featureDir) {
|
|
74
|
+
return join(featureDir, SNAPSHOT_DIR);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The checklist digest an archived feature's snapshot captured as it merged,
|
|
78
|
+
* or null when it carries none.
|
|
79
|
+
*
|
|
80
|
+
* It lives in the manifest because the manifest is the one file `archive`
|
|
81
|
+
* already writes into the feature at the moment of the merge, travels with it
|
|
82
|
+
* into `features/archive/`, and is taken away by `unarchive` — so a feature
|
|
83
|
+
* archived again gets a fresh one, captured against what it then asked. A
|
|
84
|
+
* frozen verification record cannot be judged against a checklist re-derived
|
|
85
|
+
* after the merge (the merge moved the feature's operations into the living
|
|
86
|
+
* contract, so the derivation can only mismatch); this is the question as it
|
|
87
|
+
* stood when the feature shipped, which is what `loam gate --since` holds the
|
|
88
|
+
* record to. Here rather than beside the writer for the reason this module
|
|
89
|
+
* exists: the gate is a read-only command, served over MCP as one.
|
|
90
|
+
*
|
|
91
|
+
* Tolerant to the last byte, and every failure is "none captured": a manifest
|
|
92
|
+
* an older loam wrote, a feature archived with no record, a manifest that is
|
|
93
|
+
* missing or does not parse. The reader that grades on it reads null as the
|
|
94
|
+
* frozen history it always read, and says so — it never refuses a deploy
|
|
95
|
+
* question over bookkeeping.
|
|
96
|
+
*/
|
|
97
|
+
export async function readSnapshotChecklist(featureDir) {
|
|
98
|
+
const manifest = await manifestRecord(featureDir);
|
|
99
|
+
if (manifest === null)
|
|
100
|
+
return null;
|
|
101
|
+
const { checklist } = manifest;
|
|
102
|
+
return typeof checklist === "string" && checklist !== "" ? checklist : null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The bytes one service artifact held the moment this feature merged — the
|
|
106
|
+
* pre-image the snapshot parked under `files/` — or one of the two answers
|
|
107
|
+
* that say the merge took nothing out of it ({@link SnapshotPreImage}), or
|
|
108
|
+
* null when there is nothing to trust.
|
|
109
|
+
*
|
|
110
|
+
* Read for the questions only the pre-image can answer, both asked by the
|
|
111
|
+
* retired-operation revival check (core/coherence/retired/): what a merge TOOK
|
|
112
|
+
* that its delta never spelled — a channel-slot removal in an asyncapi delta is
|
|
113
|
+
* a bare marker, while the messages it retired with the channel are written in
|
|
114
|
+
* the living contract it merged into and nowhere else — and whether a removal
|
|
115
|
+
* marker took anything at all, since the merge deletes only what it finds at
|
|
116
|
+
* the marker's slot. Here, beside `readSnapshotChecklist`, for that reader's
|
|
117
|
+
* reason: the readers are read-only commands, and this module is the half of
|
|
118
|
+
* the snapshot they may import.
|
|
119
|
+
*
|
|
120
|
+
* The entry is found by `(service, artifact)` on a keyed (version 3 or 4) manifest — the
|
|
121
|
+
* key that survives a subsystem re-cut — and by the literal path's last two
|
|
122
|
+
* segments on a version-2 one, which recorded nothing else. Tolerant to the
|
|
123
|
+
* last byte, like the checklist reader: no manifest, a layout this loam would
|
|
124
|
+
* have to guess at, no row for the artifact while some row cannot be read (it
|
|
125
|
+
* might have been this one's, so absence cannot be concluded past it), a path
|
|
126
|
+
* that does not stay inside `files/`, a pre-image that no longer hashes to the digest
|
|
127
|
+
* the manifest recorded for it, or bytes that are not UTF-8 — every one is
|
|
128
|
+
* null, and the caller grades without it. An edited pre-image is refused
|
|
129
|
+
* rather than read for the reason `unarchive` refuses to restore one: its
|
|
130
|
+
* bytes are no longer what the merge saw.
|
|
131
|
+
*/
|
|
132
|
+
export async function readSnapshotPreImage(featureDir, key) {
|
|
133
|
+
const manifest = await manifestRecord(featureDir);
|
|
134
|
+
if (manifest === null || !Array.isArray(manifest.files))
|
|
135
|
+
return null;
|
|
136
|
+
const { version } = manifest;
|
|
137
|
+
if (!isReadableSnapshotVersion(version))
|
|
138
|
+
return null;
|
|
139
|
+
// `Array.isArray` narrows to `any[]`; naming the element type keeps that
|
|
140
|
+
// `any` from spreading past the row being read (snapshot.ts's reason).
|
|
141
|
+
const rows = manifest.files;
|
|
142
|
+
const entry = rows.find((row) => isRecord(row) && entryIs(row, key));
|
|
143
|
+
if (entry === undefined) {
|
|
144
|
+
// Absence is an answer only when every row could be read: a row that is
|
|
145
|
+
// not a record with a path might have been this artifact's.
|
|
146
|
+
return rows.every((row) => isRecord(row) && typeof row.path === "string") ? { kind: "unwritten" } : null;
|
|
147
|
+
}
|
|
148
|
+
if (!isRecord(entry) || typeof entry.path !== "string")
|
|
149
|
+
return null;
|
|
150
|
+
if (entry.existed === false && entry.before === null)
|
|
151
|
+
return { kind: "created" };
|
|
152
|
+
if (typeof entry.before !== "string")
|
|
153
|
+
return null;
|
|
154
|
+
let bytes;
|
|
155
|
+
try {
|
|
156
|
+
// The manifest is data and this path is one we go and read: the lexical
|
|
157
|
+
// containment `unarchive` and the staleness check apply to the same field.
|
|
158
|
+
bytes = await readFile(resolvePortableFileInsideLexically(join(snapshotDir(featureDir), "files"), preImageRel(version, entry.path), "snapshot pre-image"));
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
if (sha256(bytes) !== entry.before || !isUtf8(bytes))
|
|
164
|
+
return null;
|
|
165
|
+
return { kind: "held", text: bytes.toString("utf8") };
|
|
166
|
+
}
|
|
167
|
+
/** Does this manifest row record `key`'s artifact? The `(service, artifact)` key when it has one, the literal path otherwise. */
|
|
168
|
+
function entryIs(row, key) {
|
|
169
|
+
if (typeof row.service === "string" && typeof row.artifact === "string") {
|
|
170
|
+
return row.service === key.service && row.artifact === key.artifact;
|
|
171
|
+
}
|
|
172
|
+
if (typeof row.path !== "string")
|
|
173
|
+
return false;
|
|
174
|
+
const parts = row.path.split("/");
|
|
175
|
+
return parts.length >= 3 && parts[0] === "services" && parts.at(-2) === key.service && parts.at(-1) === key.artifact;
|
|
176
|
+
}
|
|
177
|
+
/** The manifest as a record, or null — the one tolerant read behind both readers above. */
|
|
178
|
+
async function manifestRecord(featureDir) {
|
|
179
|
+
let manifest;
|
|
180
|
+
try {
|
|
181
|
+
manifest = JSON.parse(await readFile(join(snapshotDir(featureDir), SNAPSHOT_MANIFEST), "utf8"));
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
return isRecord(manifest) ? manifest : null;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../src/core/staging/snapshot/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where the undo snapshot lives and what its manifest says — the half of\n * `./snapshot.ts` a command can know without being able to take a snapshot.\n *\n * Split from that module when the snapshot became a package of its own: taking\n * a snapshot (and refusing to clobber one) is a commit, and the MCP server's\n * read-only claim is DERIVED from the import graph — a served command whose\n * imports reach `./snapshot.ts` is, by that test, a command that can commit.\n * The layout below is pure naming and shape, and the readers that only need\n * to find a snapshot, or read what one recorded, import it from here.\n */\nimport { isUtf8 } from \"node:buffer\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { resolvePortableFileInsideLexically } from \"../../kernel/path-safety.js\";\nimport { isRecord } from \"../../kernel/records.js\";\nimport { sha256 } from \"../writes.js\";\n\n/** Where archive parks the pre-images, inside the feature directory so they travel with it — `./snapshot.ts`'s module doc says why. */\nexport const SNAPSHOT_DIR = \".loam-before\";\nexport const SNAPSHOT_MANIFEST = \"manifest.json\";\n/**\n * Bumped only when the layout changes in a way `unarchive` must refuse to guess\n * at. 2 added `before`: a version-1 snapshot says nothing about the bytes it\n * would RESTORE, so an edited pre-image read back as an intact one. 3 re-keys\n * `services/` entries by `(service, artifact)`: a literal path pins the service\n * directory where it stood on the day of the archive, and once the subsystem\n * tree lets it move — re-cutting is expected to be routine — an undo addressed\n * by path restores into a location that is gone. The service id never moves.\n * 4 stores each pre-image under its own name PLUS {@link PRE_IMAGE_SUFFIX}: a\n * pre-image kept under its living name is a second copy of a living document\n * that every tool reading by extension takes for one. The snapshot of a\n * landscape is a `.likec4` inside the archived feature, so a feature with its\n * own `likec4.config.json` — or rendered alone, `likec4 start\n * features/archive/<FEAT>`, as the brief recommends — loaded the map twice\n * beside the delta and failed on duplicate elements, and a fleet project that\n * did not exclude `features/` failed the same way.\n */\nexport const SNAPSHOT_VERSION = 4;\n/**\n * Version 2 is still READ, forever: version-2 snapshots already sit inside\n * archived features, which carry them across loam upgrades, and each must keep\n * restoring — and keep being graded for staleness by a retried archive — under\n * its own literal-path rules. Only version 1 is refused (no restore digests).\n */\nexport const SNAPSHOT_COMPAT_VERSION = 2;\n/**\n * Version 3 is read forever for the same reason, under its own rules: rows\n * keyed by `(service, artifact)` like version 4's, pre-images stored under\n * their living names. A version-3 snapshot keeps its layout — it is restored\n * from where it was written, never migrated, because rewriting a snapshot is\n * rewriting the only copy of what the living docs once said.\n */\nconst SNAPSHOT_KEYED_VERSION = 3;\n/** Appended to every pre-image a version-4 snapshot stores; see {@link SNAPSHOT_VERSION}. */\nexport const PRE_IMAGE_SUFFIX = \".before\";\n\n/**\n * The layouts this loam reads, by LITERAL — never \"the version this loam\n * writes\". Each rule below names the version that introduced it, so the next\n * bump cannot quietly re-read an older layout under the new one's rules: the\n * key rule was spelled `version === SNAPSHOT_VERSION` until this bump, and\n * would have stopped keying every version-3 row the day the constant moved. A\n * version added to the writer and not to this union is a compile error at the\n * writer's own call to {@link preImageRel}.\n */\nexport type ReadableSnapshotVersion = typeof SNAPSHOT_COMPAT_VERSION | typeof SNAPSHOT_KEYED_VERSION | 4;\n\n/** A manifest version this loam reads: the one it writes and the two it reads forever. */\nexport function isReadableSnapshotVersion(version: unknown): version is ReadableSnapshotVersion {\n return version === SNAPSHOT_COMPAT_VERSION || version === SNAPSHOT_KEYED_VERSION || version === 4;\n}\n\n/** Does a manifest of this version key its `services/` rows by `(service, artifact)`? Version 3 on. */\nexport function keysServiceRows(version: ReadableSnapshotVersion): boolean {\n return version >= SNAPSHOT_KEYED_VERSION;\n}\n\n/**\n * Where a row's pre-image sits inside `files/`, forward slashes, under the\n * layout the manifest's VERSION names — never the layout this loam would\n * write, since a snapshot is read where it was written. Suffixed from version\n * 4 on.\n */\nexport function preImageRel(version: ReadableSnapshotVersion, path: string): string {\n return version >= 4 ? `${path}${PRE_IMAGE_SUFFIX}` : path;\n}\n\nexport interface SnapshotEntry {\n /**\n * Docs-repo-relative path, forward slashes, AS ARCHIVED — where the archive\n * left the bytes. Load-bearing even beside the key below: it names the\n * pre-image under `.loam-before/files/` (through {@link preImageRel}), and\n * it is the literal fallback when the id stops being enumerated.\n *\n * For the feature's OWN documents — the ones the archive relinks for the\n * move (`commands/archive/plan/document/links.ts`) — that is inside\n * `features/archive/<FEAT>/`, not where they were written: the rename\n * carried them there, and that is where `unarchive` finds and restores them\n * before it moves the directory back. Recorded that way, every reader of\n * this layout restores them by its literal-path rule with nothing new to\n * learn, which is why the rows needed no version of their own; the two\n * readers that meet a snapshot before its feature moved — the clobber guard\n * and the crash repair — look across the move for them.\n */\n path: string;\n /**\n * For a `services/` entry: the service id (the leaf directory name — the one\n * thing a re-cut never changes) and the artifact's path inside its directory,\n * resolved through the CURRENT enumeration at restore time. Absent on\n * landscape and `features/` entries: the landscape does not move, and a\n * feature's own documents move only with the feature, which `path` already\n * says.\n */\n service?: string;\n artifact?: string;\n /** False when archive CREATED the file: restoring it means deleting it again. */\n existed: boolean;\n /** sha256 of what archive wrote, so unarchive can tell its own merge from later edits. */\n after: string;\n /**\n * sha256 of the pre-image beside this manifest — the bytes `unarchive` will\n * RESTORE. Null when archive created the file (there is no pre-image).\n * `after` only describes what archive wrote; while nothing described the\n * restore source, a pre-image edited in the archived feature directory — a\n * bad merge, a rebase, a stray editor — was written back verbatim and every\n * loam surface certified the result.\n */\n before: string | null;\n}\n\nexport interface SnapshotManifest {\n version: number;\n feature: string;\n dirName: string;\n archivedAt: string;\n /**\n * The digest of the checklist a fresh `loam verify` derived for the feature\n * just before this merge — present when the feature carried a record then,\n * absent otherwise and in every manifest an older loam wrote. Additive, so\n * the layout version does not move: `unarchive` never reads it, and a reader\n * that predates it ignores it. See {@link readSnapshotChecklist}.\n */\n checklist?: string;\n files: SnapshotEntry[];\n}\n\nexport function snapshotDir(featureDir: string): string {\n return join(featureDir, SNAPSHOT_DIR);\n}\n\n/**\n * The checklist digest an archived feature's snapshot captured as it merged,\n * or null when it carries none.\n *\n * It lives in the manifest because the manifest is the one file `archive`\n * already writes into the feature at the moment of the merge, travels with it\n * into `features/archive/`, and is taken away by `unarchive` — so a feature\n * archived again gets a fresh one, captured against what it then asked. A\n * frozen verification record cannot be judged against a checklist re-derived\n * after the merge (the merge moved the feature's operations into the living\n * contract, so the derivation can only mismatch); this is the question as it\n * stood when the feature shipped, which is what `loam gate --since` holds the\n * record to. Here rather than beside the writer for the reason this module\n * exists: the gate is a read-only command, served over MCP as one.\n *\n * Tolerant to the last byte, and every failure is \"none captured\": a manifest\n * an older loam wrote, a feature archived with no record, a manifest that is\n * missing or does not parse. The reader that grades on it reads null as the\n * frozen history it always read, and says so — it never refuses a deploy\n * question over bookkeeping.\n */\nexport async function readSnapshotChecklist(featureDir: string): Promise<string | null> {\n const manifest = await manifestRecord(featureDir);\n if (manifest === null) return null;\n const { checklist } = manifest;\n return typeof checklist === \"string\" && checklist !== \"\" ? checklist : null;\n}\n\n/**\n * What a snapshot says about one service artifact as this feature's merge found\n * it. `held` is the pre-image itself. The other two are answers too, and the\n * same one for the question the reader asks — what did the merge take OUT of\n * the artifact: nothing. `created` is a row archive wrote for a file it\n * created (there was nothing before it); `unwritten` is an intact manifest\n * with no row for the artifact at all — archive records every file it writes,\n * so the artifact is what the merge found. Only the EVENT merge leaves a\n * contract unwritten: it returns no text when it changes nothing\n * (asyncapi/merge/merge.ts, `text: null` — a delta whose every marker missed\n * its target is exactly that). The OpenAPI merge returns text whenever the\n * delta has a path entry, so its no-op is an ordinary row whose pre-image\n * equals what it wrote — `held`, which answers the same question the same\n * way: every marker misses against it again.\n */\nexport type SnapshotPreImage =\n | { kind: \"held\"; text: string }\n | { kind: \"created\" }\n | { kind: \"unwritten\" };\n\n/**\n * The bytes one service artifact held the moment this feature merged — the\n * pre-image the snapshot parked under `files/` — or one of the two answers\n * that say the merge took nothing out of it ({@link SnapshotPreImage}), or\n * null when there is nothing to trust.\n *\n * Read for the questions only the pre-image can answer, both asked by the\n * retired-operation revival check (core/coherence/retired/): what a merge TOOK\n * that its delta never spelled — a channel-slot removal in an asyncapi delta is\n * a bare marker, while the messages it retired with the channel are written in\n * the living contract it merged into and nowhere else — and whether a removal\n * marker took anything at all, since the merge deletes only what it finds at\n * the marker's slot. Here, beside `readSnapshotChecklist`, for that reader's\n * reason: the readers are read-only commands, and this module is the half of\n * the snapshot they may import.\n *\n * The entry is found by `(service, artifact)` on a keyed (version 3 or 4) manifest — the\n * key that survives a subsystem re-cut — and by the literal path's last two\n * segments on a version-2 one, which recorded nothing else. Tolerant to the\n * last byte, like the checklist reader: no manifest, a layout this loam would\n * have to guess at, no row for the artifact while some row cannot be read (it\n * might have been this one's, so absence cannot be concluded past it), a path\n * that does not stay inside `files/`, a pre-image that no longer hashes to the digest\n * the manifest recorded for it, or bytes that are not UTF-8 — every one is\n * null, and the caller grades without it. An edited pre-image is refused\n * rather than read for the reason `unarchive` refuses to restore one: its\n * bytes are no longer what the merge saw.\n */\nexport async function readSnapshotPreImage(\n featureDir: string,\n key: { service: string; artifact: string },\n): Promise<SnapshotPreImage | null> {\n const manifest = await manifestRecord(featureDir);\n if (manifest === null || !Array.isArray(manifest.files)) return null;\n const { version } = manifest;\n if (!isReadableSnapshotVersion(version)) return null;\n // `Array.isArray` narrows to `any[]`; naming the element type keeps that\n // `any` from spreading past the row being read (snapshot.ts's reason).\n const rows: unknown[] = manifest.files;\n const entry = rows.find((row) => isRecord(row) && entryIs(row, key));\n if (entry === undefined) {\n // Absence is an answer only when every row could be read: a row that is\n // not a record with a path might have been this artifact's.\n return rows.every((row) => isRecord(row) && typeof row.path === \"string\") ? { kind: \"unwritten\" } : null;\n }\n if (!isRecord(entry) || typeof entry.path !== \"string\") return null;\n if (entry.existed === false && entry.before === null) return { kind: \"created\" };\n if (typeof entry.before !== \"string\") return null;\n let bytes: Buffer;\n try {\n // The manifest is data and this path is one we go and read: the lexical\n // containment `unarchive` and the staleness check apply to the same field.\n bytes = await readFile(\n resolvePortableFileInsideLexically(\n join(snapshotDir(featureDir), \"files\"),\n preImageRel(version, entry.path),\n \"snapshot pre-image\",\n ),\n );\n } catch {\n return null;\n }\n if (sha256(bytes) !== entry.before || !isUtf8(bytes)) return null;\n return { kind: \"held\", text: bytes.toString(\"utf8\") };\n}\n\n/** Does this manifest row record `key`'s artifact? The `(service, artifact)` key when it has one, the literal path otherwise. */\nfunction entryIs(row: Record<string, unknown>, key: { service: string; artifact: string }): boolean {\n if (typeof row.service === \"string\" && typeof row.artifact === \"string\") {\n return row.service === key.service && row.artifact === key.artifact;\n }\n if (typeof row.path !== \"string\") return false;\n const parts = row.path.split(\"/\");\n return parts.length >= 3 && parts[0] === \"services\" && parts.at(-2) === key.service && parts.at(-1) === key.artifact;\n}\n\n/** The manifest as a record, or null — the one tolerant read behind both readers above. */\nasync function manifestRecord(featureDir: string): Promise<Record<string, unknown> | null> {\n let manifest: unknown;\n try {\n manifest = JSON.parse(await readFile(join(snapshotDir(featureDir), SNAPSHOT_MANIFEST), \"utf8\"));\n } catch {\n return null;\n }\n return isRecord(manifest) ? manifest : null;\n}\n"]}
|