@ybotok/loam 0.2.0-alpha.7 → 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 +1315 -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 +83 -7
- package/SCHEMA.md +569 -98
- 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 +33 -11
- 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 +21 -25
- package/dist/commands/seed/commit.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 +3 -5
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +5 -8
- 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 +14 -41
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +17 -38
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +32 -5
- 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/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/landscape.js +23 -23
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +6 -10
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +1 -3
- package/dist/commands/validate/fleet/map/bindings.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.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +8 -28
- package/dist/commands/validate/fleet/views/root-documents.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 +56 -101
- 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 +4 -3
- 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 +32 -18
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +9 -3
- 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 +20 -16
- 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 +50 -18
- 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 +6 -18
- 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 +15 -5
- package/dist/core/brief/targets.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/documents.js +31 -6
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +6 -17
- package/dist/core/c4/project/load.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 +3 -6
- 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 +5 -2
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +4 -2
- 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/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/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 +3 -0
- 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 +12 -2
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +21 -6
- package/examples/docs/architecture/landscape.likec4 +8 -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/platform/notification-service/asyncapi.yaml +2 -1
- 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
|
@@ -13,23 +13,25 @@ import { existsSync } from "node:fs";
|
|
|
13
13
|
import { mkdir, realpath, writeFile } from "node:fs/promises";
|
|
14
14
|
import { dirname, resolve } from "node:path";
|
|
15
15
|
import { stringify as stringifyYaml } from "yaml";
|
|
16
|
+
import { isPathInside } from "../../../core/kernel/path-safety.js";
|
|
16
17
|
import { dictionary, ownValue } from "../../../core/kernel/records.js";
|
|
17
|
-
import {} from "../../../core/openspec/model/mapping.js";
|
|
18
|
+
import { activeRenameEntries } from "../../../core/openspec/model/mapping.js";
|
|
18
19
|
import {} from "../../../core/openspec/model/model.js";
|
|
19
20
|
import { OpenSpecCommandError } from "./error.js";
|
|
20
|
-
import { canonicalForCreate
|
|
21
|
+
import { canonicalForCreate } from "./paths.js";
|
|
21
22
|
export async function writeMappingSkeleton(inventory, outputArg, skeleton) {
|
|
22
23
|
const output = resolve(outputArg);
|
|
23
24
|
const canonicalOutput = await canonicalForCreate(output);
|
|
24
25
|
const canonicalSource = await realpath(inventory.inputRoot);
|
|
25
|
-
if (
|
|
26
|
+
if (isPathInside(canonicalSource, canonicalOutput)) {
|
|
26
27
|
throw new OpenSpecCommandError("invalid-option", `Refusing to write the mapping inside the OpenSpec source workspace: ${output}`);
|
|
27
28
|
}
|
|
28
|
-
if (existsSync(output)) {
|
|
29
|
-
throw new OpenSpecCommandError("already-exists", `Mapping output already exists; refusing to overwrite it: ${output}`);
|
|
30
|
-
}
|
|
31
29
|
await mkdir(dirname(output), { recursive: true });
|
|
32
30
|
try {
|
|
31
|
+
// `wx` is the existence check: an output already there (a file, a
|
|
32
|
+
// directory, or a symlink, dangling or not) fails the exclusive create, so
|
|
33
|
+
// no separate pre-check is needed — one used to sit above, saying the same
|
|
34
|
+
// `already-exists` this catch says.
|
|
33
35
|
await writeFile(output, stringifyYaml(skeleton, { lineWidth: 0 }), { encoding: "utf8", flag: "wx" });
|
|
34
36
|
}
|
|
35
37
|
catch (error) {
|
|
@@ -63,14 +65,7 @@ export function normalizedMapping(inventory, mapping) {
|
|
|
63
65
|
title: decision.title,
|
|
64
66
|
},
|
|
65
67
|
])),
|
|
66
|
-
renames:
|
|
67
|
-
.filter((rename) => rename.scope === "active")
|
|
68
|
-
.map((rename) => [rename.key, {
|
|
69
|
-
from: rename.from,
|
|
70
|
-
to: rename.to,
|
|
71
|
-
existingRequirementId: rename.existingRequirementId,
|
|
72
|
-
requirementId: rename.requirementId,
|
|
73
|
-
}])),
|
|
68
|
+
renames: activeRenameEntries(inventory),
|
|
74
69
|
artifacts: Object.fromEntries(inventory.artifactDecisions.map((decision) => [
|
|
75
70
|
decision.path,
|
|
76
71
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/decisions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,MAAM,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAsD,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/decisions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The mapping document on the way out: the blank skeleton `audit-openspec`\n * offers, the normalized record `migrate-openspec --apply` writes beside the\n * staged docs, and the one query that reads a decision back.\n *\n * `selectedServices` belongs here rather than beside either materializer\n * because it is the single spelling of the routing rule. It was written out\n * twice once — once on the living side and once on the delta side — and the two\n * copies drifted, so a split capability routed a living requirement and its own\n * delta to different services. Both materializers now call this one.\n */\nimport { existsSync } from \"node:fs\";\nimport { mkdir, realpath, writeFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\nimport { stringify as stringifyYaml } from \"yaml\";\nimport { isPathInside } from \"../../../core/kernel/path-safety.js\";\nimport { dictionary, ownValue } from \"../../../core/kernel/records.js\";\nimport { activeRenameEntries, type OpenSpecMapping, type OpenSpecMappingSkeleton } from \"../../../core/openspec/model/mapping.js\";\nimport { type OpenSpecInventory } from \"../../../core/openspec/model/model.js\";\nimport { OpenSpecCommandError } from \"./error.js\";\nimport { canonicalForCreate } from \"./paths.js\";\n\nexport async function writeMappingSkeleton(\n inventory: OpenSpecInventory,\n outputArg: string,\n skeleton: OpenSpecMappingSkeleton,\n): Promise<string> {\n const output = resolve(outputArg);\n const canonicalOutput = await canonicalForCreate(output);\n const canonicalSource = await realpath(inventory.inputRoot);\n if (isPathInside(canonicalSource, canonicalOutput)) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `Refusing to write the mapping inside the OpenSpec source workspace: ${output}`,\n );\n }\n await mkdir(dirname(output), { recursive: true });\n try {\n // `wx` is the existence check: an output already there (a file, a\n // directory, or a symlink, dangling or not) fails the exclusive create, so\n // no separate pre-check is needed — one used to sit above, saying the same\n // `already-exists` this catch says.\n await writeFile(output, stringifyYaml(skeleton, { lineWidth: 0 }), { encoding: \"utf8\", flag: \"wx\" });\n } catch (error) {\n if (existsSync(output)) {\n throw new OpenSpecCommandError(\"already-exists\", `Mapping output already exists; refusing to overwrite it: ${output}`);\n }\n throw error;\n }\n return output;\n}\n\nexport function normalizedMapping(inventory: OpenSpecInventory, mapping: OpenSpecMapping): Record<string, unknown> {\n return {\n version: 1,\n source: { root: inventory.root, inventoryDigest: inventory.inventoryDigest },\n capabilities: Object.fromEntries(inventory.mappingDecisions.map((decision) => [\n decision.capability,\n {\n services: ownValue(mapping.capabilities, decision.capability)?.services ?? [],\n requirementServices: ownValue(mapping.capabilities, decision.capability)?.requirementServices ?? dictionary(),\n },\n ])),\n changes: Object.fromEntries(inventory.changeDecisions.map((decision) => [\n decision.change,\n {\n feature: ownValue(mapping.changes, decision.change)?.feature ?? null,\n // The decision's title, which is the mapping's own title trimmed.\n // `readMapping` (`./mapping.ts`) trims `feature` but not `title`, and\n // materialization writes the trimmed one into the frontmatter, the H1\n // and the directory slug — so recording the raw one made the plan\n // disagree with the files beside it about what the feature is called.\n title: decision.title,\n },\n ])),\n renames: activeRenameEntries(inventory),\n artifacts: Object.fromEntries(inventory.artifactDecisions.map((decision) => [\n decision.path,\n {\n kind: decision.kind,\n disposition: ownValue(mapping.artifacts, decision.path) ?? null,\n },\n ])),\n };\n}\n\nexport function selectedServices(\n mapping: OpenSpecMapping,\n capability: string,\n requirement: string,\n): string[] {\n const selected = ownValue(mapping.capabilities, capability);\n if (selected === undefined) return [];\n return selected.services.length === 1\n ? selected.services\n : ownValue(selected.requirementServices, requirement) ?? [];\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {} from "../../../core/envelope/json.js";
|
|
1
|
+
import { CodedFailure } from "../../../core/envelope/json.js";
|
|
2
2
|
/**
|
|
3
3
|
* The one refusal shape both verbs raise, in a module of its own.
|
|
4
4
|
*
|
|
@@ -7,13 +7,12 @@ import {} from "../../../core/envelope/json.js";
|
|
|
7
7
|
* put the command's registration on the evaluation path of the modules being
|
|
8
8
|
* registered. So the error is the package's lowest leaf, and the wiring — which
|
|
9
9
|
* maps it onto an exit code in `reportCommandError` — sits above everything.
|
|
10
|
+
*
|
|
11
|
+
* The shape is the shared `CodedFailure`; the class stays so the migration's
|
|
12
|
+
* own refusals keep their name and can still be told from anything else thrown
|
|
13
|
+
* through a phase.
|
|
10
14
|
*/
|
|
11
|
-
export class OpenSpecCommandError extends
|
|
12
|
-
|
|
13
|
-
constructor(code, message) {
|
|
14
|
-
super(message);
|
|
15
|
-
this.code = code;
|
|
16
|
-
this.name = "OpenSpecCommandError";
|
|
17
|
-
}
|
|
15
|
+
export class OpenSpecCommandError extends CodedFailure {
|
|
16
|
+
name = "OpenSpecCommandError";
|
|
18
17
|
}
|
|
19
18
|
//# sourceMappingURL=error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/error.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAE9D;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IAClC,IAAI,GAAG,sBAAsB,CAAC;CACjD","sourcesContent":["import { CodedFailure } from \"../../../core/envelope/json.js\";\n\n/**\n * The one refusal shape both verbs raise, in a module of its own.\n *\n * It cannot live beside the commander wiring that catches it: every phase of a\n * migration throws it, and a phase that imported the wiring to reach it would\n * put the command's registration on the evaluation path of the modules being\n * registered. So the error is the package's lowest leaf, and the wiring — which\n * maps it onto an exit code in `reportCommandError` — sits above everything.\n *\n * The shape is the shared `CodedFailure`; the class stays so the migration's\n * own refusals keep their name and can still be told from anything else thrown\n * through a phase.\n */\nexport class OpenSpecCommandError extends CodedFailure {\n override readonly name = \"OpenSpecCommandError\";\n}\n"]}
|
|
@@ -10,19 +10,11 @@
|
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
11
11
|
import { parse as parseYaml } from "yaml";
|
|
12
12
|
import { serviceIdProblem } from "../../../core/kernel/ids/service.js";
|
|
13
|
-
import { asRecord, dictionary } from "../../../core/kernel/records.js";
|
|
13
|
+
import { asRecord, dictionary, isStringArray } from "../../../core/kernel/records.js";
|
|
14
14
|
import {} from "../../../core/openspec/model/mapping.js";
|
|
15
15
|
import { message } from "../../../core/staging/commit.js";
|
|
16
16
|
import { OpenSpecCommandError } from "./error.js";
|
|
17
17
|
import { decodeSource } from "./read.js";
|
|
18
|
-
/**
|
|
19
|
-
* `Array.isArray` narrows an `unknown` to `any[]`, so the element check below it
|
|
20
|
-
* proved nothing to tsc and the elements had to be asserted back to `string` at
|
|
21
|
-
* the point of use. One predicate makes the narrowing carry the check.
|
|
22
|
-
*/
|
|
23
|
-
function isStringArray(value) {
|
|
24
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
25
|
-
}
|
|
26
18
|
function stringList(value, label) {
|
|
27
19
|
if (typeof value === "string")
|
|
28
20
|
return value.trim() === "" ? [] : [value.trim()];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/mapping.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The mapping document on the way in: YAML a human edited, graded into an\n * `OpenSpecMapping` or refused with the line to fix.\n *\n * Split from `./decisions.ts`, which is the same document on the way out — the\n * blank skeleton, the normalized record written beside a migration, and the\n * routing query. Reading is where every refusal lives and writing is where none\n * do, so the two halves have almost no vocabulary in common beyond the type.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { parse as parseYaml } from \"yaml\";\nimport { serviceIdProblem } from \"../../../core/kernel/ids/service.js\";\nimport { asRecord, dictionary } from \"../../../core/kernel/records.js\";\nimport { type OpenSpecMapping } from \"../../../core/openspec/model/mapping.js\";\nimport { message } from \"../../../core/staging/commit.js\";\nimport { OpenSpecCommandError } from \"./error.js\";\nimport { decodeSource } from \"./read.js\";\n\n/**\n * `Array.isArray` narrows an `unknown` to `any[]`, so the element check below it\n * proved nothing to tsc and the elements had to be asserted back to `string` at\n * the point of use. One predicate makes the narrowing carry the check.\n */\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\nfunction stringList(value: unknown, label: string): string[] {\n if (typeof value === \"string\") return value.trim() === \"\" ? [] : [value.trim()];\n if (!isStringArray(value)) {\n throw new OpenSpecCommandError(\"invalid-option\", `${label} must be a string or an array of service ids.`);\n }\n return [...new Set(value.map((item) => item.trim()).filter(Boolean))];\n}\n\n/**\n * The service ids a mapping may name, checked against the ONE grammar.\n *\n * This used to carry its own copy of the rule — the same alphabet regex, the\n * `.`/`..` tests, the trailing dot and the Windows device names — which is how\n * the two spellings drifted apart to begin with: for a while migrate was the\n * STRICTER of the two, so the primary authoring path accepted ids a migration\n * refused. ids.ts owns the grammar now, so a mapping can only ever name a\n * `services/<id>/` the authoring commands can still address.\n *\n * The refusal keeps migrate's own shape — `OpenSpecCommandError` with\n * `invalid-option`, not an `Issue` — and `serviceIdProblem`'s label carries the\n * mapping key, so the message still points at the line of the YAML to edit.\n */\nfunction serviceList(value: unknown, label: string): string[] {\n const services = stringList(value, label);\n for (const service of services) {\n const problem = serviceIdProblem(service, label);\n if (problem !== null) throw new OpenSpecCommandError(\"invalid-option\", problem);\n }\n return services;\n}\n\nexport async function readMapping(path: string): Promise<OpenSpecMapping> {\n let bytes: Buffer;\n try {\n bytes = await readFile(path);\n } catch (error) {\n throw new OpenSpecCommandError(\"unknown-target\", `Cannot read OpenSpec mapping ${path}: ${message(error)}`);\n }\n // Decoded rather than assumed: a mapping saved as UTF-16 parses as YAML with\n // no capabilities block, and the refusal would name a missing decision\n // instead of the encoding that hid it.\n const raw = decodeSource(bytes, path, \"OpenSpec mapping\");\n let parsed: unknown;\n try {\n parsed = parseYaml(raw);\n } catch (error) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} is invalid YAML: ${message(error)}`);\n }\n const document = asRecord(parsed);\n if (document === null || document.version !== 1) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must be a YAML mapping with version: 1.`);\n }\n const sourceNode = asRecord(document.source);\n const source = sourceNode !== null\n && typeof sourceNode.root === \"string\"\n && typeof sourceNode.inventoryDigest === \"string\"\n ? { root: sourceNode.root, inventoryDigest: sourceNode.inventoryDigest }\n : null;\n const capabilitiesNode = asRecord(document.capabilities);\n if (capabilitiesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must contain a capabilities mapping.`);\n }\n const capabilities = dictionary<OpenSpecMapping[\"capabilities\"][string]>();\n for (const [capability, value] of Object.entries(capabilitiesNode)) {\n const record = asRecord(value);\n const services = serviceList(record?.services ?? value, `capabilities.${capability}.services`);\n const requirementServices = dictionary<string[]>();\n if (record?.requirementServices !== undefined) {\n const allocations = asRecord(record.requirementServices);\n if (allocations === null) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `capabilities.${capability}.requirementServices must be a mapping.`,\n );\n }\n for (const [requirement, selected] of Object.entries(allocations)) {\n requirementServices[requirement] = serviceList(\n selected,\n `capabilities.${capability}.requirementServices.${requirement}`,\n );\n }\n }\n capabilities[capability] = { services, requirementServices };\n }\n\n const changesNode = asRecord(document.changes);\n if (changesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must contain a changes mapping.`);\n }\n const changes = dictionary<OpenSpecMapping[\"changes\"][string]>();\n for (const [change, value] of Object.entries(changesNode)) {\n const record = asRecord(value);\n if (record === null) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `changes.${change} must be a mapping with explicit feature and title fields.`,\n );\n }\n const featureValue = record.feature;\n if (featureValue !== null && typeof featureValue !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `changes.${change}.feature must be a string or null.`);\n }\n if (typeof record.title !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `changes.${change}.title must be a string.`);\n }\n changes[change] = {\n feature: typeof featureValue === \"string\" && featureValue.trim() !== \"\" ? featureValue.trim() : null,\n title: record.title,\n };\n }\n\n const renames = dictionary<string>();\n if (document.renames !== undefined) {\n const renamesNode = asRecord(document.renames);\n if (renamesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} renames must be a mapping.`);\n }\n for (const [key, value] of Object.entries(renamesNode)) {\n const record = asRecord(value);\n const requirementId = record !== null && \"requirementId\" in record ? record.requirementId : value;\n // A freshly generated skeleton deliberately uses null: it remains a\n // needsIdentity decision rather than becoming a malformed mapping.\n if (requirementId === null || requirementId === \"\") continue;\n if (typeof requirementId !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `renames.${key}.requirementId must be a string or null.`);\n }\n renames[key] = requirementId.trim();\n }\n }\n // Recorded as written. Naming the disposition union here only meant asserting\n // an unreviewed string into it: `mapping.invalid-artifact-disposition` is what\n // actually grades this value, and it has to see what the human typed to be\n // able to name it back to them.\n const artifacts = dictionary<string>();\n if (document.artifacts !== undefined) {\n const artifactsNode = asRecord(document.artifacts);\n if (artifactsNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} artifacts must be a mapping.`);\n }\n for (const [artifactPath, value] of Object.entries(artifactsNode)) {\n const record = asRecord(value);\n const disposition = record !== null && \"disposition\" in record ? record.disposition : value;\n if (disposition === null || disposition === \"\") continue;\n if (typeof disposition !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `artifacts.${artifactPath}.disposition must be a string or null.`);\n }\n artifacts[artifactPath] = disposition;\n }\n }\n return { source, capabilities, changes, renames, artifacts };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/mapping.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The mapping document on the way in: YAML a human edited, graded into an\n * `OpenSpecMapping` or refused with the line to fix.\n *\n * Split from `./decisions.ts`, which is the same document on the way out — the\n * blank skeleton, the normalized record written beside a migration, and the\n * routing query. Reading is where every refusal lives and writing is where none\n * do, so the two halves have almost no vocabulary in common beyond the type.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { parse as parseYaml } from \"yaml\";\nimport { serviceIdProblem } from \"../../../core/kernel/ids/service.js\";\nimport { asRecord, dictionary, isStringArray } from \"../../../core/kernel/records.js\";\nimport { type OpenSpecMapping } from \"../../../core/openspec/model/mapping.js\";\nimport { message } from \"../../../core/staging/commit.js\";\nimport { OpenSpecCommandError } from \"./error.js\";\nimport { decodeSource } from \"./read.js\";\n\nfunction stringList(value: unknown, label: string): string[] {\n if (typeof value === \"string\") return value.trim() === \"\" ? [] : [value.trim()];\n if (!isStringArray(value)) {\n throw new OpenSpecCommandError(\"invalid-option\", `${label} must be a string or an array of service ids.`);\n }\n return [...new Set(value.map((item) => item.trim()).filter(Boolean))];\n}\n\n/**\n * The service ids a mapping may name, checked against the ONE grammar.\n *\n * This used to carry its own copy of the rule — the same alphabet regex, the\n * `.`/`..` tests, the trailing dot and the Windows device names — which is how\n * the two spellings drifted apart to begin with: for a while migrate was the\n * STRICTER of the two, so the primary authoring path accepted ids a migration\n * refused. ids.ts owns the grammar now, so a mapping can only ever name a\n * `services/<id>/` the authoring commands can still address.\n *\n * The refusal keeps migrate's own shape — `OpenSpecCommandError` with\n * `invalid-option`, not an `Issue` — and `serviceIdProblem`'s label carries the\n * mapping key, so the message still points at the line of the YAML to edit.\n */\nfunction serviceList(value: unknown, label: string): string[] {\n const services = stringList(value, label);\n for (const service of services) {\n const problem = serviceIdProblem(service, label);\n if (problem !== null) throw new OpenSpecCommandError(\"invalid-option\", problem);\n }\n return services;\n}\n\nexport async function readMapping(path: string): Promise<OpenSpecMapping> {\n let bytes: Buffer;\n try {\n bytes = await readFile(path);\n } catch (error) {\n throw new OpenSpecCommandError(\"unknown-target\", `Cannot read OpenSpec mapping ${path}: ${message(error)}`);\n }\n // Decoded rather than assumed: a mapping saved as UTF-16 parses as YAML with\n // no capabilities block, and the refusal would name a missing decision\n // instead of the encoding that hid it.\n const raw = decodeSource(bytes, path, \"OpenSpec mapping\");\n let parsed: unknown;\n try {\n parsed = parseYaml(raw);\n } catch (error) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} is invalid YAML: ${message(error)}`);\n }\n const document = asRecord(parsed);\n if (document === null || document.version !== 1) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must be a YAML mapping with version: 1.`);\n }\n const sourceNode = asRecord(document.source);\n const source = sourceNode !== null\n && typeof sourceNode.root === \"string\"\n && typeof sourceNode.inventoryDigest === \"string\"\n ? { root: sourceNode.root, inventoryDigest: sourceNode.inventoryDigest }\n : null;\n const capabilitiesNode = asRecord(document.capabilities);\n if (capabilitiesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must contain a capabilities mapping.`);\n }\n const capabilities = dictionary<OpenSpecMapping[\"capabilities\"][string]>();\n for (const [capability, value] of Object.entries(capabilitiesNode)) {\n const record = asRecord(value);\n const services = serviceList(record?.services ?? value, `capabilities.${capability}.services`);\n const requirementServices = dictionary<string[]>();\n if (record?.requirementServices !== undefined) {\n const allocations = asRecord(record.requirementServices);\n if (allocations === null) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `capabilities.${capability}.requirementServices must be a mapping.`,\n );\n }\n for (const [requirement, selected] of Object.entries(allocations)) {\n requirementServices[requirement] = serviceList(\n selected,\n `capabilities.${capability}.requirementServices.${requirement}`,\n );\n }\n }\n capabilities[capability] = { services, requirementServices };\n }\n\n const changesNode = asRecord(document.changes);\n if (changesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must contain a changes mapping.`);\n }\n const changes = dictionary<OpenSpecMapping[\"changes\"][string]>();\n for (const [change, value] of Object.entries(changesNode)) {\n const record = asRecord(value);\n if (record === null) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `changes.${change} must be a mapping with explicit feature and title fields.`,\n );\n }\n const featureValue = record.feature;\n if (featureValue !== null && typeof featureValue !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `changes.${change}.feature must be a string or null.`);\n }\n if (typeof record.title !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `changes.${change}.title must be a string.`);\n }\n changes[change] = {\n feature: typeof featureValue === \"string\" && featureValue.trim() !== \"\" ? featureValue.trim() : null,\n title: record.title,\n };\n }\n\n const renames = dictionary<string>();\n if (document.renames !== undefined) {\n const renamesNode = asRecord(document.renames);\n if (renamesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} renames must be a mapping.`);\n }\n for (const [key, value] of Object.entries(renamesNode)) {\n const record = asRecord(value);\n const requirementId = record !== null && \"requirementId\" in record ? record.requirementId : value;\n // A freshly generated skeleton deliberately uses null: it remains a\n // needsIdentity decision rather than becoming a malformed mapping.\n if (requirementId === null || requirementId === \"\") continue;\n if (typeof requirementId !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `renames.${key}.requirementId must be a string or null.`);\n }\n renames[key] = requirementId.trim();\n }\n }\n // Recorded as written. Naming the disposition union here only meant asserting\n // an unreviewed string into it: `mapping.invalid-artifact-disposition` is what\n // actually grades this value, and it has to see what the human typed to be\n // able to name it back to them.\n const artifacts = dictionary<string>();\n if (document.artifacts !== undefined) {\n const artifactsNode = asRecord(document.artifacts);\n if (artifactsNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} artifacts must be a mapping.`);\n }\n for (const [artifactPath, value] of Object.entries(artifactsNode)) {\n const record = asRecord(value);\n const disposition = record !== null && \"disposition\" in record ? record.disposition : value;\n if (disposition === null || disposition === \"\") continue;\n if (typeof disposition !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `artifacts.${artifactPath}.disposition must be a string or null.`);\n }\n artifacts[artifactPath] = disposition;\n }\n }\n return { source, capabilities, changes, renames, artifacts };\n}\n"]}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* The path arithmetic every write decision in this command rests on.
|
|
3
3
|
*
|
|
4
4
|
* Three rules, kept together because they answer one question in two
|
|
5
|
-
* directions: is a path inside a tree it must stay out of (
|
|
5
|
+
* directions: is a path inside a tree it must stay out of (kernel's
|
|
6
|
+
* `isPathInside`, which this module used to restate as `contains`), what
|
|
6
7
|
* tree is it really in once symlinks are resolved (`canonicalForCreate`), and
|
|
7
8
|
* does a path claimed by the OpenSpec source stay inside the directory it was
|
|
8
9
|
* listed under (`safeArtifactRelative`). `materialize/stage.ts` builds the
|
|
@@ -11,12 +12,8 @@
|
|
|
11
12
|
*/
|
|
12
13
|
import { existsSync } from "node:fs";
|
|
13
14
|
import { realpath } from "node:fs/promises";
|
|
14
|
-
import { basename, dirname,
|
|
15
|
+
import { basename, dirname, resolve } from "node:path";
|
|
15
16
|
import { OpenSpecCommandError } from "./error.js";
|
|
16
|
-
export function contains(parent, candidate) {
|
|
17
|
-
const rel = relative(parent, candidate);
|
|
18
|
-
return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
|
|
19
|
-
}
|
|
20
17
|
/** Resolve symlinks in the nearest existing ancestor of a not-yet-created path. */
|
|
21
18
|
export async function canonicalForCreate(path) {
|
|
22
19
|
let cursor = path;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY;IACnD,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,MAAM,KAAK,MAAM;YAAE,MAAM;QAC7B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACjC,MAAM,GAAG,MAAM,CAAC;IAClB,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,YAAoB,EAAE,MAAc,EAAE,KAAa;IACtF,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,oBAAoB,CAC5B,gBAAgB,EAChB,GAAG,KAAK,oDAAoD,YAAY,GAAG,CAC5E,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC;QAC/G,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,kDAAkD,YAAY,GAAG,CAAC,CAAC;IACtH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["/**\n * The path arithmetic every write decision in this command rests on.\n *\n * Three rules, kept together because they answer one question in two\n * directions: is a path inside a tree it must stay out of (kernel's\n * `isPathInside`, which this module used to restate as `contains`), what\n * tree is it really in once symlinks are resolved (`canonicalForCreate`), and\n * does a path claimed by the OpenSpec source stay inside the directory it was\n * listed under (`safeArtifactRelative`). `materialize/stage.ts` builds the\n * staging refusals out of the first two; `openspec/decisions.ts` uses them to\n * keep a mapping skeleton out of the workspace it describes.\n */\nimport { existsSync } from \"node:fs\";\nimport { realpath } from \"node:fs/promises\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { OpenSpecCommandError } from \"./error.js\";\n\n/** Resolve symlinks in the nearest existing ancestor of a not-yet-created path. */\nexport async function canonicalForCreate(path: string): Promise<string> {\n let cursor = path;\n const suffix: string[] = [];\n while (!existsSync(cursor)) {\n const parent = dirname(cursor);\n if (parent === cursor) break;\n suffix.unshift(basename(cursor));\n cursor = parent;\n }\n return resolve(await realpath(cursor), ...suffix);\n}\n\nexport function safeArtifactRelative(artifactPath: string, prefix: string, owner: string): string[] {\n if (!artifactPath.startsWith(prefix)) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `${owner} contains an artifact outside its own directory: ${artifactPath}.`,\n );\n }\n const segments = artifactPath.slice(prefix.length).split(\"/\");\n if (segments.length === 0 || segments.some((segment) => segment === \"\" || segment === \".\" || segment === \"..\")) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec artifact has an unsafe relative path: ${artifactPath}.`);\n }\n return segments;\n}\n"]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The capability delta `loam new --capability` scaffolds, and the id shape it
|
|
3
|
+
* offers.
|
|
4
|
+
*
|
|
5
|
+
* Split out of `./templates.ts` along the document-kind seam: the other five
|
|
6
|
+
* templates scaffold a feature's own files and one service's deltas, while this
|
|
7
|
+
* one is a delta against a fleet-level business document with its own altitude
|
|
8
|
+
* rules and its own id grammar (`REQUIREMENT_ID_RE`, which nothing else here
|
|
9
|
+
* reads). The sentinels stay single-sourced in `core/coherence/authoring/
|
|
10
|
+
* sentinels.ts`, so the move changes no string the placeholder gate matches on.
|
|
11
|
+
*/
|
|
12
|
+
import { REQUIREMENT_ID_RE } from "../../core/document/spec.js";
|
|
13
|
+
import { CAPABILITY_REQUIREMENT_SENTINEL, CAPABILITY_SHALL_SENTINEL, GIVEN_SENTINEL, SCENARIO_SENTINEL, THEN_SENTINEL, WHEN_SENTINEL, } from "../../core/coherence/authoring/sentinels.js";
|
|
14
|
+
/**
|
|
15
|
+
* The BUSINESS axis, scaffolded by `--capability`: a delta against the living
|
|
16
|
+
* `capabilities/<id>/spec.md`.
|
|
17
|
+
*
|
|
18
|
+
* Same idiom as the two spec templates in `./templates.ts`, for the same reason — the
|
|
19
|
+
* example lives INSIDE an HTML comment, indented past the line-anchored
|
|
20
|
+
* `## ADDED Requirements` / `### Requirement:` patterns core/document/parse.ts
|
|
21
|
+
* matches on — so the scaffold declares nothing until a person copies the block
|
|
22
|
+
* out. That is what lets a freshly scaffolded feature validate clean while
|
|
23
|
+
* still being refused by `loam archive` the moment the block is copied out and
|
|
24
|
+
* left unedited (`scaffold.placeholder`, which reads
|
|
25
|
+
* `features/<FEAT>/capabilities/` as well as `features/<FEAT>/specs/`).
|
|
26
|
+
*
|
|
27
|
+
* WHAT THE COMMENT TEACHES IS THE ALTITUDE, because that is the mistake this
|
|
28
|
+
* document invites. A capability requirement is a promise a customer could
|
|
29
|
+
* check; the four service-scoped lines (`Operations:`, `Covers:`, `Publishes:`,
|
|
30
|
+
* `Consumes:`) and the axis's own two (`Capability:`, `Realizes:`) are all
|
|
31
|
+
* ERRORS here — `capability.requirement-service-scoped` and
|
|
32
|
+
* `capability.requirement-inert-join` — and every one of them parses, so an
|
|
33
|
+
* author who is not told writes one and finds out at archive. `Requirement-ID:`
|
|
34
|
+
* is spelled as mandatory for the same reason: a capability document outlives
|
|
35
|
+
* every service that realizes it, so identity by heading is refused
|
|
36
|
+
* (`capability.requirement-unidentified`).
|
|
37
|
+
*
|
|
38
|
+
* No nested HTML comments, exactly as `specTemplate` (`./templates.ts`) states: an inner `-->`
|
|
39
|
+
* would end the outer comment early and re-expose the example to the parser.
|
|
40
|
+
*/
|
|
41
|
+
export function capabilityDeltaTemplate(featureId, capability) {
|
|
42
|
+
return `# ${capability} — capability delta for ${featureId}
|
|
43
|
+
|
|
44
|
+
<!-- The business axis. This is a DELTA against the living
|
|
45
|
+
capabilities/${capability}/spec.md, not the document itself: sections are
|
|
46
|
+
ADDED / MODIFIED / REMOVED, delete the ones you do not need, and a MODIFIED
|
|
47
|
+
requirement carries its full new text rather than a diff. If the fleet has
|
|
48
|
+
no capabilities/${capability}/spec.md yet, this feature's archive creates it.
|
|
49
|
+
|
|
50
|
+
WHAT BELONGS HERE is a promise somebody outside the fleet could check —
|
|
51
|
+
'a refund reaches the customer's card within five days'. What does NOT is
|
|
52
|
+
the mechanism: \`Operations:\`, \`Covers:\`, \`Publishes:\` and \`Consumes:\`
|
|
53
|
+
all resolve against ONE service's own contract or model, so a requirement
|
|
54
|
+
carrying one is a service requirement filed at the wrong altitude and
|
|
55
|
+
\`loam validate\` says so. \`Capability:\` and \`Realizes:\` are refused here
|
|
56
|
+
too — they are the joins written on the SERVICE requirement that keeps this
|
|
57
|
+
promise, one directory over in specs/<svc>/spec.md, as
|
|
58
|
+
\`Realizes: ${capability}#<Requirement-ID>\`.
|
|
59
|
+
|
|
60
|
+
\`Requirement-ID:\` is mandatory on every requirement below. A capability
|
|
61
|
+
document outlives the services that realize it, so its requirements are
|
|
62
|
+
addressed by a stable id rather than by their heading — rewording a heading
|
|
63
|
+
would otherwise be a removal and an addition, and every \`Realizes:\` line
|
|
64
|
+
pointed at it would break in silence.
|
|
65
|
+
|
|
66
|
+
A MODIFIED or REMOVED requirement also needs a \`Based-On:\` pin quoting the
|
|
67
|
+
living text it rewrites, so two features touching one capability collide
|
|
68
|
+
loudly instead of overwriting each other. Run \`loam rebase ${featureId}\`
|
|
69
|
+
and the pins are written for you.
|
|
70
|
+
|
|
71
|
+
Copy the block below out of this comment, unindent it, and replace the TODO
|
|
72
|
+
and every <angle-bracket> fill-in — \`loam archive\` refuses the scaffold's
|
|
73
|
+
own wording (\`scaffold.placeholder\`):
|
|
74
|
+
|
|
75
|
+
## ADDED Requirements
|
|
76
|
+
|
|
77
|
+
### Requirement: ${CAPABILITY_REQUIREMENT_SENTINEL}
|
|
78
|
+
Requirement-ID: ${idHint(capability)}
|
|
79
|
+
|
|
80
|
+
The fleet SHALL ${CAPABILITY_SHALL_SENTINEL}.
|
|
81
|
+
|
|
82
|
+
#### Scenario: ${SCENARIO_SENTINEL}
|
|
83
|
+
- **Given** ${GIVEN_SENTINEL}
|
|
84
|
+
- **When** ${WHEN_SENTINEL}
|
|
85
|
+
- **Then** ${THEN_SENTINEL}
|
|
86
|
+
-->
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A plausible `Requirement-ID:` for a capability, offered as a shape rather than
|
|
91
|
+
* a name: `payments/refunds` -> `PAYMENTS-REFUNDS-1`.
|
|
92
|
+
*
|
|
93
|
+
* Deliberately NOT the feature id. A service requirement's scaffolded id is
|
|
94
|
+
* `<FEAT>.<svc>.requirement` because that requirement is created by, and lives
|
|
95
|
+
* and dies with, one feature; a capability requirement outlives every feature
|
|
96
|
+
* that touches it, so an id carrying the id of the feature that happened to
|
|
97
|
+
* introduce it is exactly the identity-by-accident this axis refuses. The
|
|
98
|
+
* suffix is `-1` and not a count of anything: it is a placeholder digit inside
|
|
99
|
+
* a placeholder id, and the author is being asked to name the promise.
|
|
100
|
+
*
|
|
101
|
+
* CHECKED AGAINST THE REAL GRAMMAR, never assumed to satisfy it. A capability id
|
|
102
|
+
* may begin with a digit (`3ds`, `2fa`, `1099-filing` — `dirNameHazard` allows an
|
|
103
|
+
* alphanumeric head) while `REQUIREMENT_ID_RE` demands a LETTER, so the obvious
|
|
104
|
+
* slug hands the author `3DS-1` and `loam validate` then refuses it with
|
|
105
|
+
* `delta.requirement-id-invalid` — the author refused for using the shape the
|
|
106
|
+
* scaffold offered. Length is the same class of failure at 128 characters. The
|
|
107
|
+
* fallback prefixes rather than truncates, because a truncated id is a plausible
|
|
108
|
+
* name that means something else.
|
|
109
|
+
*/
|
|
110
|
+
function idHint(capability) {
|
|
111
|
+
const slug = capability.toUpperCase().replace(/[^A-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
112
|
+
const candidate = `${slug}-1`;
|
|
113
|
+
if (REQUIREMENT_ID_RE.test(candidate))
|
|
114
|
+
return candidate;
|
|
115
|
+
const prefixed = `CAP-${candidate}`;
|
|
116
|
+
return REQUIREMENT_ID_RE.test(prefixed) ? prefixed : "CAP-1";
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=capability-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-template.js","sourceRoot":"","sources":["../../../src/commands/new/capability-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EACL,+BAA+B,EAC/B,yBAAyB,EACzB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,aAAa,GACd,MAAM,6CAA6C,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAiB,EAAE,UAAkB;IAC3E,OAAO,KAAK,UAAU,2BAA2B,SAAS;;;oBAGxC,UAAU;;;uBAGP,UAAU;;;;;;;;;;mBAUd,UAAU;;;;;;;;;;mEAUsC,SAAS;;;;;;;;;uBASrD,+BAA+B;sBAChC,MAAM,CAAC,UAAU,CAAC;;sBAElB,yBAAyB;;qBAE1B,iBAAiB;kBACpB,cAAc;iBACf,aAAa;iBACb,aAAa;;CAE7B,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAS,MAAM,CAAC,UAAkB;IAChC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1F,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,CAAC;IAC9B,IAAI,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACxD,MAAM,QAAQ,GAAG,OAAO,SAAS,EAAE,CAAC;IACpC,OAAO,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;AAC/D,CAAC","sourcesContent":["/**\n * The capability delta `loam new --capability` scaffolds, and the id shape it\n * offers.\n *\n * Split out of `./templates.ts` along the document-kind seam: the other five\n * templates scaffold a feature's own files and one service's deltas, while this\n * one is a delta against a fleet-level business document with its own altitude\n * rules and its own id grammar (`REQUIREMENT_ID_RE`, which nothing else here\n * reads). The sentinels stay single-sourced in `core/coherence/authoring/\n * sentinels.ts`, so the move changes no string the placeholder gate matches on.\n */\nimport { REQUIREMENT_ID_RE } from \"../../core/document/spec.js\";\nimport {\n CAPABILITY_REQUIREMENT_SENTINEL,\n CAPABILITY_SHALL_SENTINEL,\n GIVEN_SENTINEL,\n SCENARIO_SENTINEL,\n THEN_SENTINEL,\n WHEN_SENTINEL,\n} from \"../../core/coherence/authoring/sentinels.js\";\n\n/**\n * The BUSINESS axis, scaffolded by `--capability`: a delta against the living\n * `capabilities/<id>/spec.md`.\n *\n * Same idiom as the two spec templates in `./templates.ts`, for the same reason — the\n * example lives INSIDE an HTML comment, indented past the line-anchored\n * `## ADDED Requirements` / `### Requirement:` patterns core/document/parse.ts\n * matches on — so the scaffold declares nothing until a person copies the block\n * out. That is what lets a freshly scaffolded feature validate clean while\n * still being refused by `loam archive` the moment the block is copied out and\n * left unedited (`scaffold.placeholder`, which reads\n * `features/<FEAT>/capabilities/` as well as `features/<FEAT>/specs/`).\n *\n * WHAT THE COMMENT TEACHES IS THE ALTITUDE, because that is the mistake this\n * document invites. A capability requirement is a promise a customer could\n * check; the four service-scoped lines (`Operations:`, `Covers:`, `Publishes:`,\n * `Consumes:`) and the axis's own two (`Capability:`, `Realizes:`) are all\n * ERRORS here — `capability.requirement-service-scoped` and\n * `capability.requirement-inert-join` — and every one of them parses, so an\n * author who is not told writes one and finds out at archive. `Requirement-ID:`\n * is spelled as mandatory for the same reason: a capability document outlives\n * every service that realizes it, so identity by heading is refused\n * (`capability.requirement-unidentified`).\n *\n * No nested HTML comments, exactly as `specTemplate` (`./templates.ts`) states: an inner `-->`\n * would end the outer comment early and re-expose the example to the parser.\n */\nexport function capabilityDeltaTemplate(featureId: string, capability: string): string {\n return `# ${capability} — capability delta for ${featureId}\n\n<!-- The business axis. This is a DELTA against the living\n capabilities/${capability}/spec.md, not the document itself: sections are\n ADDED / MODIFIED / REMOVED, delete the ones you do not need, and a MODIFIED\n requirement carries its full new text rather than a diff. If the fleet has\n no capabilities/${capability}/spec.md yet, this feature's archive creates it.\n\n WHAT BELONGS HERE is a promise somebody outside the fleet could check —\n 'a refund reaches the customer's card within five days'. What does NOT is\n the mechanism: \\`Operations:\\`, \\`Covers:\\`, \\`Publishes:\\` and \\`Consumes:\\`\n all resolve against ONE service's own contract or model, so a requirement\n carrying one is a service requirement filed at the wrong altitude and\n \\`loam validate\\` says so. \\`Capability:\\` and \\`Realizes:\\` are refused here\n too — they are the joins written on the SERVICE requirement that keeps this\n promise, one directory over in specs/<svc>/spec.md, as\n \\`Realizes: ${capability}#<Requirement-ID>\\`.\n\n \\`Requirement-ID:\\` is mandatory on every requirement below. A capability\n document outlives the services that realize it, so its requirements are\n addressed by a stable id rather than by their heading — rewording a heading\n would otherwise be a removal and an addition, and every \\`Realizes:\\` line\n pointed at it would break in silence.\n\n A MODIFIED or REMOVED requirement also needs a \\`Based-On:\\` pin quoting the\n living text it rewrites, so two features touching one capability collide\n loudly instead of overwriting each other. Run \\`loam rebase ${featureId}\\`\n and the pins are written for you.\n\n Copy the block below out of this comment, unindent it, and replace the TODO\n and every <angle-bracket> fill-in — \\`loam archive\\` refuses the scaffold's\n own wording (\\`scaffold.placeholder\\`):\n\n ## ADDED Requirements\n\n ### Requirement: ${CAPABILITY_REQUIREMENT_SENTINEL}\n Requirement-ID: ${idHint(capability)}\n\n The fleet SHALL ${CAPABILITY_SHALL_SENTINEL}.\n\n #### Scenario: ${SCENARIO_SENTINEL}\n - **Given** ${GIVEN_SENTINEL}\n - **When** ${WHEN_SENTINEL}\n - **Then** ${THEN_SENTINEL}\n-->\n`;\n}\n\n/**\n * A plausible `Requirement-ID:` for a capability, offered as a shape rather than\n * a name: `payments/refunds` -> `PAYMENTS-REFUNDS-1`.\n *\n * Deliberately NOT the feature id. A service requirement's scaffolded id is\n * `<FEAT>.<svc>.requirement` because that requirement is created by, and lives\n * and dies with, one feature; a capability requirement outlives every feature\n * that touches it, so an id carrying the id of the feature that happened to\n * introduce it is exactly the identity-by-accident this axis refuses. The\n * suffix is `-1` and not a count of anything: it is a placeholder digit inside\n * a placeholder id, and the author is being asked to name the promise.\n *\n * CHECKED AGAINST THE REAL GRAMMAR, never assumed to satisfy it. A capability id\n * may begin with a digit (`3ds`, `2fa`, `1099-filing` — `dirNameHazard` allows an\n * alphanumeric head) while `REQUIREMENT_ID_RE` demands a LETTER, so the obvious\n * slug hands the author `3DS-1` and `loam validate` then refuses it with\n * `delta.requirement-id-invalid` — the author refused for using the shape the\n * scaffold offered. Length is the same class of failure at 128 characters. The\n * fallback prefixes rather than truncates, because a truncated id is a plausible\n * name that means something else.\n */\nfunction idHint(capability: string): string {\n const slug = capability.toUpperCase().replace(/[^A-Z0-9]+/g, \"-\").replace(/^-+|-+$/g, \"\");\n const candidate = `${slug}-1`;\n if (REQUIREMENT_ID_RE.test(candidate)) return candidate;\n const prefixed = `CAP-${candidate}`;\n return REQUIREMENT_ID_RE.test(prefixed) ? prefixed : \"CAP-1\";\n}\n"]}
|
|
@@ -11,34 +11,20 @@ import {} from "../../core/kernel/ids/dirs.js";
|
|
|
11
11
|
import { resolveFeature } from "../../core/repo/repo.js";
|
|
12
12
|
import { DocsRepoUnavailableError } from "../../core/repo/state.js";
|
|
13
13
|
import { stageWrites } from "../../core/staging/commit.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
14
|
+
import {} from "../../core/staging/interrupted.js";
|
|
15
|
+
import { LOCK_WAIT_MS } from "../../core/staging/lock.js";
|
|
16
|
+
import { openCommitWindow } from "../../core/staging/recovery/recover.js";
|
|
17
17
|
import { commitStaged } from "../../core/staging/txn/transaction.js";
|
|
18
18
|
import {} from "../../core/staging/writes.js";
|
|
19
19
|
export async function commitScaffold(docsDir, featureId, writes) {
|
|
20
20
|
// The commit window holds the docs lock: two `new`s for one id serialise
|
|
21
21
|
// into a stable already-exists, and a predecessor's interrupted commit is
|
|
22
22
|
// rolled forward (or refused) before this one stages anything.
|
|
23
|
-
|
|
23
|
+
const opened = await openCommitWindow(docsDir, LOCK_WAIT_MS);
|
|
24
|
+
if (!opened.ok)
|
|
25
|
+
return opened;
|
|
26
|
+
const { recovered } = opened;
|
|
24
27
|
try {
|
|
25
|
-
releaseLock = await acquireDocsLockWaiting(docsDir, LOCK_WAIT_MS);
|
|
26
|
-
}
|
|
27
|
-
catch (err) {
|
|
28
|
-
if (err instanceof DocsBusyError)
|
|
29
|
-
return { ok: false, code: "docs-busy", message: err.message };
|
|
30
|
-
throw err;
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
let recovered;
|
|
34
|
-
try {
|
|
35
|
-
recovered = await recoverInterruptedCommit(docsDir);
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
if (!(err instanceof InterruptedCommitError))
|
|
39
|
-
throw err;
|
|
40
|
-
return { ok: false, code: "commit-interrupted", message: err.message };
|
|
41
|
-
}
|
|
42
28
|
// The one existence check, UNDER the lock and AFTER recovery: two racing
|
|
43
29
|
// runs serialise on it, and a recovered half-scaffold has been completed
|
|
44
30
|
// by the recovery above, so this refusal is truthful.
|
|
@@ -70,7 +56,7 @@ export async function commitScaffold(docsDir, featureId, writes) {
|
|
|
70
56
|
return { ok: true, recovered };
|
|
71
57
|
}
|
|
72
58
|
finally {
|
|
73
|
-
await
|
|
59
|
+
await opened.release();
|
|
74
60
|
}
|
|
75
61
|
}
|
|
76
62
|
//# sourceMappingURL=commit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/commands/new/commit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAqB,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAgB,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAuB,
|
|
1
|
+
{"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/commands/new/commit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAqB,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAgB,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAuB,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAqB,MAAM,8BAA8B,CAAC;AAmBjE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAgB,EAChB,SAAiB,EACjB,MAAsB;IAEtB,yEAAyE;IACzE,0EAA0E;IAC1E,+DAA+D;IAC/D,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC7D,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC;IAC9B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAC7B,IAAI,CAAC;QACH,yEAAyE;QACzE,yEAAyE;QACzE,sDAAsD;QACtD,IAAI,QAA6B,CAAC;QAClC,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,uEAAuE;YACvE,uEAAuE;YACvE,iCAAiC;YACjC,IAAI,CAAC,CAAC,GAAG,YAAY,wBAAwB,CAAC;gBAAE,MAAM,GAAG,CAAC;YAC1D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QACtC,CAAC;QACD,IAAI,QAAQ;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QAChF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,MAAM,YAAY,CAClC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,SAAS,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EACpF,MAAM,EACN,YAAY,CACb,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAClB,wEAAwE;YACxE,wEAAwE;YACxE,iCAAiC;YACjC,OAAO,SAAS,CAAC,KAAK;gBACpB,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE;gBAClE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;QACtE,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC;AACH,CAAC","sourcesContent":["/**\n * The scaffold's commit window: lock, recover, the one existence check, the\n * exclusive-create transaction. Split from new.ts on the same seam vouch's\n * commitStamp and gherkin's commitEmission sit on — everything in here holds\n * the docs lock, nothing outside it does — and, as there, the split keeps the\n * window's extent visible in the code shape. This module prints nothing; the\n * caller renders every arm.\n */\nimport { type FeatureEntry } from \"../../core/repo/entries.js\";\nimport { type DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { resolveFeature } from \"../../core/repo/repo.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { stageWrites } from \"../../core/staging/commit.js\";\nimport { type CommitRecovery } from \"../../core/staging/interrupted.js\";\nimport { LOCK_WAIT_MS } from \"../../core/staging/lock.js\";\nimport { openCommitWindow } from \"../../core/staging/recovery/recover.js\";\nimport { commitStaged } from \"../../core/staging/txn/transaction.js\";\nimport { type PlannedWrite } from \"../../core/staging/writes.js\";\n\nexport type ScaffoldCommit =\n | { ok: true; recovered: CommitRecovery | null }\n | {\n ok: false;\n code: \"docs-busy\" | \"commit-interrupted\" | \"merge-failed\" | \"rollback-incomplete\";\n message: string;\n }\n /** The feature exists — found by the under-lock check, or by losing the create race. */\n | { ok: false; code: \"already-exists\"; existing: FeatureEntry | null; recovered: CommitRecovery | null }\n /**\n * The docs repo went away between the command's gate and the locked read.\n * Deliberately NOT a `code:` arm: the caller renders it through the shared\n * docs-repo reporter, and the codes-drift collector reads every `code:`\n * literal as a stable emission — which this discriminant is not.\n */\n | { ok: false; repoGone: DocsRepoUnavailableError };\n\nexport async function commitScaffold(\n docsDir: DocsDir,\n featureId: string,\n writes: PlannedWrite[],\n): Promise<ScaffoldCommit> {\n // The commit window holds the docs lock: two `new`s for one id serialise\n // into a stable already-exists, and a predecessor's interrupted commit is\n // rolled forward (or refused) before this one stages anything.\n const opened = await openCommitWindow(docsDir, LOCK_WAIT_MS);\n if (!opened.ok) return opened;\n const { recovered } = opened;\n try {\n // The one existence check, UNDER the lock and AFTER recovery: two racing\n // runs serialise on it, and a recovered half-scaffold has been completed\n // by the recovery above, so this refusal is truthful.\n let existing: FeatureEntry | null;\n try {\n existing = await resolveFeature(docsDir, featureId, \"include\");\n } catch (err) {\n // The command's gate already refused both broken states, so reaching\n // here means the docs repo went away between that check and this read.\n // Same breach, same two codes — the alternative is a stack trace under\n // `internal` that names neither.\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n return { ok: false, repoGone: err };\n }\n if (existing) return { ok: false, code: \"already-exists\", existing, recovered };\n const staged = await stageWrites(writes);\n const committed = await commitStaged(\n { root: docsDir, command: \"new\", rerun: `loam new ${featureId}`, target: featureId },\n staged,\n \"scaffolded\",\n );\n if (!committed.ok) {\n // A lost exclusive-create race IS the feature existing: the other `new`\n // won, and this refusal is the same stable answer the check above gives\n // when it sees the winner first.\n return committed.raced\n ? { ok: false, code: \"already-exists\", existing: null, recovered }\n : { ok: false, code: committed.code, message: committed.message };\n }\n return { ok: true, recovered };\n } finally {\n await opened.release();\n }\n}\n"]}
|