@ybotok/loam 0.2.0-alpha.6 → 0.2.0-alpha.9
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 +1477 -1
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +40 -22
- package/QUICKSTART.md +181 -9
- package/README.md +256 -57
- package/ROADMAP.md +108 -11
- package/SCHEMA.md +721 -130
- package/WORKFLOW.md +272 -62
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/archive/archive.js +15 -18
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- 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 +25 -42
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +3 -7
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +4 -4
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/refusal.js +8 -18
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +134 -25
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +1 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +82 -33
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +4 -7
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +2 -2
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +44 -12
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +1 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/explore.js +4 -8
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +0 -4
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +38 -23
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +38 -6
- package/dist/commands/list/print.js.map +1 -1
- 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 +28 -33
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- 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 +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +55 -26
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/plan.js +64 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +6 -9
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/show.js +3 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/print.js +13 -4
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +3 -7
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +7 -7
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +60 -25
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +141 -71
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/report.js +55 -5
- package/dist/commands/subsystem/txn/report.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +121 -45
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +82 -12
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/render.js +116 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +113 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +6 -9
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +17 -28
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +6 -15
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +4 -49
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/health-deps.js +9 -6
- package/dist/commands/validate/checks/health-deps.js.map +1 -1
- package/dist/commands/validate/checks/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +6 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/feature.js +23 -19
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +26 -3
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +35 -32
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +7 -12
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +16 -5
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
- package/dist/commands/validate/fleet/map/consumers.js +43 -122
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -1
- package/dist/commands/validate/fleet/map/usecases.js +5 -11
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/obligations.js +1 -5
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +5 -1
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
- package/dist/commands/validate/fleet/views/ids.js +33 -7
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +17 -35
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
- package/dist/commands/validate/fleet/views/stale.js +31 -14
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/report.js +5 -4
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +16 -104
- package/dist/commands/validate/service/api.js.map +1 -1
- 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 +1 -1
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/service.js +16 -26
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +31 -38
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +8 -11
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/usecases.js +3 -3
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/validate.js +44 -26
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +54 -51
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +57 -102
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +4 -3
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +9 -3
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +31 -17
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +24 -6
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +37 -21
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +25 -10
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +5 -3
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +25 -22
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +54 -21
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +79 -11
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +91 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +3 -1
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +62 -24
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +30 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +31 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +10 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +30 -25
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/landscape.js +8 -12
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +8 -5
- package/dist/core/brief/map/extend.js.map +1 -1
- package/dist/core/brief/map/owed.js +8 -26
- package/dist/core/brief/map/owed.js.map +1 -1
- package/dist/core/brief/targets.js +79 -5
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +1 -1
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/likec4.js +147 -16
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +1 -9
- package/dist/core/c4/prefetch/fleet.js.map +1 -1
- package/dist/core/c4/project/architecture.js +31 -9
- package/dist/core/c4/project/architecture.js.map +1 -1
- package/dist/core/c4/project/documents.js +36 -10
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +14 -21
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +7 -3
- package/dist/core/c4/project/staged.js.map +1 -1
- 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 +25 -1
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/seed/template.js +15 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/shape.js +2 -22
- package/dist/core/c4/service-model/shape.js.map +1 -1
- package/dist/core/c4/source-mask.js +288 -4
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +203 -147
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +19 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +5 -18
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/landscape-merge.js +13 -8
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +10 -6
- package/dist/core/c4/splice/model/merge.js.map +1 -1
- package/dist/core/c4/splice/placement.js +108 -18
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/workspace.js +13 -22
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/findings.js +15 -20
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/coherence.js +30 -16
- package/dist/core/coherence/coherence.js.map +1 -1
- 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/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +119 -8
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +14 -82
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- 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/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/delta.js +1 -1
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/deployment/delta.js +15 -16
- package/dist/core/deployment/delta.js.map +1 -1
- package/dist/core/diff/base-git.js +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +28 -16
- package/dist/core/diff/base-state.js.map +1 -1
- 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 +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- 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 +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/document/apply.js +10 -2
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +17 -12
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/refusals.js +3 -3
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms.js +18 -9
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +2 -7
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +34 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +10 -3
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +64 -19
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +5 -36
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +22 -8
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- 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 +56 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +26 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +15 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +2 -16
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/paths.js +34 -29
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/repo.js +24 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/render/body.js +45 -6
- package/dist/core/repo/tree/render/body.js.map +1 -1
- 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 +19 -2
- package/dist/core/repo/tree/render/stale.js.map +1 -1
- package/dist/core/repo/tree/render/views.js +102 -33
- package/dist/core/repo/tree/render/views.js.map +1 -1
- package/dist/core/repo/tree/walk.js +8 -30
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/staging/commit.js +10 -12
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +60 -1
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +152 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +2 -2
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +5 -11
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/fleet/fleet.js +6 -2
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +26 -3
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +174 -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 +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/fleet.js +178 -31
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/core/usecases/operations.js +5 -4
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/service/flows.js +9 -6
- package/dist/core/usecases/service/flows.js.map +1 -1
- 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/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +45 -10
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- 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 +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +1 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +53 -3
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +28 -9
- package/examples/docs/architecture/landscape.likec4 +21 -7
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +3 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/order-service/model.likec4 +10 -8
- package/examples/docs/services/payment-service/model.likec4 +6 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +9 -7
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/examples/docs/services/platform/notification-service/model.likec4 +4 -5
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -16
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -221
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -180
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -256
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -10,26 +10,26 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { existsSync } from "node:fs";
|
|
12
12
|
import { mkdir, rename } from "node:fs/promises";
|
|
13
|
-
import { emitJson, repoPath, sayExplain } from "../../core/envelope/json.js";
|
|
13
|
+
import { CodedFailure, emitJson, repoPath, sayExplain } from "../../core/envelope/json.js";
|
|
14
14
|
import { message, quietPruneEmptyParents, quietRm, rollbackError, rollbackStaged, stageWrites, swapStaged, } from "../../core/staging/commit.js";
|
|
15
15
|
import { clearCommitIntent, writeCommitIntent } from "../../core/staging/recovery/intent.js";
|
|
16
|
-
import { SNAPSHOT_DIR,
|
|
16
|
+
import { SNAPSHOT_DIR, snapshotDir } from "../../core/staging/snapshot/manifest.js";
|
|
17
|
+
import { SnapshotClobberError, writeSnapshot } from "../../core/staging/snapshot/snapshot.js";
|
|
17
18
|
import { enumeratedServices } from "../../core/repo/service-target.js";
|
|
18
19
|
import { listFleetTree } from "../../core/repo/repo.js";
|
|
19
|
-
import { subsystemViewsPath } from "../../core/repo/paths.js";
|
|
20
|
-
import { viewsState } from "../../core/repo/tree/render/stale.js";
|
|
20
|
+
import { serviceViewsPath, subsystemViewsPath } from "../../core/repo/paths.js";
|
|
21
|
+
import { serviceViewsState, viewsState } from "../../core/repo/tree/render/stale.js";
|
|
21
22
|
import { loadArchitecture } from "../../core/c4/project/architecture.js";
|
|
22
23
|
import { FleetContext } from "../../core/fleet-context.js";
|
|
23
24
|
import { gate } from "./plan/gate.js";
|
|
24
|
-
import {} from "./plan/refusal.js";
|
|
25
|
+
import { issueJson, printPlan, refuseJson } from "./plan/refusal.js";
|
|
25
26
|
import { planCapabilities, planGlossary, planSpecs } from "./plan/specs.js";
|
|
26
27
|
import { planOpenapiContracts } from "./plan/contracts/openapi.js";
|
|
27
28
|
import { planAsyncapiContracts } from "./plan/contracts/asyncapi.js";
|
|
29
|
+
import { contractConsumers } from "./plan/contracts/consumers.js";
|
|
28
30
|
import { planDeployments, planFlows, planLandscape } from "./plan/landscape.js";
|
|
29
31
|
import { emptyPlan } from "./plan/state.js";
|
|
30
|
-
import {
|
|
31
|
-
import { ArchiveFailure } from "./plan/refusal.js";
|
|
32
|
-
import { printPlan } from "./plan/refusal.js";
|
|
32
|
+
import { checklistAtArchive } from "./shipped.js";
|
|
33
33
|
export async function archiveLocked(config, featureId, opts) {
|
|
34
34
|
const dryRun = opts.dryRun === true;
|
|
35
35
|
const json = opts.json === true;
|
|
@@ -63,8 +63,12 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
63
63
|
// a create-only whole-file copy, refused at the gate if the word already
|
|
64
64
|
// lives (`core/glossary/delta.ts`).
|
|
65
65
|
await planGlossary(read, gated, planned, say);
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
// One consumer index for both contract planners, built only if one of them
|
|
67
|
+
// overwrites an operation or a message (./plan/contracts/consumers.ts) —
|
|
68
|
+
// and then over the living fleet as it stands before this merge.
|
|
69
|
+
const contracts = { ...read, consumers: contractConsumers(read) };
|
|
70
|
+
await planOpenapiContracts(contracts, gated, planned, say);
|
|
71
|
+
await planAsyncapiContracts(contracts, gated, planned, say);
|
|
68
72
|
// `read`, not `config`: the C4 merge now asks which services own their own
|
|
69
73
|
// interior, and that answer comes from the fleet enumeration this run has
|
|
70
74
|
// already paid for.
|
|
@@ -77,6 +81,7 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
77
81
|
await planFlows(config, gated, planned, say);
|
|
78
82
|
await planDeployments(config, gated, planned, say);
|
|
79
83
|
const { writes, planWarns, planGates, openapiRemovals, asyncapiRemovals } = planned;
|
|
84
|
+
const revouchOwed = planned.revouchOwed.map((r) => ({ ...r, path: repoPath(config.docsDir, r.path) }));
|
|
80
85
|
// Gate on what only the plan could see: a merged operation pointing at a
|
|
81
86
|
// component that exists nowhere, a removal marker addressing no operation.
|
|
82
87
|
// Same doctrine as the coherence gate — a judgment call --approve overrides
|
|
@@ -102,6 +107,17 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
102
107
|
for (const i of planGates)
|
|
103
108
|
say(` ✗ ${i.message}`);
|
|
104
109
|
}
|
|
110
|
+
// Printed by the dry run and the real merge alike, before either commits to
|
|
111
|
+
// anything: the merge is what moves these documents, and the merge's PR is
|
|
112
|
+
// the place their re-vouches belong. Not a finding and not a refusal — the
|
|
113
|
+
// staleness that follows is honest, and `content.stale` is what grades it.
|
|
114
|
+
if (revouchOwed.length > 0) {
|
|
115
|
+
say(`\n re-vouch owed — this merge moves ${revouchOwed.length} vouched spec(s) under the vouch stamp, so content.stale follows on each until its owner re-vouches:`);
|
|
116
|
+
for (const r of revouchOwed) {
|
|
117
|
+
say(` ${r.path} (owner: ${r.owner ?? "none recorded"}) — \`loam vouch --service ${r.service}\`, run in that service's repo`);
|
|
118
|
+
}
|
|
119
|
+
say(` land the re-vouches in this PR: \`loam validate --all --base <ref> --strict\` stays red on it until they do.`);
|
|
120
|
+
}
|
|
105
121
|
// The plan as data, verb decided now — after the commit everything would read
|
|
106
122
|
// as an update. The `--json` payload is identical for a dry run and the real
|
|
107
123
|
// thing except for `archived`; what WOULD happen is what DOES happen.
|
|
@@ -122,6 +138,7 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
122
138
|
overridden: overridden.map((issue) => issueJson(issue, repoPath(config.docsDir, archiveDest))),
|
|
123
139
|
openapiRemovals,
|
|
124
140
|
asyncapiRemovals,
|
|
141
|
+
revouchOwed,
|
|
125
142
|
// Present only when this run found an interrupted commit and dealt with it:
|
|
126
143
|
// an agent that sees the docs change under it deserves to be told why, and
|
|
127
144
|
// the absent field is the ordinary case.
|
|
@@ -134,6 +151,10 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
134
151
|
printPlan(config.docsDir, writes, dirName);
|
|
135
152
|
return;
|
|
136
153
|
}
|
|
154
|
+
// The question the feature asks as it ships, captured before anything below
|
|
155
|
+
// moves the living docs it is derived from — see ./shipped.ts. It rides in
|
|
156
|
+
// the snapshot manifest; a dry run writes no snapshot, so it pays nothing.
|
|
157
|
+
const checklist = await checklistAtArchive(read, gated);
|
|
137
158
|
// COMMIT — the whole plan computed cleanly. Stage every new version beside its
|
|
138
159
|
// target, snapshot what is about to be overwritten, then swap them into place.
|
|
139
160
|
// Staging touches the filesystem — a read-only `architecture/`, a full disk,
|
|
@@ -145,7 +166,7 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
145
166
|
staged = await stageWrites(writes);
|
|
146
167
|
}
|
|
147
168
|
catch (err) {
|
|
148
|
-
throw new
|
|
169
|
+
throw new CodedFailure("merge-failed", `${message(err)} — nothing was written`);
|
|
149
170
|
}
|
|
150
171
|
let snapshot = false;
|
|
151
172
|
let createdArchiveDir;
|
|
@@ -181,6 +202,7 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
181
202
|
staged,
|
|
182
203
|
serviceKeyOf,
|
|
183
204
|
serviceDirOf,
|
|
205
|
+
...(checklist === undefined ? {} : { checklist }),
|
|
184
206
|
});
|
|
185
207
|
snapshot = true;
|
|
186
208
|
// The journal, fsynced, BEFORE the first rename: swapStaged is N renames and
|
|
@@ -202,7 +224,7 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
202
224
|
// the answer is not "re-run": a previous archive of this feature is still
|
|
203
225
|
// sitting in the living docs.
|
|
204
226
|
await rollbackStaged(staged);
|
|
205
|
-
throw new
|
|
227
|
+
throw new CodedFailure("commit-interrupted", err.message);
|
|
206
228
|
}
|
|
207
229
|
// Everything this run made, unmade: the swapped files, the snapshot inside the
|
|
208
230
|
// feature that is staying put, and features/archive/ if we are the ones who
|
|
@@ -232,21 +254,30 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
232
254
|
const kept = snapshot && failures.length > 0
|
|
233
255
|
? ` Pre-images of the overwritten files are kept in features/${dirName}/${SNAPSHOT_DIR}/files/.`
|
|
234
256
|
: "";
|
|
235
|
-
throw new
|
|
257
|
+
throw new CodedFailure(failures.length > 0 ? "rollback-incomplete" : "merge-failed", wrapped.message + kept);
|
|
236
258
|
}
|
|
237
|
-
// This merge just changed the map, and
|
|
259
|
+
// This merge just changed the map, and both generated views files are a
|
|
238
260
|
// function of the tree AND the map: a delta whose element lands inside a
|
|
239
261
|
// group that exactly covered a subsystem takes that subsystem's boundary
|
|
240
|
-
// line away,
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
// validate
|
|
247
|
-
|
|
262
|
+
// line away, a delta binding an element to a filed service adds an include,
|
|
263
|
+
// and one new edge changes exactly one service's overview. Archive does not
|
|
264
|
+
// rewrite either file — `loam subsystem sync` owns them outright, and folding
|
|
265
|
+
// a second generated artifact into this transaction would mean archive
|
|
266
|
+
// computing the post-merge tree AND the post-merge map, which is how the two
|
|
267
|
+
// writers disagree — but it must not report a repo as current when the next
|
|
268
|
+
// `validate --all` will call it stale. Graded through validate's own
|
|
269
|
+
// functions, so the two cannot answer differently.
|
|
270
|
+
const staleViews = await staleGeneratedViews(config.docsDir);
|
|
248
271
|
if (json) {
|
|
249
|
-
emitJson({
|
|
272
|
+
emitJson({
|
|
273
|
+
...payload(true),
|
|
274
|
+
// The original boolean keeps its exact meaning — `architecture/subsystems.likec4`
|
|
275
|
+
// and nothing else — because a payload key's meaning is frozen; the list
|
|
276
|
+
// beside it is the additive answer that can name the second file. Both are
|
|
277
|
+
// omitted when nothing is stale, exactly as the boolean always was.
|
|
278
|
+
...(staleViews.length === 0 ? {} : { staleViews }),
|
|
279
|
+
...(staleViews.includes("architecture/subsystems.likec4") ? { subsystemViewsStale: true } : {}),
|
|
280
|
+
});
|
|
250
281
|
return;
|
|
251
282
|
}
|
|
252
283
|
console.log(`\n archived: features/${dirName} → features/archive/${dirName}`);
|
|
@@ -257,12 +288,15 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
257
288
|
// caller told it to merge past. Printing it over either is how a red fleet
|
|
258
289
|
// gets reported as a finished one.
|
|
259
290
|
const incomplete = planWarns.filter((w) => w.code === "service.no-model");
|
|
260
|
-
if (incomplete.length === 0 && overridden.length === 0 &&
|
|
291
|
+
if (incomplete.length === 0 && overridden.length === 0 && staleViews.length === 0) {
|
|
261
292
|
console.log(" living spec + landscape are now complete + current.");
|
|
262
293
|
return;
|
|
263
294
|
}
|
|
264
|
-
|
|
265
|
-
|
|
295
|
+
// One line per stale file rather than one line listing them: each names a file
|
|
296
|
+
// a reader opens, and a comma-joined series would have to choose a verb that
|
|
297
|
+
// reads wrong for one of the two counts.
|
|
298
|
+
for (const path of staleViews) {
|
|
299
|
+
console.log(` ⚠ ${path} no longer matches the tree and this map — run \`loam subsystem sync\`.`);
|
|
266
300
|
}
|
|
267
301
|
for (const w of incomplete)
|
|
268
302
|
console.log(` ⚠ ${w.message}`);
|
|
@@ -271,29 +305,44 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
271
305
|
}
|
|
272
306
|
}
|
|
273
307
|
/**
|
|
274
|
-
*
|
|
308
|
+
* Which generated files would the next `validate --all` report
|
|
309
|
+
* `subsystem.views-stale` on? Docs-relative paths, sorted, empty for none.
|
|
275
310
|
*
|
|
276
|
-
* Asked through validate's OWN
|
|
311
|
+
* Asked through validate's OWN functions rather than a second compare, so the
|
|
277
312
|
* warning archive prints and the error validate raises can never disagree —
|
|
278
313
|
* including about the gate validate holds: a map that does not parse is
|
|
279
314
|
* `landscape.invalid`'s business, and grading a generated file against a map
|
|
280
315
|
* nobody can read would cascade a second complaint behind the first. An
|
|
281
316
|
* unreadable map therefore answers "no claim", exactly as validate does.
|
|
317
|
+
*
|
|
318
|
+
* BOTH generated files, because this merge just changed the map and the
|
|
319
|
+
* per-service overviews are a function of it in a way the subsystem views are
|
|
320
|
+
* not: a delta adding one edge changes exactly one service's peer set, and a
|
|
321
|
+
* delta binding a new element adds a whole view. Naming only the file loam
|
|
322
|
+
* happened to check first would let a repo be reported current while the very
|
|
323
|
+
* next `validate --all` calls it stale — the one thing this advisory exists to
|
|
324
|
+
* prevent.
|
|
282
325
|
*/
|
|
283
|
-
async function
|
|
326
|
+
async function staleGeneratedViews(docsDir) {
|
|
284
327
|
try {
|
|
285
328
|
const doc = await loadArchitecture(docsDir);
|
|
286
329
|
if (doc.errors.length > 0)
|
|
287
|
-
return
|
|
288
|
-
const
|
|
289
|
-
|
|
330
|
+
return [];
|
|
331
|
+
const tree = await listFleetTree(docsDir);
|
|
332
|
+
const stale = [];
|
|
333
|
+
if (!(await viewsState(subsystemViewsPath(docsDir), tree, doc)).agrees)
|
|
334
|
+
stale.push("architecture/subsystems.likec4");
|
|
335
|
+
if (!(await serviceViewsState(serviceViewsPath(docsDir), tree, doc)).agrees) {
|
|
336
|
+
stale.push("architecture/services.likec4");
|
|
337
|
+
}
|
|
338
|
+
return stale.sort();
|
|
290
339
|
}
|
|
291
340
|
catch {
|
|
292
341
|
// Tolerant for the reason the snapshot resolver above is: a docs repo with
|
|
293
342
|
// no `services/` at all still archives a landscape-only feature, and this
|
|
294
343
|
// question is an ADVISORY printed after a commit that already succeeded.
|
|
295
344
|
// Nothing here may turn a landed archive into a non-zero exit.
|
|
296
|
-
return
|
|
345
|
+
return [];
|
|
297
346
|
}
|
|
298
347
|
}
|
|
299
348
|
//# sourceMappingURL=run.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/archive/run.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The archive itself, under the docs lock: gate, plan, commit.\n *\n * The order is the command's whole safety property. Every refusal happens before\n * a byte is planned (`./plan/gate.ts`), the plan is computed entirely in memory\n * so a failure on any axis leaves the living docs untouched (`./plan/specs.ts`,\n * `./plan/contracts/`, `./plan/landscape.ts`), and only a plan that succeeded\n * on every axis is staged\n * and swapped. `--dry-run` returns between the second and the third.\n */\nimport { existsSync } from \"node:fs\";\nimport { mkdir, rename } from \"node:fs/promises\";\nimport { emitJson, repoPath, sayExplain } from \"../../core/envelope/json.js\";\nimport {\n message,\n quietPruneEmptyParents,\n quietRm,\n rollbackError,\n rollbackStaged,\n stageWrites,\n swapStaged,\n} from \"../../core/staging/commit.js\";\nimport { clearCommitIntent, writeCommitIntent } from \"../../core/staging/recovery/intent.js\";\nimport {\n SNAPSHOT_DIR,\n SnapshotClobberError,\n snapshotDir,\n writeSnapshot,\n type ServiceKey,\n} from \"../../core/staging/snapshot.js\";\nimport { enumeratedServices } from \"../../core/repo/service-target.js\";\nimport { listFleetTree } from \"../../core/repo/repo.js\";\nimport { subsystemViewsPath } from \"../../core/repo/paths.js\";\nimport { viewsState } from \"../../core/repo/tree/render/stale.js\";\nimport { loadArchitecture } from \"../../core/c4/project/architecture.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { gate } from \"./plan/gate.js\";\nimport { type ArchiveOptions } from \"./plan/refusal.js\";\nimport { planCapabilities, planGlossary, planSpecs } from \"./plan/specs.js\";\nimport { planOpenapiContracts } from \"./plan/contracts/openapi.js\";\nimport { planAsyncapiContracts } from \"./plan/contracts/asyncapi.js\";\nimport { planDeployments, planFlows, planLandscape } from \"./plan/landscape.js\";\nimport { emptyPlan } from \"./plan/state.js\";\nimport { issueJson, refuseJson } from \"./plan/refusal.js\";\nimport { ArchiveFailure } from \"./plan/refusal.js\";\nimport { printPlan } from \"./plan/refusal.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\n\nexport async function archiveLocked(\n config: { docsDir: DocsDir },\n featureId: string,\n opts: ArchiveOptions,\n): Promise<void> {\n const dryRun = opts.dryRun === true;\n const json = opts.json === true;\n // All prose goes through here so `--json` keeps stdout a single JSON document.\n const say = (line = \"\"): void => {\n if (!json) console.log(line);\n };\n\n // One read index for the whole run, threaded through every planner's\n // config record: gate, the spec merge and both contract merges each hold\n // per-service loops that resolve living paths through the enumeration, and\n // without a shared context every one of those calls re-walks the fleet —\n // O(services²) work under a lock that guarantees the tree is not moving.\n // The snapshot resolver below reads through the same context, so it pays\n // for that enumeration too.\n const read = { docsDir: config.docsDir, fleet: new FleetContext() };\n const gated = await gate(read, featureId, opts, say);\n if (gated === null) return;\n const { id, dirName, featureDir, gating, advisory, archiveDir, archiveDest, recovered, issues } = gated;\n\n say(`archive ${id}${dryRun ? \" (dry run)\" : \"\"}\\n`);\n\n // PLAN — compute every merge in memory. Nothing is written until the whole plan\n // succeeds, so a failure on any axis leaves the living docs untouched.\n const planned = emptyPlan();\n await planSpecs(read, gated, planned, say);\n // The business corpus, beside the service one and before the contracts: both\n // are requirement merges, and reading them together is how a plan stays\n // legible as \"what this feature promises\" followed by \"how it is built\".\n await planCapabilities(read, gated, planned, say);\n // The vocabulary the change brings with it, beside the promises it makes:\n // a create-only whole-file copy, refused at the gate if the word already\n // lives (`core/glossary/delta.ts`).\n await planGlossary(read, gated, planned, say);\n await planOpenapiContracts(read, gated, planned, say);\n await planAsyncapiContracts(read, gated, planned, say);\n // `read`, not `config`: the C4 merge now asks which services own their own\n // interior, and that answer comes from the fleet enumeration this run has\n // already paid for.\n await planLandscape(read, gated, planned, say);\n // The flows this feature brings, after the elements they draw hops over: the\n // copy is create-only and refused at the gate if the living tree already holds\n // one (`core/usecases/delta/flows.ts`), and it is ordered after the landscape\n // merge so a reader of the plan sees the map change before the sequence drawn\n // on it.\n await planFlows(config, gated, planned, say);\n await planDeployments(config, gated, planned, say);\n const { writes, planWarns, planGates, openapiRemovals, asyncapiRemovals } = planned;\n\n // Gate on what only the plan could see: a merged operation pointing at a\n // component that exists nowhere, a removal marker addressing no operation.\n // Same doctrine as the coherence gate — a judgment call --approve overrides\n // (unlike the mechanical merge-failed refusals), and a dry run is gated too.\n // Checked after the whole plan so the refusal costs nothing: no write has\n // happened yet either way.\n if (planGates.length > 0 && !opts.approve) {\n const msg = `archive ${id} — BLOCKED: ${planGates.length} issue(s) in the contract merge`;\n if (json) {\n refuseJson(\n \"not-coherent\",\n msg,\n [...issues, ...planWarns, ...planGates],\n `features/${dirName}`,\n );\n return;\n }\n console.error(`${msg}:`);\n for (const i of planGates) console.error(` ✗ ${i.message}`);\n console.error(`\\nFix them in the feature's openapi.yaml / asyncapi.yaml — or re-run with --approve to merge anyway.`);\n sayExplain(\"not-coherent\");\n process.exitCode = 1;\n return;\n }\n if (planGates.length > 0) {\n say(`\\n ⚠ archiving despite ${planGates.length} contract merge issue(s) (--approve):`);\n for (const i of planGates) say(` ✗ ${i.message}`);\n }\n\n // The plan as data, verb decided now — after the commit everything would read\n // as an update. The `--json` payload is identical for a dry run and the real\n // thing except for `archived`; what WOULD happen is what DOES happen.\n const warnings = [...advisory, ...planWarns];\n const overridden = opts.approve === true ? [...gating, ...planGates] : [];\n const plan: Array<Record<string, unknown>> = writes.map((w) => ({\n path: repoPath(config.docsDir, w.path),\n action: existsSync(w.path) ? \"update\" : \"create\",\n }));\n plan.push({ path: `features/${dirName}`, action: \"move\", to: `features/archive/${dirName}` });\n const payload = (archived: boolean): Record<string, unknown> => ({\n command: \"archive\",\n feature: id,\n archived,\n path: repoPath(config.docsDir, archiveDest),\n plan,\n warnings: warnings.map((issue) => issueJson(issue, repoPath(config.docsDir, archiveDest))),\n overridden: overridden.map((issue) => issueJson(issue, repoPath(config.docsDir, archiveDest))),\n openapiRemovals,\n asyncapiRemovals,\n // Present only when this run found an interrupted commit and dealt with it:\n // an agent that sees the docs change under it deserves to be told why, and\n // the absent field is the ordinary case.\n ...(recovered === null ? {} : { recovered }),\n });\n\n if (dryRun) {\n if (json) emitJson(payload(false));\n else printPlan(config.docsDir, writes, dirName);\n return;\n }\n\n // COMMIT — the whole plan computed cleanly. Stage every new version beside its\n // target, snapshot what is about to be overwritten, then swap them into place.\n // Staging touches the filesystem — a read-only `architecture/`, a full disk,\n // a target whose pre-image cannot be read. None of that is a bug in loam, and\n // reporting `internal` sends the reader looking for one; it is the same\n // answer as any other merge that could not be computed: nothing was written.\n let staged;\n try {\n staged = await stageWrites(writes);\n } catch (err) {\n throw new ArchiveFailure(\"merge-failed\", `${message(err)} — nothing was written`);\n }\n let snapshot = false;\n let createdArchiveDir: string | undefined;\n try {\n // The snapshot's resolver pair: how a docs-relative path decomposes into a\n // service identity, and where that identity lives NOW. Built here — the\n // command layer's view of the tree — and injected, so the snapshot module\n // never learns the repo layout. The seam the flat wave left is now\n // threaded: both answers come from the enumeration, so a service filed\n // into a subsystem keys its snapshot rows by the directory it actually\n // lives in, and the clobber guard grades a leftover claim at that same\n // address. The root-level parse stays as the fallback for the one path\n // shape the enumeration cannot know yet — a service this archive CREATES,\n // which always materialises unfiled at services/<id>/.\n // Tolerant on purpose: a docs repo whose services/ is absent still\n // archives a landscape-only feature exactly as before — the enumeration\n // is empty, every row keys by the flat fallback, and the missing\n // directory stays doctor's diagnosis rather than a merge-failed here.\n const dirOf = new Map(\n (await enumeratedServices(config.docsDir, read.fleet)).map((s) => [s.id as string, repoPath(config.docsDir, s.dir)]),\n );\n const serviceKeyOf = (rel: string): ServiceKey | null => {\n for (const [service, dir] of dirOf) {\n if (rel.startsWith(`${dir}/`)) return { service, artifact: rel.slice(dir.length + 1) };\n }\n const m = /^services\\/([^/]+)\\/(.+)$/.exec(rel);\n return m ? { service: m[1]!, artifact: m[2]! } : null;\n };\n const serviceDirOf = (service: string): string | null => dirOf.get(service) ?? null;\n await writeSnapshot({\n featureDir,\n docsDir: config.docsDir,\n feature: { featureId: id, dirName },\n staged,\n serviceKeyOf,\n serviceDirOf,\n });\n snapshot = true;\n // The journal, fsynced, BEFORE the first rename: swapStaged is N renames and\n // only each one of them is atomic, so a kill between two used to leave a\n // half-merged repo that nothing could name — `doctor` and `status` called it\n // healthy and `validate --all` blamed the delta. Written from `staged`, so\n // it records the same digests the swaps are about to produce.\n await writeCommitIntent(\n config.docsDir,\n { command: \"archive\", restore: \"before\", feature: id, moveFrom: featureDir, moveTo: archiveDest },\n staged,\n );\n await swapStaged(staged);\n createdArchiveDir = await mkdir(archiveDir, { recursive: true });\n await rename(featureDir, archiveDest);\n // Last: while it exists, the commit is in flight.\n await clearCommitIntent(config.docsDir);\n } catch (err) {\n if (err instanceof SnapshotClobberError) {\n // Nothing swapped — the refusal happens before the journal is even\n // written — so this only takes the temp files away. Its own code, because\n // the answer is not \"re-run\": a previous archive of this feature is still\n // sitting in the living docs.\n await rollbackStaged(staged);\n throw new ArchiveFailure(\"commit-interrupted\", err.message);\n }\n // Everything this run made, unmade: the swapped files, the snapshot inside the\n // feature that is staying put, and features/archive/ if we are the ones who\n // created it (mkdir reports nothing when it was already there).\n const failures = await rollbackStaged(staged);\n // The rollback decided the outcome, so the journal has nothing left to\n // describe — including on rollback-incomplete, where the files it names are\n // the ones the message tells a human to look at.\n await clearCommitIntent(config.docsDir);\n // The snapshot goes only when the rollback HELD. On rollback-incomplete it\n // holds the only on-disk pre-images of the very files the message tells the\n // reader to repair by hand — a MODIFIED requirement's previous text appears\n // nowhere else. Retaining it is safe: the next archive of this feature reads\n // it before it would replace it, and refuses if the living docs have moved.\n if (snapshot && failures.length === 0) await quietRm(snapshotDir(featureDir));\n // `features/archive/` is shared the moment it exists: another archive can\n // have moved a whole feature into it between our mkdir and this rollback,\n // and a recursive remove of \"the directory we created\" took that feature —\n // snapshot and all — with it, on OUR failure path, in silence. Empty\n // directories only, stopping at the first that is not.\n if (createdArchiveDir !== undefined) await quietPruneEmptyParents(archiveDir, createdArchiveDir);\n // The code is a caller's answer to \"can I trust the repo?\": merge-failed\n // means yes (rolled back), rollback-incomplete means look at it by hand.\n const wrapped = rollbackError(err, failures);\n const kept =\n snapshot && failures.length > 0\n ? ` Pre-images of the overwritten files are kept in features/${dirName}/${SNAPSHOT_DIR}/files/.`\n : \"\";\n throw new ArchiveFailure(failures.length > 0 ? \"rollback-incomplete\" : \"merge-failed\", wrapped.message + kept);\n }\n\n // This merge just changed the map, and the generated views file is a\n // function of the tree AND the map: a delta whose element lands inside a\n // group that exactly covered a subsystem takes that subsystem's boundary\n // line away, and a delta binding an element to a filed service adds an\n // include. Archive does not rewrite the file — `loam subsystem sync` owns it\n // outright, and folding a second generated artifact into this transaction\n // would mean archive computing the post-merge tree AND the post-merge map,\n // which is how the two writers disagree — but it must not report a repo as\n // current when the next `validate --all` will call it stale. Graded through\n // validate's own function, so the two cannot answer differently.\n const staleViews = await subsystemViewsStale(config.docsDir);\n\n if (json) {\n emitJson({ ...payload(true), ...(staleViews ? { subsystemViewsStale: true } : {}) });\n return;\n }\n console.log(`\\n archived: features/${dirName} → features/archive/${dirName}`);\n console.log(` snapshot: features/archive/${dirName}/${SNAPSHOT_DIR}/ — \\`loam unarchive ${id}\\` puts it back`);\n // The closing line is a claim about the whole docs repo, and it is the line a\n // reader stops at. It may be printed only when this archive left nothing the\n // next `validate --all` will fail on — a service with no model, or a gate the\n // caller told it to merge past. Printing it over either is how a red fleet\n // gets reported as a finished one.\n const incomplete = planWarns.filter((w) => w.code === \"service.no-model\");\n if (incomplete.length === 0 && overridden.length === 0 && !staleViews) {\n console.log(\" living spec + landscape are now complete + current.\");\n return;\n }\n if (staleViews) {\n console.log(\n \" ⚠ architecture/subsystems.likec4 no longer matches the tree and this map — run `loam subsystem sync`.\",\n );\n }\n for (const w of incomplete) console.log(` ⚠ ${w.message}`);\n if (overridden.length > 0) {\n console.log(\n ` ⚠ merged past ${overridden.length} gating issue(s) with --approve — the living docs carry them now; \\`loam validate --all\\` says what they cost.`,\n );\n }\n}\n\n/**\n * Would the next `validate --all` report `subsystem.views-stale`?\n *\n * Asked through validate's OWN function rather than a second compare, so the\n * warning archive prints and the error validate raises can never disagree —\n * including about the gate validate holds: a map that does not parse is\n * `landscape.invalid`'s business, and grading a generated file against a map\n * nobody can read would cascade a second complaint behind the first. An\n * unreadable map therefore answers \"no claim\", exactly as validate does.\n */\nasync function subsystemViewsStale(docsDir: DocsDir): Promise<boolean> {\n try {\n const doc = await loadArchitecture(docsDir);\n if (doc.errors.length > 0) return false;\n const state = await viewsState(subsystemViewsPath(docsDir), await listFleetTree(docsDir), doc);\n return !state.agrees;\n } catch {\n // Tolerant for the reason the snapshot resolver above is: a docs repo with\n // no `services/` at all still archives a landscape-only feature, and this\n // question is an ADVISORY printed after a commit that already succeeded.\n // Nothing here may turn a landed archive into a non-zero exit.\n return false;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/commands/archive/run.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The archive itself, under the docs lock: gate, plan, commit.\n *\n * The order is the command's whole safety property. Every refusal happens before\n * a byte is planned (`./plan/gate.ts`), the plan is computed entirely in memory\n * so a failure on any axis leaves the living docs untouched (`./plan/specs.ts`,\n * `./plan/contracts/`, `./plan/landscape.ts`), and only a plan that succeeded\n * on every axis is staged\n * and swapped. `--dry-run` returns between the second and the third.\n */\nimport { existsSync } from \"node:fs\";\nimport { mkdir, rename } from \"node:fs/promises\";\nimport { CodedFailure, emitJson, repoPath, sayExplain } from \"../../core/envelope/json.js\";\nimport {\n message,\n quietPruneEmptyParents,\n quietRm,\n rollbackError,\n rollbackStaged,\n stageWrites,\n swapStaged,\n} from \"../../core/staging/commit.js\";\nimport { clearCommitIntent, writeCommitIntent } from \"../../core/staging/recovery/intent.js\";\nimport { SNAPSHOT_DIR, snapshotDir } from \"../../core/staging/snapshot/manifest.js\";\nimport { SnapshotClobberError, writeSnapshot, type ServiceKey } from \"../../core/staging/snapshot/snapshot.js\";\nimport { enumeratedServices } from \"../../core/repo/service-target.js\";\nimport { listFleetTree } from \"../../core/repo/repo.js\";\nimport { serviceViewsPath, subsystemViewsPath } from \"../../core/repo/paths.js\";\nimport { serviceViewsState, viewsState } from \"../../core/repo/tree/render/stale.js\";\nimport { loadArchitecture } from \"../../core/c4/project/architecture.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { gate } from \"./plan/gate.js\";\nimport { issueJson, printPlan, refuseJson, type ArchiveOptions } from \"./plan/refusal.js\";\nimport { planCapabilities, planGlossary, planSpecs } from \"./plan/specs.js\";\nimport { planOpenapiContracts } from \"./plan/contracts/openapi.js\";\nimport { planAsyncapiContracts } from \"./plan/contracts/asyncapi.js\";\nimport { contractConsumers } from \"./plan/contracts/consumers.js\";\nimport { planDeployments, planFlows, planLandscape } from \"./plan/landscape.js\";\nimport { emptyPlan } from \"./plan/state.js\";\nimport { checklistAtArchive } from \"./shipped.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\n\nexport async function archiveLocked(\n config: { docsDir: DocsDir },\n featureId: string,\n opts: ArchiveOptions,\n): Promise<void> {\n const dryRun = opts.dryRun === true;\n const json = opts.json === true;\n // All prose goes through here so `--json` keeps stdout a single JSON document.\n const say = (line = \"\"): void => {\n if (!json) console.log(line);\n };\n\n // One read index for the whole run, threaded through every planner's\n // config record: gate, the spec merge and both contract merges each hold\n // per-service loops that resolve living paths through the enumeration, and\n // without a shared context every one of those calls re-walks the fleet —\n // O(services²) work under a lock that guarantees the tree is not moving.\n // The snapshot resolver below reads through the same context, so it pays\n // for that enumeration too.\n const read = { docsDir: config.docsDir, fleet: new FleetContext() };\n const gated = await gate(read, featureId, opts, say);\n if (gated === null) return;\n const { id, dirName, featureDir, gating, advisory, archiveDir, archiveDest, recovered, issues } = gated;\n\n say(`archive ${id}${dryRun ? \" (dry run)\" : \"\"}\\n`);\n\n // PLAN — compute every merge in memory. Nothing is written until the whole plan\n // succeeds, so a failure on any axis leaves the living docs untouched.\n const planned = emptyPlan();\n await planSpecs(read, gated, planned, say);\n // The business corpus, beside the service one and before the contracts: both\n // are requirement merges, and reading them together is how a plan stays\n // legible as \"what this feature promises\" followed by \"how it is built\".\n await planCapabilities(read, gated, planned, say);\n // The vocabulary the change brings with it, beside the promises it makes:\n // a create-only whole-file copy, refused at the gate if the word already\n // lives (`core/glossary/delta.ts`).\n await planGlossary(read, gated, planned, say);\n // One consumer index for both contract planners, built only if one of them\n // overwrites an operation or a message (./plan/contracts/consumers.ts) —\n // and then over the living fleet as it stands before this merge.\n const contracts = { ...read, consumers: contractConsumers(read) };\n await planOpenapiContracts(contracts, gated, planned, say);\n await planAsyncapiContracts(contracts, gated, planned, say);\n // `read`, not `config`: the C4 merge now asks which services own their own\n // interior, and that answer comes from the fleet enumeration this run has\n // already paid for.\n await planLandscape(read, gated, planned, say);\n // The flows this feature brings, after the elements they draw hops over: the\n // copy is create-only and refused at the gate if the living tree already holds\n // one (`core/usecases/delta/flows.ts`), and it is ordered after the landscape\n // merge so a reader of the plan sees the map change before the sequence drawn\n // on it.\n await planFlows(config, gated, planned, say);\n await planDeployments(config, gated, planned, say);\n const { writes, planWarns, planGates, openapiRemovals, asyncapiRemovals } = planned;\n const revouchOwed = planned.revouchOwed.map((r) => ({ ...r, path: repoPath(config.docsDir, r.path) }));\n\n // Gate on what only the plan could see: a merged operation pointing at a\n // component that exists nowhere, a removal marker addressing no operation.\n // Same doctrine as the coherence gate — a judgment call --approve overrides\n // (unlike the mechanical merge-failed refusals), and a dry run is gated too.\n // Checked after the whole plan so the refusal costs nothing: no write has\n // happened yet either way.\n if (planGates.length > 0 && !opts.approve) {\n const msg = `archive ${id} — BLOCKED: ${planGates.length} issue(s) in the contract merge`;\n if (json) {\n refuseJson(\n \"not-coherent\",\n msg,\n [...issues, ...planWarns, ...planGates],\n `features/${dirName}`,\n );\n return;\n }\n console.error(`${msg}:`);\n for (const i of planGates) console.error(` ✗ ${i.message}`);\n console.error(`\\nFix them in the feature's openapi.yaml / asyncapi.yaml — or re-run with --approve to merge anyway.`);\n sayExplain(\"not-coherent\");\n process.exitCode = 1;\n return;\n }\n if (planGates.length > 0) {\n say(`\\n ⚠ archiving despite ${planGates.length} contract merge issue(s) (--approve):`);\n for (const i of planGates) say(` ✗ ${i.message}`);\n }\n // Printed by the dry run and the real merge alike, before either commits to\n // anything: the merge is what moves these documents, and the merge's PR is\n // the place their re-vouches belong. Not a finding and not a refusal — the\n // staleness that follows is honest, and `content.stale` is what grades it.\n if (revouchOwed.length > 0) {\n say(`\\n re-vouch owed — this merge moves ${revouchOwed.length} vouched spec(s) under the vouch stamp, so content.stale follows on each until its owner re-vouches:`);\n for (const r of revouchOwed) {\n say(` ${r.path} (owner: ${r.owner ?? \"none recorded\"}) — \\`loam vouch --service ${r.service}\\`, run in that service's repo`);\n }\n say(` land the re-vouches in this PR: \\`loam validate --all --base <ref> --strict\\` stays red on it until they do.`);\n }\n\n // The plan as data, verb decided now — after the commit everything would read\n // as an update. The `--json` payload is identical for a dry run and the real\n // thing except for `archived`; what WOULD happen is what DOES happen.\n const warnings = [...advisory, ...planWarns];\n const overridden = opts.approve === true ? [...gating, ...planGates] : [];\n const plan: Array<Record<string, unknown>> = writes.map((w) => ({\n path: repoPath(config.docsDir, w.path),\n action: existsSync(w.path) ? \"update\" : \"create\",\n }));\n plan.push({ path: `features/${dirName}`, action: \"move\", to: `features/archive/${dirName}` });\n const payload = (archived: boolean): Record<string, unknown> => ({\n command: \"archive\",\n feature: id,\n archived,\n path: repoPath(config.docsDir, archiveDest),\n plan,\n warnings: warnings.map((issue) => issueJson(issue, repoPath(config.docsDir, archiveDest))),\n overridden: overridden.map((issue) => issueJson(issue, repoPath(config.docsDir, archiveDest))),\n openapiRemovals,\n asyncapiRemovals,\n revouchOwed,\n // Present only when this run found an interrupted commit and dealt with it:\n // an agent that sees the docs change under it deserves to be told why, and\n // the absent field is the ordinary case.\n ...(recovered === null ? {} : { recovered }),\n });\n\n if (dryRun) {\n if (json) emitJson(payload(false));\n else printPlan(config.docsDir, writes, dirName);\n return;\n }\n\n // The question the feature asks as it ships, captured before anything below\n // moves the living docs it is derived from — see ./shipped.ts. It rides in\n // the snapshot manifest; a dry run writes no snapshot, so it pays nothing.\n const checklist = await checklistAtArchive(read, gated);\n\n // COMMIT — the whole plan computed cleanly. Stage every new version beside its\n // target, snapshot what is about to be overwritten, then swap them into place.\n // Staging touches the filesystem — a read-only `architecture/`, a full disk,\n // a target whose pre-image cannot be read. None of that is a bug in loam, and\n // reporting `internal` sends the reader looking for one; it is the same\n // answer as any other merge that could not be computed: nothing was written.\n let staged;\n try {\n staged = await stageWrites(writes);\n } catch (err) {\n throw new CodedFailure(\"merge-failed\", `${message(err)} — nothing was written`);\n }\n let snapshot = false;\n let createdArchiveDir: string | undefined;\n try {\n // The snapshot's resolver pair: how a docs-relative path decomposes into a\n // service identity, and where that identity lives NOW. Built here — the\n // command layer's view of the tree — and injected, so the snapshot module\n // never learns the repo layout. The seam the flat wave left is now\n // threaded: both answers come from the enumeration, so a service filed\n // into a subsystem keys its snapshot rows by the directory it actually\n // lives in, and the clobber guard grades a leftover claim at that same\n // address. The root-level parse stays as the fallback for the one path\n // shape the enumeration cannot know yet — a service this archive CREATES,\n // which always materialises unfiled at services/<id>/.\n // Tolerant on purpose: a docs repo whose services/ is absent still\n // archives a landscape-only feature exactly as before — the enumeration\n // is empty, every row keys by the flat fallback, and the missing\n // directory stays doctor's diagnosis rather than a merge-failed here.\n const dirOf = new Map(\n (await enumeratedServices(config.docsDir, read.fleet)).map((s) => [s.id as string, repoPath(config.docsDir, s.dir)]),\n );\n const serviceKeyOf = (rel: string): ServiceKey | null => {\n for (const [service, dir] of dirOf) {\n if (rel.startsWith(`${dir}/`)) return { service, artifact: rel.slice(dir.length + 1) };\n }\n const m = /^services\\/([^/]+)\\/(.+)$/.exec(rel);\n return m ? { service: m[1]!, artifact: m[2]! } : null;\n };\n const serviceDirOf = (service: string): string | null => dirOf.get(service) ?? null;\n await writeSnapshot({\n featureDir,\n docsDir: config.docsDir,\n feature: { featureId: id, dirName },\n staged,\n serviceKeyOf,\n serviceDirOf,\n ...(checklist === undefined ? {} : { checklist }),\n });\n snapshot = true;\n // The journal, fsynced, BEFORE the first rename: swapStaged is N renames and\n // only each one of them is atomic, so a kill between two used to leave a\n // half-merged repo that nothing could name — `doctor` and `status` called it\n // healthy and `validate --all` blamed the delta. Written from `staged`, so\n // it records the same digests the swaps are about to produce.\n await writeCommitIntent(\n config.docsDir,\n { command: \"archive\", restore: \"before\", feature: id, moveFrom: featureDir, moveTo: archiveDest },\n staged,\n );\n await swapStaged(staged);\n createdArchiveDir = await mkdir(archiveDir, { recursive: true });\n await rename(featureDir, archiveDest);\n // Last: while it exists, the commit is in flight.\n await clearCommitIntent(config.docsDir);\n } catch (err) {\n if (err instanceof SnapshotClobberError) {\n // Nothing swapped — the refusal happens before the journal is even\n // written — so this only takes the temp files away. Its own code, because\n // the answer is not \"re-run\": a previous archive of this feature is still\n // sitting in the living docs.\n await rollbackStaged(staged);\n throw new CodedFailure(\"commit-interrupted\", err.message);\n }\n // Everything this run made, unmade: the swapped files, the snapshot inside the\n // feature that is staying put, and features/archive/ if we are the ones who\n // created it (mkdir reports nothing when it was already there).\n const failures = await rollbackStaged(staged);\n // The rollback decided the outcome, so the journal has nothing left to\n // describe — including on rollback-incomplete, where the files it names are\n // the ones the message tells a human to look at.\n await clearCommitIntent(config.docsDir);\n // The snapshot goes only when the rollback HELD. On rollback-incomplete it\n // holds the only on-disk pre-images of the very files the message tells the\n // reader to repair by hand — a MODIFIED requirement's previous text appears\n // nowhere else. Retaining it is safe: the next archive of this feature reads\n // it before it would replace it, and refuses if the living docs have moved.\n if (snapshot && failures.length === 0) await quietRm(snapshotDir(featureDir));\n // `features/archive/` is shared the moment it exists: another archive can\n // have moved a whole feature into it between our mkdir and this rollback,\n // and a recursive remove of \"the directory we created\" took that feature —\n // snapshot and all — with it, on OUR failure path, in silence. Empty\n // directories only, stopping at the first that is not.\n if (createdArchiveDir !== undefined) await quietPruneEmptyParents(archiveDir, createdArchiveDir);\n // The code is a caller's answer to \"can I trust the repo?\": merge-failed\n // means yes (rolled back), rollback-incomplete means look at it by hand.\n const wrapped = rollbackError(err, failures);\n const kept =\n snapshot && failures.length > 0\n ? ` Pre-images of the overwritten files are kept in features/${dirName}/${SNAPSHOT_DIR}/files/.`\n : \"\";\n throw new CodedFailure(failures.length > 0 ? \"rollback-incomplete\" : \"merge-failed\", wrapped.message + kept);\n }\n\n // This merge just changed the map, and both generated views files are a\n // function of the tree AND the map: a delta whose element lands inside a\n // group that exactly covered a subsystem takes that subsystem's boundary\n // line away, a delta binding an element to a filed service adds an include,\n // and one new edge changes exactly one service's overview. Archive does not\n // rewrite either file — `loam subsystem sync` owns them outright, and folding\n // a second generated artifact into this transaction would mean archive\n // computing the post-merge tree AND the post-merge map, which is how the two\n // writers disagree — but it must not report a repo as current when the next\n // `validate --all` will call it stale. Graded through validate's own\n // functions, so the two cannot answer differently.\n const staleViews = await staleGeneratedViews(config.docsDir);\n\n if (json) {\n emitJson({\n ...payload(true),\n // The original boolean keeps its exact meaning — `architecture/subsystems.likec4`\n // and nothing else — because a payload key's meaning is frozen; the list\n // beside it is the additive answer that can name the second file. Both are\n // omitted when nothing is stale, exactly as the boolean always was.\n ...(staleViews.length === 0 ? {} : { staleViews }),\n ...(staleViews.includes(\"architecture/subsystems.likec4\") ? { subsystemViewsStale: true } : {}),\n });\n return;\n }\n console.log(`\\n archived: features/${dirName} → features/archive/${dirName}`);\n console.log(` snapshot: features/archive/${dirName}/${SNAPSHOT_DIR}/ — \\`loam unarchive ${id}\\` puts it back`);\n // The closing line is a claim about the whole docs repo, and it is the line a\n // reader stops at. It may be printed only when this archive left nothing the\n // next `validate --all` will fail on — a service with no model, or a gate the\n // caller told it to merge past. Printing it over either is how a red fleet\n // gets reported as a finished one.\n const incomplete = planWarns.filter((w) => w.code === \"service.no-model\");\n if (incomplete.length === 0 && overridden.length === 0 && staleViews.length === 0) {\n console.log(\" living spec + landscape are now complete + current.\");\n return;\n }\n // One line per stale file rather than one line listing them: each names a file\n // a reader opens, and a comma-joined series would have to choose a verb that\n // reads wrong for one of the two counts.\n for (const path of staleViews) {\n console.log(` ⚠ ${path} no longer matches the tree and this map — run \\`loam subsystem sync\\`.`);\n }\n for (const w of incomplete) console.log(` ⚠ ${w.message}`);\n if (overridden.length > 0) {\n console.log(\n ` ⚠ merged past ${overridden.length} gating issue(s) with --approve — the living docs carry them now; \\`loam validate --all\\` says what they cost.`,\n );\n }\n}\n\n/**\n * Which generated files would the next `validate --all` report\n * `subsystem.views-stale` on? Docs-relative paths, sorted, empty for none.\n *\n * Asked through validate's OWN functions rather than a second compare, so the\n * warning archive prints and the error validate raises can never disagree —\n * including about the gate validate holds: a map that does not parse is\n * `landscape.invalid`'s business, and grading a generated file against a map\n * nobody can read would cascade a second complaint behind the first. An\n * unreadable map therefore answers \"no claim\", exactly as validate does.\n *\n * BOTH generated files, because this merge just changed the map and the\n * per-service overviews are a function of it in a way the subsystem views are\n * not: a delta adding one edge changes exactly one service's peer set, and a\n * delta binding a new element adds a whole view. Naming only the file loam\n * happened to check first would let a repo be reported current while the very\n * next `validate --all` calls it stale — the one thing this advisory exists to\n * prevent.\n */\nasync function staleGeneratedViews(docsDir: DocsDir): Promise<string[]> {\n try {\n const doc = await loadArchitecture(docsDir);\n if (doc.errors.length > 0) return [];\n const tree = await listFleetTree(docsDir);\n const stale: string[] = [];\n if (!(await viewsState(subsystemViewsPath(docsDir), tree, doc)).agrees) stale.push(\"architecture/subsystems.likec4\");\n if (!(await serviceViewsState(serviceViewsPath(docsDir), tree, doc)).agrees) {\n stale.push(\"architecture/services.likec4\");\n }\n return stale.sort();\n } catch {\n // Tolerant for the reason the snapshot resolver above is: a docs repo with\n // no `services/` at all still archives a landscape-only feature, and this\n // question is an ADVISORY printed after a commit that already succeeded.\n // Nothing here may turn a landed archive into a non-zero exit.\n return [];\n }\n}\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `archive` writes down about the verification a feature ships with: the
|
|
3
|
+
* digest of the checklist it asks at the moment of the merge, captured into
|
|
4
|
+
* the undo snapshot's manifest (`core/staging/snapshot/manifest.ts`,
|
|
5
|
+
* `readSnapshotChecklist`).
|
|
6
|
+
*
|
|
7
|
+
* Archive reads no verdict — verify never gates the merge, and nothing here
|
|
8
|
+
* changes that: a record's state decides nothing about whether the archive
|
|
9
|
+
* runs. But once archived, a record is frozen history, and the checklist it
|
|
10
|
+
* could be held to is gone — the merge moves the feature's operations into the
|
|
11
|
+
* living contract, so a derivation after it can only mismatch. That made
|
|
12
|
+
* archiving the cheapest way past `loam gate --since`: a record gone stale
|
|
13
|
+
* read `verified` again the moment its feature shipped. With the question as
|
|
14
|
+
* it stood at the merge written down beside the record, the gate holds the
|
|
15
|
+
* frozen record to the feature that actually shipped, and `list`, `status` and
|
|
16
|
+
* `verify` keep reading it as the frozen history they always did.
|
|
17
|
+
*
|
|
18
|
+
* A module of its own rather than lines in `./run.ts`, which sequences the
|
|
19
|
+
* commit and is near the line limit; this is the one place archive asks the
|
|
20
|
+
* done-check anything.
|
|
21
|
+
*/
|
|
22
|
+
import { featureChecklist } from "../../core/verify/checklist.js";
|
|
23
|
+
import { readVerificationState } from "../../core/verify/read/file.js";
|
|
24
|
+
/**
|
|
25
|
+
* The digest a fresh `loam verify` would derive for the feature right now —
|
|
26
|
+
* call it before the first byte of the merge lands — or undefined when there
|
|
27
|
+
* is nothing worth capturing.
|
|
28
|
+
*
|
|
29
|
+
* Only a feature that carries a READABLE record pays for the derivation (it
|
|
30
|
+
* loads the delta into a LikeC4 workspace): with no record, or one nothing can
|
|
31
|
+
* read, the gate's answer is "absent" or "unreadable" whatever the checklist
|
|
32
|
+
* was, so a capture would buy nothing.
|
|
33
|
+
*
|
|
34
|
+
* Every failure is "nothing captured", never a refusal — verify does not gate
|
|
35
|
+
* the archive, and a derivation that could not run must not start to. The
|
|
36
|
+
* gate reads an absent capture as frozen history and says so.
|
|
37
|
+
*/
|
|
38
|
+
export async function checklistAtArchive(read, feature) {
|
|
39
|
+
try {
|
|
40
|
+
const record = await readVerificationState(feature.featureDir);
|
|
41
|
+
if (record.state !== "ok")
|
|
42
|
+
return undefined;
|
|
43
|
+
return (await featureChecklist(read.docsDir, feature.featureDir, feature.id, read.fleet)).digest;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=shipped.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shipped.js","sourceRoot":"","sources":["../../../src/commands/archive/shipped.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAIvE;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAA+C,EAC/C,OAA+C;IAE/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC/D,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QAC5C,OAAO,CAAC,MAAM,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;IACnG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC","sourcesContent":["/**\n * What `archive` writes down about the verification a feature ships with: the\n * digest of the checklist it asks at the moment of the merge, captured into\n * the undo snapshot's manifest (`core/staging/snapshot/manifest.ts`,\n * `readSnapshotChecklist`).\n *\n * Archive reads no verdict — verify never gates the merge, and nothing here\n * changes that: a record's state decides nothing about whether the archive\n * runs. But once archived, a record is frozen history, and the checklist it\n * could be held to is gone — the merge moves the feature's operations into the\n * living contract, so a derivation after it can only mismatch. That made\n * archiving the cheapest way past `loam gate --since`: a record gone stale\n * read `verified` again the moment its feature shipped. With the question as\n * it stood at the merge written down beside the record, the gate holds the\n * frozen record to the feature that actually shipped, and `list`, `status` and\n * `verify` keep reading it as the frozen history they always did.\n *\n * A module of its own rather than lines in `./run.ts`, which sequences the\n * commit and is near the line limit; this is the one place archive asks the\n * done-check anything.\n */\nimport { featureChecklist } from \"../../core/verify/checklist.js\";\nimport { readVerificationState } from \"../../core/verify/read/file.js\";\nimport type { FleetContext } from \"../../core/fleet-context.js\";\nimport type { DocsDir, FeatureDir } from \"../../core/kernel/ids/dirs.js\";\n\n/**\n * The digest a fresh `loam verify` would derive for the feature right now —\n * call it before the first byte of the merge lands — or undefined when there\n * is nothing worth capturing.\n *\n * Only a feature that carries a READABLE record pays for the derivation (it\n * loads the delta into a LikeC4 workspace): with no record, or one nothing can\n * read, the gate's answer is \"absent\" or \"unreadable\" whatever the checklist\n * was, so a capture would buy nothing.\n *\n * Every failure is \"nothing captured\", never a refusal — verify does not gate\n * the archive, and a derivation that could not run must not start to. The\n * gate reads an absent capture as frozen history and says so.\n */\nexport async function checklistAtArchive(\n read: { docsDir: DocsDir; fleet: FleetContext },\n feature: { id: string; featureDir: FeatureDir },\n): Promise<string | undefined> {\n try {\n const record = await readVerificationState(feature.featureDir);\n if (record.state !== \"ok\") return undefined;\n return (await featureChecklist(read.docsDir, feature.featureDir, feature.id, read.fleet)).digest;\n } catch {\n return undefined;\n }\n}\n"]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { emitJson, fail, reportNoConfig } from "../../core/envelope/json.js";
|
|
1
|
+
import { configOrRefuse, emitJson, fail } from "../../core/envelope/json.js";
|
|
3
2
|
import { parseServiceId } from "../../core/kernel/ids/service.js";
|
|
4
3
|
import { FleetContext } from "../../core/fleet-context.js";
|
|
5
4
|
import { assemblePack, packHoles } from "../../core/pack/pack.js";
|
|
@@ -17,12 +16,10 @@ export function registerContext(program) {
|
|
|
17
16
|
.option("--json", "emit the machine contract instead of the human view")
|
|
18
17
|
.action(async (serviceArg, opts) => {
|
|
19
18
|
const json = opts.json === true;
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
22
|
-
reportNoConfig(json, loaded);
|
|
19
|
+
const config = await configOrRefuse(json);
|
|
20
|
+
if (config === null)
|
|
23
21
|
return;
|
|
24
|
-
}
|
|
25
|
-
const { docsDir } = loaded.config;
|
|
22
|
+
const { docsDir } = config;
|
|
26
23
|
// `services`, not `docs`: the pack is a statement about one service in
|
|
27
24
|
// the fleet, and an empty pack read out of a directory that is not a
|
|
28
25
|
// docs repo would say "nothing is documented here" about a repo nobody
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/commands/context/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/commands/context/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { configOrRefuse, emitJson, fail } from \"../../core/envelope/json.js\";\nimport { parseServiceId } from \"../../core/kernel/ids/service.js\";\nimport { FleetContext } from \"../../core/fleet-context.js\";\nimport { assemblePack, packHoles } from \"../../core/pack/pack.js\";\nimport { nearestIds, type FeatureEntry } from \"../../core/repo/entries.js\";\nimport { missingFeatureMessage, resolveFeature } from \"../../core/repo/repo.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from \"../policy/gate.js\";\nimport { printPack } from \"./print.js\";\n\ninterface ContextOptions {\n feature?: string;\n json?: boolean;\n}\n\nexport function registerContext(program: Command): void {\n program\n .command(\"context\")\n .argument(\"<service>\", \"service id, e.g. payment-service\")\n .description(\n \"Assemble one service's docs slice — spec, contracts, edges, permissions, capabilities, and the deltas in flight over it — as one deterministic briefing; writes nothing\",\n )\n .option(\"--feature <FEAT>\", \"narrow the in-flight section to this one feature\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (serviceArg: string, opts: ContextOptions) => {\n const json = opts.json === true;\n\n const config = await configOrRefuse(json);\n if (config === null) return;\n const { docsDir } = config;\n // `services`, not `docs`: the pack is a statement about one service in\n // the fleet, and an empty pack read out of a directory that is not a\n // docs repo would say \"nothing is documented here\" about a repo nobody\n // looked in.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n\n const context = new FleetContext();\n try {\n // The feature is resolved BEFORE the service is looked up (delta's\n // ordering): its refusal is about the flag, and it must not depend on\n // whether the service half of the invocation was right.\n let feature: FeatureEntry | null = null;\n if (opts.feature !== undefined) {\n feature = await resolveFeature(docsDir, opts.feature, \"exclude\", context);\n if (!feature) {\n fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, opts.feature, context));\n return;\n }\n }\n\n // The enumeration answers first and the grammar second —\n // core/repo/service-target.ts's settled order, and the pack is exactly\n // the case its banner argues from: `services/payment service/` is a\n // directory `validate --all` grades and `service.id-invalid` tells the\n // team to rename, so refusing it on the grammar would make the one\n // service loam complains about the one service nobody can be briefed\n // on. The enumerated id is safe to use onward — a readdir produced it,\n // which is `RawServiceId`'s whole provenance. A name that is neither\n // enumerated nor legal is refused before any path is built; a legal\n // name with no directory is `unknown-service`, because a typo must\n // never be indistinguishable from an undocumented service — an EMPTY\n // pack reads as \"nothing to know here\".\n const entries = await context.listServices(docsDir);\n const entry = entries.find((e) => e.id === serviceArg);\n if (entry === undefined) {\n const parsed = parseServiceId(serviceArg, \"service\");\n if (!parsed.ok) {\n fail(json, \"invalid-option\", parsed.problem);\n return;\n }\n const near = nearestIds(serviceArg, entries.map((e) => e.id));\n fail(\n json,\n \"unknown-service\",\n `No service '${serviceArg}' under ${docsDir}/services/.` +\n (near.length === 0 ? \"\" : ` Did you mean ${near.map((n) => `'${n}'`).join(\" or \")}?`),\n );\n return;\n }\n\n const pack = await assemblePack({ docsDir, entry, feature, context });\n // A document behind the pack that exists but does not parse empties\n // its section, and an agent loading the pack as a briefing would read\n // that as \"nothing here\" — the vacuously-green pattern. The output\n // stays as informative as ever (and `ok` stays true under --json: the\n // command ran); the exit code is what stops a pipeline from building\n // on it, so it is set BEFORE the format fork — the guard is about the\n // pack, not about how it is rendered.\n if (packHoles(pack)) process.exitCode = 1;\n\n if (json) {\n emitJson({ command: \"context\", docsDir, ...pack });\n return;\n }\n printPack(pack);\n } catch (err) {\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // The assembly is all-or-nothing, like explore's scan: it reads every\n // document the pack quotes, so the honest answer to an unreadable FILE\n // (a directory where a document belongs, bytes that are not UTF-8) is\n // a refusal naming the path, not a briefing with a silent hole in it.\n reportRepositoryUnavailable(json, err, \"the pack would be missing a document\", docsDir);\n }\n });\n}\n"]}
|
|
@@ -60,7 +60,7 @@ export function printPack(pack) {
|
|
|
60
60
|
console.log();
|
|
61
61
|
}
|
|
62
62
|
if (pack.asyncapi.unreadable) {
|
|
63
|
-
console.log(`! asyncapi.yaml does not
|
|
63
|
+
console.log(`! asyncapi.yaml does not read as AsyncAPI 3.0 — nothing here lists what ${pack.service} publishes or consumes`);
|
|
64
64
|
if (pack.asyncapi.error !== undefined)
|
|
65
65
|
console.log(` ${pack.asyncapi.error}`);
|
|
66
66
|
console.log();
|
|
@@ -243,7 +243,7 @@ function printFeature(f, service) {
|
|
|
243
243
|
console.log();
|
|
244
244
|
}
|
|
245
245
|
if (f.events.unreadable) {
|
|
246
|
-
console.log("Events: asyncapi.yaml does not
|
|
246
|
+
console.log("Events: asyncapi.yaml does not read as AsyncAPI 3.0");
|
|
247
247
|
if (f.events.error !== undefined)
|
|
248
248
|
console.log(` ${f.events.error}`);
|
|
249
249
|
console.log();
|