@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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { ASYNCAPI_BASELINES_KEY } from "../../asyncapi/digest.js";
|
|
2
|
+
import {} from "../../asyncapi/model.js";
|
|
3
|
+
import { asyncapiSurfaces } from "../../asyncapi/baseline/surfaces.js";
|
|
4
|
+
import { entryFor, readBaselineRecord, surfaceIn } from "../../openapi/baseline/record.js";
|
|
5
|
+
import { classifyBaselineDigests, OPERATION_DIGEST_LENGTH, OPERATION_DIGEST_RE, valueDigest, } from "../../openapi/digest.js";
|
|
6
|
+
/**
|
|
7
|
+
* One service's baselines against the living contract — openapi/baseline/gate.ts's
|
|
8
|
+
* verdicts, over BOTH halves of this axis: the slot pins in-value, and the
|
|
9
|
+
* component surfaces the root `x-loam-baselines` record pins. One function
|
|
10
|
+
* because the unpinned count is shared (see below), and because a delta is
|
|
11
|
+
* graded once or the two halves drift into different ideas of what it restates.
|
|
12
|
+
*/
|
|
13
|
+
export function gradeBaselines(input, issues) {
|
|
14
|
+
const { featSlots, livingSlots, svc, featureId } = input;
|
|
15
|
+
let unpinned = 0;
|
|
16
|
+
for (const slot of featSlots) {
|
|
17
|
+
// A removal marker is not a restatement of anything; its own exactness
|
|
18
|
+
// checks guard the slot.
|
|
19
|
+
if (slot.remove)
|
|
20
|
+
continue;
|
|
21
|
+
const label = `${slot.section}.${slot.key}`;
|
|
22
|
+
const living = livingSlots.get(`${slot.section}\0${slot.key}`);
|
|
23
|
+
if (slot.basedOn !== undefined && !OPERATION_DIGEST_RE.test(slot.basedOn)) {
|
|
24
|
+
issues.push({
|
|
25
|
+
severity: "error",
|
|
26
|
+
code: "asyncapi.baseline-invalid",
|
|
27
|
+
subject: svc,
|
|
28
|
+
message: `${svc}: ${label} has invalid x-loam-based-on '${slot.basedOn}' — expected ${OPERATION_DIGEST_LENGTH} lowercase hex characters, as \`loam rebase\` writes them`,
|
|
29
|
+
});
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, living?.digest);
|
|
33
|
+
if (verdict === "unfounded") {
|
|
34
|
+
issues.push({
|
|
35
|
+
severity: "error",
|
|
36
|
+
code: "asyncapi.baseline-invalid",
|
|
37
|
+
subject: svc,
|
|
38
|
+
message: `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — a new channel, operation or message has no living version to be based on; drop the marker`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else if (verdict === "stale") {
|
|
42
|
+
issues.push({
|
|
43
|
+
severity: "error",
|
|
44
|
+
code: "asyncapi.baseline-stale",
|
|
45
|
+
subject: svc,
|
|
46
|
+
message: `${svc}: ${label} was written against living version ${slot.basedOn}, but the living contract now holds ${living.digest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living slot, fold in what you still mean, then run \`loam rebase ${featureId}\`.`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else if (verdict === "unpinned" && living !== undefined) {
|
|
50
|
+
// Counted, not listed — a delta quotes most of the contract it
|
|
51
|
+
// restates, and one warning per quote teaches people to filter the
|
|
52
|
+
// code out (openapi.baseline-missing's doctrine, verbatim).
|
|
53
|
+
unpinned += 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// The surface half's unpinned count folds into the SAME counter, not a
|
|
57
|
+
// second warning: "counted, not listed" is the doctrine above, and one
|
|
58
|
+
// warning per axis-half would have a delta over a schema-heavy contract
|
|
59
|
+
// print two findings naming the same one command.
|
|
60
|
+
unpinned += gradeSurfaces(input, issues);
|
|
61
|
+
if (unpinned > 0) {
|
|
62
|
+
issues.push({
|
|
63
|
+
severity: "warn",
|
|
64
|
+
gates: true,
|
|
65
|
+
code: "asyncapi.baseline-missing",
|
|
66
|
+
subject: svc,
|
|
67
|
+
message: `${svc}: ${unpinned} slot(s) and component surface(s) in this feature's asyncapi.yaml carry no baseline pin (x-loam-based-on / ${ASYNCAPI_BASELINES_KEY}), so the merge cannot tell which ones this delta EDITS from the ones it merely restates — it will upsert all of them, reverting anything that landed on the restated ones. Run \`loam rebase ${featureId} --service ${svc}\`, or archive with --approve to merge unpinned deliberately.`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The surface half: every `components/<kind>/<name>` outside `messages`,
|
|
73
|
+
* graded from the root `x-loam-baselines` record by the same classifier the
|
|
74
|
+
* slots go through. Returns the unpinned count so the caller can fold it into
|
|
75
|
+
* the one per-service warning rather than emitting a second.
|
|
76
|
+
*
|
|
77
|
+
* The codes are REUSED rather than multiplied — `asyncapi.baseline-invalid`
|
|
78
|
+
* and `asyncapi.baseline-stale` say exactly the same thing about a surface as
|
|
79
|
+
* about a slot, and a caller that branches on them wants one branch.
|
|
80
|
+
*/
|
|
81
|
+
function gradeSurfaces(input, issues) {
|
|
82
|
+
const { featPlain, livingPlain, svc, featureId } = input;
|
|
83
|
+
const invalid = (message) => {
|
|
84
|
+
issues.push({ severity: "error", code: "asyncapi.baseline-invalid", subject: svc, message: `${svc}: ${message}` });
|
|
85
|
+
};
|
|
86
|
+
const { record, problems } = readBaselineRecord(featPlain);
|
|
87
|
+
for (const problem of problems) {
|
|
88
|
+
invalid(`${problem} — the record is \`loam rebase\`'s bookkeeping and cannot be graded as it stands; re-run \`loam rebase ${featureId}\` to rebuild it`);
|
|
89
|
+
}
|
|
90
|
+
const surfaces = asyncapiSurfaces(featPlain);
|
|
91
|
+
// An entry pinning a surface the delta does not declare is a claim about
|
|
92
|
+
// nothing — usually one the author deleted after rebasing. Distinct from
|
|
93
|
+
// `unfounded` below, where the surface IS declared and the LIVING side is
|
|
94
|
+
// what vanished. A `pathItems` section is the same fault at the axis level:
|
|
95
|
+
// an AsyncAPI document has no path items, so the whole section pins nothing,
|
|
96
|
+
// and rebase's wholesale rebuild would drop it without a word.
|
|
97
|
+
const declared = new Set(surfaces.map((s) => s.id));
|
|
98
|
+
for (const id of Object.keys(record.components)) {
|
|
99
|
+
if (declared.has(id))
|
|
100
|
+
continue;
|
|
101
|
+
invalid(`${ASYNCAPI_BASELINES_KEY} pins component '${id}', but this feature's asyncapi.yaml does not declare it — re-run \`loam rebase ${featureId}\` to rebuild the record`);
|
|
102
|
+
}
|
|
103
|
+
for (const path of Object.keys(record.pathItems)) {
|
|
104
|
+
invalid(`${ASYNCAPI_BASELINES_KEY} pins path item '${path}', but an AsyncAPI document has no path items — the section pins nothing; re-run \`loam rebase ${featureId}\` to rebuild the record`);
|
|
105
|
+
}
|
|
106
|
+
let unpinned = 0;
|
|
107
|
+
for (const s of surfaces) {
|
|
108
|
+
const entry = entryFor(record, s);
|
|
109
|
+
const living = surfaceIn(livingPlain, s);
|
|
110
|
+
const verdict = classifyBaselineDigests(entry, valueDigest(s.value), living.found ? valueDigest(living.value) : undefined);
|
|
111
|
+
if (verdict === "unpinned") {
|
|
112
|
+
// Only a surface the living contract ALREADY has can be reverted by an
|
|
113
|
+
// unpinned merge; a genuinely new schema never trips the counter.
|
|
114
|
+
if (living.found)
|
|
115
|
+
unpinned += 1;
|
|
116
|
+
}
|
|
117
|
+
else if (verdict === "stale") {
|
|
118
|
+
issues.push({
|
|
119
|
+
severity: "error",
|
|
120
|
+
code: "asyncapi.baseline-stale",
|
|
121
|
+
subject: svc,
|
|
122
|
+
message: `${svc}: component '${s.id}' was pinned to living version ${entry}, but the living contract now holds ${valueDigest(living.value)} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living value, fold in what you still mean, then run \`loam rebase ${featureId}\`.`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
else if (verdict === "unfounded") {
|
|
126
|
+
invalid(`component '${s.id}' is pinned to ${entry}, but the living contract no longer has that surface — the pin is structurally unresolvable; re-read the living contract, then run \`loam rebase ${featureId}\``);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return unpinned;
|
|
130
|
+
}
|
|
131
|
+
/** Marker exactness: the slot must exist, and a message marker must name the living declaration. */
|
|
132
|
+
export function gradeMarkers(input, svc, issues) {
|
|
133
|
+
const { featDoc, livingDoc, featSlots, livingSlots } = input;
|
|
134
|
+
for (const slot of featSlots) {
|
|
135
|
+
if (!slot.remove)
|
|
136
|
+
continue;
|
|
137
|
+
const label = `${slot.section}.${slot.key}`;
|
|
138
|
+
const living = livingSlots.get(`${slot.section}\0${slot.key}`);
|
|
139
|
+
if (living === undefined) {
|
|
140
|
+
issues.push({
|
|
141
|
+
severity: "error",
|
|
142
|
+
code: "asyncapi.remove-target-missing",
|
|
143
|
+
subject: svc,
|
|
144
|
+
message: `${svc}: removal marker at ${label} addresses no living slot — the contract has nothing there to retire; update the stale marker to the current key, or drop it if the slot is already gone`,
|
|
145
|
+
});
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (slot.section !== "components.messages")
|
|
149
|
+
continue;
|
|
150
|
+
// Removal is exact for messages the way it is for operations: the
|
|
151
|
+
// marker names both the key and the message identity it expects to
|
|
152
|
+
// delete, so a slot whose NAME moved under the delta is caught here
|
|
153
|
+
// rather than deleting somebody else's message.
|
|
154
|
+
const markerName = featDoc.messages.find((m) => m.remove === true && m.slot === label)?.name ?? slot.key;
|
|
155
|
+
const livingName = livingDoc.messages.find((m) => m.slot === label)?.name ?? slot.key;
|
|
156
|
+
if (markerName !== livingName) {
|
|
157
|
+
issues.push({
|
|
158
|
+
severity: "error",
|
|
159
|
+
code: "asyncapi.remove-target-mismatch",
|
|
160
|
+
subject: svc,
|
|
161
|
+
message: `${svc}: removal marker at ${label} names message '${markerName}', but the living declaration there is '${livingName}' — loam never deletes a different message occupying the slot`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=grades.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grades.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/grades.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The per-slot grades of one service's pair of event contracts: baseline\n * pins and removal-marker exactness. Split from ./declared.ts along the\n * subject seam — that module owns the WALK (which files, which services,\n * which sets), these functions own the verdict each slot gets once both\n * sides are in hand — when the walk grew past the line limit; the split\n * moved them verbatim.\n */\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { ASYNCAPI_BASELINES_KEY, type AsyncapiSlot } from \"../../asyncapi/digest.js\";\nimport { type AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport { asyncapiSurfaces } from \"../../asyncapi/baseline/surfaces.js\";\nimport { entryFor, readBaselineRecord, surfaceIn } from \"../../openapi/baseline/record.js\";\nimport {\n classifyBaselineDigests,\n OPERATION_DIGEST_LENGTH,\n OPERATION_DIGEST_RE,\n valueDigest,\n} from \"../../openapi/digest.js\";\n\n/** Everything the two baseline graders need about one service's pair of contracts. */\nexport interface AsyncapiBaselineInput {\n featSlots: AsyncapiSlot[];\n livingSlots: Map<string, AsyncapiSlot>;\n /** The FEATURE document's resolved parse — where the record and the surfaces are read from. */\n featPlain: unknown;\n /** The LIVING document's resolved parse; `{}` where no living contract exists. */\n livingPlain: unknown;\n svc: string;\n featureId: string;\n}\n\n/**\n * One service's baselines against the living contract — openapi/baseline/gate.ts's\n * verdicts, over BOTH halves of this axis: the slot pins in-value, and the\n * component surfaces the root `x-loam-baselines` record pins. One function\n * because the unpinned count is shared (see below), and because a delta is\n * graded once or the two halves drift into different ideas of what it restates.\n */\nexport function gradeBaselines(input: AsyncapiBaselineInput, issues: Issue[]): void {\n const { featSlots, livingSlots, svc, featureId } = input;\n let unpinned = 0;\n for (const slot of featSlots) {\n // A removal marker is not a restatement of anything; its own exactness\n // checks guard the slot.\n if (slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(`${slot.section}\\0${slot.key}`);\n if (slot.basedOn !== undefined && !OPERATION_DIGEST_RE.test(slot.basedOn)) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: `${svc}: ${label} has invalid x-loam-based-on '${slot.basedOn}' — expected ${OPERATION_DIGEST_LENGTH} lowercase hex characters, as \\`loam rebase\\` writes them`,\n });\n continue;\n }\n const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, living?.digest);\n if (verdict === \"unfounded\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — a new channel, operation or message has no living version to be based on; drop the marker`,\n });\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: ${label} was written against living version ${slot.basedOn}, but the living contract now holds ${living!.digest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living slot, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unpinned\" && living !== undefined) {\n // Counted, not listed — a delta quotes most of the contract it\n // restates, and one warning per quote teaches people to filter the\n // code out (openapi.baseline-missing's doctrine, verbatim).\n unpinned += 1;\n }\n }\n // The surface half's unpinned count folds into the SAME counter, not a\n // second warning: \"counted, not listed\" is the doctrine above, and one\n // warning per axis-half would have a delta over a schema-heavy contract\n // print two findings naming the same one command.\n unpinned += gradeSurfaces(input, issues);\n if (unpinned > 0) {\n issues.push({\n severity: \"warn\",\n gates: true,\n code: \"asyncapi.baseline-missing\",\n subject: svc,\n message: `${svc}: ${unpinned} slot(s) and component surface(s) in this feature's asyncapi.yaml carry no baseline pin (x-loam-based-on / ${ASYNCAPI_BASELINES_KEY}), so the merge cannot tell which ones this delta EDITS from the ones it merely restates — it will upsert all of them, reverting anything that landed on the restated ones. Run \\`loam rebase ${featureId} --service ${svc}\\`, or archive with --approve to merge unpinned deliberately.`,\n });\n }\n}\n\n/**\n * The surface half: every `components/<kind>/<name>` outside `messages`,\n * graded from the root `x-loam-baselines` record by the same classifier the\n * slots go through. Returns the unpinned count so the caller can fold it into\n * the one per-service warning rather than emitting a second.\n *\n * The codes are REUSED rather than multiplied — `asyncapi.baseline-invalid`\n * and `asyncapi.baseline-stale` say exactly the same thing about a surface as\n * about a slot, and a caller that branches on them wants one branch.\n */\nfunction gradeSurfaces(input: AsyncapiBaselineInput, issues: Issue[]): number {\n const { featPlain, livingPlain, svc, featureId } = input;\n const invalid = (message: string): void => {\n issues.push({ severity: \"error\", code: \"asyncapi.baseline-invalid\", subject: svc, message: `${svc}: ${message}` });\n };\n const { record, problems } = readBaselineRecord(featPlain);\n for (const problem of problems) {\n invalid(`${problem} — the record is \\`loam rebase\\`'s bookkeeping and cannot be graded as it stands; re-run \\`loam rebase ${featureId}\\` to rebuild it`);\n }\n const surfaces = asyncapiSurfaces(featPlain);\n // An entry pinning a surface the delta does not declare is a claim about\n // nothing — usually one the author deleted after rebasing. Distinct from\n // `unfounded` below, where the surface IS declared and the LIVING side is\n // what vanished. A `pathItems` section is the same fault at the axis level:\n // an AsyncAPI document has no path items, so the whole section pins nothing,\n // and rebase's wholesale rebuild would drop it without a word.\n const declared = new Set(surfaces.map((s) => s.id));\n for (const id of Object.keys(record.components)) {\n if (declared.has(id)) continue;\n invalid(`${ASYNCAPI_BASELINES_KEY} pins component '${id}', but this feature's asyncapi.yaml does not declare it — re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n for (const path of Object.keys(record.pathItems)) {\n invalid(`${ASYNCAPI_BASELINES_KEY} pins path item '${path}', but an AsyncAPI document has no path items — the section pins nothing; re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n\n let unpinned = 0;\n for (const s of surfaces) {\n const entry = entryFor(record, s);\n const living = surfaceIn(livingPlain, s);\n const verdict = classifyBaselineDigests(\n entry,\n valueDigest(s.value),\n living.found ? valueDigest(living.value) : undefined,\n );\n if (verdict === \"unpinned\") {\n // Only a surface the living contract ALREADY has can be reverted by an\n // unpinned merge; a genuinely new schema never trips the counter.\n if (living.found) unpinned += 1;\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: component '${s.id}' was pinned to living version ${entry}, but the living contract now holds ${valueDigest(living.value)} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living value, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unfounded\") {\n invalid(`component '${s.id}' is pinned to ${entry}, but the living contract no longer has that surface — the pin is structurally unresolvable; re-read the living contract, then run \\`loam rebase ${featureId}\\``);\n }\n }\n return unpinned;\n}\n\n/** Marker exactness: the slot must exist, and a message marker must name the living declaration. */\nexport function gradeMarkers(\n input: { featDoc: AsyncapiDoc; livingDoc: AsyncapiDoc; featSlots: AsyncapiSlot[]; livingSlots: Map<string, AsyncapiSlot> },\n svc: string,\n issues: Issue[],\n): void {\n const { featDoc, livingDoc, featSlots, livingSlots } = input;\n for (const slot of featSlots) {\n if (!slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(`${slot.section}\\0${slot.key}`);\n if (living === undefined) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-missing\",\n subject: svc,\n message: `${svc}: removal marker at ${label} addresses no living slot — the contract has nothing there to retire; update the stale marker to the current key, or drop it if the slot is already gone`,\n });\n continue;\n }\n if (slot.section !== \"components.messages\") continue;\n // Removal is exact for messages the way it is for operations: the\n // marker names both the key and the message identity it expects to\n // delete, so a slot whose NAME moved under the delta is caught here\n // rather than deleting somebody else's message.\n const markerName = featDoc.messages.find((m) => m.remove === true && m.slot === label)?.name ?? slot.key;\n const livingName = livingDoc.messages.find((m) => m.slot === label)?.name ?? slot.key;\n if (markerName !== livingName) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-mismatch\",\n subject: svc,\n message: `${svc}: removal marker at ${label} names message '${markerName}', but the living declaration there is '${livingName}' — loam never deletes a different message occupying the slot`,\n });\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The event axis's fleet questions, each read lazily — ../lookups.ts's
|
|
3
|
+
* pattern on the message join.
|
|
4
|
+
*
|
|
5
|
+
* Both reach outside the delta: the consumer scan is a landscape spin-up
|
|
6
|
+
* plus a walk of every other service's living requirements, and the
|
|
7
|
+
* in-flight scan reads every active feature's asyncapi delta. Closures over
|
|
8
|
+
* a cache rather than arguments computed up front, so each is paid only
|
|
9
|
+
* when its question is first asked — but the two prices differ, and the
|
|
10
|
+
* difference is deliberate: the consumer scan is paid only by a feature
|
|
11
|
+
* that actually retires production, while the in-flight scan is paid by ANY
|
|
12
|
+
* feature that declares a message at all, because
|
|
13
|
+
* `asyncapi.message-conflict`'s question ("does an in-flight feature define
|
|
14
|
+
* this elsewhere?") is asked of every healthy declaration — unlike the
|
|
15
|
+
* OpenAPI mirror, whose `definedElsewhere` is reached only inside a breach
|
|
16
|
+
* branch. That standing cost matters most on `archive`'s gate, which runs
|
|
17
|
+
* without a FleetContext, so each read here is a real parse.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync } from "node:fs";
|
|
20
|
+
import { readFile } from "node:fs/promises";
|
|
21
|
+
import { loadFile } from "../../c4/likec4.js";
|
|
22
|
+
import { serviceResolver } from "../../c4/resolve/service.js";
|
|
23
|
+
import {} from "../../kernel/ids/service.js";
|
|
24
|
+
import { featureSpecPaths, landscapePath, SPEC_AXES } from "../../repo/paths.js";
|
|
25
|
+
import { enumeratedServiceIds, locateServicePaths } from "../../repo/service-target.js";
|
|
26
|
+
import { listFeatures } from "../../repo/repo.js";
|
|
27
|
+
import { readAsyncapi } from "../../asyncapi/read.js";
|
|
28
|
+
import { parseRequirements } from "../../document/parse.js";
|
|
29
|
+
import {} from "../../document/spec.js";
|
|
30
|
+
export function eventLookups(scope, context) {
|
|
31
|
+
const { docsDir, featureId } = scope;
|
|
32
|
+
// The enumerated fleet, read once and shared by both halves of the
|
|
33
|
+
// consumer scan — ../lookups.ts's discipline, verbatim.
|
|
34
|
+
let serviceIds;
|
|
35
|
+
const enumeratedServices = async () => (serviceIds ??= await enumeratedServiceIds(docsDir, context));
|
|
36
|
+
// Living requirements per service, BOTH axes: `Publishes:`/`Consumes:`
|
|
37
|
+
// live in arch.spec.md at least as often as in spec.md (the outbox
|
|
38
|
+
// requirement), so a scan of spec.md alone would answer "nobody consumes
|
|
39
|
+
// it" over the file most authors use.
|
|
40
|
+
const livingReqs = new Map();
|
|
41
|
+
const livingRequirements = async (service) => {
|
|
42
|
+
let reqs = livingReqs.get(service);
|
|
43
|
+
if (reqs === undefined) {
|
|
44
|
+
reqs = [];
|
|
45
|
+
for (const axis of SPEC_AXES) {
|
|
46
|
+
const p = (await locateServicePaths(docsDir, service, context))[axis.key];
|
|
47
|
+
if (!existsSync(p))
|
|
48
|
+
continue;
|
|
49
|
+
reqs.push(...(context === undefined
|
|
50
|
+
? parseRequirements(await readFile(p, "utf8"))
|
|
51
|
+
: await context.readRequirements(p)));
|
|
52
|
+
}
|
|
53
|
+
livingReqs.set(service, reqs);
|
|
54
|
+
}
|
|
55
|
+
return reqs;
|
|
56
|
+
};
|
|
57
|
+
// The landscape half: a `consumes` edge is a consumer somebody drew. The
|
|
58
|
+
// message join is fleet-global (names are not service-scoped), so every
|
|
59
|
+
// edge naming the message counts, whoever its producer is — resolved
|
|
60
|
+
// through the enumerated fleet so a landscape that models CONTAINERS
|
|
61
|
+
// stays visible, ../lookups.ts's edgeConsumers lesson.
|
|
62
|
+
// (`core/diff/victims.ts` mirrors this scan over an already-read fleet —
|
|
63
|
+
// change the rules or the victim strings here and that module owes the
|
|
64
|
+
// same change; a third copy forces the shared extraction, rule 13.)
|
|
65
|
+
let livingLandscape;
|
|
66
|
+
const messageConsumers = async (service, message) => {
|
|
67
|
+
if (livingLandscape === undefined) {
|
|
68
|
+
const path = landscapePath(docsDir);
|
|
69
|
+
livingLandscape = existsSync(path)
|
|
70
|
+
? context === undefined
|
|
71
|
+
? await loadFile(path)
|
|
72
|
+
: await context.loadLikeC4(path)
|
|
73
|
+
: null;
|
|
74
|
+
}
|
|
75
|
+
const out = [];
|
|
76
|
+
// An unreadable landscape proves nothing either way; `landscape.invalid`
|
|
77
|
+
// is validate's finding to make.
|
|
78
|
+
if (livingLandscape !== null && livingLandscape.errors.length === 0) {
|
|
79
|
+
const resolve = serviceResolver(livingLandscape.elements, new Set(await enumeratedServices()));
|
|
80
|
+
for (const r of livingLandscape.relationships) {
|
|
81
|
+
if (r.consumes !== message || resolve(r.target) === service)
|
|
82
|
+
continue;
|
|
83
|
+
out.push(`edge ${resolve(r.source)} → ${resolve(r.target)}${r.title === undefined ? "" : ` ("${r.title}")`}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const other of (await enumeratedServices()).filter((id) => id !== service)) {
|
|
87
|
+
for (const r of await livingRequirements(other)) {
|
|
88
|
+
if (r.consumes.includes(message))
|
|
89
|
+
out.push(`${other}'s living requirement '${r.name}'`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
};
|
|
94
|
+
// What OTHER features in flight declare, per (service, message) — the
|
|
95
|
+
// asyncapi mirror of pending.ts's activeOpAdditions, living HERE because
|
|
96
|
+
// it shares this module's laziness and its consumers. Removal markers do
|
|
97
|
+
// not count: a feature retiring a message is not adding it. First feature
|
|
98
|
+
// wins a clash — one name to archive first is enough to make progress.
|
|
99
|
+
let inFlight = null;
|
|
100
|
+
const messageDefinedElsewhere = async (service, message) => {
|
|
101
|
+
if (inFlight === null) {
|
|
102
|
+
inFlight = new Map();
|
|
103
|
+
for (const feature of await listFeatures(docsDir, {}, context)) {
|
|
104
|
+
if (feature.id === featureId)
|
|
105
|
+
continue;
|
|
106
|
+
for (const svc of feature.services) {
|
|
107
|
+
const doc = await readAsyncapi(featureSpecPaths(feature.dir, svc).asyncapi, context);
|
|
108
|
+
for (const m of doc.messages) {
|
|
109
|
+
if (m.remove === true)
|
|
110
|
+
continue;
|
|
111
|
+
const k = `${svc} ${m.name}`;
|
|
112
|
+
if (!inFlight.has(k))
|
|
113
|
+
inFlight.set(k, feature.id);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return inFlight.get(`${service} ${message}`);
|
|
119
|
+
};
|
|
120
|
+
return { messageConsumers, messageDefinedElsewhere };
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=lookups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookups.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/lookups.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The event axis's fleet questions, each read lazily — ../lookups.ts's\n * pattern on the message join.\n *\n * Both reach outside the delta: the consumer scan is a landscape spin-up\n * plus a walk of every other service's living requirements, and the\n * in-flight scan reads every active feature's asyncapi delta. Closures over\n * a cache rather than arguments computed up front, so each is paid only\n * when its question is first asked — but the two prices differ, and the\n * difference is deliberate: the consumer scan is paid only by a feature\n * that actually retires production, while the in-flight scan is paid by ANY\n * feature that declares a message at all, because\n * `asyncapi.message-conflict`'s question (\"does an in-flight feature define\n * this elsewhere?\") is asked of every healthy declaration — unlike the\n * OpenAPI mirror, whose `definedElsewhere` is reached only inside a breach\n * branch. That standing cost matters most on `archive`'s gate, which runs\n * without a FleetContext, so each read here is a real parse.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { loadFile, type LoadedDoc } from \"../../c4/likec4.js\";\nimport { serviceResolver } from \"../../c4/resolve/service.js\";\nimport { type PathableService } from \"../../kernel/ids/service.js\";\nimport { featureSpecPaths, landscapePath, SPEC_AXES } from \"../../repo/paths.js\";\nimport { enumeratedServiceIds, locateServicePaths } from \"../../repo/service-target.js\";\nimport { listFeatures } from \"../../repo/repo.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport { parseRequirements } from \"../../document/parse.js\";\nimport { type Requirement } from \"../../document/spec.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport type { DocsDir } from \"../../kernel/ids/dirs.js\";\n\n/** What the event checks may ask about the world outside this feature's own files. */\nexport interface EventLookups {\n /**\n * Living consumers of one message a `service` is retiring: landscape\n * `consumes` edges, and other services' living requirements' `Consumes:`\n * lines (spec.md and arch.spec.md both — the outbox file is the canonical\n * home of an event line). The retiring service itself is excluded from\n * both: its own consumption leaves in the same feature, and counting it\n * would gate every self-consuming producer on its own retirement.\n */\n messageConsumers(service: string, message: string): Promise<string[]>;\n /** The in-flight feature (if any) whose asyncapi delta adds or edits (service, message). */\n messageDefinedElsewhere(service: string, message: string): Promise<string | undefined>;\n}\n\n/** The identity of the feature being graded — what the lazy scans exclude. */\nexport interface EventScope {\n docsDir: DocsDir;\n featureId: string;\n}\n\nexport function eventLookups(scope: EventScope, context?: FleetContext): EventLookups {\n const { docsDir, featureId } = scope;\n\n // The enumerated fleet, read once and shared by both halves of the\n // consumer scan — ../lookups.ts's discipline, verbatim.\n let serviceIds: PathableService[] | undefined;\n const enumeratedServices = async (): Promise<PathableService[]> =>\n (serviceIds ??= await enumeratedServiceIds(docsDir, context));\n\n // Living requirements per service, BOTH axes: `Publishes:`/`Consumes:`\n // live in arch.spec.md at least as often as in spec.md (the outbox\n // requirement), so a scan of spec.md alone would answer \"nobody consumes\n // it\" over the file most authors use.\n const livingReqs = new Map<string, Requirement[]>();\n const livingRequirements = async (service: PathableService): Promise<Requirement[]> => {\n let reqs = livingReqs.get(service);\n if (reqs === undefined) {\n reqs = [];\n for (const axis of SPEC_AXES) {\n const p = (await locateServicePaths(docsDir, service, context))[axis.key];\n if (!existsSync(p)) continue;\n reqs.push(\n ...(context === undefined\n ? parseRequirements(await readFile(p, \"utf8\"))\n : await context.readRequirements(p)),\n );\n }\n livingReqs.set(service, reqs);\n }\n return reqs;\n };\n\n // The landscape half: a `consumes` edge is a consumer somebody drew. The\n // message join is fleet-global (names are not service-scoped), so every\n // edge naming the message counts, whoever its producer is — resolved\n // through the enumerated fleet so a landscape that models CONTAINERS\n // stays visible, ../lookups.ts's edgeConsumers lesson.\n // (`core/diff/victims.ts` mirrors this scan over an already-read fleet —\n // change the rules or the victim strings here and that module owes the\n // same change; a third copy forces the shared extraction, rule 13.)\n let livingLandscape: LoadedDoc | null | undefined;\n const messageConsumers = async (service: string, message: string): Promise<string[]> => {\n if (livingLandscape === undefined) {\n const path = landscapePath(docsDir);\n livingLandscape = existsSync(path)\n ? context === undefined\n ? await loadFile(path)\n : await context.loadLikeC4(path)\n : null;\n }\n const out: string[] = [];\n // An unreadable landscape proves nothing either way; `landscape.invalid`\n // is validate's finding to make.\n if (livingLandscape !== null && livingLandscape.errors.length === 0) {\n const resolve = serviceResolver(livingLandscape.elements, new Set(await enumeratedServices()));\n for (const r of livingLandscape.relationships) {\n if (r.consumes !== message || resolve(r.target) === service) continue;\n out.push(`edge ${resolve(r.source)} → ${resolve(r.target)}${r.title === undefined ? \"\" : ` (\"${r.title}\")`}`);\n }\n }\n for (const other of (await enumeratedServices()).filter((id) => id !== service)) {\n for (const r of await livingRequirements(other)) {\n if (r.consumes.includes(message)) out.push(`${other}'s living requirement '${r.name}'`);\n }\n }\n return out;\n };\n\n // What OTHER features in flight declare, per (service, message) — the\n // asyncapi mirror of pending.ts's activeOpAdditions, living HERE because\n // it shares this module's laziness and its consumers. Removal markers do\n // not count: a feature retiring a message is not adding it. First feature\n // wins a clash — one name to archive first is enough to make progress.\n let inFlight: Map<string, string> | null = null;\n const messageDefinedElsewhere = async (service: string, message: string): Promise<string | undefined> => {\n if (inFlight === null) {\n inFlight = new Map<string, string>();\n for (const feature of await listFeatures(docsDir, {}, context)) {\n if (feature.id === featureId) continue;\n for (const svc of feature.services) {\n const doc = await readAsyncapi(featureSpecPaths(feature.dir, svc).asyncapi, context);\n for (const m of doc.messages) {\n if (m.remove === true) continue;\n const k = `${svc} ${m.name}`;\n if (!inFlight.has(k)) inFlight.set(k, feature.id);\n }\n }\n }\n }\n return inFlight.get(`${service} ${message}`);\n };\n\n return { messageConsumers, messageDefinedElsewhere };\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract as the feature would leave it: the REAL merge
|
|
3
|
+
* (core/asyncapi/merge/merge.ts) run over the two texts and read back,
|
|
4
|
+
* nothing written. One simulation feeds two gate questions — the
|
|
5
|
+
* `declares()` join in ./events.ts (a retired or edited-away send must stop
|
|
6
|
+
* answering "the service declares this" BEFORE archive, or a leftover
|
|
7
|
+
* `Publishes:` line passes the gate and breaks the post-archive fleet) and
|
|
8
|
+
* the consumer gate's wire half (a channel or operation removal that stops
|
|
9
|
+
* the service SENDING a message the fleet still consumes).
|
|
10
|
+
*
|
|
11
|
+
* Calling the merge rather than re-deriving its verdicts is the point: a
|
|
12
|
+
* second spelling of quote/edit/removal here would eventually disagree with
|
|
13
|
+
* the merge about what archive actually writes, and the gate would be
|
|
14
|
+
* promising a contract the archive does not produce.
|
|
15
|
+
*/
|
|
16
|
+
import { parse } from "yaml";
|
|
17
|
+
import { asyncapiDocOf } from "../../asyncapi/read.js";
|
|
18
|
+
import { AsyncapiMergeError } from "../../asyncapi/merge/error.js";
|
|
19
|
+
import { mergeAsyncapiSlots } from "../../asyncapi/merge/merge.js";
|
|
20
|
+
/**
|
|
21
|
+
* Simulate the archive-time merge, or stand down. Both documents are known
|
|
22
|
+
* readable here (the walk in ./declared.ts checked), so the only refusals
|
|
23
|
+
* left are the merge's own judgement calls — an aliased section, a section
|
|
24
|
+
* spelling one slot key twice. `undefined` on those rather than a throw: the
|
|
25
|
+
* caller falls back to the feature ∪ living union view, which can only be
|
|
26
|
+
* too lenient, and the archive's own plan will surface the refusal by name.
|
|
27
|
+
*/
|
|
28
|
+
export function mergedAsyncapiDoc(input) {
|
|
29
|
+
const { livingText, livingDoc, featureText, service } = input;
|
|
30
|
+
try {
|
|
31
|
+
const out = mergeAsyncapiSlots(livingText, featureText, service);
|
|
32
|
+
return out.text === null ? livingDoc : asyncapiDocOf(parse(out.text));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error instanceof AsyncapiMergeError)
|
|
36
|
+
return undefined;
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=merged.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merged.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/merged.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAWnE;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA2B;IAC3D,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,kBAAkB,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QACjE,OAAO,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,kBAAkB;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["/**\n * The contract as the feature would leave it: the REAL merge\n * (core/asyncapi/merge/merge.ts) run over the two texts and read back,\n * nothing written. One simulation feeds two gate questions — the\n * `declares()` join in ./events.ts (a retired or edited-away send must stop\n * answering \"the service declares this\" BEFORE archive, or a leftover\n * `Publishes:` line passes the gate and breaks the post-archive fleet) and\n * the consumer gate's wire half (a channel or operation removal that stops\n * the service SENDING a message the fleet still consumes).\n *\n * Calling the merge rather than re-deriving its verdicts is the point: a\n * second spelling of quote/edit/removal here would eventually disagree with\n * the merge about what archive actually writes, and the gate would be\n * promising a contract the archive does not produce.\n */\nimport { parse } from \"yaml\";\nimport { asyncapiDocOf } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport { AsyncapiMergeError } from \"../../asyncapi/merge/error.js\";\nimport { mergeAsyncapiSlots } from \"../../asyncapi/merge/merge.js\";\n\n/** The two texts the simulation merges, plus the living side's existing parse. */\nexport interface MergeSimulationInput {\n livingText: string;\n /** `readAsyncapi` of livingText — reused verbatim when the merge writes nothing. */\n livingDoc: AsyncapiDoc;\n featureText: string;\n service: string;\n}\n\n/**\n * Simulate the archive-time merge, or stand down. Both documents are known\n * readable here (the walk in ./declared.ts checked), so the only refusals\n * left are the merge's own judgement calls — an aliased section, a section\n * spelling one slot key twice. `undefined` on those rather than a throw: the\n * caller falls back to the feature ∪ living union view, which can only be\n * too lenient, and the archive's own plan will surface the refusal by name.\n */\nexport function mergedAsyncapiDoc(input: MergeSimulationInput): AsyncapiDoc | undefined {\n const { livingText, livingDoc, featureText, service } = input;\n try {\n const out = mergeAsyncapiSlots(livingText, featureText, service);\n return out.text === null ? livingDoc : asyncapiDocOf(parse(out.text));\n } catch (error) {\n if (error instanceof AsyncapiMergeError) return undefined;\n throw error;\n }\n}\n"]}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the merge would do to the LIVING documents.
|
|
3
|
+
*
|
|
4
|
+
* The refusals `loam archive` makes about the world a feature is about to be
|
|
5
|
+
* merged INTO — as opposed to the feature's own three axes agreeing, which is
|
|
6
|
+
* coherence.ts's question: a per-service delta addressed to a service that
|
|
7
|
+
* exists nowhere, a `specs/<svc>/` directory whose NAME could never be a
|
|
8
|
+
* service id, and git conflict markers in a living document the merge would
|
|
9
|
+
* rewrite. `validate` and `status` read the same functions, so the gates
|
|
10
|
+
* cannot drift into disagreeing about what they refuse.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
14
|
+
import { loadFile } from "../c4/likec4.js";
|
|
15
|
+
import { elementService } from "../c4/resolve/service.js";
|
|
16
|
+
import { closeIds } from "../c4/arch.js";
|
|
17
|
+
import { serviceIdProblem } from "../kernel/ids/service.js";
|
|
18
|
+
import { repoPath } from "../envelope/json.js";
|
|
19
|
+
import { featurePaths, landscapePath, SPEC_AXES } from "../repo/paths.js";
|
|
20
|
+
import { livingCapabilityPaths } from "../repo/authored/paths.js";
|
|
21
|
+
import { docsRepoState } from "../repo/state.js";
|
|
22
|
+
import { featureSpecServices, listServices } from "../repo/repo.js";
|
|
23
|
+
import { enumeratedServices, locateServicePaths } from "../repo/service-target.js";
|
|
24
|
+
import { documentConflictFinding, landscapeConflictFinding } from "../conflict-markers.js";
|
|
25
|
+
/* ------------------------------------------------------------------ */
|
|
26
|
+
/* What the merge would do to the LIVING documents */
|
|
27
|
+
/* ------------------------------------------------------------------ */
|
|
28
|
+
/**
|
|
29
|
+
* Per-service deltas addressed to a service that exists nowhere: no
|
|
30
|
+
* `services/<svc>/`, and nothing in this feature's own tagged delta introducing
|
|
31
|
+
* one.
|
|
32
|
+
*
|
|
33
|
+
* `validate` grades this (`delta.service-unknown`); `archive` is where it costs
|
|
34
|
+
* something, because `specs/<svc>/` is what the merge MATERIALISES
|
|
35
|
+
* `services/<svc>/` from — one wrong character in `--touches` and the fleet
|
|
36
|
+
* acquires a living service nobody meant to adopt, under a banner saying the
|
|
37
|
+
* living docs are complete. Both gates read this function so they cannot drift
|
|
38
|
+
* into disagreeing about which service ids are real; the `code:` literal lives
|
|
39
|
+
* here, once, where the drift collector sees it.
|
|
40
|
+
*
|
|
41
|
+
* A delta that did not parse proves nothing either way — whether it introduces
|
|
42
|
+
* the service is exactly what nobody can read — so the question is suspended
|
|
43
|
+
* there rather than answered by guessing. `delta.invalid` is that file's
|
|
44
|
+
* finding, and coherence already makes it.
|
|
45
|
+
*
|
|
46
|
+
* `services/<svc>/` is asked for directly rather than through the enumeration:
|
|
47
|
+
* a feature may be graded in a docs repo with no `services/` at all (repo.ts
|
|
48
|
+
* takes the same position), where enumerating is a refusal, not an answer.
|
|
49
|
+
*/
|
|
50
|
+
export async function unknownDeltaServices(docsDir, featureDir, featureId, loaded = {}) {
|
|
51
|
+
const { preloadedDelta, context } = loaded;
|
|
52
|
+
const deltaPath = featurePaths(featureDir).delta;
|
|
53
|
+
let delta;
|
|
54
|
+
if (existsSync(deltaPath)) {
|
|
55
|
+
delta = preloadedDelta ?? (context === undefined ? await loadFile(deltaPath) : await context.loadLikeC4(deltaPath));
|
|
56
|
+
}
|
|
57
|
+
if (delta !== undefined && delta.errors.length > 0)
|
|
58
|
+
return [];
|
|
59
|
+
const introduces = new Set((delta?.elements ?? []).filter((e) => e.tags.includes(featureId)).map(elementService));
|
|
60
|
+
// Enumeration MEMBERSHIP, not an existsSync of services/<svc>/ at the root:
|
|
61
|
+
// a service filed into a subsystem exists wherever the tree walk found it,
|
|
62
|
+
// and the root probe would grade it unknown. `enumeratedServices` swallows
|
|
63
|
+
// an unenumerable services/ to [] on purpose — this also runs under archive
|
|
64
|
+
// in repos validate --feature tolerates (no services/ at all), where every
|
|
65
|
+
// probe would have found nothing there anyway.
|
|
66
|
+
const known = new Set((await enumeratedServices(docsDir, context)).map((s) => s.id));
|
|
67
|
+
const unknown = (await featureSpecServices(featureDir, context)).filter((svc) => !known.has(svc) && !introduces.has(svc));
|
|
68
|
+
if (unknown.length === 0)
|
|
69
|
+
return [];
|
|
70
|
+
// The near-miss hint, on the same rule `service.unknown` uses — a typo is
|
|
71
|
+
// only diagnosable against the ids that DO exist.
|
|
72
|
+
const closeTo = docsRepoState(docsDir).kind === "ok" ? (await listServices(docsDir, context)).map((s) => s.id) : [];
|
|
73
|
+
return unknown.map((svc) => deltaServiceUnknownFinding(svc, closeTo));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The finding itself, for the two gates that reach this conclusion by different
|
|
77
|
+
* routes.
|
|
78
|
+
*
|
|
79
|
+
* `archive` gets there through `unknownDeltaServices` above; `validate` gets
|
|
80
|
+
* there through its own enumeration, because it suspends the question on an
|
|
81
|
+
* unreadable delta rather than returning early on it, and that difference is
|
|
82
|
+
* deliberate. What must NOT differ is the sentence: the two were spelled out
|
|
83
|
+
* word for word in two files, so a reworded hint in one of them would have made
|
|
84
|
+
* `loam validate` and `loam archive` describe the same typo differently, on the
|
|
85
|
+
* same code, in the same repo.
|
|
86
|
+
*
|
|
87
|
+
* `knownIds` is the fleet's real service ids — already enumerated by the caller,
|
|
88
|
+
* which is also the caller's chance to skip the enumeration entirely when there
|
|
89
|
+
* is nothing to diagnose.
|
|
90
|
+
*/
|
|
91
|
+
export function deltaServiceUnknownFinding(svc, knownIds) {
|
|
92
|
+
const close = closeIds(svc, knownIds);
|
|
93
|
+
return {
|
|
94
|
+
severity: "error",
|
|
95
|
+
code: "delta.service-unknown",
|
|
96
|
+
subject: svc,
|
|
97
|
+
message: `specs/${svc}/ addresses a service that does not exist: there is no services/${svc}/ and this feature's ` +
|
|
98
|
+
`delta.likec4 does not introduce one — archiving would create it out of the typo.` +
|
|
99
|
+
(close.length > 0 ? ` Did you mean: ${close.join(", ")}?` : " `loam list services` shows what exists."),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Per-service deltas whose DIRECTORY NAME is not a legal service id at all.
|
|
104
|
+
*
|
|
105
|
+
* `specs/<svc>/` is caller-controlled path input exactly as `--service` is:
|
|
106
|
+
* `featureSpecServices` returns whatever `readdir` found, and nothing between
|
|
107
|
+
* that readdir and the archive merge ever asked the id grammar about it. So
|
|
108
|
+
* `specs/Payment Service/` validated green — "requirements covered", even,
|
|
109
|
+
* because a tagged element whose TITLE matched the directory counted as
|
|
110
|
+
* introducing the service — and `loam archive` then materialised
|
|
111
|
+
* `services/Payment Service/`: a directory `service.id-invalid` calls an error
|
|
112
|
+
* on the very next `validate --all`, and one no loam command can address or
|
|
113
|
+
* re-create. Both gates read this function for `unknownDeltaServices`' reason:
|
|
114
|
+
* validate and archive must refuse the same directory with the same sentence.
|
|
115
|
+
*
|
|
116
|
+
* NOT suppressed by what the delta introduces, and NOT suspended on
|
|
117
|
+
* `delta.invalid`: the name is illegal whatever the architecture axis says,
|
|
118
|
+
* because the name itself is what becomes the path.
|
|
119
|
+
*/
|
|
120
|
+
export async function invalidSpecServiceFindings(featureDir, context) {
|
|
121
|
+
const out = [];
|
|
122
|
+
for (const svc of await featureSpecServices(featureDir, context)) {
|
|
123
|
+
const problem = serviceIdProblem(svc, "specs/ directory name");
|
|
124
|
+
if (problem === null)
|
|
125
|
+
continue;
|
|
126
|
+
out.push({
|
|
127
|
+
severity: "error",
|
|
128
|
+
code: "delta.service-id-invalid",
|
|
129
|
+
subject: svc,
|
|
130
|
+
message: `specs/${svc}/ — ${problem} ` +
|
|
131
|
+
`\`loam archive\` materialises services/${svc}/ from this directory name, and that directory is one ` +
|
|
132
|
+
`no loam command can address afterwards (\`service.id-invalid\`). Rename the specs/ directory to a legal id.`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Git conflict markers in the LIVING documents a merge of this feature would
|
|
139
|
+
* rewrite: each touched service's requirement files, and the fleet map.
|
|
140
|
+
*
|
|
141
|
+
* The rule and the sentence are conflict-markers's — one spelling of the breach,
|
|
142
|
+
* wherever it is found. What this adds is the SET: the documents `archive`
|
|
143
|
+
* rewrites. A conflicted `services/<svc>/spec.md` parses as prose, so every
|
|
144
|
+
* check upstream of the merge reads it as a valid document with some odd
|
|
145
|
+
* headings in it; the rewrite then drops whichever marker lines fall inside the
|
|
146
|
+
* requirements run it owns, and a conflict anyone could see becomes a file
|
|
147
|
+
* nobody can tell is wrong. For a shared docs repo that a fleet lands in
|
|
148
|
+
* through PRs, that is the default failure and not an edge.
|
|
149
|
+
*
|
|
150
|
+
* `openapi.yaml` is deliberately absent: markers make it unparseable, so the
|
|
151
|
+
* YAML reader already refuses it by name and a second finding would only say
|
|
152
|
+
* the same thing later.
|
|
153
|
+
*
|
|
154
|
+
* `capabilities` is the same question on the business corpus, and it is not
|
|
155
|
+
* optional politeness: a living `capabilities/<id>/spec.md` the feature's
|
|
156
|
+
* capability delta merges into is rewritten by exactly the same
|
|
157
|
+
* `rewriteRequirementsRun`, so a conflicted one loses its marker lines exactly
|
|
158
|
+
* the same way. Passed in rather than walked here because the caller has
|
|
159
|
+
* already read the feature's delta tree and this function must not decide which
|
|
160
|
+
* documents a merge touches.
|
|
161
|
+
*
|
|
162
|
+
* IDS, not the `CapabilityDoc` records the caller holds, and that is a guard
|
|
163
|
+
* rather than a preference: a `CapabilityDoc` from a feature's delta walk
|
|
164
|
+
* carries `spec` and `dir` pointing at the FEATURE'S copy, and the document
|
|
165
|
+
* this function is about is the LIVING one. Taking the whole record would leave
|
|
166
|
+
* `doc.spec` in scope here — the wrong file, one property access away.
|
|
167
|
+
*/
|
|
168
|
+
export async function livingMergeConflicts(docsDir, services, capabilities, context) {
|
|
169
|
+
const out = [];
|
|
170
|
+
const read = async (path) => context === undefined ? readFile(path, "utf8") : context.readText(path);
|
|
171
|
+
for (const svc of services) {
|
|
172
|
+
const paths = await locateServicePaths(docsDir, svc, context);
|
|
173
|
+
for (const axis of SPEC_AXES) {
|
|
174
|
+
const path = paths[axis.key];
|
|
175
|
+
if (!existsSync(path))
|
|
176
|
+
continue;
|
|
177
|
+
const finding = documentConflictFinding(repoPath(docsDir, path), svc, await read(path));
|
|
178
|
+
if (finding !== null)
|
|
179
|
+
out.push(finding);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const id of capabilities) {
|
|
183
|
+
const path = livingCapabilityPaths(docsDir, id).spec;
|
|
184
|
+
if (!existsSync(path))
|
|
185
|
+
continue;
|
|
186
|
+
const finding = documentConflictFinding(repoPath(docsDir, path), id, await read(path));
|
|
187
|
+
if (finding !== null)
|
|
188
|
+
out.push(finding);
|
|
189
|
+
}
|
|
190
|
+
const landscape = landscapePath(docsDir);
|
|
191
|
+
if (existsSync(landscape)) {
|
|
192
|
+
const finding = landscapeConflictFinding(repoPath(docsDir, landscape), await read(landscape));
|
|
193
|
+
if (finding !== null)
|
|
194
|
+
out.push(finding);
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=living.js.map
|