@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.2
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 +2635 -86
- package/COMPARISON.md +80 -63
- package/CONTRIBUTING.md +84 -0
- package/MIGRATING-from-OpenSpec.md +213 -54
- package/QUICKSTART.md +125 -0
- package/README.md +450 -150
- package/ROADMAP.md +599 -0
- package/SCHEMA.md +2355 -256
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +701 -0
- package/dist/cli.js +113 -19
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +209 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +268 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +77 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +159 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +323 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +180 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +111 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +237 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +27 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +252 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/context/context.js +99 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +283 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +279 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +205 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +9 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +93 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +151 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +29 -24
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +115 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +54 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +146 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +246 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +149 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +28 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +146 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +72 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +65 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +157 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +343 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +301 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/views.js +105 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +183 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +34 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +170 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +19 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/commit.js +76 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/new.js +283 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +355 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +203 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +275 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +192 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +133 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +172 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +178 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/marks.js +24 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/{show.js → show/service.js} +63 -152
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +77 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/json.js +45 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +157 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +119 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +189 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +265 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +115 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +101 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +103 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +94 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +70 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +160 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +67 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +204 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +296 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +55 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +254 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +76 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/checks/vocabulary.js +31 -0
- package/dist/commands/validate/checks/vocabulary.js.map +1 -0
- package/dist/commands/validate/feature.js +216 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +69 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +353 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +52 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +270 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +63 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/steps.js +197 -0
- package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +40 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +59 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +92 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/report.js +269 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/service/api.js +338 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +219 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +131 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/service.js +251 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +251 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +113 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/validate.js +338 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +182 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +72 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +241 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +226 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +223 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +246 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +217 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +271 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +142 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +285 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +125 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +123 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +176 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/artifacts.js +220 -0
- package/dist/core/agent/agents-md/artifacts.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +324 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/cycle.js +165 -0
- package/dist/core/agent/agents-md/cycle.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +34 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +41 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +62 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +32 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +60 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +246 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
- package/dist/core/agent/agents-md/spine.js +40 -0
- package/dist/core/agent/agents-md/spine.js.map +1 -0
- package/dist/core/agent/agents-md.js +170 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +50 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +259 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +103 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +150 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +228 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +133 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +337 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +262 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +105 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +105 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +209 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +65 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +188 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +16 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +297 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +110 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +141 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +91 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +33 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +308 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +215 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +107 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/{asyncapi.js → asyncapi/read.js} +95 -109
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +225 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +250 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +194 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/targets.js +226 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +48 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +174 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/likec4.js +90 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +158 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +57 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +75 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/project/architecture.js +45 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/documents.js +58 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +187 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/project/staged.js +220 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/service.js +159 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/resolve/steps.js +144 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +109 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/source-mask.js +82 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +227 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +101 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +10 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +139 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +36 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +269 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +194 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/workspace.js +174 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +50 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +342 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +181 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +143 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +199 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +126 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +277 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +177 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +74 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/coherence.js +287 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/declared.js +269 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +206 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +204 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +166 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +122 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/merged.js +40 -0
- package/dist/core/coherence/events/merged.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +155 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +296 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/deployment/delta.js +188 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +148 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +209 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/current-state.js +96 -0
- package/dist/core/diff/current-state.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +188 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/semantic.js +243 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/diff/victims.js +247 -0
- package/dist/core/diff/victims.js.map +1 -0
- package/dist/core/docs.js +7 -64
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +222 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +135 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +275 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +77 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +263 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +221 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +241 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +79 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +175 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +175 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +181 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms.js +144 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/{explore.js → explore/explore.js} +50 -101
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +120 -78
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +201 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +205 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +113 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +234 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +106 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +86 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +68 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +104 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +236 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +111 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +72 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +314 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +166 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +112 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +175 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/depth.js +122 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +189 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +44 -106
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +234 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +26 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +208 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +256 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +128 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +221 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +76 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +65 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +235 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +200 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +260 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +133 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +137 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +76 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/provenance/findings.js +208 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +225 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +182 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +263 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +232 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +210 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +277 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +248 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +52 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +64 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/views.js +172 -0
- package/dist/core/repo/tree/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +234 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +99 -2
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +131 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +241 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +206 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +98 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +126 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot.js +202 -0
- package/dist/core/staging/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +171 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +49 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +102 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +196 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +207 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +150 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +197 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +207 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +201 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/usecases/capability.js +169 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +116 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +103 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +72 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +137 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/operations.js +111 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +173 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +150 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +201 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/file.js +257 -0
- package/dist/core/verify/file.js.map +1 -0
- package/dist/core/verify/pins/lint.js +157 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/record.js +142 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +89 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +57 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +58 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +145 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/{report.js → vocabulary/report.js} +30 -2
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +189 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +280 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/subsystems.likec4 +11 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +9 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +109 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +127 -0
- package/examples/docs/services/order-service/asyncapi.yaml +108 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +138 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +73 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +67 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
- package/examples/docs/services/platform/notification-service/health.yaml +35 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -295
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -596
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -489
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1132
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
- /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
- /package/dist/core/{records.js → kernel/records.js} +0 -0
package/dist/core/repo.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/core/repo.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Read model of the docs repo — the single place that knows its layout.\n *\n * Enumeration goes by the filesystem, and only by the filesystem: files are the\n * source of truth, so `services/` IS the list of services and there is no\n * manifest that can disagree with it. Every command that needs \"which services\n * exist\", \"which features are in flight\" or \"where does artifact X live\" asks\n * here, so the layout is spelled exactly once.\n */\nimport { existsSync, statSync, type Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { listField, readFrontmatter, stringField } from \"./frontmatter.js\";\nimport type { FleetContext } from \"./fleet-context.js\";\nimport { serviceIdProblem } from \"./ids.js\";\nimport type { Finding } from \"./report.js\";\n\n/** Directory under features/ holding shipped features. Never a feature itself. */\nconst ARCHIVE_DIR = \"archive\";\n\nexport interface ServiceArtifacts {\n model: boolean;\n spec: boolean;\n openapi: boolean;\n /**\n * The async contract (AsyncAPI 3). Presence-tracked only, like `runbook` and\n * `health` — deliberately NOT part of the maturity ladder (core/maturity.ts),\n * whose `documented` rung is the model/spec/openapi triple. Adding a fourth\n * required artifact would demote every already-`documented` service in the\n * fleet on upgrade, without one byte of their files changing.\n */\n asyncapi: boolean;\n runbook: boolean;\n health: boolean;\n}\n\nexport interface ServiceEntry {\n /** Canonical service id — the directory name under services/. */\n id: string;\n /** Absolute path to services/<id>/. */\n dir: string;\n has: ServiceArtifacts;\n /** Number of ADR files under adrs/. */\n adrs: number;\n /** `status` from the living spec's frontmatter; null when nobody has said. */\n status: string | null;\n /**\n * Provenance signals from the living spec's frontmatter: whether it declares\n * any `sources`, and whether a `sources_digest` stamp exists. Presence only —\n * whether the digest still matches the code is `validate`'s question, and it\n * can only be answered from inside the service's own repo.\n */\n sources: { declared: boolean; stamped: boolean };\n /**\n * Why no loam command can author this directory, or absent when the name is a\n * legal service id — `serviceIdProblem`'s own sentence, so the read model and\n * the refusal say the same thing.\n *\n * The enumeration still LISTS it: the directory is there, it holds somebody's\n * documents, and hiding it would repeat the symlink mistake one field up. But\n * every authoring command (`adopt`, `delta`, `rebase`, `new --touches`,\n * `init`) refuses this id with `invalid-option`, so grading it as an ordinary\n * service left the fleet gate demanding fixes no loam command can make. The\n * NAME is the finding — see `serviceIdFindings`.\n */\n idProblem?: string;\n}\n\nexport interface FeatureEntry {\n /** Feature id, derived from the directory name (FEAT-1-split -> FEAT-1). */\n id: string;\n /** The directory name as it is on disk, slug and all. */\n dirName: string;\n /** Absolute path to the feature directory. */\n dir: string;\n archived: boolean;\n /** Services this feature carries a delta for, from specs/<svc>/. */\n services: string[];\n has: { intent: boolean; delta: boolean };\n}\n\n/**\n * Compare ids so digit runs sort numerically: FEAT-2 before FEAT-10.\n * Deterministic and locale-independent — ordering is part of the output\n * contract for `list`, and `--json` consumers diff it.\n */\nexport function compareIds(a: string, b: string): number {\n const ta = tokenize(a);\n const tb = tokenize(b);\n for (let i = 0; i < Math.max(ta.length, tb.length); i += 1) {\n const x = ta[i];\n const y = tb[i];\n if (x === undefined) return -1;\n if (y === undefined) return 1;\n if (typeof x === \"number\" && typeof y === \"number\") {\n if (x !== y) return x - y;\n } else if (String(x) !== String(y)) {\n return String(x) < String(y) ? -1 : 1;\n }\n }\n return 0;\n}\n\nfunction tokenize(s: string): (string | number)[] {\n return (s.match(/\\d+|\\D+/g) ?? []).map((t) => (/^\\d/.test(t) ? Number(t) : t));\n}\n\n/**\n * Known ids within a small edit distance of `id`, closest first, at most three.\n *\n * The did-you-mean hint for a name somebody typed: `loam new --touches` and\n * `loam delta <svc>` answer the same mistake, and each carried its own copy of\n * these twenty-four lines. It belongs here because the ids being scored are\n * this module's ids — service directory names, feature ids — and because\n * `compareIds` right above is the tiebreak that keeps the list deterministic\n * when two candidates score equally. Not in `core/ids.ts`: `compareIds` lives\n * here and repo.ts imports ids.js, so putting it there would close a cycle.\n *\n * `arch.ts`'s `closeIds` looks similar and is not: substring and prefix, capped\n * at five, answering \"which element could this be\" rather than \"did you\n * misspell a directory\". Two rules, two homes.\n */\nexport function nearestIds(id: string, known: string[]): string[] {\n const budget = Math.max(1, Math.floor(id.length / 4));\n return known\n .map((candidate) => ({ candidate, distance: editDistance(id.toLowerCase(), candidate.toLowerCase()) }))\n .filter((scored) => scored.distance <= budget)\n .sort((a, b) => a.distance - b.distance || compareIds(a.candidate, b.candidate))\n .slice(0, 3)\n .map((scored) => scored.candidate);\n}\n\n/** Plain Levenshtein — ids are short, and the row-at-a-time form keeps it obvious. */\nfunction editDistance(a: string, b: string): number {\n let previous = Array.from({ length: b.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i += 1) {\n const current = [i];\n for (let j = 1; j <= b.length; j += 1) {\n const substitution = previous[j - 1]! + (a[i - 1] === b[j - 1] ? 0 : 1);\n current.push(Math.min(previous[j]! + 1, current[j - 1]! + 1, substitution));\n }\n previous = current;\n }\n return previous[b.length]!;\n}\n\n/**\n * Feature id from a directory name: everything up to and including the first\n * number run (`FEAT-101-payment-splitting` -> `FEAT-101`). A name with no\n * `<word>-<number>` head is its own id. Quirk: a dated slug keeps only its first\n * segment (`release-2024-01-x` -> `release-2024`) — ids are not meant to be dates.\n */\nexport function featureIdFromDirName(dirName: string): string {\n const m = /^(.*?-\\d+)(?:-|$)/.exec(dirName);\n return m ? m[1]! : dirName;\n}\n\n/* ------------------------------------------------------------------ */\n/* Paths — artifact filenames are spelled here and nowhere else */\n/* ------------------------------------------------------------------ */\n\nexport interface ServicePaths {\n dir: string;\n model: string;\n spec: string;\n archSpec: string;\n openapi: string;\n /** The async contract — the event axis's sibling of `openapi`. */\n asyncapi: string;\n runbook: string;\n health: string;\n adrsDir: string;\n}\n\nexport function servicePaths(docsDir: string, service: string): ServicePaths {\n const dir = join(docsDir, \"services\", service);\n return {\n dir,\n model: join(dir, \"model.likec4\"),\n spec: join(dir, \"spec.md\"),\n archSpec: join(dir, \"arch.spec.md\"),\n openapi: join(dir, \"openapi.yaml\"),\n asyncapi: join(dir, \"asyncapi.yaml\"),\n runbook: join(dir, \"runbook.md\"),\n health: join(dir, \"health.yaml\"),\n adrsDir: join(dir, \"adrs\"),\n };\n}\n\n/**\n * The pair of requirement-carrying spec files, living and delta alike: the\n * business spec and the architecture spec. Same grammar, same delta algebra,\n * same merge — everything that walks \"the spec files\" of a service walks this\n * list, so the two axes cannot drift apart in what handles them. `key` indexes\n * ServicePaths/FeatureSpecPaths; `label` is how the axis names itself in prose.\n */\nexport const SPEC_AXES = [\n { key: \"spec\", file: \"spec.md\", label: \"requirements\" },\n { key: \"archSpec\", file: \"arch.spec.md\", label: \"arch requirements\" },\n] as const;\nexport type SpecAxis = (typeof SPEC_AXES)[number];\n\nexport interface FeaturePaths {\n dir: string;\n intent: string;\n delta: string;\n specsDir: string;\n adrsDir: string;\n}\n\nexport function featurePaths(featureDir: string): FeaturePaths {\n return {\n dir: featureDir,\n intent: join(featureDir, \"intent.md\"),\n delta: join(featureDir, \"delta.likec4\"),\n specsDir: join(featureDir, \"specs\"),\n adrsDir: join(featureDir, \"adrs\"),\n };\n}\n\nexport interface FeatureSpecPaths {\n dir: string;\n spec: string;\n archSpec: string;\n openapi: string;\n}\n\nexport function featureSpecPaths(featureDir: string, service: string): FeatureSpecPaths {\n const dir = join(featureDir, \"specs\", service);\n return {\n dir,\n spec: join(dir, \"spec.md\"),\n archSpec: join(dir, \"arch.spec.md\"),\n openapi: join(dir, \"openapi.yaml\"),\n };\n}\n\nexport function landscapePath(docsDir: string): string {\n return join(docsDir, \"architecture\", \"landscape.likec4\");\n}\n\nexport function featuresDir(docsDir: string): string {\n return join(docsDir, \"features\");\n}\n\n/**\n * The agent contract's filename, and where it sits in a docs repo.\n *\n * Both spellings are exported because the two questions are different: `init`,\n * `doctor` and `validate` want a path under a docsDir they already hold, while\n * `docs.ts` builds the scaffold as `[relative path, bytes]` pairs and needs the\n * bare name. Four modules spelled the literal out for themselves, and one of\n * them — `init` — uses it to decide whether a directory IS a docs repo at all,\n * so a rename that reached three of the four would leave `init` recognising a\n * file nothing else writes any more.\n *\n * `openspec-inventory.ts`'s \"AGENTS.md\" is deliberately NOT this constant: it\n * names a file in a foreign OpenSpec tree that happens to share the name.\n */\nexport const AGENTS_FILENAME = \"AGENTS.md\";\n\nexport function agentsPath(docsDir: string): string {\n return join(docsDir, AGENTS_FILENAME);\n}\n\n/**\n * Where shipped features live. `features/archive/` is spelled here and nowhere\n * else for the same reason every other artifact path is: `archive` and\n * `unarchive` move directories in and out of it, and a layout string that two\n * commands re-derive is a layout two commands can disagree about. The\n * `features/archive/…` strings in refusal PROSE are not this path — they are\n * what a reader types into `ls`, and they stay spelled out.\n */\nexport function archiveDir(docsDir: string): string {\n return join(featuresDir(docsDir), ARCHIVE_DIR);\n}\n\n/* ------------------------------------------------------------------ */\n/* Is there a docs repo there at all? */\n/* ------------------------------------------------------------------ */\n\n/**\n * What is actually at `docsDir`. Three answers, because they point at three\n * different fixes and the enumeration below used to give all of them the same\n * one — an empty list:\n *\n * - `missing` — nothing there (or not a directory): `docsDir` in loam.json\n * is wrong, or the docs repo was never cloned;\n * - `no-services` — a directory, but with no `services/`: it is some other\n * directory, most often the service repo itself after a typo;\n * - `ok` — a docs repo. `services/` may still be EMPTY, and that is a\n * legitimate state: a docs repo before the first `loam adopt`.\n *\n * \"Empty fleet\" and \"wrong path\" are the same output only if nobody asks this\n * question, and a green `loam list` over a docsDir that does not exist is worse\n * than a red one: it says the fleet is fine.\n */\nexport type DocsRepoKind = \"missing\" | \"no-services\" | \"ok\";\n\nexport interface DocsRepoState {\n kind: DocsRepoKind;\n /** The path examined, so a caller can quote it without re-deriving it. */\n path: string;\n}\n\nexport function docsRepoState(docsDir: string): DocsRepoState {\n const path = docsDir;\n let isDir = false;\n try {\n isDir = statSync(path).isDirectory();\n } catch {\n isDir = false;\n }\n if (!isDir) return { kind: \"missing\", path };\n return { kind: existsSync(join(path, \"services\")) ? \"ok\" : \"no-services\", path };\n}\n\n/**\n * The docs repo is not there. Thrown rather than returned because every caller\n * of the enumeration wants the same thing — to stop — and a `[]` return is the\n * bug this closes. `message` is written for a human at a terminal: the CLI's\n * top-level handler prints it verbatim, and commands that catch it report\n * `repository-unavailable`.\n */\nexport class DocsRepoUnavailableError extends Error {\n constructor(\n readonly state: DocsRepoState,\n message: string,\n ) {\n super(message);\n this.name = \"DocsRepoUnavailableError\";\n }\n}\n\n/**\n * Refuse to enumerate a docs repo that is not one. `allow` names the states the\n * caller can honestly survive: features/ may be absent from a real docs repo\n * (nothing is in flight yet), so feature enumeration only insists the docs repo\n * EXISTS, while service enumeration insists it is shaped like a docs repo — a\n * `services/` directory is what makes it one.\n */\nfunction requireDocsRepo(docsDir: string, allow: DocsRepoKind[]): void {\n const state = docsRepoState(docsDir);\n if (allow.includes(state.kind)) return;\n if (state.kind === \"missing\") {\n throw new DocsRepoUnavailableError(\n state,\n `The configured docs repo does not exist: ${state.path}. ` +\n \"Fix `docsDir` in loam.json, clone the docs repo there, \" +\n \"or run `loam init --docs <dir> --create` to make a new one.\",\n );\n }\n throw new DocsRepoUnavailableError(\n state,\n `${state.path} is not a docs repo — it has no services/ directory. ` +\n \"Point `docsDir` in loam.json at the shared docs repo, \" +\n \"or run `loam init --docs <dir> --create` to make one.\",\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Enumeration */\n/* ------------------------------------------------------------------ */\n\n/**\n * What a directory entry IS, following a symlink to find out.\n *\n * `readdir(withFileTypes)` does not follow symlinks: for a symlink `Dirent`\n * every `isDirectory()`/`isFile()` is false, whatever it points at. That made a\n * symlinked `services/<id>/` or `features/<id>/` vanish from every enumeration\n * at once — `list`, `validate --all`, the fleet gate — with no diagnostic\n * anywhere, which is the one outcome a shared docs repo cannot survive: the\n * fleet gate reported a service that is not there as fine because it never saw\n * it. Composing a docs repo out of symlinks (a worktree, a submodule, one\n * service's directory shared between two checkouts) is legitimate, so they are\n * FOLLOWED rather than refused. A dangling link stats as nothing and is skipped\n * exactly as an absent directory is — it is not a service, and saying so would\n * mean inventing an entry to hang the complaint on.\n */\nfunction entryIs(dir: string, e: Dirent, want: \"dir\" | \"file\"): boolean {\n if (!e.isSymbolicLink()) return want === \"dir\" ? e.isDirectory() : e.isFile();\n try {\n const s = statSync(join(dir, e.name));\n return want === \"dir\" ? s.isDirectory() : s.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Sorted names of the real subdirectories of `dir` (dot-dirs and files skipped).\n * A missing `dir` is an empty list — the swallow is deliberate but narrow: it\n * only ever runs INSIDE a docs repo that `requireDocsRepo` has already\n * confirmed, where an absent `features/` or `specs/` means \"none yet\" and\n * nothing else.\n */\nasync function subdirs(dir: string): Promise<string[]> {\n if (!existsSync(dir)) return [];\n const entries = await readdir(dir, { withFileTypes: true });\n return entries\n .filter((e) => !e.name.startsWith(\".\") && entryIs(dir, e, \"dir\"))\n .map((e) => e.name)\n .sort(compareIds);\n}\n\n/**\n * Markdown files sitting directly in `dir` — the ADR count, and the only place\n * that rule is spelled. Exported because `show` kept a second copy that tested\n * `Dirent.isFile()` itself: always false for a symlink, whatever it points at,\n * so a docs repo composed of symlinks (the layout `entryIs` above exists to\n * support) had `loam list` and `loam show` reporting different ADR counts for\n * the same service. One rule, one answer.\n */\nexport async function countMarkdown(dir: string): Promise<number> {\n if (!existsSync(dir)) return 0;\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.name.endsWith(\".md\") && entryIs(dir, e, \"file\")).length;\n}\n\n/** Services a feature carries a delta for, ordered — the subdirs of its specs/. */\nexport async function featureSpecServices(\n featureDir: string,\n context?: FleetContext,\n): Promise<string[]> {\n if (context !== undefined) return context.featureSpecServices(featureDir);\n return subdirs(featurePaths(featureDir).specsDir);\n}\n\n/** Every service in the docs repo, ordered by id. */\nexport async function listServices(docsDir: string, context?: FleetContext): Promise<ServiceEntry[]> {\n if (context !== undefined) return context.listServices(docsDir);\n requireDocsRepo(docsDir, [\"ok\"]);\n const names = await subdirs(join(docsDir, \"services\"));\n return Promise.all(\n names.map(async (id): Promise<ServiceEntry> => {\n const p = servicePaths(docsDir, id);\n const fm = await readFrontmatter(p.spec);\n const idProblem = serviceIdProblem(id, \"directory name\");\n return {\n id,\n dir: p.dir,\n ...(idProblem === null ? {} : { idProblem }),\n has: {\n model: existsSync(p.model),\n spec: existsSync(p.spec),\n openapi: existsSync(p.openapi),\n asyncapi: existsSync(p.asyncapi),\n runbook: existsSync(p.runbook),\n health: existsSync(p.health),\n },\n adrs: await countMarkdown(p.adrsDir),\n status: stringField(fm, \"status\") ?? null,\n sources: {\n declared: listField(fm, \"sources\").length > 0,\n stamped: stringField(fm, \"sources_digest\") !== undefined,\n },\n };\n }),\n );\n}\n\n/**\n * The directories under `services/` that no loam command can address.\n *\n * A finding rather than a silent skip, and an ERROR rather than a warning,\n * because the state is not survivable in either direction: `loam adopt`,\n * `loam delta`, `loam rebase` and `loam new --touches` all refuse the id with\n * `invalid-option`, so the documents in there can never be updated by loam\n * again, while `validate --all` used to grade the directory as an ordinary\n * service and demand `model.likec4`, a spec and a landscape binding for it. The\n * fix is a rename, and it is a rename loam cannot do for you — the id is\n * spelled in the landscape binding, in the frontmatter and in every feature\n * that touched the service — so the message names all three.\n *\n * Emitted from the fleet target (`validate --all`), where the SET of service\n * directories is the thing being checked; a per-service run is already refused\n * by its own `--service` guard before it gets here.\n */\nexport function serviceIdFindings(services: ServiceEntry[]): Finding[] {\n return services\n .filter((s) => s.idProblem !== undefined)\n .map((s) => ({\n severity: \"error\" as const,\n code: \"service.id-invalid\",\n subject: s.id,\n message:\n `services/${s.id}/ — ${s.idProblem} ` +\n `Every authoring command refuses this id (\\`loam adopt\\`, \\`loam delta\\`, \\`loam new --touches\\`), so nothing in that directory can be changed through loam. ` +\n `Rename the directory to a legal id, then update its \\`service:\\` frontmatter, its \\`metadata { service '${s.id}' }\\` binding in architecture/landscape.likec4, and any features/<FEAT>/specs/${s.id}/ that names it.`,\n }));\n}\n\nasync function readFeature(dir: string, dirName: string, archived: boolean): Promise<FeatureEntry> {\n const p = featurePaths(dir);\n return {\n id: featureIdFromDirName(dirName),\n dirName,\n dir,\n archived,\n services: await subdirs(p.specsDir),\n has: { intent: existsSync(p.intent), delta: existsSync(p.delta) },\n };\n}\n\n/**\n * Features in the docs repo, ordered by id (active and archived interleaved when\n * both are asked for). `features/archive/` is the archive, never a feature.\n */\nexport async function listFeatures(\n docsDir: string,\n opts: { includeArchived?: boolean } = {},\n context?: FleetContext,\n): Promise<FeatureEntry[]> {\n if (context !== undefined) return context.listFeatures(docsDir, opts);\n // \"no-services\" is tolerated here on purpose: a docs repo whose services/ is\n // gone is broken, but it is `listServices` (and `loam doctor`) that says so.\n // Refusing here too would turn one diagnosis into two contradictory ones.\n requireDocsRepo(docsDir, [\"ok\", \"no-services\"]);\n const root = featuresDir(docsDir);\n const active = (await subdirs(root)).filter((n) => n !== ARCHIVE_DIR);\n const out = await Promise.all(active.map((n) => readFeature(join(root, n), n, false)));\n\n if (opts.includeArchived) {\n const archiveRoot = join(root, ARCHIVE_DIR);\n const archived = await subdirs(archiveRoot);\n out.push(...(await Promise.all(archived.map((n) => readFeature(join(archiveRoot, n), n, true)))));\n }\n\n return out.sort((a, b) => compareIds(a.id, b.id) || compareIds(a.dirName, b.dirName));\n}\n\n/**\n * Which side of `features/archive/` a lookup may land on. The policy is an\n * argument, not a default, so every command's stance on shipped features is\n * visible at its call site: delta/validate/archive work on in-flight features\n * (\"exclude\"), show/verify/new read shipped ones too (\"include\"), unarchive\n * takes back nothing else (\"only\").\n */\nexport type ArchivedPolicy = \"exclude\" | \"include\" | \"only\";\n\n/**\n * Resolve a feature argument — a canonical id (`FEAT-5`) or an exact directory\n * name (`FEAT-5-slug`) — to its entry. The entry's `id` is the canonical\n * spelling, and it is the only name a caller may use from here on: tag filters,\n * coherence, and the cross-feature self-exclusion scans all compare against\n * `feature.id`, never against the raw argument.\n *\n * Matching is EXACT, on one of two spellings: the directory name as it is on\n * disk, or the canonical id derived from it. It used to also match any dirName\n * starting with `arg + \"-\"`, which made the argument a prefix query in\n * disguise: with FEAT-401 and FEAT-402 in flight, `loam archive FEAT` archived\n * whichever sorted first, and `loam delta billing` reached into\n * `billing-7-rewrite`. A destructive command must never pick a target the\n * caller did not name, so a prefix now resolves to nothing at all.\n *\n * An exact directory name still wins over the id spelling (`FEAT-5` the\n * directory over `FEAT-5-slug`, whose id is also `FEAT-5`), and under \"include\"\n * an active feature wins over an archived one. Ties among equally-exact\n * candidates go to the first by name — deterministic, unlike the raw readdir\n * order this replaces; `ambiguousFeatureMessage` lists them for the caller.\n */\nexport async function resolveFeature(\n docsDir: string,\n arg: string,\n archived: ArchivedPolicy,\n context?: FleetContext,\n): Promise<FeatureEntry | null> {\n return (await featureCandidates(docsDir, arg, archived, context))[0] ?? null;\n}\n\n/**\n * Every feature the argument names exactly, best first. Split out of\n * `resolveFeature` so a caller that wants to REFUSE an ambiguous argument can\n * see the tie instead of inheriting the winner.\n */\nexport async function featureCandidates(\n docsDir: string,\n arg: string,\n archived: ArchivedPolicy,\n context?: FleetContext,\n): Promise<FeatureEntry[]> {\n const all = await listFeatures(docsDir, { includeArchived: archived !== \"exclude\" }, context);\n return all\n .filter((f) => archived === \"include\" || f.archived === (archived === \"only\"))\n .filter((f) => f.dirName === arg || f.id === arg)\n .sort(\n (a, b) =>\n Number(b.dirName === arg) - Number(a.dirName === arg) ||\n Number(a.archived) - Number(b.archived) ||\n compareIds(a.dirName, b.dirName),\n );\n}\n\n/**\n * The message for an argument that names more than one feature — two directories\n * carrying the same id (`FEAT-6-aaa` and `FEAT-6-zzz`). `resolveFeature` still\n * picks one deterministically, but a command that is about to WRITE should say\n * which directories collided and make the caller spell one out.\n */\nexport function ambiguousFeatureMessage(arg: string, candidates: FeatureEntry[]): string {\n return (\n `'${arg}' names ${candidates.length} features: ${candidates.map((c) => c.dirName).join(\", \")}. ` +\n \"Pass the directory name you mean.\"\n );\n}\n\n/**\n * The message for an exclude-mode miss. When the feature exists under\n * `features/archive/`, \"no feature\" is a lie the caller would have to debug —\n * the honest answer is \"already archived\", plus how to look at it. The `--json`\n * code stays `unknown-target` either way: the target is unknown to the command\n * that asked, and the prose carries the diagnosis.\n */\nexport async function missingFeatureMessage(\n docsDir: string,\n arg: string,\n context?: FleetContext,\n): Promise<string> {\n const shipped = await resolveFeature(docsDir, arg, \"only\", context);\n if (shipped !== null) {\n return (\n `Feature '${shipped.id}' is already archived (features/archive/${shipped.dirName}) — ` +\n `\\`loam show ${shipped.id}\\` reads it, \\`loam unarchive ${shipped.id}\\` re-opens it.`\n );\n }\n return `No feature '${arg}' under ${featuresDir(docsDir)}.`;\n}\n"]}
|
package/dist/core/report.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/core/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAA6B,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AA8C1F,6EAA6E;AAC7E,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAuB;IACjD,OAAO,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAuB,EAAE,QAAkB;IACvE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAU;IACpC,OAAO;QACL,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QAC1D,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACpD,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,CAAe;IACxC,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;QACrB,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC;KACtC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The validation report — what `loam validate` produces before anything is\n * printed.\n *\n * Checks build findings; renderers turn them into a human view or into the JSON\n * contract. Keeping the two apart is what makes `--all` and `--json` possible\n * without a second implementation of every check, and it is why a finding\n * carries a stable `code`: prose is for people, codes are for callers.\n */\n\nexport type Severity = \"ok\" | \"warn\" | \"error\";\n\n/**\n * The glyph each severity is printed with. Five sites had written it out\n * themselves — four as a two-armed ternary that spelled `error` and let\n * everything else fall to ⚠ — and a reader who learns the marks from one\n * command's output has to be able to read them in the next. It lives beside\n * `Severity` because it is a fact about the union, not about any one renderer;\n * `TextHint.marker` above already describes it as \"the ✓/⚠/✗ marker\".\n */\nexport const SEVERITY_MARK: Record<Severity, string> = { ok: \"✓\", warn: \"⚠\", error: \"✗\" };\n\n/** How the text renderer should lay a finding out. The JSON contract ignores this. */\nexport interface TextHint {\n /** Leading spaces. Default 0. */\n indent?: number;\n /** Print the ✓/⚠/✗ marker. Default true. */\n marker?: boolean;\n /** Block header printed once, before the first finding that carries it. */\n header?: string;\n /** Prefix for each detail line (e.g. \"- \" for a list of names). */\n detailPrefix?: string;\n}\n\nexport interface Finding {\n severity: Severity;\n /** Stable machine identifier — prose may change, this may not. */\n code: string;\n message: string;\n /**\n * What the finding is about, when that is narrower than the target: the\n * service a feature's per-service check ran on, say. Without it a caller\n * would have to read the service name back out of the message.\n */\n subject?: string;\n details?: string[];\n /**\n * Coherence findings only: whether `loam archive` refuses on this issue\n * without `--approve` (issue.ts explains why severity alone cannot say).\n * Absent on findings that never face the archive gate.\n */\n gates?: boolean;\n text?: TextHint;\n}\n\nexport interface TargetReport {\n /**\n * What was validated. `landscape` is the fleet itself — the checks that need\n * every service and every element in view at once, so they belong to no single\n * service and only run under `--all`.\n */\n kind: \"service\" | \"feature\" | \"landscape\";\n id: string;\n findings: Finding[];\n}\n\n/** A target is valid when nothing in it is an error. Warnings never gate. */\nexport function targetValid(target: TargetReport): boolean {\n return !target.findings.some((f) => f.severity === \"error\");\n}\n\nexport function reportValid(targets: TargetReport[]): boolean {\n return targets.every(targetValid);\n}\n\nexport function countSeverity(targets: TargetReport[], severity: Severity): number {\n return targets.reduce((n, t) => n + t.findings.filter((f) => f.severity === severity).length, 0);\n}\n\nexport function findingJson(f: Finding): Record<string, unknown> {\n return {\n severity: f.severity,\n code: f.code,\n ...(f.subject === undefined ? {} : { subject: f.subject }),\n ...(f.gates === undefined ? {} : { gates: f.gates }),\n message: f.message,\n details: f.details ?? [],\n };\n}\n\nexport function targetJson(t: TargetReport): Record<string, unknown> {\n return {\n kind: t.kind,\n id: t.id,\n valid: targetValid(t),\n findings: t.findings.map(findingJson),\n };\n}\n"]}
|
package/dist/core/spec.js
DELETED
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parser for OpenSpec-style requirement documents.
|
|
3
|
-
*
|
|
4
|
-
* Living specs use `## Requirements` with `### Requirement:` + `#### Scenario:`.
|
|
5
|
-
* Feature deltas group requirements under `## ADDED|MODIFIED|REMOVED Requirements`.
|
|
6
|
-
* Scenarios are the acceptance criteria (Given/When/Then) and the source for tests.
|
|
7
|
-
*/
|
|
8
|
-
import { createHash } from "node:crypto";
|
|
9
|
-
import { readFile } from "node:fs/promises";
|
|
10
|
-
import { decodeDocument } from "./document-bytes.js";
|
|
11
|
-
import { scenarioGherkin } from "./steps.js";
|
|
12
|
-
export const KIND_RE = /^##\s+(ADDED|MODIFIED|REMOVED)\s+Requirements\s*$/i;
|
|
13
|
-
const REQ_RE = /^###\s+Requirement:\s*(.+?)\s*$/;
|
|
14
|
-
const SCN_RE = /^####\s+Scenario:\s*(.+?)\s*$/;
|
|
15
|
-
const REQUIREMENT_ID_LINE_RE = /^\s*Requirement-ID:\s*(.*?)\s*$/i;
|
|
16
|
-
const BASED_ON_LINE_RE = /^\s*Based-On:\s*(.*?)\s*$/i;
|
|
17
|
-
/** Portable, review-friendly stable IDs. Case-sensitive by design. */
|
|
18
|
-
export const REQUIREMENT_ID_RE = /^[A-Za-z][A-Za-z0-9._-]{0,127}$/;
|
|
19
|
-
/**
|
|
20
|
-
* How much of the sha256 a `Based-On:` line carries — the length
|
|
21
|
-
* `sources_digest` already uses (core/provenance.ts), for one reason a reader
|
|
22
|
-
* can hold: every digest loam writes into a document reads the same.
|
|
23
|
-
*/
|
|
24
|
-
export const REQUIREMENT_DIGEST_LENGTH = 16;
|
|
25
|
-
/** The value shape of a `Based-On:` line, exactly as `requirementDigest` writes it. */
|
|
26
|
-
export const REQUIREMENT_DIGEST_RE = new RegExp(`^[0-9a-f]{${REQUIREMENT_DIGEST_LENGTH}}$`);
|
|
27
|
-
/** Every authored Requirement-ID value in a requirement body, including blanks. */
|
|
28
|
-
export function requirementIdDeclarations(requirement) {
|
|
29
|
-
return requirement.text.flatMap((line) => {
|
|
30
|
-
const match = REQUIREMENT_ID_LINE_RE.exec(line);
|
|
31
|
-
return match === null ? [] : [match[1].trim()];
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
/** Structural ID problems within one spec/delta document. */
|
|
35
|
-
export function requirementIdProblems(reqs) {
|
|
36
|
-
const problems = [];
|
|
37
|
-
const owners = new Map();
|
|
38
|
-
for (const requirement of reqs) {
|
|
39
|
-
const declarations = requirementIdDeclarations(requirement);
|
|
40
|
-
const values = declarations.length > 0
|
|
41
|
-
? declarations
|
|
42
|
-
: requirement.id === undefined
|
|
43
|
-
? []
|
|
44
|
-
: [requirement.id];
|
|
45
|
-
if (declarations.length > 1) {
|
|
46
|
-
problems.push({ kind: "repeated", requirement: requirement.name, values: declarations });
|
|
47
|
-
}
|
|
48
|
-
for (const value of values) {
|
|
49
|
-
if (!REQUIREMENT_ID_RE.test(value)) {
|
|
50
|
-
problems.push({ kind: "invalid", requirement: requirement.name, value });
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// A repeated declaration is already an error. Do not also pretend its
|
|
54
|
-
// keep-last parse result is an unambiguous document identity.
|
|
55
|
-
if (values.length !== 1 || !REQUIREMENT_ID_RE.test(values[0]))
|
|
56
|
-
continue;
|
|
57
|
-
const names = owners.get(values[0]) ?? [];
|
|
58
|
-
names.push(requirement.name);
|
|
59
|
-
owners.set(values[0], names);
|
|
60
|
-
}
|
|
61
|
-
for (const [id, requirements] of owners) {
|
|
62
|
-
if (requirements.length > 1)
|
|
63
|
-
problems.push({ kind: "duplicate", id, requirements });
|
|
64
|
-
}
|
|
65
|
-
return problems;
|
|
66
|
-
}
|
|
67
|
-
/** Every authored `Based-On:` value in a requirement body, including blanks. */
|
|
68
|
-
export function basedOnDeclarations(requirement) {
|
|
69
|
-
return requirement.text.flatMap((line) => {
|
|
70
|
-
const match = BASED_ON_LINE_RE.exec(line);
|
|
71
|
-
return match === null ? [] : [match[1].trim()];
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* The requirement without its baseline marker — what a digest is taken over,
|
|
76
|
-
* and what archive merges into the living document.
|
|
77
|
-
*
|
|
78
|
-
* `Based-On:` is a statement ABOUT a delta ("this was written against that"),
|
|
79
|
-
* never part of the requirement it describes, and both consequences of
|
|
80
|
-
* forgetting that are bad. Left in the digest input, a requirement's digest
|
|
81
|
-
* would depend on the pin pointing at it, so no baseline could ever be
|
|
82
|
-
* self-consistent. Left in the merged text — `applyRequirementDelta` copies the
|
|
83
|
-
* delta's body into living wholesale — the living document would grow a pin to
|
|
84
|
-
* a version of itself, and the NEXT feature's baseline would be taken over a
|
|
85
|
-
* document containing the previous feature's bookkeeping.
|
|
86
|
-
*/
|
|
87
|
-
function withoutBaseline(requirement) {
|
|
88
|
-
const { basedOn: _dropped, ...rest } = requirement;
|
|
89
|
-
return { ...rest, text: requirement.text.filter((line) => !BASED_ON_LINE_RE.test(line)) };
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* The identity of a requirement's CONTENT: sha256 over its canonical
|
|
93
|
-
* serialization, truncated like every other digest loam stamps.
|
|
94
|
-
*
|
|
95
|
-
* Canonical, not raw bytes, and deliberately: a living spec is rewritten by
|
|
96
|
-
* `serializeRequirements` on every archive, so hashing the file's bytes would
|
|
97
|
-
* make a baseline go stale over reflowed blank lines — a false alarm on the one
|
|
98
|
-
* check whose whole value is that it only fires when something real moved.
|
|
99
|
-
* What the hash therefore covers is exactly what survives a round trip: the
|
|
100
|
-
* heading, the body (`Requirement-ID:` and `Operations:`/`Covers:` lines
|
|
101
|
-
* included), and every scenario with its Given/When/Then lines. `kind` and
|
|
102
|
-
* `section` are not serialized and so are not hashed — a requirement does not
|
|
103
|
-
* change because the document quoting it moved it under another heading.
|
|
104
|
-
*/
|
|
105
|
-
export function requirementDigest(requirement) {
|
|
106
|
-
return createHash("sha256")
|
|
107
|
-
.update(serializeRequirements([withoutBaseline(requirement)]), "utf8")
|
|
108
|
-
.digest("hex")
|
|
109
|
-
.slice(0, REQUIREMENT_DIGEST_LENGTH);
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* THE requirements section: the one H2 a living spec keeps its requirements
|
|
113
|
-
* under, the one section `loam archive` rewrites, and the one heading a delta
|
|
114
|
-
* may legally quote the living state beneath. One definition on purpose — the
|
|
115
|
-
* archive cut point, archive's stray-requirement guard, and the delta-shape
|
|
116
|
-
* exemption (core/delta.ts) used to spell this invariant three different ways,
|
|
117
|
-
* and the cut point's substring `indexOf("\n## Requirements")` was a PREFIX
|
|
118
|
-
* match that also hit `## Requirements Extra`. Full-line match on a trimmed
|
|
119
|
-
* heading, with KIND_RE's case and interior-whitespace tolerance.
|
|
120
|
-
*/
|
|
121
|
-
const REQUIREMENTS_SECTION_RE = /^##\s+Requirements\s*$/i;
|
|
122
|
-
/** Is this H2 heading line (as sectionHeadings/`Requirement.section` spell it) the `## Requirements` heading? */
|
|
123
|
-
export function isRequirementsHeading(heading) {
|
|
124
|
-
return REQUIREMENTS_SECTION_RE.test(heading.trim());
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Track ``` / ~~~ fences line by line. Returns true while the line is fenced
|
|
128
|
-
* content — including the fence marker itself — so heading-like lines inside a
|
|
129
|
-
* code block are never mistaken for structure.
|
|
130
|
-
*/
|
|
131
|
-
function fenceTracker() {
|
|
132
|
-
let fence = null;
|
|
133
|
-
return (line) => {
|
|
134
|
-
const m = /^\s*(```|~~~)/.exec(line);
|
|
135
|
-
if (m) {
|
|
136
|
-
if (fence === null)
|
|
137
|
-
fence = m[1];
|
|
138
|
-
else if (fence === m[1])
|
|
139
|
-
fence = null;
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
return fence !== null;
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Strip a leading UTF-8 BOM (U+FEFF), which editors on Windows write unasked.
|
|
147
|
-
*
|
|
148
|
-
* It has to happen here, at the two functions that take raw markdown, rather than
|
|
149
|
-
* at each `readFile` call: those are spread across half the commands, so any one of
|
|
150
|
-
* them could forget, and the failure is invisible. One byte was enough to void an
|
|
151
|
-
* entire delta — `KIND_RE` is anchored `^##`, delta files routinely open with
|
|
152
|
-
* `## MODIFIED Requirements` on line 1, and behind a BOM that line stopped being a
|
|
153
|
-
* heading, so every requirement in the file fell back to BASE and
|
|
154
|
-
* `applyRequirementDelta` skips BASE. Archive merged nothing and said nothing.
|
|
155
|
-
* OpenSpec's own reader strips it for exactly this reason.
|
|
156
|
-
*
|
|
157
|
-
* Only at position 0: elsewhere U+FEFF is a zero-width no-break space and belongs
|
|
158
|
-
* to the author's text.
|
|
159
|
-
*/
|
|
160
|
-
function stripBom(md) {
|
|
161
|
-
return md.charCodeAt(0) === 0xfeff ? md.slice(1) : md;
|
|
162
|
-
}
|
|
163
|
-
/** Every H2 heading outside a fenced block, with its 1-based line number. */
|
|
164
|
-
export function sectionHeadings(md) {
|
|
165
|
-
const fenced = fenceTracker();
|
|
166
|
-
const out = [];
|
|
167
|
-
stripBom(md).split(/\r?\n/).forEach((line, i) => {
|
|
168
|
-
if (fenced(line))
|
|
169
|
-
return;
|
|
170
|
-
if (/^##\s+/.test(line) && !line.startsWith("###"))
|
|
171
|
-
out.push({ text: line.trim(), line: i + 1 });
|
|
172
|
-
});
|
|
173
|
-
return out;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Split a living spec around its requirements RUN — the stretch of
|
|
177
|
-
* `### Requirement:` blocks inside the `## Requirements` section — so a merge
|
|
178
|
-
* can rewrite exactly that and nothing else. All three slices reassemble the
|
|
179
|
-
* input byte-for-byte (`head + run + tail === text`):
|
|
180
|
-
*
|
|
181
|
-
* - `head` — everything before the first requirement in the section: the
|
|
182
|
-
* intro, the heading line itself, any prose under the heading.
|
|
183
|
-
* - `run` — from the first `### Requirement:` line to the section's end (the
|
|
184
|
-
* next `## ` heading, or EOF). Empty when the section holds no requirements.
|
|
185
|
-
* - `tail` — everything from the section's end onward.
|
|
186
|
-
*
|
|
187
|
-
* Returns null when the document has no `## Requirements` heading at all.
|
|
188
|
-
* Only the FIRST matching heading opens the section; a caller that must not
|
|
189
|
-
* lose content behind a duplicate (archive) counts matches via
|
|
190
|
-
* sectionHeadings() and refuses. Heading detection matches parseRequirements:
|
|
191
|
-
* fenced lines are never structure, and a leading BOM hides no heading — but
|
|
192
|
-
* the slices themselves are raw bytes, BOM and CRLF included.
|
|
193
|
-
*/
|
|
194
|
-
export function splitRequirementsSection(text) {
|
|
195
|
-
const fenced = fenceTracker();
|
|
196
|
-
let inSection = false;
|
|
197
|
-
let sectionStart = -1;
|
|
198
|
-
let runStart = -1;
|
|
199
|
-
let sectionEnd = text.length;
|
|
200
|
-
let offset = 0;
|
|
201
|
-
while (offset < text.length) {
|
|
202
|
-
const nl = text.indexOf("\n", offset);
|
|
203
|
-
const lineEnd = nl === -1 ? text.length : nl + 1;
|
|
204
|
-
let line = text.slice(offset, nl === -1 ? text.length : nl);
|
|
205
|
-
if (line.endsWith("\r"))
|
|
206
|
-
line = line.slice(0, -1);
|
|
207
|
-
// Position 0 only, mirroring stripBom: elsewhere U+FEFF is content.
|
|
208
|
-
if (offset === 0 && line.charCodeAt(0) === 0xfeff)
|
|
209
|
-
line = line.slice(1);
|
|
210
|
-
if (!fenced(line)) {
|
|
211
|
-
if (/^##\s+/.test(line) && !line.startsWith("###")) {
|
|
212
|
-
if (inSection) {
|
|
213
|
-
sectionEnd = offset;
|
|
214
|
-
inSection = false;
|
|
215
|
-
}
|
|
216
|
-
else if (sectionStart === -1 && isRequirementsHeading(line)) {
|
|
217
|
-
sectionStart = offset;
|
|
218
|
-
inSection = true;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
else if (inSection && runStart === -1 && REQ_RE.test(line)) {
|
|
222
|
-
runStart = offset;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
offset = lineEnd;
|
|
226
|
-
}
|
|
227
|
-
if (sectionStart === -1)
|
|
228
|
-
return null;
|
|
229
|
-
const cut = runStart === -1 ? sectionEnd : runStart;
|
|
230
|
-
return { head: text.slice(0, cut), run: text.slice(cut, sectionEnd), tail: text.slice(sectionEnd) };
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* A requirement document from disk as text — decoded, or refused by name.
|
|
234
|
-
*
|
|
235
|
-
* The read that belongs with the parser, because `readFile(path, "utf8")` and
|
|
236
|
-
* `parseRequirements` together are a trap: a UTF-16 spec.md decodes to a string
|
|
237
|
-
* in which no line matches `^###\s+Requirement:`, so the parser returns [] and
|
|
238
|
-
* the document grades as an empty baseline instead of an unreadable one. Every
|
|
239
|
-
* question loam asks of a spec — is it covered, does the delta merge, what does
|
|
240
|
-
* the C4 spine touch — is then answered about a file nobody read.
|
|
241
|
-
*
|
|
242
|
-
* It THROWS (`NotUtf8DocumentError`, which names the path), unlike
|
|
243
|
-
* `readFrontmatter` next door, because the two failures are not the same
|
|
244
|
-
* failure. Frontmatter is read by the fleet enumeration, where one bad file
|
|
245
|
-
* must not cost the other 119 services, so there it is a flag. A requirement
|
|
246
|
-
* document is read by ONE target, and the caller is the only one who knows what
|
|
247
|
-
* to do about it: a read-only command turns it into a finding on that target
|
|
248
|
-
* (validate's `guarded`, which names the file and keeps grading the rest), a
|
|
249
|
-
* command that writes refuses outright rather than computing over mojibake.
|
|
250
|
-
*/
|
|
251
|
-
export async function readRequirementsDocument(path) {
|
|
252
|
-
return decodeDocument(await readFile(path), path);
|
|
253
|
-
}
|
|
254
|
-
/** Parse all requirements (with their scenarios and delta kind) from a markdown doc. */
|
|
255
|
-
export function parseRequirements(md) {
|
|
256
|
-
const out = [];
|
|
257
|
-
let kind = "BASE";
|
|
258
|
-
let section;
|
|
259
|
-
let req = null;
|
|
260
|
-
let scn = null;
|
|
261
|
-
const fenced = fenceTracker();
|
|
262
|
-
for (const [index, line] of stripBom(md).split(/\r?\n/).entries()) {
|
|
263
|
-
if (fenced(line)) {
|
|
264
|
-
// Fenced content, marker included: body of whatever is open, never structure.
|
|
265
|
-
if (scn)
|
|
266
|
-
scn.lines.push(line);
|
|
267
|
-
else if (req)
|
|
268
|
-
req.text.push(line);
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
// Any H2 heading ends the current requirement/scenario capture — section prose
|
|
272
|
-
// must not leak into the previous scenario's body.
|
|
273
|
-
if (/^##\s+/.test(line) && !line.startsWith("###")) {
|
|
274
|
-
req = null;
|
|
275
|
-
scn = null;
|
|
276
|
-
section = line.trim();
|
|
277
|
-
const mk = KIND_RE.exec(line);
|
|
278
|
-
// A non-delta H2 (## Notes, ## Requirements…) starts an unrelated section:
|
|
279
|
-
// requirements under it are plain BASE, not part of a stale delta section.
|
|
280
|
-
kind = mk ? mk[1].toUpperCase() : "BASE";
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
const mr = REQ_RE.exec(line);
|
|
284
|
-
if (mr) {
|
|
285
|
-
req = {
|
|
286
|
-
kind,
|
|
287
|
-
name: mr[1],
|
|
288
|
-
text: [],
|
|
289
|
-
operations: [],
|
|
290
|
-
covers: [],
|
|
291
|
-
publishes: [],
|
|
292
|
-
consumes: [],
|
|
293
|
-
scenarios: [],
|
|
294
|
-
section,
|
|
295
|
-
line: index + 1,
|
|
296
|
-
};
|
|
297
|
-
out.push(req);
|
|
298
|
-
scn = null;
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
const ms = SCN_RE.exec(line);
|
|
302
|
-
if (ms && req) {
|
|
303
|
-
scn = { name: ms[1], lines: [] };
|
|
304
|
-
req.scenarios.push(scn);
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
if (scn) {
|
|
308
|
-
scn.lines.push(line);
|
|
309
|
-
}
|
|
310
|
-
else if (req) {
|
|
311
|
-
req.text.push(line);
|
|
312
|
-
const mi = REQUIREMENT_ID_LINE_RE.exec(line);
|
|
313
|
-
if (mi)
|
|
314
|
-
req.id = mi[1].trim();
|
|
315
|
-
// Same keep-last quirk as Operations:/Covers: below — a repeated line is
|
|
316
|
-
// a document problem, reported as one (`delta.baseline-invalid`), never
|
|
317
|
-
// resolved differently by whoever happens to be reading.
|
|
318
|
-
const mb = BASED_ON_LINE_RE.exec(line);
|
|
319
|
-
if (mb)
|
|
320
|
-
req.basedOn = mb[1].trim();
|
|
321
|
-
const mo = /^\s*Operations?:\s*(.+?)\s*$/i.exec(line);
|
|
322
|
-
if (mo)
|
|
323
|
-
req.operations = mo[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
324
|
-
// The Covers: mirror — same placement (requirement body, not a scenario),
|
|
325
|
-
// same comma grammar, and deliberately the same quirk: a second line
|
|
326
|
-
// REPLACES the first (assignment, not append), so the two lists can never
|
|
327
|
-
// drift apart in how they read a repeated line.
|
|
328
|
-
const mc = /^\s*Covers?:\s*(.+?)\s*$/i.exec(line);
|
|
329
|
-
if (mc)
|
|
330
|
-
req.covers = mc[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
331
|
-
// The event axis, same grammar and the same keep-last quirk again. Note
|
|
332
|
-
// what is NOT happening here: the line stays in `req.text` like every
|
|
333
|
-
// other body line (the push above is unconditional), so it round-trips
|
|
334
|
-
// through `serializeRequirements` and rides inside `requirementDigest`
|
|
335
|
-
// exactly as `Operations:` does. That is what makes this parse purely
|
|
336
|
-
// additive — no living document's digest moves, and no `Based-On:` pin
|
|
337
|
-
// goes stale because loam learned to read one more line.
|
|
338
|
-
const mp = /^\s*Publishes?:\s*(.+?)\s*$/i.exec(line);
|
|
339
|
-
if (mp)
|
|
340
|
-
req.publishes = mp[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
341
|
-
const mcon = /^\s*Consumes?:\s*(.+?)\s*$/i.exec(line);
|
|
342
|
-
if (mcon)
|
|
343
|
-
req.consumes = mcon[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return out;
|
|
347
|
-
}
|
|
348
|
-
/** Requirements with no scenario — the OpenSpec coverage rule (every requirement needs ≥1). */
|
|
349
|
-
export function requirementsMissingScenarios(reqs) {
|
|
350
|
-
return reqs.filter((r) => r.kind !== "REMOVED" && r.scenarios.length === 0);
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Scenarios that satisfy the coverage rule above and test nothing.
|
|
354
|
-
*
|
|
355
|
-
* `requirementsMissingScenarios` counts HEADINGS, so a `#### Scenario:` with no
|
|
356
|
-
* recognizable Given/When/Then line satisfied the fleet gate's coverage claim
|
|
357
|
-
* outright — and it is not a hypothetical shape: it is what `loam new`
|
|
358
|
-
* scaffolds and what an agent writes when it puts the criteria in prose.
|
|
359
|
-
* Downstream the same emptiness is fatal twice over: cucumber runs a stepless
|
|
360
|
-
* scenario vacuously green, and `loam verify --results` can never confirm it.
|
|
361
|
-
*
|
|
362
|
-
* The detector is `loam gherkin`'s own — `scenarioGherkin` decides what a step
|
|
363
|
-
* IS, and a second opinion here would mean the gate and the emitted suite could
|
|
364
|
-
* disagree about whether a scenario has any. That import is the one place these
|
|
365
|
-
* two modules are circular (gherkin.ts parses requirements); both sides export
|
|
366
|
-
* only function declarations, which are hoisted, so neither evaluation order
|
|
367
|
-
* can observe a half-built module.
|
|
368
|
-
*/
|
|
369
|
-
export function steplessScenarios(reqs) {
|
|
370
|
-
const out = [];
|
|
371
|
-
for (const r of reqs) {
|
|
372
|
-
if (r.kind === "REMOVED")
|
|
373
|
-
continue;
|
|
374
|
-
for (const s of r.scenarios) {
|
|
375
|
-
if (scenarioGherkin(s.lines).steps.length === 0)
|
|
376
|
-
out.push({ requirement: r.name, scenario: s.name });
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
return out;
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* The stepless half of the coverage check, as findings — the missing-scenario
|
|
383
|
-
* half stays where it is (`requirementsMissingScenarios`), because the two are
|
|
384
|
-
* different states with different fixes: one requirement owes acceptance
|
|
385
|
-
* criteria, the other owes STEPS in criteria somebody has already written.
|
|
386
|
-
*
|
|
387
|
-
* `label` is the coverage check's own label (`payment-service: requirements`),
|
|
388
|
-
* so both halves of one document name it the same way.
|
|
389
|
-
*/
|
|
390
|
-
export function steplessFindings(label, subject, reqs) {
|
|
391
|
-
const bare = steplessScenarios(reqs);
|
|
392
|
-
if (bare.length === 0)
|
|
393
|
-
return [];
|
|
394
|
-
return [
|
|
395
|
-
{
|
|
396
|
-
severity: "error",
|
|
397
|
-
code: "requirements.stepless-scenario",
|
|
398
|
-
subject,
|
|
399
|
-
message: `${label}: ${bare.length} scenario(s) have a heading and no recognizable steps — ` +
|
|
400
|
-
`they satisfy the coverage rule and test nothing. Cucumber runs a stepless scenario vacuously green and ` +
|
|
401
|
-
`\`loam verify --results\` can never confirm it. Write the body as \`- **Given/When/Then**\` bullets.`,
|
|
402
|
-
details: bare.map((s) => `${s.requirement} — ${s.scenario}`),
|
|
403
|
-
text: { detailPrefix: "- " },
|
|
404
|
-
},
|
|
405
|
-
];
|
|
406
|
-
}
|
|
407
|
-
/** Serialize requirements back to OpenSpec markdown (`### Requirement:` + `#### Scenario:`). */
|
|
408
|
-
export function serializeRequirements(reqs) {
|
|
409
|
-
// Framing (blank lines between sections) is normalized here; body content is
|
|
410
|
-
// only edge-trimmed, never collapsed — blank lines inside a scenario (e.g. in
|
|
411
|
-
// fenced code blocks) are verbatim content.
|
|
412
|
-
const chunks = [];
|
|
413
|
-
for (const r of reqs) {
|
|
414
|
-
const chunk = [`### Requirement: ${r.name}`];
|
|
415
|
-
const body = [...r.text];
|
|
416
|
-
// Parsed documents already carry the authored line in `text`. This branch
|
|
417
|
-
// also makes programmatically constructed Requirements serialize fully.
|
|
418
|
-
if (r.id !== undefined && !body.some((line) => REQUIREMENT_ID_LINE_RE.test(line))) {
|
|
419
|
-
body.unshift(`Requirement-ID: ${r.id}`);
|
|
420
|
-
}
|
|
421
|
-
// Directly under the identity it pins, which is where `loam rebase` writes
|
|
422
|
-
// it too — the two lines are read together, so they are never apart.
|
|
423
|
-
if (r.basedOn !== undefined && !body.some((line) => BASED_ON_LINE_RE.test(line))) {
|
|
424
|
-
body.splice(body.findIndex((line) => REQUIREMENT_ID_LINE_RE.test(line)) + 1, 0, `Based-On: ${r.basedOn}`);
|
|
425
|
-
}
|
|
426
|
-
const text = body.join("\n").trim();
|
|
427
|
-
if (text)
|
|
428
|
-
chunk.push("", text);
|
|
429
|
-
for (const s of r.scenarios) {
|
|
430
|
-
chunk.push("", `#### Scenario: ${s.name}`);
|
|
431
|
-
const body = s.lines.join("\n").trim();
|
|
432
|
-
if (body)
|
|
433
|
-
chunk.push(body);
|
|
434
|
-
}
|
|
435
|
-
chunks.push(chunk.join("\n"));
|
|
436
|
-
}
|
|
437
|
-
return chunks.join("\n\n").trim() + "\n";
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* A delta requirement as it lands in the living document, with the bookkeeping
|
|
441
|
-
* that belongs to the DELTA left behind: the `Based-On:` pin (a claim about
|
|
442
|
-
* which living version this was written against — meaningless once it IS the
|
|
443
|
-
* living version, and poison for the next feature's baseline, which would then
|
|
444
|
-
* hash the previous feature's pin) and the line it was parsed from.
|
|
445
|
-
*/
|
|
446
|
-
function asLiving(d) {
|
|
447
|
-
const { line: _dropped, ...rest } = withoutBaseline(d);
|
|
448
|
-
return { ...rest, kind: "BASE" };
|
|
449
|
-
}
|
|
450
|
-
/** Apply a feature's ADDED/MODIFIED/REMOVED requirements onto a living requirement set. */
|
|
451
|
-
export function applyRequirementDelta(living, delta) {
|
|
452
|
-
const malformed = [...requirementIdProblems(living), ...requirementIdProblems(delta)];
|
|
453
|
-
if (malformed.length > 0) {
|
|
454
|
-
throw new Error("cannot merge requirements with invalid, repeated, or duplicate Requirement-ID declarations");
|
|
455
|
-
}
|
|
456
|
-
let result = living.map((r) => ({ ...r, kind: "BASE" }));
|
|
457
|
-
for (const d of delta) {
|
|
458
|
-
// BASE is not a delta kind — a requirement outside an ADDED/MODIFIED/REMOVED
|
|
459
|
-
// section (e.g. quoted under ## Notes) is documentation, not a change.
|
|
460
|
-
if (d.kind === "BASE")
|
|
461
|
-
continue;
|
|
462
|
-
if (d.id !== undefined) {
|
|
463
|
-
const idMatches = result
|
|
464
|
-
.map((r, i) => (r.id === d.id ? i : -1))
|
|
465
|
-
.filter((i) => i >= 0);
|
|
466
|
-
const nameMatches = result
|
|
467
|
-
.map((r, i) => (r.name === d.name ? i : -1))
|
|
468
|
-
.filter((i) => i >= 0);
|
|
469
|
-
const otherNameMatches = nameMatches.filter((i) => !idMatches.includes(i));
|
|
470
|
-
if (idMatches.length > 1 || otherNameMatches.length > 0) {
|
|
471
|
-
throw new Error(`ambiguous requirement identity for '${d.name}' (${d.id}): Requirement-ID and heading select different living requirements`);
|
|
472
|
-
}
|
|
473
|
-
if (d.kind === "REMOVED") {
|
|
474
|
-
const selected = new Set(idMatches);
|
|
475
|
-
result = result.filter((_, i) => !selected.has(i));
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
const merged = asLiving(d);
|
|
479
|
-
if (idMatches.length === 1)
|
|
480
|
-
result[idMatches[0]] = merged;
|
|
481
|
-
else
|
|
482
|
-
result.push(merged);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
else if (d.kind === "REMOVED") {
|
|
486
|
-
result = result.filter((r) => r.name !== d.name);
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
const i = result.findIndex((r) => r.name === d.name);
|
|
490
|
-
// A legacy delta may still modify an ID-bearing living requirement by
|
|
491
|
-
// its exact heading. Keep the identity while the repository migrates.
|
|
492
|
-
const inheritedId = i >= 0 ? result[i].id : undefined;
|
|
493
|
-
const merged = {
|
|
494
|
-
...asLiving(d),
|
|
495
|
-
...(inheritedId === undefined ? {} : { id: inheritedId }),
|
|
496
|
-
};
|
|
497
|
-
if (i >= 0)
|
|
498
|
-
result[i] = merged;
|
|
499
|
-
else
|
|
500
|
-
result.push(merged);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return result;
|
|
504
|
-
}
|
|
505
|
-
//# sourceMappingURL=spec.js.map
|