@ybotok/loam 0.1.0-beta.3 → 0.2.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5992 -82
- package/COMPARISON.md +168 -63
- package/CONTRIBUTING.md +121 -0
- package/MIGRATING-from-OpenSpec.md +238 -55
- package/QUICKSTART.md +302 -0
- package/README.md +653 -144
- package/ROADMAP.md +1035 -0
- package/SCHEMA.md +3916 -263
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +927 -0
- package/dist/cli.js +133 -21
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +204 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +309 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +74 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/closing.js +42 -0
- package/dist/commands/archive/closing.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +172 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +193 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/document/head.js +151 -0
- package/dist/commands/archive/plan/document/head.js.map +1 -0
- package/dist/commands/archive/plan/document/links.js +284 -0
- package/dist/commands/archive/plan/document/links.js.map +1 -0
- package/dist/commands/archive/plan/document/run.js +136 -0
- package/dist/commands/archive/plan/document/run.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +319 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +298 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/model/extending.js +85 -0
- package/dist/commands/archive/plan/model/extending.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +106 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +318 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +30 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +355 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +166 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +305 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +367 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +221 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +6 -8
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +86 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +65 -5
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +227 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +39 -28
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +152 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +66 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +105 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +262 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +146 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +72 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +142 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +72 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +63 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +155 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +358 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +344 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +108 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +238 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +75 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +175 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +143 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +86 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +18 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +162 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +40 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +62 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +328 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +279 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +199 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +38 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +300 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +221 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +278 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +174 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +182 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/introduced.js +157 -0
- package/dist/commands/show/introduced.js.map +1 -0
- package/dist/commands/show/marks.js +53 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/show/service.js +239 -0
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +87 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/fleet-next.js +90 -0
- package/dist/commands/status/fleet-next.js.map +1 -0
- package/dist/commands/status/json.js +47 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +209 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +115 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +269 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +325 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +113 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +98 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +318 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/exclude.js +132 -0
- package/dist/commands/subsystem/txn/exclude.js.map +1 -0
- package/dist/commands/subsystem/txn/links.js +128 -0
- package/dist/commands/subsystem/txn/links.js.map +1 -0
- package/dist/commands/subsystem/txn/report.js +174 -0
- package/dist/commands/subsystem/txn/report.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +196 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +249 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/tasks/actions.js +107 -0
- package/dist/commands/tasks/actions.js.map +1 -0
- package/dist/commands/tasks/render.js +253 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +119 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +176 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/unarchive/restore.js +254 -0
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +59 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +245 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +204 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +58 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/introduced.js +102 -0
- package/dist/commands/validate/checks/introduced.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +264 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +85 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/coverage/covers-after.js +105 -0
- package/dist/commands/validate/coverage/covers-after.js.map +1 -0
- package/dist/commands/validate/feature.js +222 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +93 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +371 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +118 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/map/attest.js +98 -0
- package/dist/commands/validate/fleet/map/attest.js.map +1 -0
- package/dist/commands/validate/fleet/map/bindings.js +107 -0
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
- package/dist/commands/validate/fleet/map/consumers.js +244 -0
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
- package/dist/commands/validate/fleet/map/isolation.js +116 -0
- package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
- package/dist/commands/validate/fleet/map/usecases.js +54 -0
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +266 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +71 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +162 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +75 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/projects.js +278 -0
- package/dist/commands/validate/fleet/views/projects.js.map +1 -0
- package/dist/commands/validate/fleet/views/root-documents.js +83 -0
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +52 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +89 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/render/repeats.js +63 -0
- package/dist/commands/validate/render/repeats.js.map +1 -0
- package/dist/commands/validate/render/rollups.js +115 -0
- package/dist/commands/validate/render/rollups.js.map +1 -0
- package/dist/commands/validate/render/text.js +199 -0
- package/dist/commands/validate/render/text.js.map +1 -0
- package/dist/commands/validate/report.js +145 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +252 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +241 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +177 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/model/diverged.js +226 -0
- package/dist/commands/validate/service/model/diverged.js.map +1 -0
- package/dist/commands/validate/service/model/grade.js +163 -0
- package/dist/commands/validate/service/model/grade.js.map +1 -0
- package/dist/commands/validate/service/service.js +310 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +279 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +148 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/service/usecases/tags.js +113 -0
- package/dist/commands/validate/service/usecases/tags.js.map +1 -0
- package/dist/commands/validate/service/usecases/usecases.js +209 -0
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/validate.js +363 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +178 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +81 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +263 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +278 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +206 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +328 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +220 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +266 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +141 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +230 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +111 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +155 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +169 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +299 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +46 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +50 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +59 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +65 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +44 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +78 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +104 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +138 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +273 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md.js +203 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/{agents-stamp.js → agent/agents-stamp.js} +60 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +254 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +144 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +151 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +193 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +280 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +373 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +323 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +220 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +188 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +311 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +67 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +210 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +19 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +338 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +43 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +146 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +92 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +38 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +290 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +195 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +83 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/asyncapi/read.js +263 -0
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +285 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +385 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +273 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/map/extend.js +84 -0
- package/dist/core/brief/map/extend.js.map +1 -0
- package/dist/core/brief/map/owed.js +266 -0
- package/dist/core/brief/map/owed.js.map +1 -0
- package/dist/core/brief/targets.js +304 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +58 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +190 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/direction/direction.js +114 -0
- package/dist/core/c4/direction/direction.js.map +1 -0
- package/dist/core/c4/likec4.js +243 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +160 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +110 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +113 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/prefetch/fleet.js +134 -0
- package/dist/core/c4/prefetch/fleet.js.map +1 -0
- package/dist/core/c4/project/architecture.js +161 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/batch.js +140 -0
- package/dist/core/c4/project/batch.js.map +1 -0
- package/dist/core/c4/project/documents.js +89 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +219 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/project/staged.js +277 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/attested.js +0 -0
- package/dist/core/c4/resolve/attested.js.map +1 -0
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +174 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/resolve/steps.js +290 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/resolve/systems.js +154 -0
- package/dist/core/c4/resolve/systems.js.map +1 -0
- package/dist/core/c4/root-project/exclude.js +352 -0
- package/dist/core/c4/root-project/exclude.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +173 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/service-model/created/models.js +96 -0
- package/dist/core/c4/service-model/created/models.js.map +1 -0
- package/dist/core/c4/service-model/documents.js +47 -0
- package/dist/core/c4/service-model/documents.js.map +1 -0
- package/dist/core/c4/service-model/fleet/extending.js +82 -0
- package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
- package/dist/core/c4/service-model/load.js +165 -0
- package/dist/core/c4/service-model/load.js.map +1 -0
- package/dist/core/c4/service-model/renderer.js +128 -0
- package/dist/core/c4/service-model/renderer.js.map +1 -0
- package/dist/core/c4/service-model/shape.js +106 -0
- package/dist/core/c4/service-model/shape.js.map +1 -0
- package/dist/core/c4/service-model/slice.js +188 -0
- package/dist/core/c4/service-model/slice.js.map +1 -0
- package/dist/core/c4/source-mask.js +369 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +308 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +167 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +30 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +126 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/declared.js +87 -0
- package/dist/core/c4/splice/identity/declared.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +73 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/identity/existing.js +80 -0
- package/dist/core/c4/splice/identity/existing.js.map +1 -0
- package/dist/core/c4/splice/identity/interior.js +80 -0
- package/dist/core/c4/splice/identity/interior.js.map +1 -0
- package/dist/core/c4/splice/identity/owner.js +50 -0
- package/dist/core/c4/splice/identity/owner.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +337 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/model/merge.js +151 -0
- package/dist/core/c4/splice/model/merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +298 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/splice/text/spot.js +131 -0
- package/dist/core/c4/splice/text/spot.js.map +1 -0
- package/dist/core/c4/workspace.js +166 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +47 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +338 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +174 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +123 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +205 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +104 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +267 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +185 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +75 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/calls/called.js +52 -0
- package/dist/core/coherence/calls/called.js.map +1 -0
- package/dist/core/coherence/coherence.js +332 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/covers/health.js +85 -0
- package/dist/core/coherence/covers/health.js.map +1 -0
- package/dist/core/coherence/declared.js +295 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +191 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +335 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +189 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +63 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/produced.js +100 -0
- package/dist/core/coherence/events/produced.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +95 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +341 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/deployment/delta.js +191 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +230 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +237 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +204 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +312 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/docs.js +37 -85
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +238 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +139 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +355 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/reports/scan.js +242 -0
- package/dist/core/doctor/reports/scan.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +224 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +103 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +286 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +238 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +234 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +89 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +209 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +180 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +194 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms/documents.js +227 -0
- package/dist/core/explain/terms/documents.js.map +1 -0
- package/dist/core/explain/terms/done-check.js +144 -0
- package/dist/core/explain/terms/done-check.js.map +1 -0
- package/dist/core/explain/terms/entry.js +14 -0
- package/dist/core/explain/terms/entry.js.map +1 -0
- package/dist/core/explain/terms/stages.js +71 -0
- package/dist/core/explain/terms/stages.js.map +1 -0
- package/dist/core/explain/terms/tasks.js +97 -0
- package/dist/core/explain/terms/tasks.js.map +1 -0
- package/dist/core/explain/terms.js +103 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/explore/explore.js +204 -0
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +174 -146
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +208 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +246 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +176 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +210 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +109 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +73 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/{concurrency.js → kernel/concurrency.js} +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
- package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +56 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/{records.js → kernel/records.js} +11 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +66 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +118 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +265 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +212 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +73 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +324 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +207 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +142 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +174 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +301 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +136 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +239 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +54 -108
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +199 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +16 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +204 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +232 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +110 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +220 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +78 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +70 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +241 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +191 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +304 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +116 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +157 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +120 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/projection/introduced.js +14 -0
- package/dist/core/projection/introduced.js.map +1 -0
- package/dist/core/provenance/findings.js +244 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +201 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +139 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +280 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +220 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +210 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +322 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +308 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +61 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +90 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/render/body.js +258 -0
- package/dist/core/repo/tree/render/body.js.map +1 -0
- package/dist/core/repo/tree/render/projects.js +182 -0
- package/dist/core/repo/tree/render/projects.js.map +1 -0
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +42 -0
- package/dist/core/repo/tree/render/stale.js.map +1 -0
- package/dist/core/repo/tree/render/text.js +84 -0
- package/dist/core/repo/tree/render/text.js.map +1 -0
- package/dist/core/repo/tree/render/views.js +293 -0
- package/dist/core/repo/tree/render/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +255 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +146 -14
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +239 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/legacy.js +271 -0
- package/dist/core/scaffold/legacy.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +252 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +268 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +95 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +204 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot/manifest.js +188 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/snapshot/snapshot.js +196 -0
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +181 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +60 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +126 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +196 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +214 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +163 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/feature/words.js +96 -0
- package/dist/core/status/feature/words.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +216 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +302 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +216 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/tasks/packet.js +199 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +166 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +140 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +101 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/modified.js +315 -0
- package/dist/core/usecases/delta/modified.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +76 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +296 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/grade/capability-tag.js +93 -0
- package/dist/core/usecases/grade/capability-tag.js.map +1 -0
- package/dist/core/usecases/grade/feature.js +88 -0
- package/dist/core/usecases/grade/feature.js.map +1 -0
- package/dist/core/usecases/grade/requirement-tag.js +94 -0
- package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
- package/dist/core/usecases/grade/steps.js +266 -0
- package/dist/core/usecases/grade/steps.js.map +1 -0
- package/dist/core/usecases/grade/usecases.js +112 -0
- package/dist/core/usecases/grade/usecases.js.map +1 -0
- package/dist/core/usecases/operations.js +123 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/service/exclude.js +119 -0
- package/dist/core/usecases/service/exclude.js.map +1 -0
- package/dist/core/usecases/service/flows.js +164 -0
- package/dist/core/usecases/service/flows.js.map +1 -0
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +180 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +170 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +259 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +158 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/read/shape.js +169 -0
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +165 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +99 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +61 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +59 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +216 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/vocabulary/report.js +90 -0
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +232 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +336 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +18 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +74 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +132 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +31 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +73 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +8 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +92 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +134 -0
- package/examples/docs/services/order-service/asyncapi.yaml +109 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +126 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +76 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +68 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +120 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +81 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +200 -0
- package/examples/docs/services/platform/notification-service/health.yaml +40 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +97 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -295
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js +0 -320
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js +0 -317
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/asyncapi.js +0 -253
- package/dist/core/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -596
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js +0 -225
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -489
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js +0 -47
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1132
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AsyncAPI contract merge: the feature's asyncapi deltas folded into each
|
|
3
|
+
* living event contract — the event axis's mirror of ./openapi.ts, slot-keyed
|
|
4
|
+
* where that one is path-keyed.
|
|
5
|
+
*
|
|
6
|
+
* Nothing is written. Everything lands in the `Plan`, so a failure on any axis
|
|
7
|
+
* leaves the living docs exactly as they were.
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { parse } from "yaml";
|
|
11
|
+
import { CodedFailure, repoPath } from "../../../../core/envelope/json.js";
|
|
12
|
+
import { planWrite, readUtf8 } from "../../../../core/staging/writes.js";
|
|
13
|
+
import { featureSpecPaths } from "../../../../core/repo/paths.js";
|
|
14
|
+
import { locateServicePaths } from "../../../../core/repo/service-target.js";
|
|
15
|
+
import { readAsyncapi } from "../../../../core/asyncapi/read.js";
|
|
16
|
+
import { asyncapiSlots } from "../../../../core/asyncapi/digest.js";
|
|
17
|
+
import { asyncapiSurfaces } from "../../../../core/asyncapi/baseline/surfaces.js";
|
|
18
|
+
import { AsyncapiMergeError } from "../../../../core/asyncapi/merge/error.js";
|
|
19
|
+
import { stripAsyncapiMarkers } from "../../../../core/asyncapi/merge/markers.js";
|
|
20
|
+
import { mergeAsyncapiSlots } from "../../../../core/asyncapi/merge/merge.js";
|
|
21
|
+
import {} from "../state.js";
|
|
22
|
+
import { consumerClause } from "./consumers.js";
|
|
23
|
+
import { infoDrift, sayQuoted, sayStale, warnInfoDrift, warnOverwrite } from "./verdict-lines.js";
|
|
24
|
+
/** The per-section overwrite code — three codes, one per slot shape, mirroring the OpenAPI axis's. */
|
|
25
|
+
function modifiedCode(section) {
|
|
26
|
+
if (section === "components.messages")
|
|
27
|
+
return "asyncapi.message-modified";
|
|
28
|
+
return section === "channels" ? "asyncapi.channel-modified" : "asyncapi.operation-modified";
|
|
29
|
+
}
|
|
30
|
+
export async function planAsyncapiContracts(config, gated, plan, say) {
|
|
31
|
+
const { featureDir, deltaServices } = gated;
|
|
32
|
+
const { writes, planGates, asyncapiRemovals } = plan;
|
|
33
|
+
for (const svc of deltaServices) {
|
|
34
|
+
const featAsyncapi = featureSpecPaths(featureDir, svc).asyncapi;
|
|
35
|
+
if (!existsSync(featAsyncapi))
|
|
36
|
+
continue;
|
|
37
|
+
const featText = await readUtf8(featAsyncapi);
|
|
38
|
+
const featDoc = await readAsyncapi(featAsyncapi);
|
|
39
|
+
// Coherence grades `asyncapi.invalid` and gates on it; the merge stays
|
|
40
|
+
// mechanical under --approve for the OpenAPI planner's reason: a document
|
|
41
|
+
// loam cannot read is one it cannot merge, and that is the same answer as
|
|
42
|
+
// any other merge it could not compute — nothing is written.
|
|
43
|
+
if (featDoc.unreadable) {
|
|
44
|
+
throw new CodedFailure("merge-failed", `feature asyncapi ${repoPath(config.docsDir, featAsyncapi)} for ${svc} does not read as an AsyncAPI document (${featDoc.error ?? "not a YAML mapping"}) — the event axis cannot be merged; fix the file, or delete it if this feature has no contract delta`);
|
|
45
|
+
}
|
|
46
|
+
const names = featDoc.messages.filter((m) => m.remove !== true).map((m) => m.name);
|
|
47
|
+
// An `x-loam-remove` NESTED on an inline channel message retires nothing:
|
|
48
|
+
// an inline message is channel-slot interior (SCHEMA.md's decision), so
|
|
49
|
+
// the marker is just channel content — and when the restated channel is
|
|
50
|
+
// otherwise identical to living, not even that: the merge deduplicates it
|
|
51
|
+
// away and the marker lands in no bucket at all. The merge is safe either
|
|
52
|
+
// way (the deep strip keeps the key out of living), but the author asked
|
|
53
|
+
// for a removal that will not happen, and silence there is how a retired
|
|
54
|
+
// message would stay on the wire — openapi.remove-marker-path-level's
|
|
55
|
+
// lesson at the nested depth. Gated like the other plan-visible breaches,
|
|
56
|
+
// --approve and all.
|
|
57
|
+
for (const m of featDoc.messages) {
|
|
58
|
+
if (m.remove !== true || !m.slot.startsWith("channels."))
|
|
59
|
+
continue;
|
|
60
|
+
const channel = m.slot.slice("channels.".length, m.slot.lastIndexOf(".messages."));
|
|
61
|
+
planGates.push({
|
|
62
|
+
severity: "error",
|
|
63
|
+
code: "asyncapi.remove-marker-inline",
|
|
64
|
+
subject: svc,
|
|
65
|
+
message: `${svc}: '${m.slot}' carries x-loam-remove nested on an inline channel message — inline messages are channel interior, not slots, so this retires nothing. Retire the whole channel (x-loam-remove: true at channels.${channel}), or declare the message under components.messages (the channel $ref-ing it) and put the marker there.`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// The shared context, for the OpenAPI planner's reason: once per delta
|
|
69
|
+
// service must not mean one fleet walk per delta service.
|
|
70
|
+
const livingAsyncapi = (await locateServicePaths(config.docsDir, svc, config.fleet)).asyncapi;
|
|
71
|
+
try {
|
|
72
|
+
if (!existsSync(livingAsyncapi)) {
|
|
73
|
+
// A removal against a non-existent contract is gated by coherence;
|
|
74
|
+
// under --approve the strip drops the markers, so the one thing left
|
|
75
|
+
// to decide is whether a document remains. It must not when nothing
|
|
76
|
+
// survives: an asyncapi.yaml's very PRESENCE is graded on this axis
|
|
77
|
+
// (absence is `service.no-asyncapi`'s question, silence otherwise),
|
|
78
|
+
// so a slotless or all-markers delta creating an empty living
|
|
79
|
+
// contract would put a presence claim on a service that has none.
|
|
80
|
+
// SLOTS only, deliberately, now that component surfaces merge: a
|
|
81
|
+
// document holding schemas and no channel, operation or message
|
|
82
|
+
// declares no event at all, and creating it would make exactly the
|
|
83
|
+
// presence claim this branch exists to refuse.
|
|
84
|
+
const surviving = asyncapiSlots(parse(featText)).filter((slot) => !slot.remove);
|
|
85
|
+
if (surviving.length === 0) {
|
|
86
|
+
say(` asyncapi: ${svc} — nothing to merge (the delta declares no surviving slots), no living asyncapi.yaml created`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
writes.push(planWrite(livingAsyncapi, stripAsyncapiMarkers(featText, svc)));
|
|
90
|
+
say(` asyncapi: ${svc} — created (${names.join(", ")})`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const livingText = await readUtf8(livingAsyncapi);
|
|
94
|
+
const merge = mergeAsyncapiSlots(livingText, featText, svc);
|
|
95
|
+
// What the delta declares OUTSIDE the three slot sections — the one
|
|
96
|
+
// question the merge result cannot answer for the silence below, because
|
|
97
|
+
// a merge that wrote nothing reports nothing to distinguish "the delta
|
|
98
|
+
// was all quotes" from "the delta was all schemas and I dropped them".
|
|
99
|
+
const surfaces = asyncapiSurfaces(parse(featText));
|
|
100
|
+
if (merge.text !== null) {
|
|
101
|
+
writes.push(planWrite(livingAsyncapi, merge.text));
|
|
102
|
+
// The parenthesised list names the MESSAGES the delta declares, and a
|
|
103
|
+
// delta whose whole change is a component surface has none — so this
|
|
104
|
+
// printed `merged ()`, the same empty-parenthesis lie the create
|
|
105
|
+
// branch above is commented for: it reads as "this merge wrote
|
|
106
|
+
// nothing" about a merge that just published a shared schema to every
|
|
107
|
+
// consumer of the fleet. Drop the parentheses when there is nothing to
|
|
108
|
+
// put in them; the surface lines below name what actually moved.
|
|
109
|
+
say(` asyncapi: ${svc} — merged${names.length > 0 ? ` (${names.join(", ")})` : ""}`);
|
|
110
|
+
}
|
|
111
|
+
else if (surfaces.length > 0) {
|
|
112
|
+
// "The plan wrote less than my delta spells" is the sentence a reader
|
|
113
|
+
// cannot infer from an unchanged file — the quote line's own comment
|
|
114
|
+
// (./verdict-lines.ts) names it for slots, and a components-only
|
|
115
|
+
// delta is the shape where silence is indistinguishable from the
|
|
116
|
+
// defect that made it merge nothing at all. Not a warning: every
|
|
117
|
+
// surface being a quote is correct authoring and the correct merge.
|
|
118
|
+
say(` asyncapi: ${svc} — nothing to write: every one of the ${surfaces.length} component surface(s) this delta declares already stands as living has it`);
|
|
119
|
+
}
|
|
120
|
+
// Asked whatever the merge wrote: a delta whose ONLY change is its `info`
|
|
121
|
+
// merges nothing at all, and that is exactly the shape that archived in
|
|
122
|
+
// silence (./verdict-lines.ts).
|
|
123
|
+
warnInfoDrift(plan, say, {
|
|
124
|
+
svc, code: "asyncapi.info-not-merged", file: "asyncapi.yaml", carries: "channels, operations and components",
|
|
125
|
+
labels: infoDrift(parse(livingText), parse(featText)),
|
|
126
|
+
});
|
|
127
|
+
if (merge.removed.length > 0) {
|
|
128
|
+
asyncapiRemovals.push({ service: svc, slots: merge.removed.map((slot) => slot.label) });
|
|
129
|
+
for (const slot of merge.removed)
|
|
130
|
+
say(` - removes ${slot.label}`);
|
|
131
|
+
}
|
|
132
|
+
// The quote and stale lines (./verdict-lines.ts says why each is said),
|
|
133
|
+
// the component surfaces' lines mirroring the slots' around them.
|
|
134
|
+
sayQuoted(say, merge.quoted.map((slot) => slot.label));
|
|
135
|
+
sayQuoted(say, merge.componentsQuoted.map((id) => `components.${id}`));
|
|
136
|
+
sayStale(say, merge.baselineStale.map((slot) => slot.label));
|
|
137
|
+
sayStale(say, merge.componentsStale.map((id) => `components.${id}`));
|
|
138
|
+
for (const id of merge.componentsModified) {
|
|
139
|
+
warnOverwrite(plan, say, {
|
|
140
|
+
svc, code: "asyncapi.component-modified", label: `components.${id}`,
|
|
141
|
+
message: `${svc}: the delta declares components.${id}, which the living AsyncAPI already has with different content — the merge overwrites the living component wholesale`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
for (const slot of merge.modified) {
|
|
145
|
+
// A message is the one slot a consumer names (`Consumes:` lines,
|
|
146
|
+
// `consumes` edges), so only `asyncapi.message-modified` says who the
|
|
147
|
+
// overwrite lands on — the OpenAPI planner's sentence, through the
|
|
148
|
+
// same scan (./consumers.ts). Channels and operations join by nothing.
|
|
149
|
+
const name = merge.modifiedMessageNames.get(slot.label);
|
|
150
|
+
const named = name === undefined ? null : consumerClause(await config.consumers.message(svc, name));
|
|
151
|
+
warnOverwrite(plan, say, {
|
|
152
|
+
svc, code: modifiedCode(slot.section), label: slot.label, named,
|
|
153
|
+
message: `${svc}: the delta redefines ${slot.label}, which the living AsyncAPI already has — the merge overwrites the living slot wholesale`,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
for (const ref of merge.unresolved) {
|
|
157
|
+
planGates.push({
|
|
158
|
+
severity: "error",
|
|
159
|
+
code: "asyncapi.ref-unresolved",
|
|
160
|
+
subject: svc,
|
|
161
|
+
message: `${svc}: $ref '${ref}' resolves in neither the feature's asyncapi.yaml nor the living one — the merged document would carry a dangling reference`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
if (err instanceof AsyncapiMergeError)
|
|
167
|
+
throw new CodedFailure("merge-failed", err.message);
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=asyncapi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asyncapi.js","sourceRoot":"","sources":["../../../../../src/commands/archive/plan/contracts/asyncapi.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The AsyncAPI contract merge: the feature's asyncapi deltas folded into each\n * living event contract — the event axis's mirror of ./openapi.ts, slot-keyed\n * where that one is path-keyed.\n *\n * Nothing is written. Everything lands in the `Plan`, so a failure on any axis\n * leaves the living docs exactly as they were.\n */\nimport { existsSync } from \"node:fs\";\nimport { parse } from \"yaml\";\nimport { CodedFailure, repoPath } from \"../../../../core/envelope/json.js\";\nimport { planWrite, readUtf8 } from \"../../../../core/staging/writes.js\";\nimport { featureSpecPaths } from \"../../../../core/repo/paths.js\";\nimport { locateServicePaths } from \"../../../../core/repo/service-target.js\";\nimport { readAsyncapi } from \"../../../../core/asyncapi/read.js\";\nimport { asyncapiSlots, type AsyncapiSection } from \"../../../../core/asyncapi/digest.js\";\nimport { asyncapiSurfaces } from \"../../../../core/asyncapi/baseline/surfaces.js\";\nimport { AsyncapiMergeError } from \"../../../../core/asyncapi/merge/error.js\";\nimport { stripAsyncapiMarkers } from \"../../../../core/asyncapi/merge/markers.js\";\nimport { mergeAsyncapiSlots, type AsyncapiMergeResult } from \"../../../../core/asyncapi/merge/merge.js\";\nimport { type Gated, type Plan } from \"../state.js\";\nimport { consumerClause, type ContractConsumers } from \"./consumers.js\";\nimport { infoDrift, sayQuoted, sayStale, warnInfoDrift, warnOverwrite } from \"./verdict-lines.js\";\nimport type { FleetContext } from \"../../../../core/fleet-context.js\";\nimport type { IssueCode } from \"../../../../core/vocabulary/issue.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\n\n/** The per-section overwrite code — three codes, one per slot shape, mirroring the OpenAPI axis's. */\nfunction modifiedCode(section: AsyncapiSection): IssueCode {\n if (section === \"components.messages\") return \"asyncapi.message-modified\";\n return section === \"channels\" ? \"asyncapi.channel-modified\" : \"asyncapi.operation-modified\";\n}\n\nexport async function planAsyncapiContracts(\n config: { docsDir: DocsDir; fleet: FleetContext; consumers: ContractConsumers },\n gated: Gated,\n plan: Plan,\n say: (line?: string) => void,\n): Promise<void> {\n const { featureDir, deltaServices } = gated;\n const { writes, planGates, asyncapiRemovals } = plan;\n for (const svc of deltaServices) {\n const featAsyncapi = featureSpecPaths(featureDir, svc).asyncapi;\n if (!existsSync(featAsyncapi)) continue;\n const featText = await readUtf8(featAsyncapi);\n const featDoc = await readAsyncapi(featAsyncapi);\n // Coherence grades `asyncapi.invalid` and gates on it; the merge stays\n // mechanical under --approve for the OpenAPI planner's reason: a document\n // loam cannot read is one it cannot merge, and that is the same answer as\n // any other merge it could not compute — nothing is written.\n if (featDoc.unreadable) {\n throw new CodedFailure(\n \"merge-failed\",\n `feature asyncapi ${repoPath(config.docsDir, featAsyncapi)} for ${svc} does not read as an AsyncAPI document (${featDoc.error ?? \"not a YAML mapping\"}) — the event axis cannot be merged; fix the file, or delete it if this feature has no contract delta`,\n );\n }\n const names = featDoc.messages.filter((m) => m.remove !== true).map((m) => m.name);\n // An `x-loam-remove` NESTED on an inline channel message retires nothing:\n // an inline message is channel-slot interior (SCHEMA.md's decision), so\n // the marker is just channel content — and when the restated channel is\n // otherwise identical to living, not even that: the merge deduplicates it\n // away and the marker lands in no bucket at all. The merge is safe either\n // way (the deep strip keeps the key out of living), but the author asked\n // for a removal that will not happen, and silence there is how a retired\n // message would stay on the wire — openapi.remove-marker-path-level's\n // lesson at the nested depth. Gated like the other plan-visible breaches,\n // --approve and all.\n for (const m of featDoc.messages) {\n if (m.remove !== true || !m.slot.startsWith(\"channels.\")) continue;\n const channel = m.slot.slice(\"channels.\".length, m.slot.lastIndexOf(\".messages.\"));\n planGates.push({\n severity: \"error\",\n code: \"asyncapi.remove-marker-inline\",\n subject: svc,\n message: `${svc}: '${m.slot}' carries x-loam-remove nested on an inline channel message — inline messages are channel interior, not slots, so this retires nothing. Retire the whole channel (x-loam-remove: true at channels.${channel}), or declare the message under components.messages (the channel $ref-ing it) and put the marker there.`,\n });\n }\n // The shared context, for the OpenAPI planner's reason: once per delta\n // service must not mean one fleet walk per delta service.\n const livingAsyncapi = (await locateServicePaths(config.docsDir, svc, config.fleet)).asyncapi;\n try {\n if (!existsSync(livingAsyncapi)) {\n // A removal against a non-existent contract is gated by coherence;\n // under --approve the strip drops the markers, so the one thing left\n // to decide is whether a document remains. It must not when nothing\n // survives: an asyncapi.yaml's very PRESENCE is graded on this axis\n // (absence is `service.no-asyncapi`'s question, silence otherwise),\n // so a slotless or all-markers delta creating an empty living\n // contract would put a presence claim on a service that has none.\n // SLOTS only, deliberately, now that component surfaces merge: a\n // document holding schemas and no channel, operation or message\n // declares no event at all, and creating it would make exactly the\n // presence claim this branch exists to refuse.\n const surviving = asyncapiSlots(parse(featText)).filter((slot) => !slot.remove);\n if (surviving.length === 0) {\n say(` asyncapi: ${svc} — nothing to merge (the delta declares no surviving slots), no living asyncapi.yaml created`);\n continue;\n }\n writes.push(planWrite(livingAsyncapi, stripAsyncapiMarkers(featText, svc)));\n say(` asyncapi: ${svc} — created (${names.join(\", \")})`);\n continue;\n }\n const livingText = await readUtf8(livingAsyncapi);\n const merge: AsyncapiMergeResult = mergeAsyncapiSlots(livingText, featText, svc);\n // What the delta declares OUTSIDE the three slot sections — the one\n // question the merge result cannot answer for the silence below, because\n // a merge that wrote nothing reports nothing to distinguish \"the delta\n // was all quotes\" from \"the delta was all schemas and I dropped them\".\n const surfaces = asyncapiSurfaces(parse(featText));\n if (merge.text !== null) {\n writes.push(planWrite(livingAsyncapi, merge.text));\n // The parenthesised list names the MESSAGES the delta declares, and a\n // delta whose whole change is a component surface has none — so this\n // printed `merged ()`, the same empty-parenthesis lie the create\n // branch above is commented for: it reads as \"this merge wrote\n // nothing\" about a merge that just published a shared schema to every\n // consumer of the fleet. Drop the parentheses when there is nothing to\n // put in them; the surface lines below name what actually moved.\n say(` asyncapi: ${svc} — merged${names.length > 0 ? ` (${names.join(\", \")})` : \"\"}`);\n } else if (surfaces.length > 0) {\n // \"The plan wrote less than my delta spells\" is the sentence a reader\n // cannot infer from an unchanged file — the quote line's own comment\n // (./verdict-lines.ts) names it for slots, and a components-only\n // delta is the shape where silence is indistinguishable from the\n // defect that made it merge nothing at all. Not a warning: every\n // surface being a quote is correct authoring and the correct merge.\n say(` asyncapi: ${svc} — nothing to write: every one of the ${surfaces.length} component surface(s) this delta declares already stands as living has it`);\n }\n // Asked whatever the merge wrote: a delta whose ONLY change is its `info`\n // merges nothing at all, and that is exactly the shape that archived in\n // silence (./verdict-lines.ts).\n warnInfoDrift(plan, say, {\n svc, code: \"asyncapi.info-not-merged\", file: \"asyncapi.yaml\", carries: \"channels, operations and components\",\n labels: infoDrift(parse(livingText), parse(featText)),\n });\n if (merge.removed.length > 0) {\n asyncapiRemovals.push({ service: svc, slots: merge.removed.map((slot) => slot.label) });\n for (const slot of merge.removed) say(` - removes ${slot.label}`);\n }\n // The quote and stale lines (./verdict-lines.ts says why each is said),\n // the component surfaces' lines mirroring the slots' around them.\n sayQuoted(say, merge.quoted.map((slot) => slot.label));\n sayQuoted(say, merge.componentsQuoted.map((id) => `components.${id}`));\n sayStale(say, merge.baselineStale.map((slot) => slot.label));\n sayStale(say, merge.componentsStale.map((id) => `components.${id}`));\n for (const id of merge.componentsModified) {\n warnOverwrite(plan, say, {\n svc, code: \"asyncapi.component-modified\", label: `components.${id}`,\n message: `${svc}: the delta declares components.${id}, which the living AsyncAPI already has with different content — the merge overwrites the living component wholesale`,\n });\n }\n for (const slot of merge.modified) {\n // A message is the one slot a consumer names (`Consumes:` lines,\n // `consumes` edges), so only `asyncapi.message-modified` says who the\n // overwrite lands on — the OpenAPI planner's sentence, through the\n // same scan (./consumers.ts). Channels and operations join by nothing.\n const name = merge.modifiedMessageNames.get(slot.label);\n const named = name === undefined ? null : consumerClause(await config.consumers.message(svc, name));\n warnOverwrite(plan, say, {\n svc, code: modifiedCode(slot.section), label: slot.label, named,\n message: `${svc}: the delta redefines ${slot.label}, which the living AsyncAPI already has — the merge overwrites the living slot wholesale`,\n });\n }\n for (const ref of merge.unresolved) {\n planGates.push({\n severity: \"error\",\n code: \"asyncapi.ref-unresolved\",\n subject: svc,\n message: `${svc}: $ref '${ref}' resolves in neither the feature's asyncapi.yaml nor the living one — the merged document would carry a dangling reference`,\n });\n }\n } catch (err) {\n if (err instanceof AsyncapiMergeError) throw new CodedFailure(\"merge-failed\", err.message);\n throw err;\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who in the CURRENT fleet consumes what an archive is about to overwrite — the
|
|
3
|
+
* consumers an `openapi.op-modified` / `asyncapi.message-modified` warning
|
|
4
|
+
* names, so "the merge overwrites the living operation wholesale" says whom
|
|
5
|
+
* that lands on instead of leaving the reader to go and find out.
|
|
6
|
+
*
|
|
7
|
+
* NOT A NEW JOIN. This is the one consumer index (core/consumers/consumers.ts)
|
|
8
|
+
* — the one `loam diff` names victims with and the coherence gate refuses on —
|
|
9
|
+
* asked over the living fleet as it stands before the merge: the same edges of
|
|
10
|
+
* the `architecture/` project, the same living requirements, the same use-case
|
|
11
|
+
* hops, the same suspensions for a consumer nobody could read or a caller
|
|
12
|
+
* drawn with no op, and the same sentence for the answer (`consumerStance`).
|
|
13
|
+
* Its `own` list (the provider's own documents) is the gate's to count and is
|
|
14
|
+
* not named here: a warning about whom an overwrite lands on is about OTHER
|
|
15
|
+
* services, as the diff's is.
|
|
16
|
+
*
|
|
17
|
+
* LAZY, AND PAID ONLY WHEN A WARNING FIRES. The index reads every service's
|
|
18
|
+
* four documents (`livingFleet`) and, at the first question, parses the
|
|
19
|
+
* `architecture/` project — through the run's read index, so a second question
|
|
20
|
+
* pays for neither again; the archive's own gate calls coherence without that
|
|
21
|
+
* index, so its read is not shared with this one. Measured 2026-10-10 on the
|
|
22
|
+
* 120-service benchmark fleet (scripts/bench-validate.ts's shape), with one
|
|
23
|
+
* feature overwriting one operation: `archive --dry-run --json` went from a
|
|
24
|
+
* 1216 ms to a 1346 ms median over five runs through tsx (~0.5 s of which is
|
|
25
|
+
* startup); taken apart, about 50 ms of document reads and about 120 ms of
|
|
26
|
+
* LikeC4. An archive that overwrites nothing never builds it.
|
|
27
|
+
*
|
|
28
|
+
* What it does NOT answer: `openapi.component-modified` and
|
|
29
|
+
* `asyncapi.component-modified`. A component's consumers are the operations or
|
|
30
|
+
* messages whose `$ref` closure reaches it, and then THEIR consumers; nothing
|
|
31
|
+
* indexes a contract by component today (an operation carries its closure's
|
|
32
|
+
* digest, not its members), and building that reverse index for a warning is
|
|
33
|
+
* the heavy new structure this reuse was allowed only without.
|
|
34
|
+
*/
|
|
35
|
+
import { consumerIndex } from "../../../../core/consumers/consumers.js";
|
|
36
|
+
import { consumerStance, scanDetails } from "../../../../core/consumers/scan.js";
|
|
37
|
+
import { FleetContext } from "../../../../core/fleet-context.js";
|
|
38
|
+
export function contractConsumers(config) {
|
|
39
|
+
// The run's own read index: the planners have already read the delta
|
|
40
|
+
// services' living contracts through it, and the index memoises its fleet
|
|
41
|
+
// and its map on it. Constructing the index reads nothing.
|
|
42
|
+
const index = consumerIndex({ docsDir: config.docsDir, context: config.fleet });
|
|
43
|
+
return {
|
|
44
|
+
op: (provider, operationId) => index.opConsumers(provider, operationId),
|
|
45
|
+
message: (provider, name) => index.messageConsumers(provider, name),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The clause an overwrite warning's message ends with, and the lines its
|
|
50
|
+
* `details` carry: positive evidence first, then what could not be checked —
|
|
51
|
+
* the order `loam diff` lists them in.
|
|
52
|
+
*/
|
|
53
|
+
export function consumerClause(scan) {
|
|
54
|
+
return { clause: consumerStance(scan), details: scanDetails(scan) };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=consumers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumers.js","sourceRoot":"","sources":["../../../../../src/commands/archive/plan/contracts/consumers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAqB,MAAM,oCAAoC,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAWjE,MAAM,UAAU,iBAAiB,CAAC,MAAiD;IACjF,qEAAqE;IACrE,0EAA0E;IAC1E,2DAA2D;IAC3D,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAChF,OAAO;QACL,EAAE,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC;QACvE,OAAO,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC;KACpE,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAkB;IAC/C,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;AACtE,CAAC","sourcesContent":["/**\n * Who in the CURRENT fleet consumes what an archive is about to overwrite — the\n * consumers an `openapi.op-modified` / `asyncapi.message-modified` warning\n * names, so \"the merge overwrites the living operation wholesale\" says whom\n * that lands on instead of leaving the reader to go and find out.\n *\n * NOT A NEW JOIN. This is the one consumer index (core/consumers/consumers.ts)\n * — the one `loam diff` names victims with and the coherence gate refuses on —\n * asked over the living fleet as it stands before the merge: the same edges of\n * the `architecture/` project, the same living requirements, the same use-case\n * hops, the same suspensions for a consumer nobody could read or a caller\n * drawn with no op, and the same sentence for the answer (`consumerStance`).\n * Its `own` list (the provider's own documents) is the gate's to count and is\n * not named here: a warning about whom an overwrite lands on is about OTHER\n * services, as the diff's is.\n *\n * LAZY, AND PAID ONLY WHEN A WARNING FIRES. The index reads every service's\n * four documents (`livingFleet`) and, at the first question, parses the\n * `architecture/` project — through the run's read index, so a second question\n * pays for neither again; the archive's own gate calls coherence without that\n * index, so its read is not shared with this one. Measured 2026-10-10 on the\n * 120-service benchmark fleet (scripts/bench-validate.ts's shape), with one\n * feature overwriting one operation: `archive --dry-run --json` went from a\n * 1216 ms to a 1346 ms median over five runs through tsx (~0.5 s of which is\n * startup); taken apart, about 50 ms of document reads and about 120 ms of\n * LikeC4. An archive that overwrites nothing never builds it.\n *\n * What it does NOT answer: `openapi.component-modified` and\n * `asyncapi.component-modified`. A component's consumers are the operations or\n * messages whose `$ref` closure reaches it, and then THEIR consumers; nothing\n * indexes a contract by component today (an operation carries its closure's\n * digest, not its members), and building that reverse index for a warning is\n * the heavy new structure this reuse was allowed only without.\n */\nimport { consumerIndex } from \"../../../../core/consumers/consumers.js\";\nimport { consumerStance, scanDetails, type ConsumerScan } from \"../../../../core/consumers/scan.js\";\nimport { FleetContext } from \"../../../../core/fleet-context.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\n\n/** The two questions the contract planners ask, each answered at most once per run's index. */\nexport interface ContractConsumers {\n /** Current consumers of `provider`'s operation `operationId`. */\n op(provider: string, operationId: string): Promise<ConsumerScan>;\n /** Current consumers of message `name` that `provider` declares. */\n message(provider: string, name: string): Promise<ConsumerScan>;\n}\n\nexport function contractConsumers(config: { docsDir: DocsDir; fleet: FleetContext }): ContractConsumers {\n // The run's own read index: the planners have already read the delta\n // services' living contracts through it, and the index memoises its fleet\n // and its map on it. Constructing the index reads nothing.\n const index = consumerIndex({ docsDir: config.docsDir, context: config.fleet });\n return {\n op: (provider, operationId) => index.opConsumers(provider, operationId),\n message: (provider, name) => index.messageConsumers(provider, name),\n };\n}\n\n/**\n * The clause an overwrite warning's message ends with, and the lines its\n * `details` carry: positive evidence first, then what could not be checked —\n * the order `loam diff` lists them in.\n */\nexport function consumerClause(scan: ConsumerScan): { clause: string; details: string[] } {\n return { clause: consumerStance(scan), details: scanDetails(scan) };\n}\n"]}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OpenAPI contract merge: the feature's openapi deltas folded into each
|
|
3
|
+
* living service API — the half of the archive plan that answers to the
|
|
4
|
+
* contract axis, split out of ../specs.ts so the requirements merge and the
|
|
5
|
+
* contract merges stop sharing one file.
|
|
6
|
+
*
|
|
7
|
+
* Nothing is written. Everything lands in the `Plan`, so a failure on any axis
|
|
8
|
+
* leaves the living docs exactly as they were.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import { parse } from "yaml";
|
|
12
|
+
import { CodedFailure, repoPath } from "../../../../core/envelope/json.js";
|
|
13
|
+
import { isRecord } from "../../../../core/kernel/records.js";
|
|
14
|
+
import { planWrite, readUtf8 } from "../../../../core/staging/writes.js";
|
|
15
|
+
import { featureSpecPaths } from "../../../../core/repo/paths.js";
|
|
16
|
+
import { locateServicePaths } from "../../../../core/repo/service-target.js";
|
|
17
|
+
import { readOpenapi } from "../../../../core/openapi/doc.js";
|
|
18
|
+
import { OpenapiMergeError } from "../../../../core/openapi/merge/error.js";
|
|
19
|
+
import { stripOpenapiRemovalMarkers } from "../../../../core/openapi/merge/markers.js";
|
|
20
|
+
import { mergeOpenapiPaths } from "../../../../core/openapi/merge/merge.js";
|
|
21
|
+
import {} from "../state.js";
|
|
22
|
+
import { consumerClause } from "./consumers.js";
|
|
23
|
+
import { infoDrift, sayQuoted, sayStale, warnInfoDrift, warnOverwrite } from "./verdict-lines.js";
|
|
24
|
+
export async function planOpenapiContracts(config, gated, plan, say) {
|
|
25
|
+
const { featureDir, deltaServices } = gated;
|
|
26
|
+
const { writes, planGates, openapiRemovals } = plan;
|
|
27
|
+
// OpenAPI merge — fold the feature's openapi deltas into the living service APIs.
|
|
28
|
+
for (const svc of deltaServices) {
|
|
29
|
+
const featOpenapi = featureSpecPaths(featureDir, svc).openapi;
|
|
30
|
+
if (!existsSync(featOpenapi))
|
|
31
|
+
continue;
|
|
32
|
+
const featText = await readUtf8(featOpenapi);
|
|
33
|
+
// The shared context: this planner runs once per delta service, and a
|
|
34
|
+
// context-less locate is a full fleet walk per service.
|
|
35
|
+
const livingOpenapi = (await locateServicePaths(config.docsDir, svc, config.fleet)).openapi;
|
|
36
|
+
const featDoc = await readOpenapi(featOpenapi);
|
|
37
|
+
// Every other reader of this flag suspends its own judgement when it is set
|
|
38
|
+
// — validate grades `openapi.invalid`, show and status print that the file
|
|
39
|
+
// does not parse. Archive was the only one that never looked, and it is the
|
|
40
|
+
// command that WRITES: a feature openapi.yaml holding a sequence instead of
|
|
41
|
+
// a mapping was planned verbatim into services/<svc>/openapi.yaml, `ok:
|
|
42
|
+
// true`, printing `created ()` because the reader saw no operations in it.
|
|
43
|
+
// A document loam cannot read is one it cannot merge, and that is the same
|
|
44
|
+
// answer as any other merge it could not compute: nothing is written.
|
|
45
|
+
if (featDoc.unreadable) {
|
|
46
|
+
throw new CodedFailure("merge-failed", `feature openapi ${repoPath(config.docsDir, featOpenapi)} for ${svc} does not read as an OpenAPI document (${featDoc.error ?? "not a YAML mapping"}) — the API axis cannot be merged; fix the file, or delete it if this feature has no contract delta`);
|
|
47
|
+
}
|
|
48
|
+
const ops = featDoc.ops.filter((op) => !op.remove).map((op) => op.id);
|
|
49
|
+
// An `x-loam-remove: true` written at PATH level retires nothing: the marker
|
|
50
|
+
// addresses one operation, and beside the methods there is no operation for
|
|
51
|
+
// it to address. The merge now strips it on every branch, so the living
|
|
52
|
+
// contract is safe either way — but the author asked for a removal that will
|
|
53
|
+
// not happen, and silence there is how a retired endpoint stayed live.
|
|
54
|
+
// Gated like the other plan-visible breaches, --approve and all.
|
|
55
|
+
for (const path of featDoc.pathLevelRemovals) {
|
|
56
|
+
planGates.push({
|
|
57
|
+
severity: "error",
|
|
58
|
+
code: "openapi.remove-marker-path-level",
|
|
59
|
+
subject: svc,
|
|
60
|
+
message: `${svc}: '${path}' carries x-loam-remove at PATH level, beside the methods — a removal marker names ONE operation, so this retires nothing and is not a contract key either. Move it inside the operation you are retiring (with its operationId), or delete it.`,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (!existsSync(livingOpenapi)) {
|
|
64
|
+
// A removal against a non-existent contract is gated by coherence. Keep
|
|
65
|
+
// the feature-only marker out of living docs even under --approve — and
|
|
66
|
+
// ask the DOCUMENT, not the operation reader: a marker with no
|
|
67
|
+
// operationId is invisible to `operations()`, so gating the strip on
|
|
68
|
+
// "does the reader see a removal" let exactly that marker through into a
|
|
69
|
+
// living contract, published to every consumer of the fleet.
|
|
70
|
+
const content = stripOpenapiRemovalMarkers(featText, svc);
|
|
71
|
+
// A contract that describes nothing is not created, on the spec axis's
|
|
72
|
+
// rule for a delta that leaves no requirements (../specs.ts). A
|
|
73
|
+
// `paths: {}` stub is what an author wrote to quiet a status that used to
|
|
74
|
+
// demand a contract from every new service, and copying it in published
|
|
75
|
+
// "this service exposes nothing" as a living contract the fleet then
|
|
76
|
+
// joins against — announced as `created ()`. With no file, the service
|
|
77
|
+
// reads as one with no HTTP surface, which `service.no-openapi` already
|
|
78
|
+
// grades: silent while nothing calls it, an error once something does.
|
|
79
|
+
if (describesNothing(content)) {
|
|
80
|
+
say(` openapi: ${svc} — nothing to merge (delta defines no paths or components), no living openapi.yaml created`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
writes.push(planWrite(livingOpenapi, content));
|
|
84
|
+
say(` openapi: ${svc} — created (${ops.join(", ")})`);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const livingText = await readUtf8(livingOpenapi);
|
|
88
|
+
let merge;
|
|
89
|
+
try {
|
|
90
|
+
merge = mergeOpenapiPaths(livingText, featText, svc);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (err instanceof OpenapiMergeError)
|
|
94
|
+
throw new CodedFailure("merge-failed", err.message);
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
const { text, modified, modifiedOperationIds, pathItemModified, removed, quoted, pathItemQuoted, pathItemStale, componentsModified, componentsAdded, componentsQuoted, componentsStale, unresolved, } = merge;
|
|
98
|
+
if (text !== null) {
|
|
99
|
+
writes.push(planWrite(livingOpenapi, text));
|
|
100
|
+
// The parenthesised list names the OPERATIONS the delta merged, and a
|
|
101
|
+
// delta whose whole change is a component has none — so this printed
|
|
102
|
+
// `merged ()`, the same empty-parenthesis lie the create branch above
|
|
103
|
+
// is commented for: it reads as "this merge wrote nothing" about a
|
|
104
|
+
// merge that just published a shared schema to every consumer of the
|
|
105
|
+
// fleet. Name the components it added instead, and drop the
|
|
106
|
+
// parentheses entirely when there is nothing to put in them — a delta
|
|
107
|
+
// that only EDITED existing surfaces is named by the lines below.
|
|
108
|
+
const mergedNames = ops.length > 0 ? ops : componentsAdded.map((comp) => `component ${comp}`);
|
|
109
|
+
say(` openapi: ${svc} — merged${mergedNames.length > 0 ? ` (${mergedNames.join(", ")})` : ""}`);
|
|
110
|
+
}
|
|
111
|
+
// Asked whatever the merge wrote, for the event planner's reason: an
|
|
112
|
+
// `info`-only delta merges nothing, and archived in silence.
|
|
113
|
+
warnInfoDrift(plan, say, {
|
|
114
|
+
svc, code: "openapi.info-not-merged", file: "openapi.yaml", carries: "paths and components",
|
|
115
|
+
labels: infoDrift(parse(livingText), parse(featText)),
|
|
116
|
+
});
|
|
117
|
+
if (removed.length > 0) {
|
|
118
|
+
openapiRemovals.push({ service: svc, operations: removed });
|
|
119
|
+
for (const label of removed)
|
|
120
|
+
say(` - removes ${label}`);
|
|
121
|
+
}
|
|
122
|
+
// The quote and stale lines (./verdict-lines.ts says why each is said).
|
|
123
|
+
sayQuoted(say, quoted);
|
|
124
|
+
sayQuoted(say, pathItemQuoted.map((label) => `path-level ${label}`));
|
|
125
|
+
sayQuoted(say, componentsQuoted.map((comp) => `component ${comp}`));
|
|
126
|
+
sayStale(say, pathItemStale.map((label) => `path-level ${label}`));
|
|
127
|
+
sayStale(say, componentsStale.map((comp) => `component ${comp}`));
|
|
128
|
+
for (const label of modified) {
|
|
129
|
+
// Who the overwrite lands on: the living fleet's consumers of the
|
|
130
|
+
// operation, through `loam diff`'s own scan (./consumers.ts). An
|
|
131
|
+
// operation with no operationId on either side has nothing anyone can
|
|
132
|
+
// name it by, so the warning stays the sentence it always was.
|
|
133
|
+
const id = modifiedOperationIds.get(label);
|
|
134
|
+
const named = id === undefined ? null : consumerClause(await config.consumers.op(svc, id));
|
|
135
|
+
warnOverwrite(plan, say, {
|
|
136
|
+
svc, code: "openapi.op-modified", label, named,
|
|
137
|
+
message: `${svc}: the delta redefines ${label}, which the living OpenAPI already has — the merge overwrites the living operation wholesale`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
for (const label of pathItemModified) {
|
|
141
|
+
warnOverwrite(plan, say, {
|
|
142
|
+
svc, code: "openapi.path-item-modified", label: `path-level ${label}`,
|
|
143
|
+
message: `${svc}: the delta redefines the path-level key ${label}, which the living OpenAPI already has — the merge overwrites it wholesale, and it applies to EVERY operation on that path, including ones this feature never mentions`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
for (const comp of componentsModified) {
|
|
147
|
+
warnOverwrite(plan, say, {
|
|
148
|
+
svc, code: "openapi.component-modified", label: `component ${comp}`,
|
|
149
|
+
message: `${svc}: the delta declares component '${comp}', which the living OpenAPI already defines differently — the merge overwrites the living component wholesale`,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
for (const u of unresolved) {
|
|
153
|
+
planGates.push({
|
|
154
|
+
severity: "error",
|
|
155
|
+
code: "openapi.ref-unresolved",
|
|
156
|
+
subject: svc,
|
|
157
|
+
message: `${svc}: $ref '${u.ref}' (referenced from ${u.from}) resolves in neither the feature's openapi.yaml nor the living one — the merged document would carry a dangling reference`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Does this contract describe no surface at all — no path, no webhook, and no
|
|
165
|
+
* component in any section? Only the document's frame is left (`openapi`,
|
|
166
|
+
* `info`, `servers`, `tags`), which is a title and nothing anyone can call.
|
|
167
|
+
*
|
|
168
|
+
* Conservative in one direction: anything that is not an empty mapping where a
|
|
169
|
+
* mapping belongs counts as content, so a document this cannot classify is
|
|
170
|
+
* created exactly as before. The reader above has already refused one that
|
|
171
|
+
* does not parse.
|
|
172
|
+
*/
|
|
173
|
+
function describesNothing(text) {
|
|
174
|
+
let doc;
|
|
175
|
+
try {
|
|
176
|
+
doc = parse(text);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Not a document this can classify, so it is content: created as before.
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (doc === null || doc === undefined)
|
|
183
|
+
return true;
|
|
184
|
+
if (!isRecord(doc))
|
|
185
|
+
return false;
|
|
186
|
+
const components = doc["components"];
|
|
187
|
+
const sections = components === undefined || components === null ? [] : isRecord(components) ? Object.values(components) : [components];
|
|
188
|
+
return [doc["paths"], doc["webhooks"], ...sections].every(isEmptyMapping);
|
|
189
|
+
}
|
|
190
|
+
function isEmptyMapping(value) {
|
|
191
|
+
return value === undefined || value === null || (isRecord(value) && Object.keys(value).length === 0);
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=openapi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../../../../src/commands/archive/plan/contracts/openapi.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The OpenAPI contract merge: the feature's openapi deltas folded into each\n * living service API — the half of the archive plan that answers to the\n * contract axis, split out of ../specs.ts so the requirements merge and the\n * contract merges stop sharing one file.\n *\n * Nothing is written. Everything lands in the `Plan`, so a failure on any axis\n * leaves the living docs exactly as they were.\n */\nimport { existsSync } from \"node:fs\";\nimport { parse } from \"yaml\";\nimport { CodedFailure, repoPath } from \"../../../../core/envelope/json.js\";\nimport { isRecord } from \"../../../../core/kernel/records.js\";\nimport { planWrite, readUtf8 } from \"../../../../core/staging/writes.js\";\nimport { featureSpecPaths } from \"../../../../core/repo/paths.js\";\nimport { locateServicePaths } from \"../../../../core/repo/service-target.js\";\nimport { readOpenapi } from \"../../../../core/openapi/doc.js\";\nimport { OpenapiMergeError } from \"../../../../core/openapi/merge/error.js\";\nimport { stripOpenapiRemovalMarkers } from \"../../../../core/openapi/merge/markers.js\";\nimport { mergeOpenapiPaths, type OpenapiMergeResult } from \"../../../../core/openapi/merge/merge.js\";\nimport { type Gated, type Plan } from \"../state.js\";\nimport { consumerClause, type ContractConsumers } from \"./consumers.js\";\nimport { infoDrift, sayQuoted, sayStale, warnInfoDrift, warnOverwrite } from \"./verdict-lines.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\nimport type { FleetContext } from \"../../../../core/fleet-context.js\";\n\nexport async function planOpenapiContracts(\n config: { docsDir: DocsDir; fleet: FleetContext; consumers: ContractConsumers },\n gated: Gated,\n plan: Plan,\n say: (line?: string) => void,\n): Promise<void> {\n const { featureDir, deltaServices } = gated;\n const { writes, planGates, openapiRemovals } = plan;\n // OpenAPI merge — fold the feature's openapi deltas into the living service APIs.\n for (const svc of deltaServices) {\n const featOpenapi = featureSpecPaths(featureDir, svc).openapi;\n if (!existsSync(featOpenapi)) continue;\n const featText = await readUtf8(featOpenapi);\n // The shared context: this planner runs once per delta service, and a\n // context-less locate is a full fleet walk per service.\n const livingOpenapi = (await locateServicePaths(config.docsDir, svc, config.fleet)).openapi;\n const featDoc = await readOpenapi(featOpenapi);\n // Every other reader of this flag suspends its own judgement when it is set\n // — validate grades `openapi.invalid`, show and status print that the file\n // does not parse. Archive was the only one that never looked, and it is the\n // command that WRITES: a feature openapi.yaml holding a sequence instead of\n // a mapping was planned verbatim into services/<svc>/openapi.yaml, `ok:\n // true`, printing `created ()` because the reader saw no operations in it.\n // A document loam cannot read is one it cannot merge, and that is the same\n // answer as any other merge it could not compute: nothing is written.\n if (featDoc.unreadable) {\n throw new CodedFailure(\n \"merge-failed\",\n `feature openapi ${repoPath(config.docsDir, featOpenapi)} for ${svc} does not read as an OpenAPI document (${featDoc.error ?? \"not a YAML mapping\"}) — the API axis cannot be merged; fix the file, or delete it if this feature has no contract delta`,\n );\n }\n const ops = featDoc.ops.filter((op) => !op.remove).map((op) => op.id);\n // An `x-loam-remove: true` written at PATH level retires nothing: the marker\n // addresses one operation, and beside the methods there is no operation for\n // it to address. The merge now strips it on every branch, so the living\n // contract is safe either way — but the author asked for a removal that will\n // not happen, and silence there is how a retired endpoint stayed live.\n // Gated like the other plan-visible breaches, --approve and all.\n for (const path of featDoc.pathLevelRemovals) {\n planGates.push({\n severity: \"error\",\n code: \"openapi.remove-marker-path-level\",\n subject: svc,\n message: `${svc}: '${path}' carries x-loam-remove at PATH level, beside the methods — a removal marker names ONE operation, so this retires nothing and is not a contract key either. Move it inside the operation you are retiring (with its operationId), or delete it.`,\n });\n }\n if (!existsSync(livingOpenapi)) {\n // A removal against a non-existent contract is gated by coherence. Keep\n // the feature-only marker out of living docs even under --approve — and\n // ask the DOCUMENT, not the operation reader: a marker with no\n // operationId is invisible to `operations()`, so gating the strip on\n // \"does the reader see a removal\" let exactly that marker through into a\n // living contract, published to every consumer of the fleet.\n const content = stripOpenapiRemovalMarkers(featText, svc);\n // A contract that describes nothing is not created, on the spec axis's\n // rule for a delta that leaves no requirements (../specs.ts). A\n // `paths: {}` stub is what an author wrote to quiet a status that used to\n // demand a contract from every new service, and copying it in published\n // \"this service exposes nothing\" as a living contract the fleet then\n // joins against — announced as `created ()`. With no file, the service\n // reads as one with no HTTP surface, which `service.no-openapi` already\n // grades: silent while nothing calls it, an error once something does.\n if (describesNothing(content)) {\n say(` openapi: ${svc} — nothing to merge (delta defines no paths or components), no living openapi.yaml created`);\n continue;\n }\n writes.push(planWrite(livingOpenapi, content));\n say(` openapi: ${svc} — created (${ops.join(\", \")})`);\n } else {\n const livingText = await readUtf8(livingOpenapi);\n let merge: OpenapiMergeResult;\n try {\n merge = mergeOpenapiPaths(livingText, featText, svc);\n } catch (err) {\n if (err instanceof OpenapiMergeError) throw new CodedFailure(\"merge-failed\", err.message);\n throw err;\n }\n const {\n text, modified, modifiedOperationIds, pathItemModified, removed, quoted, pathItemQuoted, pathItemStale,\n componentsModified, componentsAdded, componentsQuoted, componentsStale, unresolved,\n } = merge;\n if (text !== null) {\n writes.push(planWrite(livingOpenapi, text));\n // The parenthesised list names the OPERATIONS the delta merged, and a\n // delta whose whole change is a component has none — so this printed\n // `merged ()`, the same empty-parenthesis lie the create branch above\n // is commented for: it reads as \"this merge wrote nothing\" about a\n // merge that just published a shared schema to every consumer of the\n // fleet. Name the components it added instead, and drop the\n // parentheses entirely when there is nothing to put in them — a delta\n // that only EDITED existing surfaces is named by the lines below.\n const mergedNames = ops.length > 0 ? ops : componentsAdded.map((comp) => `component ${comp}`);\n say(` openapi: ${svc} — merged${mergedNames.length > 0 ? ` (${mergedNames.join(\", \")})` : \"\"}`);\n }\n // Asked whatever the merge wrote, for the event planner's reason: an\n // `info`-only delta merges nothing, and archived in silence.\n warnInfoDrift(plan, say, {\n svc, code: \"openapi.info-not-merged\", file: \"openapi.yaml\", carries: \"paths and components\",\n labels: infoDrift(parse(livingText), parse(featText)),\n });\n if (removed.length > 0) {\n openapiRemovals.push({ service: svc, operations: removed });\n for (const label of removed) say(` - removes ${label}`);\n }\n // The quote and stale lines (./verdict-lines.ts says why each is said).\n sayQuoted(say, quoted);\n sayQuoted(say, pathItemQuoted.map((label) => `path-level ${label}`));\n sayQuoted(say, componentsQuoted.map((comp) => `component ${comp}`));\n sayStale(say, pathItemStale.map((label) => `path-level ${label}`));\n sayStale(say, componentsStale.map((comp) => `component ${comp}`));\n for (const label of modified) {\n // Who the overwrite lands on: the living fleet's consumers of the\n // operation, through `loam diff`'s own scan (./consumers.ts). An\n // operation with no operationId on either side has nothing anyone can\n // name it by, so the warning stays the sentence it always was.\n const id = modifiedOperationIds.get(label);\n const named = id === undefined ? null : consumerClause(await config.consumers.op(svc, id));\n warnOverwrite(plan, say, {\n svc, code: \"openapi.op-modified\", label, named,\n message: `${svc}: the delta redefines ${label}, which the living OpenAPI already has — the merge overwrites the living operation wholesale`,\n });\n }\n for (const label of pathItemModified) {\n warnOverwrite(plan, say, {\n svc, code: \"openapi.path-item-modified\", label: `path-level ${label}`,\n message: `${svc}: the delta redefines the path-level key ${label}, which the living OpenAPI already has — the merge overwrites it wholesale, and it applies to EVERY operation on that path, including ones this feature never mentions`,\n });\n }\n for (const comp of componentsModified) {\n warnOverwrite(plan, say, {\n svc, code: \"openapi.component-modified\", label: `component ${comp}`,\n message: `${svc}: the delta declares component '${comp}', which the living OpenAPI already defines differently — the merge overwrites the living component wholesale`,\n });\n }\n for (const u of unresolved) {\n planGates.push({\n severity: \"error\",\n code: \"openapi.ref-unresolved\",\n subject: svc,\n message: `${svc}: $ref '${u.ref}' (referenced from ${u.from}) resolves in neither the feature's openapi.yaml nor the living one — the merged document would carry a dangling reference`,\n });\n }\n }\n }\n}\n\n/**\n * Does this contract describe no surface at all — no path, no webhook, and no\n * component in any section? Only the document's frame is left (`openapi`,\n * `info`, `servers`, `tags`), which is a title and nothing anyone can call.\n *\n * Conservative in one direction: anything that is not an empty mapping where a\n * mapping belongs counts as content, so a document this cannot classify is\n * created exactly as before. The reader above has already refused one that\n * does not parse.\n */\nfunction describesNothing(text: string): boolean {\n let doc: unknown;\n try {\n doc = parse(text);\n } catch {\n // Not a document this can classify, so it is content: created as before.\n return false;\n }\n if (doc === null || doc === undefined) return true;\n if (!isRecord(doc)) return false;\n const components = doc[\"components\"];\n const sections = components === undefined || components === null ? [] : isRecord(components) ? Object.values(components) : [components];\n return [doc[\"paths\"], doc[\"webhooks\"], ...sections].every(isEmptyMapping);\n}\n\nfunction isEmptyMapping(value: unknown): boolean {\n return value === undefined || value === null || (isRecord(value) && Object.keys(value).length === 0);\n}\n"]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plan lines a contract merge's verdicts print — the quote, the stale
|
|
3
|
+
* baseline, the overwrite and the `info` the merge does not carry — spelled
|
|
4
|
+
* once for both planners
|
|
5
|
+
* (./openapi.ts, ./asyncapi.ts), which used to print them in fourteen parallel
|
|
6
|
+
* loops. Every surface reaches the reader by exactly the same sentence an
|
|
7
|
+
* operation or a slot does, because it is graded by exactly the same verdict;
|
|
8
|
+
* one spelling is what keeps the two axes from describing one verdict in two
|
|
9
|
+
* sentences. The label prefix (`path-level …`, `component …`, `components.…`)
|
|
10
|
+
* stays at the call site: it is the one part that differs.
|
|
11
|
+
*/
|
|
12
|
+
import { isDeepStrictEqual } from "node:util";
|
|
13
|
+
import { withoutFeatureKeysDeep } from "../../../../core/openapi/digest.js";
|
|
14
|
+
import { isRecord } from "../../../../core/kernel/records.js";
|
|
15
|
+
import { compareIds } from "../../../../core/repo/entries.js";
|
|
16
|
+
/**
|
|
17
|
+
* Said out loud, because "the plan wrote less than my delta spells" is the one
|
|
18
|
+
* thing a reader cannot infer from a merged file. Not a warning: quoting the
|
|
19
|
+
* contract around your change is correct authoring, and leaving the quote
|
|
20
|
+
* alone is the correct merge. The path-level and component quotes get the
|
|
21
|
+
* same sentence for the same reason.
|
|
22
|
+
*/
|
|
23
|
+
export function sayQuoted(say, labels) {
|
|
24
|
+
for (const label of labels)
|
|
25
|
+
say(` · quotes ${label} — unchanged since it was pinned, left as living has it`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Stale surfaces reaching the merge at all means --approve pushed past the
|
|
29
|
+
* gate's `openapi.baseline-stale` / `asyncapi.baseline-stale` — the plan still
|
|
30
|
+
* names what it cost.
|
|
31
|
+
*/
|
|
32
|
+
export function sayStale(say, labels) {
|
|
33
|
+
for (const label of labels) {
|
|
34
|
+
say(` ⚠ stale baseline on ${label} — the living value moved since this delta was pinned; the overwrite is what --approve chose`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Warn about one overwrite and print it. The consumer clause, when there is
|
|
39
|
+
* one, ends both the warning's message and the printed line, and its evidence
|
|
40
|
+
* rides as the warning's `details` and as indented lines under it.
|
|
41
|
+
*/
|
|
42
|
+
export function warnOverwrite(plan, say, overwrite) {
|
|
43
|
+
const named = overwrite.named ?? null;
|
|
44
|
+
const clause = named === null ? "" : `; ${named.clause}`;
|
|
45
|
+
plan.planWarns.push({
|
|
46
|
+
severity: "warn",
|
|
47
|
+
code: overwrite.code,
|
|
48
|
+
subject: overwrite.svc,
|
|
49
|
+
message: overwrite.message + clause,
|
|
50
|
+
...(named === null ? {} : { details: named.details }),
|
|
51
|
+
});
|
|
52
|
+
say(` ⚠ overwrites ${overwrite.label} — the living definition differs${clause}`);
|
|
53
|
+
for (const line of named?.details ?? [])
|
|
54
|
+
say(` ${line}`);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The `info` keys a feature's contract spells differently from the living one,
|
|
58
|
+
* as `info.<key>` labels, sorted — empty when the delta writes no `info`, or
|
|
59
|
+
* writes exactly what living has.
|
|
60
|
+
*
|
|
61
|
+
* Neither merge carries `info` (nor any root key outside the sections it
|
|
62
|
+
* merges: `../../../../core/asyncapi/merge/markers.ts` names what those are),
|
|
63
|
+
* and that is a decision rather than a gap — a delta is a diff of operations
|
|
64
|
+
* and slots, and its `info` is most often the scaffold's or a copy. But a
|
|
65
|
+
* delta that DID move it — a new version, a rewritten description — lost the
|
|
66
|
+
* change with exit 0 and nothing in the plan, so the reader learned it from
|
|
67
|
+
* the living file weeks later. Only keys the delta declares are compared: a
|
|
68
|
+
* key living has and the delta leaves out is a key nobody asked to change.
|
|
69
|
+
* Feature bookkeeping is never compared: an `x-loam-*` key of `info` itself is
|
|
70
|
+
* skipped, and the merges' own markers are stripped from the values below it
|
|
71
|
+
* (`withoutFeatureKeysDeep`), so a pin can never read as drift.
|
|
72
|
+
*/
|
|
73
|
+
export function infoDrift(livingPlain, featurePlain) {
|
|
74
|
+
const feature = isRecord(featurePlain) ? featurePlain.info : undefined;
|
|
75
|
+
if (!isRecord(feature))
|
|
76
|
+
return [];
|
|
77
|
+
const living = isRecord(livingPlain) && isRecord(livingPlain.info) ? livingPlain.info : {};
|
|
78
|
+
return Object.keys(feature)
|
|
79
|
+
.filter((key) => !key.startsWith("x-loam-"))
|
|
80
|
+
.filter((key) => !isDeepStrictEqual(withoutFeatureKeysDeep(feature[key]), living[key]))
|
|
81
|
+
.map((key) => `info.${key}`)
|
|
82
|
+
.sort(compareIds);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Warn about one contract's `info` drift and print it. A warning, not a gate:
|
|
86
|
+
* keeping the living `info` is the merge working as designed, and the remedy —
|
|
87
|
+
* edit the living file in the same change — is the author's call. What must
|
|
88
|
+
* not happen is the silence.
|
|
89
|
+
*/
|
|
90
|
+
export function warnInfoDrift(plan, say, drift) {
|
|
91
|
+
if (drift.labels.length === 0)
|
|
92
|
+
return;
|
|
93
|
+
const which = drift.labels.join(", ");
|
|
94
|
+
plan.planWarns.push({
|
|
95
|
+
severity: "warn",
|
|
96
|
+
code: drift.code,
|
|
97
|
+
subject: drift.svc,
|
|
98
|
+
message: `${drift.svc}: the delta's ${which} differ(s) from the living ${drift.file} — the merge carries ${drift.carries} and never ` +
|
|
99
|
+
`info, so the living contract keeps its own. If it should move, edit the living ${drift.file} in this change.`,
|
|
100
|
+
});
|
|
101
|
+
say(` ⚠ info not merged — the delta's ${which} differ(s) from the living ${drift.file}, which keeps its own`);
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=verdict-lines.js.map
|