@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,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generated-contract check: the build's own OpenAPI document against the
|
|
3
|
+
* copy the docs repo committed.
|
|
4
|
+
*
|
|
5
|
+
* It sits beside the HTTP axis (`../api.ts`) rather than inside it because it
|
|
6
|
+
* reads a different document for a different question. The axis grades the
|
|
7
|
+
* committed contract against the living spec; this asks whether that committed
|
|
8
|
+
* contract is still the one the service serves, and it runs only in a service
|
|
9
|
+
* repo. They used to share one file, and naming the operationIds that moved
|
|
10
|
+
* pushed it over the line limit.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import { parse as parseYaml } from "yaml";
|
|
16
|
+
import { resolveInside } from "../../../../core/kernel/path-safety.js";
|
|
17
|
+
import { canonicalJson } from "../../../../core/openapi/digest.js";
|
|
18
|
+
import { openapiFromText } from "../../../../core/openapi/doc.js";
|
|
19
|
+
import {} from "../../../../core/vocabulary/report.js";
|
|
20
|
+
/**
|
|
21
|
+
* The build's own contract document, compared with the copy the docs repo
|
|
22
|
+
* committed — `openapi.generated-stale`, `contracts.source-missing`,
|
|
23
|
+
* `contracts.source-invalid`.
|
|
24
|
+
*
|
|
25
|
+
* WHY THIS IS NOT AN EXTRACTOR. It reads no line of service code and derives no
|
|
26
|
+
* meaning: it parses a standard document the team's own build emitted, at a
|
|
27
|
+
* path a human wrote into `loam.json`, and compares a digest. That is the same
|
|
28
|
+
* category as `verify --results` ingesting a cucumber report, and it is the
|
|
29
|
+
* only thing in the product that can notice the assumption SCHEMA quietly makes
|
|
30
|
+
* — that the committed `services/<id>/openapi.yaml` is the contract the service
|
|
31
|
+
* actually serves. Most fleets generate that document; a fleet that copies it
|
|
32
|
+
* by hand in month one is a fleet whose spine checks grade last quarter's
|
|
33
|
+
* endpoints by month six, and until now nothing could say so.
|
|
34
|
+
*
|
|
35
|
+
* IT NEVER WRITES. The copy stays a human `cp`, reviewed in a pull request,
|
|
36
|
+
* because that is what makes the docs-repo document a contract somebody agreed
|
|
37
|
+
* to rather than a cache of whatever the build last produced. loam reports the
|
|
38
|
+
* divergence and stops.
|
|
39
|
+
*
|
|
40
|
+
* THE DIGEST IS OVER CANONICAL JSON, never raw bytes. Two generator versions
|
|
41
|
+
* that order keys differently, or a YAML dumper that re-wraps a description,
|
|
42
|
+
* produce identical documents and must not produce a permanent warning nobody
|
|
43
|
+
* can clear — the lesson `requirementDigest` (core/document/spec.ts) already records.
|
|
44
|
+
*
|
|
45
|
+
* THE DIGEST DECIDES, THE operationId SETS EXPLAIN. A reworded description and
|
|
46
|
+
* 29 vanished endpoints were one identical warning, each costing a manual diff
|
|
47
|
+
* to tell apart, so a fired warning names the set difference both ways (or says
|
|
48
|
+
* the sets agree). The sets never fire it on their own: the digest still does.
|
|
49
|
+
*
|
|
50
|
+
* Runs only in the SERVICE repo. From the docs repo there is no build output to
|
|
51
|
+
* read and the whole family stays silent, exactly as `sources` provenance does.
|
|
52
|
+
*/
|
|
53
|
+
export async function generatedContractFindings(service, repoDir, spelled, committedPath) {
|
|
54
|
+
if (spelled === undefined)
|
|
55
|
+
return [];
|
|
56
|
+
let generatedPath;
|
|
57
|
+
try {
|
|
58
|
+
// Containment is checked HERE rather than at config load, with the service
|
|
59
|
+
// in hand so the refusal can name it — and a path that escapes the repo is
|
|
60
|
+
// a finding, never a thrown config error that would take down every other
|
|
61
|
+
// command in the repo.
|
|
62
|
+
generatedPath = resolveInside(repoDir, spelled, `"contracts.openapi"`);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return [{
|
|
66
|
+
severity: "error",
|
|
67
|
+
code: "contracts.source-invalid",
|
|
68
|
+
subject: service,
|
|
69
|
+
message: `${service}: loam.json names contracts.openapi '${spelled}', which does not resolve inside this repository — ` +
|
|
70
|
+
`${err instanceof Error ? err.message : String(err)}`,
|
|
71
|
+
}];
|
|
72
|
+
}
|
|
73
|
+
if (!existsSync(generatedPath)) {
|
|
74
|
+
return [{
|
|
75
|
+
severity: "error",
|
|
76
|
+
code: "contracts.source-missing",
|
|
77
|
+
subject: service,
|
|
78
|
+
message: `${service}: loam.json names contracts.openapi '${spelled}', and nothing is there. ` +
|
|
79
|
+
"Either the build has not run in this checkout, or the path is wrong — CI that validates before it builds " +
|
|
80
|
+
"sees exactly this. Run the build first, or correct the path",
|
|
81
|
+
}];
|
|
82
|
+
}
|
|
83
|
+
const digests = [];
|
|
84
|
+
const idSets = [];
|
|
85
|
+
for (const [label, path] of [["generated", generatedPath], ["committed", committedPath]]) {
|
|
86
|
+
if (label === "committed" && !existsSync(path))
|
|
87
|
+
return [];
|
|
88
|
+
let parsed, text;
|
|
89
|
+
try {
|
|
90
|
+
text = await readFile(path, "utf8");
|
|
91
|
+
parsed = parseYaml(text);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
// An unreadable GENERATED document is this check's business; an
|
|
95
|
+
// unreadable committed one is already `openapi.invalid` and must not be
|
|
96
|
+
// reported twice under two codes.
|
|
97
|
+
if (label === "committed")
|
|
98
|
+
return [];
|
|
99
|
+
return [{
|
|
100
|
+
severity: "error",
|
|
101
|
+
code: "contracts.source-invalid",
|
|
102
|
+
subject: service,
|
|
103
|
+
message: `${service}: contracts.openapi '${spelled}' does not parse as YAML or JSON — ` +
|
|
104
|
+
`${err instanceof Error ? err.message : String(err)}`,
|
|
105
|
+
}];
|
|
106
|
+
}
|
|
107
|
+
digests.push(createHash("sha256").update(canonicalJson(parsed), "utf8").digest("hex"));
|
|
108
|
+
// The walk every operation join uses: "served" means what a spine check sees.
|
|
109
|
+
idSets.push(new Set(openapiFromText(text).ops.map((op) => op.id)));
|
|
110
|
+
}
|
|
111
|
+
if (digests[0] === digests[1])
|
|
112
|
+
return [];
|
|
113
|
+
const [served, committed] = idSets;
|
|
114
|
+
const added = [...served].filter((id) => !committed.has(id)).sort();
|
|
115
|
+
const removed = [...committed].filter((id) => !served.has(id)).sort();
|
|
116
|
+
const details = [
|
|
117
|
+
...added.map((id) => `served, not in the committed copy: ${id}`),
|
|
118
|
+
...removed.map((id) => `committed, no longer served: ${id}`),
|
|
119
|
+
];
|
|
120
|
+
const moved = details.length === 0
|
|
121
|
+
? "Every operationId matches, so what moved is inside an operation or outside `paths`. "
|
|
122
|
+
: `${added.length} operation(s) served but not committed, ${removed.length} committed but no longer served (details). `;
|
|
123
|
+
return [{
|
|
124
|
+
severity: "warn",
|
|
125
|
+
code: "openapi.generated-stale",
|
|
126
|
+
subject: service,
|
|
127
|
+
message: `${service}: the committed openapi.yaml differs from what this repository's build produces (${spelled}). ${moved}` +
|
|
128
|
+
"The docs-repo copy is what every operation join is graded against, so the fleet is being checked against a " +
|
|
129
|
+
"contract this service no longer serves. Review the difference and copy the generated document across — " +
|
|
130
|
+
"loam never does that for you, because the committed contract is one somebody agreed to, not a cache of the build",
|
|
131
|
+
...(details.length === 0 ? {} : { details }),
|
|
132
|
+
}];
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=generated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generated.js","sourceRoot":"","sources":["../../../../../src/commands/validate/service/contracts/generated.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The generated-contract check: the build's own OpenAPI document against the\n * copy the docs repo committed.\n *\n * It sits beside the HTTP axis (`../api.ts`) rather than inside it because it\n * reads a different document for a different question. The axis grades the\n * committed contract against the living spec; this asks whether that committed\n * contract is still the one the service serves, and it runs only in a service\n * repo. They used to share one file, and naming the operationIds that moved\n * pushed it over the line limit.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport { parse as parseYaml } from \"yaml\";\nimport { resolveInside } from \"../../../../core/kernel/path-safety.js\";\nimport { canonicalJson } from \"../../../../core/openapi/digest.js\";\nimport { openapiFromText } from \"../../../../core/openapi/doc.js\";\nimport { type Finding } from \"../../../../core/vocabulary/report.js\";\n\n/**\n * The build's own contract document, compared with the copy the docs repo\n * committed — `openapi.generated-stale`, `contracts.source-missing`,\n * `contracts.source-invalid`.\n *\n * WHY THIS IS NOT AN EXTRACTOR. It reads no line of service code and derives no\n * meaning: it parses a standard document the team's own build emitted, at a\n * path a human wrote into `loam.json`, and compares a digest. That is the same\n * category as `verify --results` ingesting a cucumber report, and it is the\n * only thing in the product that can notice the assumption SCHEMA quietly makes\n * — that the committed `services/<id>/openapi.yaml` is the contract the service\n * actually serves. Most fleets generate that document; a fleet that copies it\n * by hand in month one is a fleet whose spine checks grade last quarter's\n * endpoints by month six, and until now nothing could say so.\n *\n * IT NEVER WRITES. The copy stays a human `cp`, reviewed in a pull request,\n * because that is what makes the docs-repo document a contract somebody agreed\n * to rather than a cache of whatever the build last produced. loam reports the\n * divergence and stops.\n *\n * THE DIGEST IS OVER CANONICAL JSON, never raw bytes. Two generator versions\n * that order keys differently, or a YAML dumper that re-wraps a description,\n * produce identical documents and must not produce a permanent warning nobody\n * can clear — the lesson `requirementDigest` (core/document/spec.ts) already records.\n *\n * THE DIGEST DECIDES, THE operationId SETS EXPLAIN. A reworded description and\n * 29 vanished endpoints were one identical warning, each costing a manual diff\n * to tell apart, so a fired warning names the set difference both ways (or says\n * the sets agree). The sets never fire it on their own: the digest still does.\n *\n * Runs only in the SERVICE repo. From the docs repo there is no build output to\n * read and the whole family stays silent, exactly as `sources` provenance does.\n */\nexport async function generatedContractFindings(\n service: string,\n repoDir: string,\n spelled: string | undefined,\n committedPath: string,\n): Promise<Finding[]> {\n if (spelled === undefined) return [];\n let generatedPath: string;\n try {\n // Containment is checked HERE rather than at config load, with the service\n // in hand so the refusal can name it — and a path that escapes the repo is\n // a finding, never a thrown config error that would take down every other\n // command in the repo.\n generatedPath = resolveInside(repoDir, spelled, `\"contracts.openapi\"`);\n } catch (err) {\n return [{\n severity: \"error\",\n code: \"contracts.source-invalid\",\n subject: service,\n message:\n `${service}: loam.json names contracts.openapi '${spelled}', which does not resolve inside this repository — ` +\n `${err instanceof Error ? err.message : String(err)}`,\n }];\n }\n if (!existsSync(generatedPath)) {\n return [{\n severity: \"error\",\n code: \"contracts.source-missing\",\n subject: service,\n message:\n `${service}: loam.json names contracts.openapi '${spelled}', and nothing is there. ` +\n \"Either the build has not run in this checkout, or the path is wrong — CI that validates before it builds \" +\n \"sees exactly this. Run the build first, or correct the path\",\n }];\n }\n const digests: string[] = [];\n const idSets: Set<string>[] = [];\n for (const [label, path] of [[\"generated\", generatedPath], [\"committed\", committedPath]] as const) {\n if (label === \"committed\" && !existsSync(path)) return [];\n let parsed: unknown, text: string;\n try {\n text = await readFile(path, \"utf8\");\n parsed = parseYaml(text);\n } catch (err) {\n // An unreadable GENERATED document is this check's business; an\n // unreadable committed one is already `openapi.invalid` and must not be\n // reported twice under two codes.\n if (label === \"committed\") return [];\n return [{\n severity: \"error\",\n code: \"contracts.source-invalid\",\n subject: service,\n message:\n `${service}: contracts.openapi '${spelled}' does not parse as YAML or JSON — ` +\n `${err instanceof Error ? err.message : String(err)}`,\n }];\n }\n digests.push(createHash(\"sha256\").update(canonicalJson(parsed), \"utf8\").digest(\"hex\"));\n // The walk every operation join uses: \"served\" means what a spine check sees.\n idSets.push(new Set(openapiFromText(text).ops.map((op) => op.id)));\n }\n if (digests[0] === digests[1]) return [];\n const [served, committed] = idSets as [Set<string>, Set<string>];\n const added = [...served].filter((id) => !committed.has(id)).sort();\n const removed = [...committed].filter((id) => !served.has(id)).sort();\n const details = [\n ...added.map((id) => `served, not in the committed copy: ${id}`),\n ...removed.map((id) => `committed, no longer served: ${id}`),\n ];\n const moved = details.length === 0\n ? \"Every operationId matches, so what moved is inside an operation or outside `paths`. \"\n : `${added.length} operation(s) served but not committed, ${removed.length} committed but no longer served (details). `;\n return [{\n severity: \"warn\",\n code: \"openapi.generated-stale\",\n subject: service,\n message:\n `${service}: the committed openapi.yaml differs from what this repository's build produces (${spelled}). ${moved}` +\n \"The docs-repo copy is what every operation join is graded against, so the fleet is being checked against a \" +\n \"contract this service no longer serves. Review the difference and copy the generated document across — \" +\n \"loam never does that for you, because the committed contract is one somebody agreed to, not a cache of the build\",\n ...(details.length === 0 ? {} : { details }),\n }];\n}\n"]}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The async contract axis — AsyncAPI messages on the landscape spine.
|
|
3
|
+
*
|
|
4
|
+
* The HTTP spine (`./spine.js`) and this one are mirror images, and the mirror
|
|
5
|
+
* is the whole reason this is a second axis rather than a second `metadata`
|
|
6
|
+
* key. On the HTTP axis the PROVIDER owns the contract: an edge names an
|
|
7
|
+
* operation and the target's openapi.yaml settles it, locally, in one file.
|
|
8
|
+
* Here the PRODUCER owns the message and the consumer joins to it from another
|
|
9
|
+
* repository — so "is this message declared" is local, while "does anybody
|
|
10
|
+
* publish it" is a fleet question with no local answer at all. That second
|
|
11
|
+
* question is the only check on either spine that reads other services'
|
|
12
|
+
* contracts; it lives in `./message-producers.js`, and this axis only decides
|
|
13
|
+
* when it is worth paying for — exactly when something actually consumes.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import {} from "../../../../core/kernel/ids/service.js";
|
|
17
|
+
import {} from "../../../../core/c4/likec4.js";
|
|
18
|
+
import { eventKeys, infrastructureKind, unboundSentence } from "../../../../core/c4/direction/direction.js";
|
|
19
|
+
import {} from "../../../../core/repo/paths.js";
|
|
20
|
+
import {} from "../../../../core/vocabulary/report.js";
|
|
21
|
+
import {} from "../../../../core/document/spec.js";
|
|
22
|
+
import { readAsyncapi, slotsOf } from "../../../../core/asyncapi/read.js";
|
|
23
|
+
import { FleetContext } from "../../../../core/fleet-context.js";
|
|
24
|
+
import { repoPath } from "../../../../core/envelope/json.js";
|
|
25
|
+
import { producerFindings } from "./message-producers.js";
|
|
26
|
+
export async function eventAxisFindings(axis) {
|
|
27
|
+
const { docsDir, service, me, paths, reqs, livingReqs, archReqs, land, landSvcOf, known, fleet } = axis;
|
|
28
|
+
const findings = [];
|
|
29
|
+
// Where this axis started, so the closing ok-finding can be "nothing here
|
|
30
|
+
// broke" rather than a second, independently-computed claim about the same
|
|
31
|
+
// facts. A positive finding derived from the same walk cannot disagree with it.
|
|
32
|
+
const eventFindingsAt = findings.length;
|
|
33
|
+
const events = await readAsyncapi(paths.asyncapi, fleet);
|
|
34
|
+
const sentHere = new Set(events.sent);
|
|
35
|
+
const receivedHere = new Set(events.received);
|
|
36
|
+
// Every edge that binds THIS service to a message: `publishes` binds the
|
|
37
|
+
// edge's source (a service produces what it declares an `action: send` for),
|
|
38
|
+
// `consumes` binds its target — `eventKeys` is where that rule is written,
|
|
39
|
+
// once, for this scope and the feature gate alike. One edge may legitimately
|
|
40
|
+
// carry both: a producer drawn straight to its consumer is a single arrow.
|
|
41
|
+
const readable = land !== null && land.errors.length === 0 && landSvcOf !== null;
|
|
42
|
+
const eventEdges = !readable
|
|
43
|
+
? []
|
|
44
|
+
: land.relationships.flatMap((r) => eventKeys(r)
|
|
45
|
+
.filter((k) => landSvcOf(k.bound) === me)
|
|
46
|
+
.map((k) => ({ direction: k.direction, message: k.message, other: landSvcOf(k.other) })));
|
|
47
|
+
// The edges the filter above drops that THIS service drew the wrong way
|
|
48
|
+
// round: a key bound to infrastructure (a topic, which neither publishes nor
|
|
49
|
+
// consumes) while this service sits at the other end. It used to fall out of
|
|
50
|
+
// `eventEdges` without a word, so a consume edge drawn from the consumer to
|
|
51
|
+
// the topic graded nothing and said nothing. A key bound to an `#external`
|
|
52
|
+
// system is NOT this case — an outside producer or consumer, which the
|
|
53
|
+
// drawing states correctly (core/c4/direction/direction.ts says why). Raised on
|
|
54
|
+
// the service at the unbound end, because it is the one with a drawing to fix.
|
|
55
|
+
const knownIds = known;
|
|
56
|
+
if (readable) {
|
|
57
|
+
const infraKindOf = infrastructureKind(land.elements);
|
|
58
|
+
for (const r of land.relationships) {
|
|
59
|
+
for (const key of eventKeys(r)) {
|
|
60
|
+
const kind = infraKindOf(key.bound);
|
|
61
|
+
if (kind === undefined || landSvcOf(key.other) !== me || knownIds.has(landSvcOf(key.bound)))
|
|
62
|
+
continue;
|
|
63
|
+
findings.push({
|
|
64
|
+
severity: "warn",
|
|
65
|
+
code: "spine.message-unbound",
|
|
66
|
+
subject: service,
|
|
67
|
+
message: `${service}: landscape ${unboundSentence(r, key, kind)}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Both requirement namespaces, and a REMOVED requirement links nothing —
|
|
73
|
+
// the `livingReqs` rule the API axis already follows, applied to the pair.
|
|
74
|
+
const eventLinks = [...livingReqs, ...archReqs.filter((r) => r.kind !== "REMOVED")].flatMap((r) => [
|
|
75
|
+
...r.publishes.map((m) => ({ direction: "publishes", message: m, requirement: r.name })),
|
|
76
|
+
...r.consumes.map((m) => ({ direction: "consumes", message: m, requirement: r.name })),
|
|
77
|
+
]);
|
|
78
|
+
if (!existsSync(paths.asyncapi)) {
|
|
79
|
+
// Deliberately TWO grades, not the three `service.no-openapi` uses. Its
|
|
80
|
+
// middle grade — warn when the landscape cannot prove nobody calls this
|
|
81
|
+
// service — rests on HTTP being the default: most services expose one, so
|
|
82
|
+
// "I could not look" is worth saying. An event contract is genuinely
|
|
83
|
+
// optional; most services in a legacy fleet touch no topic at all. Warning
|
|
84
|
+
// every one of them the moment a landscape fails to parse would put a
|
|
85
|
+
// finding on the whole fleet that names a file nobody owes.
|
|
86
|
+
//
|
|
87
|
+
// So: something already joins into the absent file, or silence.
|
|
88
|
+
const dangling = [
|
|
89
|
+
...new Set([...eventEdges.map((e) => e.message), ...eventLinks.map((e) => e.message)]),
|
|
90
|
+
].sort();
|
|
91
|
+
if (dangling.length > 0) {
|
|
92
|
+
findings.push({
|
|
93
|
+
severity: "error",
|
|
94
|
+
code: "service.no-asyncapi",
|
|
95
|
+
subject: service,
|
|
96
|
+
message: `No async contract at ${repoPath(docsDir, paths.asyncapi)}, and ${dangling.length} message link(s) already point into it — ` +
|
|
97
|
+
`every landscape edge and requirement naming one of them resolves to nothing until the file is back`,
|
|
98
|
+
details: dangling,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (events.unreadable) {
|
|
103
|
+
// The openapi.invalid discipline: a contract that EXISTS but does not read
|
|
104
|
+
// is a broken source of truth, not an empty one. Grading the links against
|
|
105
|
+
// an empty message set would turn one file into one `spine.message-undefined`
|
|
106
|
+
// per edge, every one of them pointing at the landscape.
|
|
107
|
+
findings.push({
|
|
108
|
+
severity: "error",
|
|
109
|
+
code: "asyncapi.invalid",
|
|
110
|
+
subject: service,
|
|
111
|
+
message: `${service}: asyncapi.yaml does not read as AsyncAPI 3.0 (it does not parse, or it declares an older version) — the event spine is unchecked for this service`,
|
|
112
|
+
...(events.error === undefined ? {} : { details: [events.error] }),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Feature-only bookkeeping published to the fleet — the openapi axis's
|
|
117
|
+
// `openapi.remove-marker-living` discipline, one code for every depth the
|
|
118
|
+
// reader collects markers at (slots and inline channel messages alike).
|
|
119
|
+
if (events.markers.length > 0) {
|
|
120
|
+
findings.push({
|
|
121
|
+
severity: "error",
|
|
122
|
+
code: "asyncapi.remove-marker-living",
|
|
123
|
+
subject: service,
|
|
124
|
+
message: `${service}: living asyncapi.yaml carries x-loam-remove: true at ${events.markers.join(", ")} — removal markers are valid only in feature deltas; retire a slot through a feature's asyncapi.yaml instead`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
for (const name of events.duplicateNames) {
|
|
128
|
+
findings.push({
|
|
129
|
+
severity: "warn",
|
|
130
|
+
code: "asyncapi.duplicate-message",
|
|
131
|
+
subject: service,
|
|
132
|
+
message: `${service}: asyncapi.yaml declares message '${name}' in ${slotsOf(events, name).join(" and ")} — every join on the name (an edge's metadata { publishes }, a requirement's Publishes: line) picks one of those slots arbitrarily`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
// The contract-depth probes. Form validated and depth did not: a payload
|
|
136
|
+
// of bare `type: object` and a `$ref` to nothing both read as cleanly as
|
|
137
|
+
// a full schema, while a reader still cannot rebuild the payload from the
|
|
138
|
+
// green contract. Presence probes only — a non-JSON schemaFormat
|
|
139
|
+
// is skipped, so Avro stays a document change.
|
|
140
|
+
const empties = events.messages.filter((m) => m.payloadEmpty === true).map((m) => m.name);
|
|
141
|
+
if (empties.length > 0) {
|
|
142
|
+
findings.push({
|
|
143
|
+
severity: "warn",
|
|
144
|
+
code: "asyncapi.payload-undescribed",
|
|
145
|
+
subject: service,
|
|
146
|
+
message: `${service}: ${empties.length} message(s) declare no payload shape (${empties.join(", ")}) — ` +
|
|
147
|
+
`the name joins the spine, but nothing defines what a consumer would read; ` +
|
|
148
|
+
`declare properties, or a schemaFormat for a non-JSON schema`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (events.danglingRefs.length > 0) {
|
|
152
|
+
findings.push({
|
|
153
|
+
severity: "warn",
|
|
154
|
+
code: "asyncapi.ref-unresolved",
|
|
155
|
+
subject: service,
|
|
156
|
+
message: `${service}: asyncapi.yaml contains ${events.danglingRefs.length} internal $ref(s) that resolve ` +
|
|
157
|
+
`to nothing in the document — whatever they were meant to carry is silently absent from the ` +
|
|
158
|
+
`spine's view of this contract`,
|
|
159
|
+
details: events.danglingRefs,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// Local resolution, both directions and both sources of a claim. An edge and
|
|
163
|
+
// a requirement making the same broken claim get the same sentence under
|
|
164
|
+
// different codes, matching how `spine.op-undefined` and
|
|
165
|
+
// `spec-api.op-undefined` split the HTTP axis: one is the fleet map's
|
|
166
|
+
// mistake, the other is this document's, and the fix is in a different file.
|
|
167
|
+
const declares = (direction, message) => direction === "publishes" ? sentHere.has(message) : receivedHere.has(message);
|
|
168
|
+
const action = (direction) => direction === "publishes" ? "send" : "receive";
|
|
169
|
+
for (const e of eventEdges) {
|
|
170
|
+
if (declares(e.direction, e.message))
|
|
171
|
+
continue;
|
|
172
|
+
findings.push({
|
|
173
|
+
severity: "error",
|
|
174
|
+
code: "spine.message-undefined",
|
|
175
|
+
subject: service,
|
|
176
|
+
message: `${service}: landscape edge ${e.direction === "publishes" ? `${service} → ${e.other}` : `${e.other} → ${service}`} ` +
|
|
177
|
+
`${e.direction} '${e.message}', but ${service}'s asyncapi.yaml declares no operation with action: ${action(e.direction)} for it`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
for (const e of eventLinks) {
|
|
181
|
+
if (declares(e.direction, e.message))
|
|
182
|
+
continue;
|
|
183
|
+
findings.push({
|
|
184
|
+
severity: "error",
|
|
185
|
+
code: "spec-event.message-undefined",
|
|
186
|
+
subject: service,
|
|
187
|
+
message: `${service}: requirement '${e.requirement}' ${e.direction} '${e.message}', but ${service}'s asyncapi.yaml declares no operation with action: ${action(e.direction)} for it`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// The migration-debt case, `api.ops-unlinked`'s twin: a contract and
|
|
191
|
+
// requirements that never name each other leave this axis vacuously green.
|
|
192
|
+
if (events.messages.length > 0 && reqs.length + archReqs.length > 0 && eventLinks.length === 0) {
|
|
193
|
+
findings.push({
|
|
194
|
+
severity: "warn",
|
|
195
|
+
code: "event.messages-unlinked",
|
|
196
|
+
subject: service,
|
|
197
|
+
message: `${service}: asyncapi.yaml declares ${events.messages.length} message(s) but no requirement links any — the event axis is unchecked for this service`,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
// `api.ungoverned`'s event twin, and the per-message half the check above
|
|
201
|
+
// cannot see: `event.messages-unlinked` fires only when ZERO links exist,
|
|
202
|
+
// so a contract with five declared messages and three governed ones
|
|
203
|
+
// reported nothing about the other two. The join is against requirement
|
|
204
|
+
// lines ONLY, deliberately ignoring landscape edges — an edge is a claim
|
|
205
|
+
// about traffic, a requirement is governance — exactly as the API axis
|
|
206
|
+
// ignores inbound edges when grading coverage.
|
|
207
|
+
const governed = new Set(eventLinks.map((e) => e.message));
|
|
208
|
+
const orphans = [...new Set(events.messages.map((m) => m.name))].filter((name) => !governed.has(name));
|
|
209
|
+
if (orphans.length > 0) {
|
|
210
|
+
findings.push({
|
|
211
|
+
severity: "warn",
|
|
212
|
+
code: "event.ungoverned",
|
|
213
|
+
subject: service,
|
|
214
|
+
message: `${service}: ${orphans.length} message(s) not governed by any requirement's Publishes:/Consumes: line — ${orphans.join(", ")}`,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// The consumed-message list is the handover to the fleet question in
|
|
219
|
+
// ./message-producers.js — a fleet where nothing consumes walks nothing.
|
|
220
|
+
const consumed = [
|
|
221
|
+
...new Set([
|
|
222
|
+
...eventEdges.filter((e) => e.direction === "consumes").map((e) => e.message),
|
|
223
|
+
...eventLinks.filter((e) => e.direction === "consumes").map((e) => e.message),
|
|
224
|
+
]),
|
|
225
|
+
].sort();
|
|
226
|
+
findings.push(...(await producerFindings({ docsDir, service, land, landSvcOf, known, events, consumed, fleet })));
|
|
227
|
+
// Positive confirmation, on the same rule `spine.resolved` follows: claimed
|
|
228
|
+
// only where the axis actually checked something. A service that touches no
|
|
229
|
+
// topic says nothing here — an "event axis clean" on every worker in the fleet
|
|
230
|
+
// would be a green tick for work nobody did.
|
|
231
|
+
const eventChecks = eventEdges.length + eventLinks.length;
|
|
232
|
+
if (eventChecks > 0 && findings.length === eventFindingsAt) {
|
|
233
|
+
findings.push({
|
|
234
|
+
severity: "ok",
|
|
235
|
+
code: "event.covered",
|
|
236
|
+
message: `${service}: event spine (${eventChecks} message link(s) resolve to asyncapi.yaml)`,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return findings;
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../../src/commands/validate/service/events/events.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The async contract axis — AsyncAPI messages on the landscape spine.\n *\n * The HTTP spine (`./spine.js`) and this one are mirror images, and the mirror\n * is the whole reason this is a second axis rather than a second `metadata`\n * key. On the HTTP axis the PROVIDER owns the contract: an edge names an\n * operation and the target's openapi.yaml settles it, locally, in one file.\n * Here the PRODUCER owns the message and the consumer joins to it from another\n * repository — so \"is this message declared\" is local, while \"does anybody\n * publish it\" is a fleet question with no local answer at all. That second\n * question is the only check on either spine that reads other services'\n * contracts; it lives in `./message-producers.js`, and this axis only decides\n * when it is worth paying for — exactly when something actually consumes.\n */\nimport { existsSync } from \"node:fs\";\nimport {\n type DeclaredService,\n type PathableService,\n type RawServiceId,\n} from \"../../../../core/kernel/ids/service.js\";\nimport { type LoadedDoc } from \"../../../../core/c4/likec4.js\";\nimport { eventKeys, infrastructureKind, unboundSentence } from \"../../../../core/c4/direction/direction.js\";\nimport { type ServicePaths } from \"../../../../core/repo/paths.js\";\nimport { type Finding } from \"../../../../core/vocabulary/report.js\";\nimport { type Requirement } from \"../../../../core/document/spec.js\";\nimport { readAsyncapi, slotsOf } from \"../../../../core/asyncapi/read.js\";\nimport { FleetContext } from \"../../../../core/fleet-context.js\";\nimport { repoPath } from \"../../../../core/envelope/json.js\";\nimport { producerFindings } from \"./message-producers.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\n\n/** What the service target hands the event axis. */\nexport interface EventAxis {\n docsDir: DocsDir;\n service: PathableService;\n /** `service` widened for `===` against the resolver's DOCUMENT text — see service.ts. */\n me: string;\n paths: ServicePaths;\n /** Every requirement the living spec declares; `event.messages-unlinked` counts what is written. */\n reqs: Requirement[];\n /** The ones that still govern something — a REMOVED requirement links nothing. */\n livingReqs: Requirement[];\n /** arch.spec.md's requirements, which carry as many event links as spec.md's do. */\n archReqs: Requirement[];\n /** The living landscape, or null when the repo has none. */\n land: LoadedDoc | null;\n /** Its element→service resolver; null exactly when `land` is. */\n landSvcOf: ((id: string) => DeclaredService) | null;\n /** Service directories that exist — the fleet the producer question is asked over. */\n known: ReadonlySet<RawServiceId>;\n fleet: FleetContext;\n}\n\nexport async function eventAxisFindings(axis: EventAxis): Promise<Finding[]> {\n const { docsDir, service, me, paths, reqs, livingReqs, archReqs, land, landSvcOf, known, fleet } =\n axis;\n const findings: Finding[] = [];\n\n // Where this axis started, so the closing ok-finding can be \"nothing here\n // broke\" rather than a second, independently-computed claim about the same\n // facts. A positive finding derived from the same walk cannot disagree with it.\n const eventFindingsAt = findings.length;\n const events = await readAsyncapi(paths.asyncapi, fleet);\n const sentHere = new Set(events.sent);\n const receivedHere = new Set(events.received);\n\n // Every edge that binds THIS service to a message: `publishes` binds the\n // edge's source (a service produces what it declares an `action: send` for),\n // `consumes` binds its target — `eventKeys` is where that rule is written,\n // once, for this scope and the feature gate alike. One edge may legitimately\n // carry both: a producer drawn straight to its consumer is a single arrow.\n const readable = land !== null && land.errors.length === 0 && landSvcOf !== null;\n const eventEdges = !readable\n ? []\n : land.relationships.flatMap((r) =>\n eventKeys(r)\n .filter((k) => landSvcOf(k.bound) === me)\n .map((k) => ({ direction: k.direction, message: k.message, other: landSvcOf(k.other) })),\n );\n // The edges the filter above drops that THIS service drew the wrong way\n // round: a key bound to infrastructure (a topic, which neither publishes nor\n // consumes) while this service sits at the other end. It used to fall out of\n // `eventEdges` without a word, so a consume edge drawn from the consumer to\n // the topic graded nothing and said nothing. A key bound to an `#external`\n // system is NOT this case — an outside producer or consumer, which the\n // drawing states correctly (core/c4/direction/direction.ts says why). Raised on\n // the service at the unbound end, because it is the one with a drawing to fix.\n const knownIds: ReadonlySet<string> = known;\n if (readable) {\n const infraKindOf = infrastructureKind(land.elements);\n for (const r of land.relationships) {\n for (const key of eventKeys(r)) {\n const kind = infraKindOf(key.bound);\n if (kind === undefined || landSvcOf(key.other) !== me || knownIds.has(landSvcOf(key.bound))) continue;\n findings.push({\n severity: \"warn\",\n code: \"spine.message-unbound\",\n subject: service,\n message: `${service}: landscape ${unboundSentence(r, key, kind)}`,\n });\n }\n }\n }\n // Both requirement namespaces, and a REMOVED requirement links nothing —\n // the `livingReqs` rule the API axis already follows, applied to the pair.\n const eventLinks = [...livingReqs, ...archReqs.filter((r) => r.kind !== \"REMOVED\")].flatMap((r) => [\n ...r.publishes.map((m) => ({ direction: \"publishes\" as const, message: m, requirement: r.name })),\n ...r.consumes.map((m) => ({ direction: \"consumes\" as const, message: m, requirement: r.name })),\n ]);\n\n if (!existsSync(paths.asyncapi)) {\n // Deliberately TWO grades, not the three `service.no-openapi` uses. Its\n // middle grade — warn when the landscape cannot prove nobody calls this\n // service — rests on HTTP being the default: most services expose one, so\n // \"I could not look\" is worth saying. An event contract is genuinely\n // optional; most services in a legacy fleet touch no topic at all. Warning\n // every one of them the moment a landscape fails to parse would put a\n // finding on the whole fleet that names a file nobody owes.\n //\n // So: something already joins into the absent file, or silence.\n const dangling = [\n ...new Set([...eventEdges.map((e) => e.message), ...eventLinks.map((e) => e.message)]),\n ].sort();\n if (dangling.length > 0) {\n findings.push({\n severity: \"error\",\n code: \"service.no-asyncapi\",\n subject: service,\n message:\n `No async contract at ${repoPath(docsDir, paths.asyncapi)}, and ${dangling.length} message link(s) already point into it — ` +\n `every landscape edge and requirement naming one of them resolves to nothing until the file is back`,\n details: dangling,\n });\n }\n } else if (events.unreadable) {\n // The openapi.invalid discipline: a contract that EXISTS but does not read\n // is a broken source of truth, not an empty one. Grading the links against\n // an empty message set would turn one file into one `spine.message-undefined`\n // per edge, every one of them pointing at the landscape.\n findings.push({\n severity: \"error\",\n code: \"asyncapi.invalid\",\n subject: service,\n message: `${service}: asyncapi.yaml does not read as AsyncAPI 3.0 (it does not parse, or it declares an older version) — the event spine is unchecked for this service`,\n ...(events.error === undefined ? {} : { details: [events.error] }),\n });\n } else {\n // Feature-only bookkeeping published to the fleet — the openapi axis's\n // `openapi.remove-marker-living` discipline, one code for every depth the\n // reader collects markers at (slots and inline channel messages alike).\n if (events.markers.length > 0) {\n findings.push({\n severity: \"error\",\n code: \"asyncapi.remove-marker-living\",\n subject: service,\n message: `${service}: living asyncapi.yaml carries x-loam-remove: true at ${events.markers.join(\", \")} — removal markers are valid only in feature deltas; retire a slot through a feature's asyncapi.yaml instead`,\n });\n }\n for (const name of events.duplicateNames) {\n findings.push({\n severity: \"warn\",\n code: \"asyncapi.duplicate-message\",\n subject: service,\n message: `${service}: asyncapi.yaml declares message '${name}' in ${slotsOf(events, name).join(\" and \")} — every join on the name (an edge's metadata { publishes }, a requirement's Publishes: line) picks one of those slots arbitrarily`,\n });\n }\n // The contract-depth probes. Form validated and depth did not: a payload\n // of bare `type: object` and a `$ref` to nothing both read as cleanly as\n // a full schema, while a reader still cannot rebuild the payload from the\n // green contract. Presence probes only — a non-JSON schemaFormat\n // is skipped, so Avro stays a document change.\n const empties = events.messages.filter((m) => m.payloadEmpty === true).map((m) => m.name);\n if (empties.length > 0) {\n findings.push({\n severity: \"warn\",\n code: \"asyncapi.payload-undescribed\",\n subject: service,\n message:\n `${service}: ${empties.length} message(s) declare no payload shape (${empties.join(\", \")}) — ` +\n `the name joins the spine, but nothing defines what a consumer would read; ` +\n `declare properties, or a schemaFormat for a non-JSON schema`,\n });\n }\n if (events.danglingRefs.length > 0) {\n findings.push({\n severity: \"warn\",\n code: \"asyncapi.ref-unresolved\",\n subject: service,\n message:\n `${service}: asyncapi.yaml contains ${events.danglingRefs.length} internal $ref(s) that resolve ` +\n `to nothing in the document — whatever they were meant to carry is silently absent from the ` +\n `spine's view of this contract`,\n details: events.danglingRefs,\n });\n }\n // Local resolution, both directions and both sources of a claim. An edge and\n // a requirement making the same broken claim get the same sentence under\n // different codes, matching how `spine.op-undefined` and\n // `spec-api.op-undefined` split the HTTP axis: one is the fleet map's\n // mistake, the other is this document's, and the fix is in a different file.\n const declares = (direction: \"publishes\" | \"consumes\", message: string): boolean =>\n direction === \"publishes\" ? sentHere.has(message) : receivedHere.has(message);\n const action = (direction: \"publishes\" | \"consumes\"): string =>\n direction === \"publishes\" ? \"send\" : \"receive\";\n for (const e of eventEdges) {\n if (declares(e.direction, e.message)) continue;\n findings.push({\n severity: \"error\",\n code: \"spine.message-undefined\",\n subject: service,\n message:\n `${service}: landscape edge ${e.direction === \"publishes\" ? `${service} → ${e.other}` : `${e.other} → ${service}`} ` +\n `${e.direction} '${e.message}', but ${service}'s asyncapi.yaml declares no operation with action: ${action(e.direction)} for it`,\n });\n }\n for (const e of eventLinks) {\n if (declares(e.direction, e.message)) continue;\n findings.push({\n severity: \"error\",\n code: \"spec-event.message-undefined\",\n subject: service,\n message: `${service}: requirement '${e.requirement}' ${e.direction} '${e.message}', but ${service}'s asyncapi.yaml declares no operation with action: ${action(e.direction)} for it`,\n });\n }\n // The migration-debt case, `api.ops-unlinked`'s twin: a contract and\n // requirements that never name each other leave this axis vacuously green.\n if (events.messages.length > 0 && reqs.length + archReqs.length > 0 && eventLinks.length === 0) {\n findings.push({\n severity: \"warn\",\n code: \"event.messages-unlinked\",\n subject: service,\n message: `${service}: asyncapi.yaml declares ${events.messages.length} message(s) but no requirement links any — the event axis is unchecked for this service`,\n });\n }\n // `api.ungoverned`'s event twin, and the per-message half the check above\n // cannot see: `event.messages-unlinked` fires only when ZERO links exist,\n // so a contract with five declared messages and three governed ones\n // reported nothing about the other two. The join is against requirement\n // lines ONLY, deliberately ignoring landscape edges — an edge is a claim\n // about traffic, a requirement is governance — exactly as the API axis\n // ignores inbound edges when grading coverage.\n const governed = new Set(eventLinks.map((e) => e.message));\n const orphans = [...new Set(events.messages.map((m) => m.name))].filter((name) => !governed.has(name));\n if (orphans.length > 0) {\n findings.push({\n severity: \"warn\",\n code: \"event.ungoverned\",\n subject: service,\n message: `${service}: ${orphans.length} message(s) not governed by any requirement's Publishes:/Consumes: line — ${orphans.join(\", \")}`,\n });\n }\n }\n\n // The consumed-message list is the handover to the fleet question in\n // ./message-producers.js — a fleet where nothing consumes walks nothing.\n const consumed = [\n ...new Set([\n ...eventEdges.filter((e) => e.direction === \"consumes\").map((e) => e.message),\n ...eventLinks.filter((e) => e.direction === \"consumes\").map((e) => e.message),\n ]),\n ].sort();\n findings.push(...(await producerFindings({ docsDir, service, land, landSvcOf, known, events, consumed, fleet })));\n\n // Positive confirmation, on the same rule `spine.resolved` follows: claimed\n // only where the axis actually checked something. A service that touches no\n // topic says nothing here — an \"event axis clean\" on every worker in the fleet\n // would be a green tick for work nobody did.\n const eventChecks = eventEdges.length + eventLinks.length;\n if (eventChecks > 0 && findings.length === eventFindingsAt) {\n findings.push({\n severity: \"ok\",\n code: \"event.covered\",\n message: `${service}: event spine (${eventChecks} message link(s) resolve to asyncapi.yaml)`,\n });\n }\n\n return findings;\n}\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fleet question the event axis asks: who produces what this service
|
|
3
|
+
* consumes. Split out of `./events.js` along the seam its own header always
|
|
4
|
+
* named — the local axis grades one service's files, while this is the only
|
|
5
|
+
* check on either spine that reads OTHER services' contracts, and the two
|
|
6
|
+
* subjects share nothing but the consumed-message list the axis hands over.
|
|
7
|
+
*/
|
|
8
|
+
import {} from "../../../../core/kernel/ids/service.js";
|
|
9
|
+
import {} from "../../../../core/c4/likec4.js";
|
|
10
|
+
import {} from "../../../../core/vocabulary/report.js";
|
|
11
|
+
import { producersByMessage } from "../../../../core/asyncapi/producers.js";
|
|
12
|
+
import {} from "../../../../core/asyncapi/model.js";
|
|
13
|
+
import { FleetContext } from "../../../../core/fleet-context.js";
|
|
14
|
+
import { externalProducerOf } from "../../../../core/c4/resolve/external.js";
|
|
15
|
+
// The fleet question, and the one check on either spine that a single
|
|
16
|
+
// repository cannot answer: a consumer joins to a message whose schema lives
|
|
17
|
+
// in the producer's contract, in the producer's repo. Paid only when something
|
|
18
|
+
// actually consumes — a fleet where nobody has adopted the axis walks nothing.
|
|
19
|
+
export async function producerFindings(input) {
|
|
20
|
+
const { docsDir, service, land, landSvcOf, known, events, consumed, fleet } = input;
|
|
21
|
+
const findings = [];
|
|
22
|
+
if (consumed.length === 0)
|
|
23
|
+
return findings;
|
|
24
|
+
const producers = await producersByMessage(docsDir, [...known], fleet);
|
|
25
|
+
for (const message of consumed) {
|
|
26
|
+
const who = producers.byMessage.get(message) ?? [];
|
|
27
|
+
// "Nobody produces this" is an argument from absence, and it is only
|
|
28
|
+
// sound over a fleet loam could actually read. One unreadable contract
|
|
29
|
+
// anywhere suspends it — that service may be the producer, and the
|
|
30
|
+
// alternative is blaming every consumer for somebody else's broken YAML.
|
|
31
|
+
// The external answer and the contested answer below rest on positive
|
|
32
|
+
// evidence and need no such guard.
|
|
33
|
+
if (who.length === 0) {
|
|
34
|
+
// A message produced OUTSIDE the fleet used to be inexpressible: the
|
|
35
|
+
// landscape said the producer was #external, and this error fired
|
|
36
|
+
// anyway — so honesty cost a red build, and the exit ramp people took
|
|
37
|
+
// was deleting the link. The landscape already carries the answer; an
|
|
38
|
+
// external producer shifts the contract question to the one file that
|
|
39
|
+
// can settle it, this service's own asyncapi.yaml.
|
|
40
|
+
const ext = externalProducerOf(message, land, landSvcOf, known);
|
|
41
|
+
if (ext !== null) {
|
|
42
|
+
const local = events.messages.find((m) => m.name === message);
|
|
43
|
+
if (local === undefined || local.payloadEmpty === true) {
|
|
44
|
+
findings.push({
|
|
45
|
+
severity: "warn",
|
|
46
|
+
code: "spine.message-external",
|
|
47
|
+
subject: service,
|
|
48
|
+
message: `${service}: consumes '${message}' from '${ext}', a system outside the fleet — the ` +
|
|
49
|
+
`producer declares no contract here, so the only payload definition is this service's ` +
|
|
50
|
+
`own asyncapi.yaml, and it currently defines no shape for it; declare the message with ` +
|
|
51
|
+
`its payload`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// A carried contract closes the question: the landscape states the
|
|
55
|
+
// external dependency, the consumer owns the schema — nothing to say.
|
|
56
|
+
}
|
|
57
|
+
else if (producers.unreadable.length === 0) {
|
|
58
|
+
findings.push({
|
|
59
|
+
severity: "error",
|
|
60
|
+
code: "spine.message-unproduced",
|
|
61
|
+
subject: service,
|
|
62
|
+
message: `${service}: consumes '${message}', but no service in the fleet and no #external element declares it is sent — the message has no producer anywhere, so nothing defines its payload`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (who.length > 1) {
|
|
67
|
+
findings.push({
|
|
68
|
+
severity: "warn",
|
|
69
|
+
code: "asyncapi.message-contested",
|
|
70
|
+
subject: service,
|
|
71
|
+
message: `${service}: consumes '${message}', which ${who.length} services declare they send (${who.join(", ")}) — every consumer's join picks one of them arbitrarily, so the payload this service reads is whichever one happens to win`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return findings;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=message-producers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-producers.js","sourceRoot":"","sources":["../../../../../src/commands/validate/service/events/message-producers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The fleet question the event axis asks: who produces what this service\n * consumes. Split out of `./events.js` along the seam its own header always\n * named — the local axis grades one service's files, while this is the only\n * check on either spine that reads OTHER services' contracts, and the two\n * subjects share nothing but the consumed-message list the axis hands over.\n */\nimport { type DeclaredService, type PathableService, type RawServiceId } from \"../../../../core/kernel/ids/service.js\";\nimport { type LoadedDoc } from \"../../../../core/c4/likec4.js\";\nimport { type Finding } from \"../../../../core/vocabulary/report.js\";\nimport { producersByMessage } from \"../../../../core/asyncapi/producers.js\";\nimport { type AsyncapiDoc } from \"../../../../core/asyncapi/model.js\";\nimport { FleetContext } from \"../../../../core/fleet-context.js\";\nimport { externalProducerOf } from \"../../../../core/c4/resolve/external.js\";\nimport type { DocsDir } from \"../../../../core/kernel/ids/dirs.js\";\n\n/**\n * What the producer question is asked over. One record rather than a parameter\n * list: everything here is already bound together as one service's view of the\n * fleet, and the 4-parameter limit says so too.\n */\nexport interface ProducerQuestion {\n docsDir: DocsDir;\n service: PathableService;\n /** The living landscape, or null when the repo has none. */\n land: LoadedDoc | null;\n /** Its element→service resolver; null exactly when `land` is. */\n landSvcOf: ((id: string) => DeclaredService) | null;\n /** Service directories that exist — the fleet the producer question is asked over. */\n known: ReadonlySet<RawServiceId>;\n /** This service's own parsed contract — the local-shape half of the external answer. */\n events: AsyncapiDoc;\n /** The deduped message names this service consumes, from edges and requirement lines alike. */\n consumed: readonly string[];\n fleet: FleetContext;\n}\n\n// The fleet question, and the one check on either spine that a single\n// repository cannot answer: a consumer joins to a message whose schema lives\n// in the producer's contract, in the producer's repo. Paid only when something\n// actually consumes — a fleet where nobody has adopted the axis walks nothing.\nexport async function producerFindings(input: ProducerQuestion): Promise<Finding[]> {\n const { docsDir, service, land, landSvcOf, known, events, consumed, fleet } = input;\n const findings: Finding[] = [];\n if (consumed.length === 0) return findings;\n const producers = await producersByMessage(docsDir, [...known], fleet);\n for (const message of consumed) {\n const who = producers.byMessage.get(message) ?? [];\n // \"Nobody produces this\" is an argument from absence, and it is only\n // sound over a fleet loam could actually read. One unreadable contract\n // anywhere suspends it — that service may be the producer, and the\n // alternative is blaming every consumer for somebody else's broken YAML.\n // The external answer and the contested answer below rest on positive\n // evidence and need no such guard.\n if (who.length === 0) {\n // A message produced OUTSIDE the fleet used to be inexpressible: the\n // landscape said the producer was #external, and this error fired\n // anyway — so honesty cost a red build, and the exit ramp people took\n // was deleting the link. The landscape already carries the answer; an\n // external producer shifts the contract question to the one file that\n // can settle it, this service's own asyncapi.yaml.\n const ext = externalProducerOf(message, land, landSvcOf, known);\n if (ext !== null) {\n const local = events.messages.find((m) => m.name === message);\n if (local === undefined || local.payloadEmpty === true) {\n findings.push({\n severity: \"warn\",\n code: \"spine.message-external\",\n subject: service,\n message:\n `${service}: consumes '${message}' from '${ext}', a system outside the fleet — the ` +\n `producer declares no contract here, so the only payload definition is this service's ` +\n `own asyncapi.yaml, and it currently defines no shape for it; declare the message with ` +\n `its payload`,\n });\n }\n // A carried contract closes the question: the landscape states the\n // external dependency, the consumer owns the schema — nothing to say.\n } else if (producers.unreadable.length === 0) {\n findings.push({\n severity: \"error\",\n code: \"spine.message-unproduced\",\n subject: service,\n message: `${service}: consumes '${message}', but no service in the fleet and no #external element declares it is sent — the message has no producer anywhere, so nothing defines its payload`,\n });\n }\n } else if (who.length > 1) {\n findings.push({\n severity: \"warn\",\n code: \"asyncapi.message-contested\",\n subject: service,\n message: `${service}: consumes '${message}', which ${who.length} services declare they send (${who.join(\", \")}) — every consumer's join picks one of them arbitrarily, so the payload this service reads is whichever one happens to win`,\n });\n }\n }\n return findings;\n}\n"]}
|