@ybotok/loam 0.2.0-alpha.1 → 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 +3448 -0
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +47 -23
- package/QUICKSTART.md +189 -12
- package/README.md +274 -65
- package/ROADMAP.md +572 -31
- package/SCHEMA.md +1709 -155
- package/WORKFLOW.md +298 -72
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/adopt/render.js +50 -9
- package/dist/commands/adopt/render.js.map +1 -1
- package/dist/commands/archive/archive.js +16 -19
- package/dist/commands/archive/archive.js.map +1 -1
- 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 +36 -42
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +77 -43
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- 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 +21 -19
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +167 -30
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- 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 +14 -19
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +137 -56
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +3 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +134 -30
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +85 -18
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +29 -7
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +102 -14
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +17 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/doctor.js +60 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +89 -13
- package/dist/commands/explain/explain.js.map +1 -1
- package/dist/commands/explore.js +16 -10
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +5 -7
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +44 -29
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +52 -9
- package/dist/commands/list/print.js.map +1 -1
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +37 -34
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +55 -26
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/plan.js +158 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +13 -11
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/feature.js +9 -5
- package/dist/commands/show/feature.js.map +1 -1
- package/dist/commands/show/introduced.js +157 -0
- package/dist/commands/show/introduced.js.map +1 -0
- package/dist/commands/show/marks.js +35 -6
- package/dist/commands/show/marks.js.map +1 -1
- package/dist/commands/show/service.js +41 -33
- package/dist/commands/show/service.js.map +1 -1
- package/dist/commands/show/show.js +17 -7
- package/dist/commands/show/show.js.map +1 -1
- 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 +2 -0
- package/dist/commands/status/json.js.map +1 -1
- package/dist/commands/status/print.js +89 -37
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +103 -23
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +7 -7
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +106 -46
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +289 -74
- package/dist/commands/subsystem/sync.js.map +1 -1
- 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 +147 -45
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +200 -21
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- 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 +42 -26
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +83 -33
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +78 -27
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +34 -126
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/health-deps.js +9 -6
- package/dist/commands/validate/checks/health-deps.js.map +1 -1
- 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 +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +11 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- 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 +26 -20
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +37 -13
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +144 -126
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/load.js +66 -0
- package/dist/commands/validate/fleet/load.js.map +1 -1
- 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 +44 -14
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +10 -2
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
- 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 +50 -15
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- 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 +40 -47
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/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 +36 -160
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +21 -107
- package/dist/commands/validate/service/api.js.map +1 -1
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +36 -14
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/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 +121 -61
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +88 -28
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +38 -3
- package/dist/commands/validate/service/spine.js.map +1 -1
- 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 +85 -60
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +55 -52
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +81 -106
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +21 -9
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +24 -8
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +80 -8
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +88 -10
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +49 -22
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +39 -6
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/protocol.js +1 -1
- package/dist/core/agent/protocol.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/support/report.js +46 -5
- package/dist/core/agent/support/report.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +158 -11
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +78 -39
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +88 -27
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +128 -13
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +96 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +106 -4
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/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 +6 -3
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +68 -27
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +32 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +33 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +12 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +96 -36
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/checks.js +138 -3
- package/dist/core/brief/checks.js.map +1 -1
- package/dist/core/brief/landscape.js +209 -130
- package/dist/core/brief/landscape.js.map +1 -1
- 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 +109 -31
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +14 -3
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/arch.js +90 -182
- package/dist/core/c4/arch.js.map +1 -1
- 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 +169 -14
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +160 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/parsed/view-ids.js +39 -1
- package/dist/core/c4/parsed/view-ids.js.map +1 -1
- 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 +123 -7
- package/dist/core/c4/project/architecture.js.map +1 -1
- 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 +43 -12
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +79 -43
- package/dist/core/c4/project/load.js.map +1 -1
- 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 +78 -17
- package/dist/core/c4/resolve/service.js.map +1 -1
- 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/template.js +65 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- 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 +297 -10
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +222 -141
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +79 -13
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/contract.js +20 -0
- package/dist/core/c4/splice/contract.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +20 -20
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- 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 +37 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -1
- 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 +182 -114
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- 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 +160 -56
- package/dist/core/c4/splice/placement.js.map +1 -1
- 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 +13 -19
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/delta/uncovered.js +2 -2
- package/dist/core/capabilities/delta/uncovered.js.map +1 -1
- package/dist/core/capabilities/findings.js +18 -22
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/calls/called.js +52 -0
- package/dist/core/coherence/calls/called.js.map +1 -0
- package/dist/core/coherence/coherence.js +103 -22
- package/dist/core/coherence/coherence.js.map +1 -1
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/covers/health.js +85 -0
- package/dist/core/coherence/covers/health.js.map +1 -0
- package/dist/core/coherence/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +147 -16
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +28 -87
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/events/produced.js +100 -0
- package/dist/core/coherence/events/produced.js.map +1 -0
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/delta.js +51 -6
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/dependencies/fanin.js +2 -2
- package/dist/core/dependencies/fanin.js.map +1 -1
- 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 +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +49 -21
- package/dist/core/diff/base-state.js.map +1 -1
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/docs.js +31 -22
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/config.js +7 -3
- package/dist/core/doctor/config.js.map +1 -1
- package/dist/core/doctor/doctor.js +91 -11
- package/dist/core/doctor/doctor.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/doctor/reports/scan.js +242 -0
- package/dist/core/doctor/reports/scan.js.map +1 -0
- package/dist/core/document/apply.js +158 -11
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +26 -21
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/lookup.js +14 -1
- package/dist/core/explain/lookup.js.map +1 -1
- package/dist/core/explain/refusals.js +5 -5
- package/dist/core/explain/refusals.js.map +1 -1
- 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 +68 -109
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +40 -10
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +81 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +11 -4
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +69 -28
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/delta.js +10 -7
- package/dist/core/glossary/delta.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
- package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
- package/dist/core/kernel/path-safety.js +38 -0
- package/dist/core/kernel/path-safety.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +51 -37
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/links/findings.js.map +1 -1
- package/dist/core/links/parse.js +38 -9
- package/dist/core/links/parse.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +40 -10
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +301 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +28 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +17 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +43 -4
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/pack/pack.js +50 -6
- package/dist/core/pack/pack.js.map +1 -1
- package/dist/core/projection/arch-slice.js +2 -2
- package/dist/core/projection/arch-slice.js.map +1 -1
- package/dist/core/projection/introduced.js +14 -0
- package/dist/core/projection/introduced.js.map +1 -0
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/authored/paths.js +18 -0
- package/dist/core/repo/authored/paths.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/paths.js +92 -6
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/repo.js +74 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/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 +53 -32
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +121 -13
- package/dist/core/scaffold/landscape.js.map +1 -1
- package/dist/core/scaffold/legacy.js +271 -0
- package/dist/core/scaffold/legacy.js.map +1 -0
- package/dist/core/staging/commit.js +26 -15
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +84 -6
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +188 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +24 -14
- package/dist/core/staging/txn/forward.js.map +1 -1
- package/dist/core/staging/writes.js.map +1 -1
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/contracts.js +39 -15
- package/dist/core/status/contracts.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +33 -8
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +24 -17
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/feature/next.js +20 -7
- package/dist/core/status/feature/next.js.map +1 -1
- 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 +43 -14
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +153 -58
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/scan.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +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 +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/claims.js +31 -7
- package/dist/core/usecases/delta/claims.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/delta/modified.js +315 -0
- package/dist/core/usecases/delta/modified.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +32 -191
- package/dist/core/usecases/delta/overlay.js.map +1 -1
- package/dist/core/usecases/fleet.js +195 -36
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
- 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/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
- package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
- package/dist/core/usecases/grade/steps.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
- package/dist/core/usecases/grade/usecases.js.map +1 -0
- package/dist/core/usecases/operations.js +26 -14
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/place.js.map +1 -1
- 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/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +70 -12
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +4 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +75 -4
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/dist/core/vocabulary/report.js +16 -1
- package/dist/core/vocabulary/report.js.map +1 -1
- package/examples/README.md +54 -11
- package/examples/docs/architecture/landscape.likec4 +62 -6
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +7 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/likec4.config.json +0 -1
- package/examples/docs/services/checkout-web/model.likec4 +39 -56
- package/examples/docs/services/order-service/arch.spec.md +13 -6
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/order-service/model.likec4 +58 -70
- package/examples/docs/services/payment-service/arch.spec.md +7 -4
- package/examples/docs/services/payment-service/model.likec4 +38 -37
- package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/examples/docs/services/platform/notification-service/health.yaml +8 -3
- package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -31
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -220
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -165
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -247
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/repo/tree/views.js +0 -172
- package/dist/core/repo/tree/views.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* delta facts it already holds (tagged relationships and the element
|
|
8
8
|
* resolver): ./declared.ts walks the feature's own contracts and grades
|
|
9
9
|
* what one service's pair of documents can settle; ./lookups.ts answers the
|
|
10
|
-
*
|
|
10
|
+
* in-flight question lazily, and the consumer index (core/consumers/) who
|
|
11
|
+
* still consumes a message; this module joins the three axes — the exact
|
|
11
12
|
* shape coherence.ts gives the operation join, on the message name.
|
|
12
13
|
*
|
|
13
14
|
* A package of its own rather than lines in coherence.ts because that file
|
|
@@ -16,17 +17,100 @@
|
|
|
16
17
|
* there does.
|
|
17
18
|
*/
|
|
18
19
|
import {} from "../../c4/likec4.js";
|
|
20
|
+
import { eventKeys, unboundSentence } from "../../c4/direction/direction.js";
|
|
19
21
|
import {} from "../../kernel/ids/service.js";
|
|
20
22
|
import { locateServicePaths } from "../../repo/service-target.js";
|
|
21
23
|
import { enumeratedServiceIndex } from "../../repo/service-target.js";
|
|
22
24
|
import { readAsyncapi } from "../../asyncapi/read.js";
|
|
23
25
|
import { declaredEvents } from "./declared.js";
|
|
24
26
|
import { eventLookups } from "./lookups.js";
|
|
27
|
+
import { revivedEvents } from "../retired/revival/revived.js";
|
|
28
|
+
import { nothingStill, refusalDetails, refusalMessage, stillNamed } from "../consumed/refusal.js";
|
|
29
|
+
import { fleetSendersAfter, unproducedIssues } from "./produced.js";
|
|
25
30
|
export async function eventCoherence(request) {
|
|
26
|
-
const { scope, svcNames, taggedRels, svcOf, context } = request;
|
|
31
|
+
const { scope, svcNames, taggedRels, svcOf, context, consumers } = request;
|
|
27
32
|
const issues = [];
|
|
28
33
|
const declared = await declaredEvents(scope, svcNames, issues, context);
|
|
29
34
|
const lookups = eventLookups({ docsDir: scope.docsDir, featureId: scope.featureId }, context);
|
|
35
|
+
// Who would still name a message after this merge: the consumer index's
|
|
36
|
+
// consumers, and its `own` — the provider's own `Publishes:` lines as the
|
|
37
|
+
// merge leaves them. That is the event twin of the overwrite's own-requirement
|
|
38
|
+
// scan: a message re-keyed or no longer sent while the provider's living
|
|
39
|
+
// requirement still says it `Publishes:` it archived at exit 0, and the next
|
|
40
|
+
// `validate --all` reported `spec-event.message-undefined` against the
|
|
41
|
+
// provider (review 2026-10-12, finding 3) — a REMOVED requirement justifies a
|
|
42
|
+
// marker, but nothing asks a re-key or a turned-around operation for one.
|
|
43
|
+
// A consumer document nobody could read or place refuses too, as on the
|
|
44
|
+
// contract axis (../consumed/refusal.ts).
|
|
45
|
+
//
|
|
46
|
+
// But a CONSUMER is stranded only when nothing would send the message after
|
|
47
|
+
// this merge — no service, and no #external element whose edge publishes it.
|
|
48
|
+
// That is the rule `spine.message-unproduced` grades a consumer by — it stays
|
|
49
|
+
// quiet over an outside producer (`externalProducerOf`,
|
|
50
|
+
// core/c4/resolve/external.ts) — and the one `loam diff` asks first
|
|
51
|
+
// (`producers`, then `externalProducer`, core/diff/semantic.ts).
|
|
52
|
+
// Counting every consumer "whoever its producer is" refused a retirement from
|
|
53
|
+
// one of two senders over the other sender's own consumes edge, and told the
|
|
54
|
+
// author to drop it, while `loam diff` called the same removal a warning
|
|
55
|
+
// ("still sent by …") and the next `validate --all` was clean (the
|
|
56
|
+
// verification of the consumer fixes, N2). Counting the fleet's senders alone
|
|
57
|
+
// then refused a retirement from a message's only fleet sender while an
|
|
58
|
+
// #external element still published it, and `loam diff` called the archived
|
|
59
|
+
// removal `diff.message-removed-consumed`, while the next `validate --all` was
|
|
60
|
+
// clean again (the verification of N1–N3, D1). The provider's OWN documents are
|
|
61
|
+
// another matter: the spine and validate grade them against the provider's
|
|
62
|
+
// own contract, so they strand whoever else sends the message, and `own`
|
|
63
|
+
// still refuses beside a surviving sender.
|
|
64
|
+
//
|
|
65
|
+
// The senders are asked of the MERGE, never of the living fleet alone:
|
|
66
|
+
// `producers` reads the fleet as it stands, so a feature retiring the message
|
|
67
|
+
// from both senders would let each one pass on the other's living contract.
|
|
68
|
+
// A service this feature carries an event delta for sends what the
|
|
69
|
+
// simulation leaves it (`declared.sentAfter`), or vouches for nothing; every
|
|
70
|
+
// other service sends what its living contract does. Positive evidence only,
|
|
71
|
+
// as `producers` is: an unreadable contract, or a merge that stood down,
|
|
72
|
+
// never keeps a message produced. The outside producer is the living map's
|
|
73
|
+
// edge and tag, which the landscape merge cannot take away (it only adds to
|
|
74
|
+
// the map), asked over the directories the merge leaves: each `specs/<svc>/`
|
|
75
|
+
// of this feature becomes a `services/<svc>/`, and a directory the #external
|
|
76
|
+
// element resolves to outranks its tag, so a feature onboarding the outside
|
|
77
|
+
// system as a service takes the producer away. Asked over the directories
|
|
78
|
+
// as they stood, the gate passed that retirement while the next `loam diff`
|
|
79
|
+
// reported `diff.message-removed-consumed` and `validate --all`
|
|
80
|
+
// `spine.message-unproduced` (the verification of D1, X2). One this
|
|
81
|
+
// feature's own C4 delta would draw is not counted, so that shape errs
|
|
82
|
+
// toward refusing — `--approve`, or draw the edge in the living map. The
|
|
83
|
+
// consumer check in ./produced.ts DOES count it, deliberately: there the
|
|
84
|
+
// closed direction is the other one (that module says why), and the fleet
|
|
85
|
+
// half of the senders is the one helper both read.
|
|
86
|
+
const sendersAfter = async (svc, message) => {
|
|
87
|
+
const fleet = (await fleetSendersAfter(declared, consumers, message)).filter((id) => id !== svc);
|
|
88
|
+
const outside = await consumers.externalProducer(message);
|
|
89
|
+
return outside === null ? fleet : [...fleet, `${outside} (#external)`];
|
|
90
|
+
};
|
|
91
|
+
const still = async (svc, message) => {
|
|
92
|
+
const answer = await consumers.messageConsumers(svc, message);
|
|
93
|
+
const senders = await sendersAfter(svc, message);
|
|
94
|
+
return { senders, found: stillNamed(senders.length === 0 ? answer : { ...answer, victims: [], suspended: [] }) };
|
|
95
|
+
};
|
|
96
|
+
// The landscape merge only ADDS edges, so an edge is dropped in the living map
|
|
97
|
+
// itself — a consumer's consumes edge, or the provider's own publishes edge,
|
|
98
|
+
// which the spine grades on its source (`own`, core/consumers/consumers.ts).
|
|
99
|
+
const route = `drop its consumes edge, or drop or redraw its publishes edge, in the architecture/ project, MODIFY or REMOVE the requirement whose Consumes:/Publishes: line names it in this feature's specs/<service>/ delta`;
|
|
100
|
+
// With a sender left only the provider's own documents are named, so the
|
|
101
|
+
// remedy is theirs alone — telling the author to drop a consumer's edge
|
|
102
|
+
// there is the false route this gate used to give.
|
|
103
|
+
const ownRoute = (svc) => `drop or redraw its publishes edge in the architecture/ project, or MODIFY or REMOVE the requirement whose Publishes: line names it in this feature's specs/${svc}/ delta`;
|
|
104
|
+
const ownOnly = (svc, senders) => `${senders.join(", ")} would still send it after this merge, so its consumers keep a producer, but ${svc}'s own documents would still name it`;
|
|
105
|
+
// The route a consumer's refusal gains: another sender keeps the message
|
|
106
|
+
// produced, and one this feature adds counts (`declared.sentAfter`); so does
|
|
107
|
+
// the #external element that publishes it, drawn in the living map — the one
|
|
108
|
+
// place `sendersAfter` reads an outside producer from — unless this feature
|
|
109
|
+
// makes it a service. The route says so: a feature onboarding the gateway is
|
|
110
|
+
// told no #external element publishes the message while its living map
|
|
111
|
+
// draws one doing exactly that, and the way out is the first route, the new
|
|
112
|
+
// service's own send operation in its specs/<service>/ delta.
|
|
113
|
+
const anotherSender = "keep another service sending it (an action: send operation in its asyncapi.yaml, living or in this feature's specs/<service>/ delta) or an #external element publishing it (a publishes edge from it in the living architecture/ project; one whose element this feature's specs/<service>/ makes a service no longer counts)";
|
|
30
114
|
// Retiring a message the fleet still consumes — openapi.remove-op-consumed's
|
|
31
115
|
// twin, and like it a question neither exactness nor justification asks:
|
|
32
116
|
// the merge deletes the declaration while the consumes edge and the other
|
|
@@ -34,14 +118,23 @@ export async function eventCoherence(request) {
|
|
|
34
118
|
// repository whose author was never in this feature.
|
|
35
119
|
for (const [svc, net] of declared.netRemoved) {
|
|
36
120
|
for (const message of net) {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
121
|
+
const { found, senders } = await still(svc, message);
|
|
122
|
+
if (nothingStill(found))
|
|
39
123
|
continue;
|
|
124
|
+
const lost = `this feature retires message '${message}'`;
|
|
40
125
|
issues.push({
|
|
41
126
|
severity: "error",
|
|
42
127
|
code: "asyncapi.remove-message-consumed",
|
|
43
128
|
subject: svc,
|
|
44
|
-
|
|
129
|
+
details: refusalDetails(found),
|
|
130
|
+
message: refusalMessage(found, {
|
|
131
|
+
subject: svc,
|
|
132
|
+
lost,
|
|
133
|
+
verb: "name",
|
|
134
|
+
named: (list) => senders.length === 0
|
|
135
|
+
? `${svc}: ${lost}, and no readable contract would still send it after this merge, nor does an #external element publish it, but the fleet would still name it — ${list}. Retire each in the same change (${route}), ${anotherSender}, or archive with --approve to break it deliberately.`
|
|
136
|
+
: `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,
|
|
137
|
+
}),
|
|
45
138
|
});
|
|
46
139
|
}
|
|
47
140
|
}
|
|
@@ -56,17 +149,37 @@ export async function eventCoherence(request) {
|
|
|
56
149
|
for (const message of stopped) {
|
|
57
150
|
if (net?.has(message) === true)
|
|
58
151
|
continue;
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
152
|
+
const { found, senders } = await still(svc, message);
|
|
153
|
+
if (nothingStill(found))
|
|
61
154
|
continue;
|
|
155
|
+
const lost = `after this feature's merge, ${svc}'s asyncapi.yaml would no longer declare an action: send operation for '${message}'`;
|
|
62
156
|
issues.push({
|
|
63
157
|
severity: "error",
|
|
64
158
|
code: "asyncapi.remove-message-consumed",
|
|
65
159
|
subject: svc,
|
|
66
|
-
|
|
160
|
+
details: refusalDetails(found),
|
|
161
|
+
message: refusalMessage(found, {
|
|
162
|
+
subject: svc,
|
|
163
|
+
lost,
|
|
164
|
+
verb: "name",
|
|
165
|
+
named: (list) => senders.length === 0
|
|
166
|
+
? `${svc}: ${lost}, and no other readable contract would send it either, nor does an #external element publish it, but the fleet would still name it — ${list}. Keep an operation sending it, ${anotherSender}, retire each in the same change (${route}), or archive with --approve to break it deliberately.`
|
|
167
|
+
: `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Keep an operation sending it, retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,
|
|
168
|
+
}),
|
|
67
169
|
});
|
|
68
170
|
}
|
|
69
171
|
}
|
|
172
|
+
// A revival: a slot or message this delta declares that the living
|
|
173
|
+
// contract no longer has because an ARCHIVED feature retired it, or a
|
|
174
|
+
// message name declared again at a key an archived feature took it out of
|
|
175
|
+
// — the contract axis's `openapi.op-revived`, on the slot and the message
|
|
176
|
+
// name (../retired/revival/revived.ts). Asked of the index only when the merged
|
|
177
|
+
// contract declares something the living one does not.
|
|
178
|
+
for (const [svc, arrived] of declared.revivalCandidates) {
|
|
179
|
+
if (arrived.slots.length === 0 && arrived.messages.length === 0)
|
|
180
|
+
continue;
|
|
181
|
+
issues.push(...revivedEvents(svc, arrived, await request.retired()));
|
|
182
|
+
}
|
|
70
183
|
// Two features declaring one (service, message): both deltas apply
|
|
71
184
|
// cleanly, so whichever archives second replaces the other's declaration
|
|
72
185
|
// wholesale — delta.added-conflict's shape on the message join.
|
|
@@ -133,21 +246,35 @@ export async function eventCoherence(request) {
|
|
|
133
246
|
const enumeratedTarget = enumeratedServiceIndex(scope.docsDir, svcNames, context);
|
|
134
247
|
// Tagged edges: `publishes` binds the edge's SOURCE, `consumes` its
|
|
135
248
|
// TARGET — the same reading validate's event spine takes of the living
|
|
136
|
-
// landscape, applied to the delta
|
|
249
|
+
// landscape, applied to the delta, from the one statement of the rule
|
|
250
|
+
// (core/c4/direction/direction.ts).
|
|
251
|
+
// Every consumption this merge would add, collected as the two loops below
|
|
252
|
+
// walk the edges and the lines, and asked for a producer at the end
|
|
253
|
+
// (./produced.ts) — `spine.message-unproduced` at feature scope.
|
|
254
|
+
const claims = [];
|
|
137
255
|
for (const r of taggedRels) {
|
|
138
|
-
const bound = [];
|
|
139
256
|
const edge = `${svcOf(r.source)} → ${svcOf(r.target)}`;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (r.consumes !== undefined)
|
|
143
|
-
bound.push({ direction: "consumes", message: r.consumes, name: svcOf(r.target), edge });
|
|
144
|
-
for (const b of bound) {
|
|
257
|
+
for (const key of eventKeys(r)) {
|
|
258
|
+
const b = { direction: key.direction, message: key.message, name: svcOf(key.bound), edge };
|
|
145
259
|
const pathable = await enumeratedTarget(b.name);
|
|
146
|
-
if (pathable === undefined)
|
|
260
|
+
if (pathable === undefined) {
|
|
261
|
+
// The skip above, unless the key is bound to infrastructure and a
|
|
262
|
+
// service sits at the OTHER end: then it is on the wrong end of its
|
|
263
|
+
// edge and binds nothing, which the living spine reports under the
|
|
264
|
+
// same code once this delta merges. An `#external` system at the bound
|
|
265
|
+
// end is an outside producer or consumer, and stays skipped.
|
|
266
|
+
const kind = request.infraKindOf(key.bound);
|
|
267
|
+
const drawer = kind === undefined ? undefined : await enumeratedTarget(svcOf(key.other));
|
|
268
|
+
if (kind !== undefined && drawer !== undefined) {
|
|
269
|
+
issues.push({ severity: "warn", code: "spine.message-unbound", subject: drawer, message: `delta ${unboundSentence(r, key, kind)}` });
|
|
270
|
+
}
|
|
147
271
|
continue;
|
|
272
|
+
}
|
|
148
273
|
// asyncapi.invalid already gates; one breach, one finding.
|
|
149
274
|
if (declared.unreadable.has(pathable))
|
|
150
275
|
continue;
|
|
276
|
+
if (b.direction === "consumes")
|
|
277
|
+
claims.push({ service: pathable, message: b.message, source: "edge", via: `edge ${b.edge}` });
|
|
151
278
|
const ok = await declares(pathable, b.direction, b.message);
|
|
152
279
|
if (ok !== false)
|
|
153
280
|
continue;
|
|
@@ -177,6 +304,9 @@ export async function eventCoherence(request) {
|
|
|
177
304
|
if (declared.unreadable.has(svc))
|
|
178
305
|
continue;
|
|
179
306
|
for (const link of declared.links.get(svc) ?? []) {
|
|
307
|
+
if (link.direction === "consumes") {
|
|
308
|
+
claims.push({ service: svc, message: link.message, source: "requirement", via: `requirement '${link.requirement}'` });
|
|
309
|
+
}
|
|
180
310
|
const ok = await declares(svc, link.direction, link.message);
|
|
181
311
|
if (ok !== false)
|
|
182
312
|
continue;
|
|
@@ -199,6 +329,7 @@ export async function eventCoherence(request) {
|
|
|
199
329
|
}
|
|
200
330
|
}
|
|
201
331
|
}
|
|
332
|
+
issues.push(...(await unproducedIssues({ scope, declared, consumers, lookups, claims, known: request.known, ...(context === undefined ? {} : { context }) })));
|
|
202
333
|
return issues;
|
|
203
334
|
}
|
|
204
335
|
//# sourceMappingURL=events.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAwB,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAGtD,OAAO,EAAE,cAAc,EAAwB,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAc5C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA8B;IACjE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAChE,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;IAE9F,6EAA6E;IAC7E,yEAAyE;IACzE,0EAA0E;IAC1E,oEAAoE;IACpE,qDAAqD;IACrD,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC7C,KAAK,MAAM,OAAO,IAAI,GAAG,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACrC,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,kCAAkC;gBACxC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,mCAAmC,OAAO,+CAA+C,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,8LAA8L;aAC3T,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,oEAAoE;IACpE,sEAAsE;IACtE,yEAAyE;IACzE,0EAA0E;IAC1E,2EAA2E;IAC3E,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;QACrD,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzC,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;YAC9B,IAAI,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI;gBAAE,SAAS;YACzC,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACrC,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,kCAAkC;gBACxC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,iCAAiC,GAAG,2EAA2E,OAAO,+CAA+C,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,8HAA8H;aACvU,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,yEAAyE;IACzE,gEAAgE;IAChE,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI;gBAAE,SAAS;YAChC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,uBAAuB,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YACjE,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,2BAA2B;gBACjC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,oCAAoC,CAAC,CAAC,IAAI,sBAAsB,KAAK,4HAA4H;aACjN,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,uEAAuE;IACvE,2EAA2E;IAC3E,2EAA2E;IAC3E,0EAA0E;IAC1E,kEAAkE;IAClE,wEAAwE;IACxE,sEAAsE;IACtE,0EAA0E;IAC1E,wEAAwE;IACxE,2CAA2C;IAC3C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAgC,CAAC;IAC3D,MAAM,QAAQ,GAAG,KAAK,EAAE,GAAoB,EAAwB,EAAE;QACpE,IAAI,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,GAAG,GAAG,MAAM,YAAY,CAAC,CAAC,MAAM,kBAAkB,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACpG,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IACF,uEAAuE;IACvE,sEAAsE;IACtE,wEAAwE;IACxE,YAAY;IACZ,MAAM,QAAQ,GAAG,KAAK,EACpB,GAAoB,EACpB,SAAmC,EACnC,OAAe,EACe,EAAE;QAChC,MAAM,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACvG,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,MAAM,CAAC,UAAU;YAAE,OAAO,SAAS,CAAC;QACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,OAAO,SAAS,KAAK,WAAW;YAC9B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC;YAC1E,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC;IACvF,CAAC,CAAC;IACF,MAAM,MAAM,GAAG,CAAC,SAAmC,EAAU,EAAE,CAAC,CAAC,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEjH,yEAAyE;IACzE,mEAAmE;IACnE,wEAAwE;IACxE,oEAAoE;IACpE,0EAA0E;IAC1E,qEAAqE;IACrE,eAAe;IACf,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAElF,oEAAoE;IACpE,uEAAuE;IACvE,mCAAmC;IACnC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,MAAM,KAAK,GAA2F,EAAE,CAAC;QACzG,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACzH,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACtH,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,QAAQ,KAAK,SAAS;gBAAE,SAAS;YACrC,2DAA2D;YAC3D,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAChD,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YAC5D,IAAI,EAAE,KAAK,KAAK;gBAAE,SAAS;YAC3B,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,uBAAuB,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YACzE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,CAAC,IAAI,CAAC;oBACV,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,0BAA0B;oBAChC,OAAO,EAAE,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,OAAO,2BAA2B,KAAK,qBAAqB;iBAC1G,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC;oBACV,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,4BAA4B;oBAClC,OAAO,EAAE,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,IAAI,sFAAsF,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS;iBACvL,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,0EAA0E;IAC1E,uEAAuE;IACvE,0EAA0E;IAC1E,0EAA0E;IAC1E,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC3C,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YACjD,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7D,IAAI,EAAE,KAAK,KAAK;gBAAE,SAAS;YAC3B,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,uBAAuB,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACvE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,CAAC,IAAI,CAAC;oBACV,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,4BAA4B;oBAClC,OAAO,EAAE,GAAG;oBACZ,OAAO,EAAE,GAAG,GAAG,kBAAkB,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,OAAO,2BAA2B,KAAK,qBAAqB;iBAC3I,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC;oBACV,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,8BAA8B;oBACpC,OAAO,EAAE,GAAG;oBACZ,OAAO,EAAE,GAAG,GAAG,kBAAkB,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,OAAO,UAAU,GAAG,uDAAuD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS;iBACzL,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * The event axis of the feature gate: do the C4 delta's tagged edges, the\n * requirement deltas' `Publishes:`/`Consumes:` lines, and the asyncapi\n * contracts agree about what each service puts on the wire?\n *\n * The package's orchestrator, called once from featureCoherence with the\n * delta facts it already holds (tagged relationships and the element\n * resolver): ./declared.ts walks the feature's own contracts and grades\n * what one service's pair of documents can settle; ./lookups.ts answers the\n * fleet questions lazily; this module joins the three axes — the exact\n * shape coherence.ts gives the operation join, on the message name.\n *\n * A package of its own rather than lines in coherence.ts because that file\n * ran at its 300-line limit before the event axis existed; the seam is\n * real, not cosmetic — everything here joins on a message name, nothing\n * there does.\n */\nimport { type Rel } from \"../../c4/likec4.js\";\nimport { type PathableService } from \"../../kernel/ids/service.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { locateServicePaths } from \"../../repo/service-target.js\";\nimport { enumeratedServiceIndex } from \"../../repo/service-target.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport { declaredEvents, type EventDeltaScope } from \"./declared.js\";\nimport { eventLookups } from \"./lookups.js\";\n\n/** What featureCoherence hands the event axis — the delta facts it already computed. */\nexport interface EventCoherenceRequest {\n scope: EventDeltaScope;\n /** The services this feature's specs/ touch — the walk's subjects. */\n svcNames: PathableService[];\n /** The delta's tagged relationships — the edges the landscape merge would splice. */\n taggedRels: Rel[];\n /** The element→service resolver over the delta's elements plus the enumerated fleet. */\n svcOf: (id: string) => string;\n context?: FleetContext;\n}\n\nexport async function eventCoherence(request: EventCoherenceRequest): Promise<Issue[]> {\n const { scope, svcNames, taggedRels, svcOf, context } = request;\n const issues: Issue[] = [];\n const declared = await declaredEvents(scope, svcNames, issues, context);\n const lookups = eventLookups({ docsDir: scope.docsDir, featureId: scope.featureId }, context);\n\n // Retiring a message the fleet still consumes — openapi.remove-op-consumed's\n // twin, and like it a question neither exactness nor justification asks:\n // the merge deletes the declaration while the consumes edge and the other\n // repo's requirement stay, so the next `validate --all` breaks on a\n // repository whose author was never in this feature.\n for (const [svc, net] of declared.netRemoved) {\n for (const message of net) {\n const consumers = await lookups.messageConsumers(svc, message);\n if (consumers.length === 0) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-message-consumed\",\n subject: svc,\n message: `${svc}: this feature retires message '${message}', but the living fleet still consumes it — ${consumers.join(\"; \")}. Retire the consumer in the same feature (drop the consumes edge from architecture/landscape.likec4, or the requirement that names it), or archive with --approve to break it deliberately.`,\n });\n }\n }\n\n // The WIRE half of the same question: a channel or operation removal (or\n // an edit) can stop the service SENDING a message whose declaration\n // survives — the merge applies cleanly, and the next `validate --all`\n // breaks on the consumer's repository (spine.message-unproduced) exactly\n // as a deleted declaration would. Asked of the simulated merge's sent-set\n // diff; a name the net-removal loop above already asked keeps one finding.\n for (const [svc, stopped] of declared.stoppedSending) {\n const net = declared.netRemoved.get(svc);\n for (const message of stopped) {\n if (net?.has(message) === true) continue;\n const consumers = await lookups.messageConsumers(svc, message);\n if (consumers.length === 0) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-message-consumed\",\n subject: svc,\n message: `${svc}: after this feature's merge, ${svc}'s asyncapi.yaml would no longer declare an action: send operation for '${message}', but the living fleet still consumes it — ${consumers.join(\"; \")}. Keep an operation sending it, retire the consumer in the same feature, or archive with --approve to break it deliberately.`,\n });\n }\n }\n\n // Two features declaring one (service, message): both deltas apply\n // cleanly, so whichever archives second replaces the other's declaration\n // wholesale — delta.added-conflict's shape on the message join.\n for (const [svc, doc] of declared.featureDocs) {\n for (const m of doc.messages) {\n if (m.remove === true) continue;\n const other = await lookups.messageDefinedElsewhere(svc, m.name);\n if (other === undefined) continue;\n issues.push({\n severity: \"warn\",\n code: \"asyncapi.message-conflict\",\n subject: svc,\n message: `${svc}: this feature declares message '${m.name}', which in-flight ${other} also adds or edits — whichever archives second replaces the other's declaration wholesale; coordinate before either ships`,\n });\n }\n }\n\n // The bound service's contract as the feature would leave it. Where the\n // feature carries a delta over a readable living contract, that is the\n // SIMULATED merge (declared.mergedDocs): a slot the delta retires or edits\n // away stops answering here BEFORE archive — a leftover Publishes: line on\n // a surviving requirement used to pass this gate on living's say-so, then\n // break `validate --all` on the post-archive docs. Everywhere the\n // simulation has no answer — no delta for the service, a side that does\n // not read, a document the merge refuses — the feature ∪ living union\n // stands in, which can only be too lenient, never wrongly red; the living\n // side is read lazily per service, since an edge may bind a service the\n // feature carries no specs/ directory for.\n const livingDocs = new Map<PathableService, AsyncapiDoc>();\n const livingOf = async (svc: PathableService): Promise<AsyncapiDoc> => {\n let doc = livingDocs.get(svc);\n if (doc === undefined) {\n doc = await readAsyncapi((await locateServicePaths(scope.docsDir, svc, context)).asyncapi, context);\n livingDocs.set(svc, doc);\n }\n return doc;\n };\n // undefined = no verdict: an unreadable LIVING contract proves nothing\n // about the message (validate's asyncapi.invalid owns that file), and\n // grading against its empty parse would blame every edge for one broken\n // document.\n const declares = async (\n svc: PathableService,\n direction: \"publishes\" | \"consumes\",\n message: string,\n ): Promise<boolean | undefined> => {\n const merged = declared.mergedDocs.get(svc);\n if (merged !== undefined) {\n return direction === \"publishes\" ? merged.sent.includes(message) : merged.received.includes(message);\n }\n const living = await livingOf(svc);\n if (living.unreadable) return undefined;\n const feat = declared.featureDocs.get(svc);\n return direction === \"publishes\"\n ? living.sent.includes(message) || (feat?.sent.includes(message) ?? false)\n : living.received.includes(message) || (feat?.received.includes(message) ?? false);\n };\n const action = (direction: \"publishes\" | \"consumes\"): string => (direction === \"publishes\" ? \"send\" : \"receive\");\n\n // The declared→enumerated bridge coherence.ts uses for op edges, for the\n // same reason: every lookup below joins into services/<id>/ or the\n // feature's own specs/. A bound element NEITHER knows is skipped rather\n // than graded — an #external producer is the documented shape for a\n // message produced outside the fleet (spine.message-external), so grading\n // its edge against an absent service directory would turn the worked\n // example red.\n const enumeratedTarget = enumeratedServiceIndex(scope.docsDir, svcNames, context);\n\n // Tagged edges: `publishes` binds the edge's SOURCE, `consumes` its\n // TARGET — the same reading validate's event spine takes of the living\n // landscape, applied to the delta.\n for (const r of taggedRels) {\n const bound: { direction: \"publishes\" | \"consumes\"; message: string; name: string; edge: string }[] = [];\n const edge = `${svcOf(r.source)} → ${svcOf(r.target)}`;\n if (r.publishes !== undefined) bound.push({ direction: \"publishes\", message: r.publishes, name: svcOf(r.source), edge });\n if (r.consumes !== undefined) bound.push({ direction: \"consumes\", message: r.consumes, name: svcOf(r.target), edge });\n for (const b of bound) {\n const pathable = await enumeratedTarget(b.name);\n if (pathable === undefined) continue;\n // asyncapi.invalid already gates; one breach, one finding.\n if (declared.unreadable.has(pathable)) continue;\n const ok = await declares(pathable, b.direction, b.message);\n if (ok !== false) continue;\n const other = await lookups.messageDefinedElsewhere(pathable, b.message);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"c4-event.message-pending\",\n message: `edge ${b.edge} ${b.direction} '${b.message}', defined by in-flight ${other} — archive it first`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"c4-event.message-undefined\",\n message: `edge ${b.edge} ${b.direction} '${b.message}', but ${b.name}'s asyncapi contract (feature and living alike) declares no operation with action: ${action(b.direction)} for it`,\n });\n }\n }\n }\n\n // The requirement deltas' own lines, graded the same way — the same\n // sentence validate gives the living-side breach, under the same code, so\n // one breach keeps one name across scopes. The lines were read once by\n // ./declared.ts's walk (declared.links); re-reading them here per service\n // was a second parse of both spec files on the context-less archive path.\n for (const svc of svcNames) {\n if (declared.unreadable.has(svc)) continue;\n for (const link of declared.links.get(svc) ?? []) {\n const ok = await declares(svc, link.direction, link.message);\n if (ok !== false) continue;\n const other = await lookups.messageDefinedElsewhere(svc, link.message);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"spec-event.message-pending\",\n subject: svc,\n message: `${svc}: requirement '${link.requirement}' ${link.direction} '${link.message}', defined by in-flight ${other} — archive it first`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"spec-event.message-undefined\",\n subject: svc,\n message: `${svc}: requirement '${link.requirement}' ${link.direction} '${link.message}', but ${svc}'s asyncapi.yaml declares no operation with action: ${action(link.direction)} for it`,\n });\n }\n }\n }\n\n return issues;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/events.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The event axis of the feature gate: do the C4 delta's tagged edges, the\n * requirement deltas' `Publishes:`/`Consumes:` lines, and the asyncapi\n * contracts agree about what each service puts on the wire?\n *\n * The package's orchestrator, called once from featureCoherence with the\n * delta facts it already holds (tagged relationships and the element\n * resolver): ./declared.ts walks the feature's own contracts and grades\n * what one service's pair of documents can settle; ./lookups.ts answers the\n * in-flight question lazily, and the consumer index (core/consumers/) who\n * still consumes a message; this module joins the three axes — the exact\n * shape coherence.ts gives the operation join, on the message name.\n *\n * A package of its own rather than lines in coherence.ts because that file\n * ran at its 300-line limit before the event axis existed; the seam is\n * real, not cosmetic — everything here joins on a message name, nothing\n * there does.\n */\nimport { type Rel } from \"../../c4/likec4.js\";\nimport { eventKeys, unboundSentence } from \"../../c4/direction/direction.js\";\nimport { type PathableService, type RawServiceId } from \"../../kernel/ids/service.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { locateServicePaths } from \"../../repo/service-target.js\";\nimport { enumeratedServiceIndex } from \"../../repo/service-target.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport { declaredEvents, type EventDeltaScope } from \"./declared.js\";\nimport { eventLookups } from \"./lookups.js\";\nimport type { RetiredLookup } from \"../retired/archived.js\";\nimport { revivedEvents } from \"../retired/revival/revived.js\";\nimport type { ConsumerIndex } from \"../../consumers/consumers.js\";\nimport { nothingStill, refusalDetails, refusalMessage, stillNamed, type StillNamed } from \"../consumed/refusal.js\";\nimport { fleetSendersAfter, unproducedIssues, type ConsumeClaim } from \"./produced.js\";\n\n/** What featureCoherence hands the event axis — the delta facts it already computed. */\nexport interface EventCoherenceRequest {\n scope: EventDeltaScope;\n /** The services this feature's specs/ touch — the walk's subjects. */\n svcNames: PathableService[];\n /** The delta's tagged relationships — the edges the landscape merge would splice. */\n taggedRels: Rel[];\n /** The element→service resolver over the delta's elements plus the enumerated fleet. */\n svcOf: (id: string) => string;\n /** The infrastructure kind of a delta element or its ancestor (core/c4/direction/direction.ts) — a key bound there is on the wrong end. */\n infraKindOf: (id: string) => string | undefined;\n context?: FleetContext;\n /** The retired index, shared with the contract axis so one call walks `features/archive/` once at most. */\n retired: RetiredLookup;\n /** The consumer index, read after this feature's merge — shared with the contract axis so the fleet is read once. */\n consumers: ConsumerIndex;\n /** The services the merge leaves a directory for — the enumerated fleet plus this feature's `specs/` (./produced.ts). */\n known: ReadonlySet<RawServiceId>;\n}\n\nexport async function eventCoherence(request: EventCoherenceRequest): Promise<Issue[]> {\n const { scope, svcNames, taggedRels, svcOf, context, consumers } = request;\n const issues: Issue[] = [];\n const declared = await declaredEvents(scope, svcNames, issues, context);\n const lookups = eventLookups({ docsDir: scope.docsDir, featureId: scope.featureId }, context);\n\n // Who would still name a message after this merge: the consumer index's\n // consumers, and its `own` — the provider's own `Publishes:` lines as the\n // merge leaves them. That is the event twin of the overwrite's own-requirement\n // scan: a message re-keyed or no longer sent while the provider's living\n // requirement still says it `Publishes:` it archived at exit 0, and the next\n // `validate --all` reported `spec-event.message-undefined` against the\n // provider (review 2026-10-12, finding 3) — a REMOVED requirement justifies a\n // marker, but nothing asks a re-key or a turned-around operation for one.\n // A consumer document nobody could read or place refuses too, as on the\n // contract axis (../consumed/refusal.ts).\n //\n // But a CONSUMER is stranded only when nothing would send the message after\n // this merge — no service, and no #external element whose edge publishes it.\n // That is the rule `spine.message-unproduced` grades a consumer by — it stays\n // quiet over an outside producer (`externalProducerOf`,\n // core/c4/resolve/external.ts) — and the one `loam diff` asks first\n // (`producers`, then `externalProducer`, core/diff/semantic.ts).\n // Counting every consumer \"whoever its producer is\" refused a retirement from\n // one of two senders over the other sender's own consumes edge, and told the\n // author to drop it, while `loam diff` called the same removal a warning\n // (\"still sent by …\") and the next `validate --all` was clean (the\n // verification of the consumer fixes, N2). Counting the fleet's senders alone\n // then refused a retirement from a message's only fleet sender while an\n // #external element still published it, and `loam diff` called the archived\n // removal `diff.message-removed-consumed`, while the next `validate --all` was\n // clean again (the verification of N1–N3, D1). The provider's OWN documents are\n // another matter: the spine and validate grade them against the provider's\n // own contract, so they strand whoever else sends the message, and `own`\n // still refuses beside a surviving sender.\n //\n // The senders are asked of the MERGE, never of the living fleet alone:\n // `producers` reads the fleet as it stands, so a feature retiring the message\n // from both senders would let each one pass on the other's living contract.\n // A service this feature carries an event delta for sends what the\n // simulation leaves it (`declared.sentAfter`), or vouches for nothing; every\n // other service sends what its living contract does. Positive evidence only,\n // as `producers` is: an unreadable contract, or a merge that stood down,\n // never keeps a message produced. The outside producer is the living map's\n // edge and tag, which the landscape merge cannot take away (it only adds to\n // the map), asked over the directories the merge leaves: each `specs/<svc>/`\n // of this feature becomes a `services/<svc>/`, and a directory the #external\n // element resolves to outranks its tag, so a feature onboarding the outside\n // system as a service takes the producer away. Asked over the directories\n // as they stood, the gate passed that retirement while the next `loam diff`\n // reported `diff.message-removed-consumed` and `validate --all`\n // `spine.message-unproduced` (the verification of D1, X2). One this\n // feature's own C4 delta would draw is not counted, so that shape errs\n // toward refusing — `--approve`, or draw the edge in the living map. The\n // consumer check in ./produced.ts DOES count it, deliberately: there the\n // closed direction is the other one (that module says why), and the fleet\n // half of the senders is the one helper both read.\n const sendersAfter = async (svc: string, message: string): Promise<string[]> => {\n const fleet = (await fleetSendersAfter(declared, consumers, message)).filter((id) => id !== svc);\n const outside = await consumers.externalProducer(message);\n return outside === null ? fleet : [...fleet, `${outside} (#external)`];\n };\n const still = async (svc: string, message: string): Promise<{ found: StillNamed; senders: string[] }> => {\n const answer = await consumers.messageConsumers(svc, message);\n const senders = await sendersAfter(svc, message);\n return { senders, found: stillNamed(senders.length === 0 ? answer : { ...answer, victims: [], suspended: [] }) };\n };\n // The landscape merge only ADDS edges, so an edge is dropped in the living map\n // itself — a consumer's consumes edge, or the provider's own publishes edge,\n // which the spine grades on its source (`own`, core/consumers/consumers.ts).\n const route = `drop its consumes edge, or drop or redraw its publishes edge, in the architecture/ project, MODIFY or REMOVE the requirement whose Consumes:/Publishes: line names it in this feature's specs/<service>/ delta`;\n // With a sender left only the provider's own documents are named, so the\n // remedy is theirs alone — telling the author to drop a consumer's edge\n // there is the false route this gate used to give.\n const ownRoute = (svc: string): string =>\n `drop or redraw its publishes edge in the architecture/ project, or MODIFY or REMOVE the requirement whose Publishes: line names it in this feature's specs/${svc}/ delta`;\n const ownOnly = (svc: string, senders: string[]): string =>\n `${senders.join(\", \")} would still send it after this merge, so its consumers keep a producer, but ${svc}'s own documents would still name it`;\n // The route a consumer's refusal gains: another sender keeps the message\n // produced, and one this feature adds counts (`declared.sentAfter`); so does\n // the #external element that publishes it, drawn in the living map — the one\n // place `sendersAfter` reads an outside producer from — unless this feature\n // makes it a service. The route says so: a feature onboarding the gateway is\n // told no #external element publishes the message while its living map\n // draws one doing exactly that, and the way out is the first route, the new\n // service's own send operation in its specs/<service>/ delta.\n const anotherSender =\n \"keep another service sending it (an action: send operation in its asyncapi.yaml, living or in this feature's specs/<service>/ delta) or an #external element publishing it (a publishes edge from it in the living architecture/ project; one whose element this feature's specs/<service>/ makes a service no longer counts)\";\n\n // Retiring a message the fleet still consumes — openapi.remove-op-consumed's\n // twin, and like it a question neither exactness nor justification asks:\n // the merge deletes the declaration while the consumes edge and the other\n // repo's requirement stay, so the next `validate --all` breaks on a\n // repository whose author was never in this feature.\n for (const [svc, net] of declared.netRemoved) {\n for (const message of net) {\n const { found, senders } = await still(svc, message);\n if (nothingStill(found)) continue;\n const lost = `this feature retires message '${message}'`;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-message-consumed\",\n subject: svc,\n details: refusalDetails(found),\n message: refusalMessage(found, {\n subject: svc,\n lost,\n verb: \"name\",\n named: (list) =>\n senders.length === 0\n ? `${svc}: ${lost}, and no readable contract would still send it after this merge, nor does an #external element publish it, but the fleet would still name it — ${list}. Retire each in the same change (${route}), ${anotherSender}, or archive with --approve to break it deliberately.`\n : `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,\n }),\n });\n }\n }\n\n // The WIRE half of the same question: a channel or operation removal (or\n // an edit) can stop the service SENDING a message whose declaration\n // survives — the merge applies cleanly, and the next `validate --all`\n // breaks on the consumer's repository (spine.message-unproduced) exactly\n // as a deleted declaration would. Asked of the simulated merge's sent-set\n // diff; a name the net-removal loop above already asked keeps one finding.\n for (const [svc, stopped] of declared.stoppedSending) {\n const net = declared.netRemoved.get(svc);\n for (const message of stopped) {\n if (net?.has(message) === true) continue;\n const { found, senders } = await still(svc, message);\n if (nothingStill(found)) continue;\n const lost = `after this feature's merge, ${svc}'s asyncapi.yaml would no longer declare an action: send operation for '${message}'`;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-message-consumed\",\n subject: svc,\n details: refusalDetails(found),\n message: refusalMessage(found, {\n subject: svc,\n lost,\n verb: \"name\",\n named: (list) =>\n senders.length === 0\n ? `${svc}: ${lost}, and no other readable contract would send it either, nor does an #external element publish it, but the fleet would still name it — ${list}. Keep an operation sending it, ${anotherSender}, retire each in the same change (${route}), or archive with --approve to break it deliberately.`\n : `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Keep an operation sending it, retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,\n }),\n });\n }\n }\n\n // A revival: a slot or message this delta declares that the living\n // contract no longer has because an ARCHIVED feature retired it, or a\n // message name declared again at a key an archived feature took it out of\n // — the contract axis's `openapi.op-revived`, on the slot and the message\n // name (../retired/revival/revived.ts). Asked of the index only when the merged\n // contract declares something the living one does not.\n for (const [svc, arrived] of declared.revivalCandidates) {\n if (arrived.slots.length === 0 && arrived.messages.length === 0) continue;\n issues.push(...revivedEvents(svc, arrived, await request.retired()));\n }\n\n // Two features declaring one (service, message): both deltas apply\n // cleanly, so whichever archives second replaces the other's declaration\n // wholesale — delta.added-conflict's shape on the message join.\n for (const [svc, doc] of declared.featureDocs) {\n for (const m of doc.messages) {\n if (m.remove === true) continue;\n const other = await lookups.messageDefinedElsewhere(svc, m.name);\n if (other === undefined) continue;\n issues.push({\n severity: \"warn\",\n code: \"asyncapi.message-conflict\",\n subject: svc,\n message: `${svc}: this feature declares message '${m.name}', which in-flight ${other} also adds or edits — whichever archives second replaces the other's declaration wholesale; coordinate before either ships`,\n });\n }\n }\n\n // The bound service's contract as the feature would leave it. Where the\n // feature carries a delta over a readable living contract, that is the\n // SIMULATED merge (declared.mergedDocs): a slot the delta retires or edits\n // away stops answering here BEFORE archive — a leftover Publishes: line on\n // a surviving requirement used to pass this gate on living's say-so, then\n // break `validate --all` on the post-archive docs. Everywhere the\n // simulation has no answer — no delta for the service, a side that does\n // not read, a document the merge refuses — the feature ∪ living union\n // stands in, which can only be too lenient, never wrongly red; the living\n // side is read lazily per service, since an edge may bind a service the\n // feature carries no specs/ directory for.\n const livingDocs = new Map<PathableService, AsyncapiDoc>();\n const livingOf = async (svc: PathableService): Promise<AsyncapiDoc> => {\n let doc = livingDocs.get(svc);\n if (doc === undefined) {\n doc = await readAsyncapi((await locateServicePaths(scope.docsDir, svc, context)).asyncapi, context);\n livingDocs.set(svc, doc);\n }\n return doc;\n };\n // undefined = no verdict: an unreadable LIVING contract proves nothing\n // about the message (validate's asyncapi.invalid owns that file), and\n // grading against its empty parse would blame every edge for one broken\n // document.\n const declares = async (\n svc: PathableService,\n direction: \"publishes\" | \"consumes\",\n message: string,\n ): Promise<boolean | undefined> => {\n const merged = declared.mergedDocs.get(svc);\n if (merged !== undefined) {\n return direction === \"publishes\" ? merged.sent.includes(message) : merged.received.includes(message);\n }\n const living = await livingOf(svc);\n if (living.unreadable) return undefined;\n const feat = declared.featureDocs.get(svc);\n return direction === \"publishes\"\n ? living.sent.includes(message) || (feat?.sent.includes(message) ?? false)\n : living.received.includes(message) || (feat?.received.includes(message) ?? false);\n };\n const action = (direction: \"publishes\" | \"consumes\"): string => (direction === \"publishes\" ? \"send\" : \"receive\");\n\n // The declared→enumerated bridge coherence.ts uses for op edges, for the\n // same reason: every lookup below joins into services/<id>/ or the\n // feature's own specs/. A bound element NEITHER knows is skipped rather\n // than graded — an #external producer is the documented shape for a\n // message produced outside the fleet (spine.message-external), so grading\n // its edge against an absent service directory would turn the worked\n // example red.\n const enumeratedTarget = enumeratedServiceIndex(scope.docsDir, svcNames, context);\n\n // Tagged edges: `publishes` binds the edge's SOURCE, `consumes` its\n // TARGET — the same reading validate's event spine takes of the living\n // landscape, applied to the delta, from the one statement of the rule\n // (core/c4/direction/direction.ts).\n // Every consumption this merge would add, collected as the two loops below\n // walk the edges and the lines, and asked for a producer at the end\n // (./produced.ts) — `spine.message-unproduced` at feature scope.\n const claims: ConsumeClaim[] = [];\n for (const r of taggedRels) {\n const edge = `${svcOf(r.source)} → ${svcOf(r.target)}`;\n for (const key of eventKeys(r)) {\n const b = { direction: key.direction, message: key.message, name: svcOf(key.bound), edge };\n const pathable = await enumeratedTarget(b.name);\n if (pathable === undefined) {\n // The skip above, unless the key is bound to infrastructure and a\n // service sits at the OTHER end: then it is on the wrong end of its\n // edge and binds nothing, which the living spine reports under the\n // same code once this delta merges. An `#external` system at the bound\n // end is an outside producer or consumer, and stays skipped.\n const kind = request.infraKindOf(key.bound);\n const drawer = kind === undefined ? undefined : await enumeratedTarget(svcOf(key.other));\n if (kind !== undefined && drawer !== undefined) {\n issues.push({ severity: \"warn\", code: \"spine.message-unbound\", subject: drawer, message: `delta ${unboundSentence(r, key, kind)}` });\n }\n continue;\n }\n // asyncapi.invalid already gates; one breach, one finding.\n if (declared.unreadable.has(pathable)) continue;\n if (b.direction === \"consumes\") claims.push({ service: pathable, message: b.message, source: \"edge\", via: `edge ${b.edge}` });\n const ok = await declares(pathable, b.direction, b.message);\n if (ok !== false) continue;\n const other = await lookups.messageDefinedElsewhere(pathable, b.message);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"c4-event.message-pending\",\n message: `edge ${b.edge} ${b.direction} '${b.message}', defined by in-flight ${other} — archive it first`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"c4-event.message-undefined\",\n message: `edge ${b.edge} ${b.direction} '${b.message}', but ${b.name}'s asyncapi contract (feature and living alike) declares no operation with action: ${action(b.direction)} for it`,\n });\n }\n }\n }\n\n // The requirement deltas' own lines, graded the same way — the same\n // sentence validate gives the living-side breach, under the same code, so\n // one breach keeps one name across scopes. The lines were read once by\n // ./declared.ts's walk (declared.links); re-reading them here per service\n // was a second parse of both spec files on the context-less archive path.\n for (const svc of svcNames) {\n if (declared.unreadable.has(svc)) continue;\n for (const link of declared.links.get(svc) ?? []) {\n if (link.direction === \"consumes\") {\n claims.push({ service: svc, message: link.message, source: \"requirement\", via: `requirement '${link.requirement}'` });\n }\n const ok = await declares(svc, link.direction, link.message);\n if (ok !== false) continue;\n const other = await lookups.messageDefinedElsewhere(svc, link.message);\n if (other !== undefined) {\n issues.push({\n severity: \"warn\",\n code: \"spec-event.message-pending\",\n subject: svc,\n message: `${svc}: requirement '${link.requirement}' ${link.direction} '${link.message}', defined by in-flight ${other} — archive it first`,\n });\n } else {\n issues.push({\n severity: \"error\",\n code: \"spec-event.message-undefined\",\n subject: svc,\n message: `${svc}: requirement '${link.requirement}' ${link.direction} '${link.message}', but ${svc}'s asyncapi.yaml declares no operation with action: ${action(link.direction)} for it`,\n });\n }\n }\n }\n\n issues.push(...(await unproducedIssues({ scope, declared, consumers, lookups, claims, known: request.known, ...(context === undefined ? {} : { context }) })));\n return issues;\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ASYNCAPI_BASELINES_KEY } from "../../asyncapi/digest.js";
|
|
2
|
-
import {} from "
|
|
1
|
+
import { ASYNCAPI_BASELINES_KEY, slotKey } from "../../asyncapi/digest.js";
|
|
2
|
+
import { moveBackRemedy } from "../retired/revival/revived.js";
|
|
3
3
|
import { asyncapiSurfaces } from "../../asyncapi/baseline/surfaces.js";
|
|
4
|
+
import { messageName } from "../../asyncapi/read.js";
|
|
4
5
|
import { entryFor, readBaselineRecord, surfaceIn } from "../../openapi/baseline/record.js";
|
|
5
6
|
import { classifyBaselineDigests, OPERATION_DIGEST_LENGTH, OPERATION_DIGEST_RE, valueDigest, } from "../../openapi/digest.js";
|
|
6
7
|
/**
|
|
@@ -11,7 +12,7 @@ import { classifyBaselineDigests, OPERATION_DIGEST_LENGTH, OPERATION_DIGEST_RE,
|
|
|
11
12
|
* graded once or the two halves drift into different ideas of what it restates.
|
|
12
13
|
*/
|
|
13
14
|
export function gradeBaselines(input, issues) {
|
|
14
|
-
const { featSlots, livingSlots, svc, featureId } = input;
|
|
15
|
+
const { featSlots, livingSlots, livingDoc, svc, featureId } = input;
|
|
15
16
|
let unpinned = 0;
|
|
16
17
|
for (const slot of featSlots) {
|
|
17
18
|
// A removal marker is not a restatement of anything; its own exactness
|
|
@@ -19,7 +20,7 @@ export function gradeBaselines(input, issues) {
|
|
|
19
20
|
if (slot.remove)
|
|
20
21
|
continue;
|
|
21
22
|
const label = `${slot.section}.${slot.key}`;
|
|
22
|
-
const living = livingSlots.get(
|
|
23
|
+
const living = livingSlots.get(slotKey(slot));
|
|
23
24
|
if (slot.basedOn !== undefined && !OPERATION_DIGEST_RE.test(slot.basedOn)) {
|
|
24
25
|
issues.push({
|
|
25
26
|
severity: "error",
|
|
@@ -31,11 +32,33 @@ export function gradeBaselines(input, issues) {
|
|
|
31
32
|
}
|
|
32
33
|
const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, living?.digest);
|
|
33
34
|
if (verdict === "unfounded") {
|
|
35
|
+
// openapi/baseline/gate.ts's reading, on the slot: a pin is only ever
|
|
36
|
+
// written over a living slot, so one with nothing behind it usually
|
|
37
|
+
// means a parallel feature retired the slot — and "drop the marker",
|
|
38
|
+
// which this used to say, archived the quote as an addition and put the
|
|
39
|
+
// retired slot back. Or MOVED it, on the one slot a move vacates on
|
|
40
|
+
// this axis: a `components.messages` key whose message name the living
|
|
41
|
+
// contract now declares elsewhere. A channel or operation slot's
|
|
42
|
+
// identity is its key, so it is never "moved" — it is retired. The
|
|
43
|
+
// move-back remedy is the revival finding's own (../retired/revival/revived.ts):
|
|
44
|
+
// where the name now stands inline under a channel, a marker there
|
|
45
|
+
// retires nothing, so the remedy names the channel instead. The two
|
|
46
|
+
// places are said as a condition ("while it is still declared there"),
|
|
47
|
+
// not as what this delta does: one that already restates or retires
|
|
48
|
+
// that channel takes the name out of it, and only the pin is left.
|
|
49
|
+
const name = slot.section === "components.messages" ? messageName(slot.node, slot.key) : undefined;
|
|
50
|
+
const movedTo = livingDoc.messages.filter((m) => m.remove !== true && m.name === name).map((m) => m.slot);
|
|
51
|
+
// The channels still pointing at a key the remedy retires are read off
|
|
52
|
+
// the contract the merge would leave; where it refuses (and the archive
|
|
53
|
+
// with it), off the living one, which is what the merge would start from.
|
|
54
|
+
const remedy = () => moveBackRemedy(movedTo, label, input.merged()?.root ?? input.livingPlain);
|
|
34
55
|
issues.push({
|
|
35
56
|
severity: "error",
|
|
36
57
|
code: "asyncapi.baseline-invalid",
|
|
37
58
|
subject: svc,
|
|
38
|
-
message:
|
|
59
|
+
message: movedTo.length > 0
|
|
60
|
+
? `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — message '${name}' is declared at ${movedTo.join(" and ")} now: a parallel feature moved it there (or retired it here, and another re-declared it there). Re-read it there and delete this slot from the delta, making any change at its new key; archived while it is still declared there, it would declare '${name}' at ${label} as well. To move it back on purpose, drop the pin and ${remedy()} in this delta.`
|
|
61
|
+
: `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — the version it was pinned to is gone. If a parallel feature retired it, re-read the living contract and delete this slot from the delta, unless this feature deliberately brings it back (then drop the pin and archive with --approve past \`asyncapi.slot-revived\`). A genuinely new channel, operation or message has no living version to be based on and carries no pin.`,
|
|
39
62
|
});
|
|
40
63
|
}
|
|
41
64
|
else if (verdict === "stale") {
|
|
@@ -129,13 +152,13 @@ function gradeSurfaces(input, issues) {
|
|
|
129
152
|
return unpinned;
|
|
130
153
|
}
|
|
131
154
|
/** Marker exactness: the slot must exist, and a message marker must name the living declaration. */
|
|
132
|
-
export function gradeMarkers(
|
|
133
|
-
const { featDoc, livingDoc, featSlots, livingSlots } =
|
|
155
|
+
export function gradeMarkers(pair, svc, issues) {
|
|
156
|
+
const { featDoc, livingDoc, featSlots, livingSlots } = pair;
|
|
134
157
|
for (const slot of featSlots) {
|
|
135
158
|
if (!slot.remove)
|
|
136
159
|
continue;
|
|
137
160
|
const label = `${slot.section}.${slot.key}`;
|
|
138
|
-
const living = livingSlots.get(
|
|
161
|
+
const living = livingSlots.get(slotKey(slot));
|
|
139
162
|
if (living === undefined) {
|
|
140
163
|
issues.push({
|
|
141
164
|
severity: "error",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grades.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/grades.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The per-slot grades of one service's pair of event contracts: baseline\n * pins and removal-marker exactness. Split from ./declared.ts along the\n * subject seam — that module owns the WALK (which files, which services,\n * which sets), these functions own the verdict each slot gets once both\n * sides are in hand — when the walk grew past the line limit; the split\n * moved them verbatim.\n */\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { ASYNCAPI_BASELINES_KEY, type AsyncapiSlot } from \"../../asyncapi/digest.js\";\nimport { type AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport { asyncapiSurfaces } from \"../../asyncapi/baseline/surfaces.js\";\nimport { entryFor, readBaselineRecord, surfaceIn } from \"../../openapi/baseline/record.js\";\nimport {\n classifyBaselineDigests,\n OPERATION_DIGEST_LENGTH,\n OPERATION_DIGEST_RE,\n valueDigest,\n} from \"../../openapi/digest.js\";\n\n/** Everything the two baseline graders need about one service's pair of contracts. */\nexport interface AsyncapiBaselineInput {\n featSlots: AsyncapiSlot[];\n livingSlots: Map<string, AsyncapiSlot>;\n /** The FEATURE document's resolved parse — where the record and the surfaces are read from. */\n featPlain: unknown;\n /** The LIVING document's resolved parse; `{}` where no living contract exists. */\n livingPlain: unknown;\n svc: string;\n featureId: string;\n}\n\n/**\n * One service's baselines against the living contract — openapi/baseline/gate.ts's\n * verdicts, over BOTH halves of this axis: the slot pins in-value, and the\n * component surfaces the root `x-loam-baselines` record pins. One function\n * because the unpinned count is shared (see below), and because a delta is\n * graded once or the two halves drift into different ideas of what it restates.\n */\nexport function gradeBaselines(input: AsyncapiBaselineInput, issues: Issue[]): void {\n const { featSlots, livingSlots, svc, featureId } = input;\n let unpinned = 0;\n for (const slot of featSlots) {\n // A removal marker is not a restatement of anything; its own exactness\n // checks guard the slot.\n if (slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(`${slot.section}\\0${slot.key}`);\n if (slot.basedOn !== undefined && !OPERATION_DIGEST_RE.test(slot.basedOn)) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: `${svc}: ${label} has invalid x-loam-based-on '${slot.basedOn}' — expected ${OPERATION_DIGEST_LENGTH} lowercase hex characters, as \\`loam rebase\\` writes them`,\n });\n continue;\n }\n const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, living?.digest);\n if (verdict === \"unfounded\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — a new channel, operation or message has no living version to be based on; drop the marker`,\n });\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: ${label} was written against living version ${slot.basedOn}, but the living contract now holds ${living!.digest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living slot, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unpinned\" && living !== undefined) {\n // Counted, not listed — a delta quotes most of the contract it\n // restates, and one warning per quote teaches people to filter the\n // code out (openapi.baseline-missing's doctrine, verbatim).\n unpinned += 1;\n }\n }\n // The surface half's unpinned count folds into the SAME counter, not a\n // second warning: \"counted, not listed\" is the doctrine above, and one\n // warning per axis-half would have a delta over a schema-heavy contract\n // print two findings naming the same one command.\n unpinned += gradeSurfaces(input, issues);\n if (unpinned > 0) {\n issues.push({\n severity: \"warn\",\n gates: true,\n code: \"asyncapi.baseline-missing\",\n subject: svc,\n message: `${svc}: ${unpinned} slot(s) and component surface(s) in this feature's asyncapi.yaml carry no baseline pin (x-loam-based-on / ${ASYNCAPI_BASELINES_KEY}), so the merge cannot tell which ones this delta EDITS from the ones it merely restates — it will upsert all of them, reverting anything that landed on the restated ones. Run \\`loam rebase ${featureId} --service ${svc}\\`, or archive with --approve to merge unpinned deliberately.`,\n });\n }\n}\n\n/**\n * The surface half: every `components/<kind>/<name>` outside `messages`,\n * graded from the root `x-loam-baselines` record by the same classifier the\n * slots go through. Returns the unpinned count so the caller can fold it into\n * the one per-service warning rather than emitting a second.\n *\n * The codes are REUSED rather than multiplied — `asyncapi.baseline-invalid`\n * and `asyncapi.baseline-stale` say exactly the same thing about a surface as\n * about a slot, and a caller that branches on them wants one branch.\n */\nfunction gradeSurfaces(input: AsyncapiBaselineInput, issues: Issue[]): number {\n const { featPlain, livingPlain, svc, featureId } = input;\n const invalid = (message: string): void => {\n issues.push({ severity: \"error\", code: \"asyncapi.baseline-invalid\", subject: svc, message: `${svc}: ${message}` });\n };\n const { record, problems } = readBaselineRecord(featPlain);\n for (const problem of problems) {\n invalid(`${problem} — the record is \\`loam rebase\\`'s bookkeeping and cannot be graded as it stands; re-run \\`loam rebase ${featureId}\\` to rebuild it`);\n }\n const surfaces = asyncapiSurfaces(featPlain);\n // An entry pinning a surface the delta does not declare is a claim about\n // nothing — usually one the author deleted after rebasing. Distinct from\n // `unfounded` below, where the surface IS declared and the LIVING side is\n // what vanished. A `pathItems` section is the same fault at the axis level:\n // an AsyncAPI document has no path items, so the whole section pins nothing,\n // and rebase's wholesale rebuild would drop it without a word.\n const declared = new Set(surfaces.map((s) => s.id));\n for (const id of Object.keys(record.components)) {\n if (declared.has(id)) continue;\n invalid(`${ASYNCAPI_BASELINES_KEY} pins component '${id}', but this feature's asyncapi.yaml does not declare it — re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n for (const path of Object.keys(record.pathItems)) {\n invalid(`${ASYNCAPI_BASELINES_KEY} pins path item '${path}', but an AsyncAPI document has no path items — the section pins nothing; re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n\n let unpinned = 0;\n for (const s of surfaces) {\n const entry = entryFor(record, s);\n const living = surfaceIn(livingPlain, s);\n const verdict = classifyBaselineDigests(\n entry,\n valueDigest(s.value),\n living.found ? valueDigest(living.value) : undefined,\n );\n if (verdict === \"unpinned\") {\n // Only a surface the living contract ALREADY has can be reverted by an\n // unpinned merge; a genuinely new schema never trips the counter.\n if (living.found) unpinned += 1;\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: component '${s.id}' was pinned to living version ${entry}, but the living contract now holds ${valueDigest(living.value)} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living value, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unfounded\") {\n invalid(`component '${s.id}' is pinned to ${entry}, but the living contract no longer has that surface — the pin is structurally unresolvable; re-read the living contract, then run \\`loam rebase ${featureId}\\``);\n }\n }\n return unpinned;\n}\n\n/** Marker exactness: the slot must exist, and a message marker must name the living declaration. */\nexport function gradeMarkers(\n input: { featDoc: AsyncapiDoc; livingDoc: AsyncapiDoc; featSlots: AsyncapiSlot[]; livingSlots: Map<string, AsyncapiSlot> },\n svc: string,\n issues: Issue[],\n): void {\n const { featDoc, livingDoc, featSlots, livingSlots } = input;\n for (const slot of featSlots) {\n if (!slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(`${slot.section}\\0${slot.key}`);\n if (living === undefined) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-missing\",\n subject: svc,\n message: `${svc}: removal marker at ${label} addresses no living slot — the contract has nothing there to retire; update the stale marker to the current key, or drop it if the slot is already gone`,\n });\n continue;\n }\n if (slot.section !== \"components.messages\") continue;\n // Removal is exact for messages the way it is for operations: the\n // marker names both the key and the message identity it expects to\n // delete, so a slot whose NAME moved under the delta is caught here\n // rather than deleting somebody else's message.\n const markerName = featDoc.messages.find((m) => m.remove === true && m.slot === label)?.name ?? slot.key;\n const livingName = livingDoc.messages.find((m) => m.slot === label)?.name ?? slot.key;\n if (markerName !== livingName) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-mismatch\",\n subject: svc,\n message: `${svc}: removal marker at ${label} names message '${markerName}', but the living declaration there is '${livingName}' — loam never deletes a different message occupying the slot`,\n });\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"grades.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/grades.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The per-slot grades of one service's pair of event contracts: baseline\n * pins and removal-marker exactness. Split from ./declared.ts along the\n * subject seam — that module owns the WALK (which files, which services,\n * which sets), these functions own the verdict each slot gets once both\n * sides are in hand — when the walk grew past the line limit; the split\n * moved them verbatim.\n */\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { ASYNCAPI_BASELINES_KEY, slotKey } from \"../../asyncapi/digest.js\";\nimport type { EventPair, EventPairRead } from \"../retired/event-pair.js\";\nimport { moveBackRemedy } from \"../retired/revival/revived.js\";\nimport { asyncapiSurfaces } from \"../../asyncapi/baseline/surfaces.js\";\nimport { messageName } from \"../../asyncapi/read.js\";\nimport { entryFor, readBaselineRecord, surfaceIn } from \"../../openapi/baseline/record.js\";\nimport {\n classifyBaselineDigests,\n OPERATION_DIGEST_LENGTH,\n OPERATION_DIGEST_RE,\n valueDigest,\n} from \"../../openapi/digest.js\";\n\n/** Everything the two baseline graders need about one service's pair of contracts. */\nexport interface AsyncapiBaselineInput extends EventPair, Pick<EventPairRead, \"merged\"> {\n /** The FEATURE document's resolved parse — where the record and the surfaces are read from. */\n featPlain: unknown;\n /** The LIVING document's resolved parse; `{}` where no living contract exists. */\n livingPlain: unknown;\n svc: string;\n featureId: string;\n}\n\n/**\n * One service's baselines against the living contract — openapi/baseline/gate.ts's\n * verdicts, over BOTH halves of this axis: the slot pins in-value, and the\n * component surfaces the root `x-loam-baselines` record pins. One function\n * because the unpinned count is shared (see below), and because a delta is\n * graded once or the two halves drift into different ideas of what it restates.\n */\nexport function gradeBaselines(input: AsyncapiBaselineInput, issues: Issue[]): void {\n const { featSlots, livingSlots, livingDoc, svc, featureId } = input;\n let unpinned = 0;\n for (const slot of featSlots) {\n // A removal marker is not a restatement of anything; its own exactness\n // checks guard the slot.\n if (slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(slotKey(slot));\n if (slot.basedOn !== undefined && !OPERATION_DIGEST_RE.test(slot.basedOn)) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: `${svc}: ${label} has invalid x-loam-based-on '${slot.basedOn}' — expected ${OPERATION_DIGEST_LENGTH} lowercase hex characters, as \\`loam rebase\\` writes them`,\n });\n continue;\n }\n const verdict = classifyBaselineDigests(slot.basedOn, slot.digest, living?.digest);\n if (verdict === \"unfounded\") {\n // openapi/baseline/gate.ts's reading, on the slot: a pin is only ever\n // written over a living slot, so one with nothing behind it usually\n // means a parallel feature retired the slot — and \"drop the marker\",\n // which this used to say, archived the quote as an addition and put the\n // retired slot back. Or MOVED it, on the one slot a move vacates on\n // this axis: a `components.messages` key whose message name the living\n // contract now declares elsewhere. A channel or operation slot's\n // identity is its key, so it is never \"moved\" — it is retired. The\n // move-back remedy is the revival finding's own (../retired/revival/revived.ts):\n // where the name now stands inline under a channel, a marker there\n // retires nothing, so the remedy names the channel instead. The two\n // places are said as a condition (\"while it is still declared there\"),\n // not as what this delta does: one that already restates or retires\n // that channel takes the name out of it, and only the pin is left.\n const name = slot.section === \"components.messages\" ? messageName(slot.node, slot.key) : undefined;\n const movedTo = livingDoc.messages.filter((m) => m.remove !== true && m.name === name).map((m) => m.slot);\n // The channels still pointing at a key the remedy retires are read off\n // the contract the merge would leave; where it refuses (and the archive\n // with it), off the living one, which is what the merge would start from.\n const remedy = (): string => moveBackRemedy(movedTo, label, input.merged()?.root ?? input.livingPlain);\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-invalid\",\n subject: svc,\n message: movedTo.length > 0\n ? `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — message '${name}' is declared at ${movedTo.join(\" and \")} now: a parallel feature moved it there (or retired it here, and another re-declared it there). Re-read it there and delete this slot from the delta, making any change at its new key; archived while it is still declared there, it would declare '${name}' at ${label} as well. To move it back on purpose, drop the pin and ${remedy()} in this delta.`\n : `${svc}: ${label} carries x-loam-based-on, but the living contract has no slot there — the version it was pinned to is gone. If a parallel feature retired it, re-read the living contract and delete this slot from the delta, unless this feature deliberately brings it back (then drop the pin and archive with --approve past \\`asyncapi.slot-revived\\`). A genuinely new channel, operation or message has no living version to be based on and carries no pin.`,\n });\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: ${label} was written against living version ${slot.basedOn}, but the living contract now holds ${living!.digest} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living slot, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unpinned\" && living !== undefined) {\n // Counted, not listed — a delta quotes most of the contract it\n // restates, and one warning per quote teaches people to filter the\n // code out (openapi.baseline-missing's doctrine, verbatim).\n unpinned += 1;\n }\n }\n // The surface half's unpinned count folds into the SAME counter, not a\n // second warning: \"counted, not listed\" is the doctrine above, and one\n // warning per axis-half would have a delta over a schema-heavy contract\n // print two findings naming the same one command.\n unpinned += gradeSurfaces(input, issues);\n if (unpinned > 0) {\n issues.push({\n severity: \"warn\",\n gates: true,\n code: \"asyncapi.baseline-missing\",\n subject: svc,\n message: `${svc}: ${unpinned} slot(s) and component surface(s) in this feature's asyncapi.yaml carry no baseline pin (x-loam-based-on / ${ASYNCAPI_BASELINES_KEY}), so the merge cannot tell which ones this delta EDITS from the ones it merely restates — it will upsert all of them, reverting anything that landed on the restated ones. Run \\`loam rebase ${featureId} --service ${svc}\\`, or archive with --approve to merge unpinned deliberately.`,\n });\n }\n}\n\n/**\n * The surface half: every `components/<kind>/<name>` outside `messages`,\n * graded from the root `x-loam-baselines` record by the same classifier the\n * slots go through. Returns the unpinned count so the caller can fold it into\n * the one per-service warning rather than emitting a second.\n *\n * The codes are REUSED rather than multiplied — `asyncapi.baseline-invalid`\n * and `asyncapi.baseline-stale` say exactly the same thing about a surface as\n * about a slot, and a caller that branches on them wants one branch.\n */\nfunction gradeSurfaces(input: AsyncapiBaselineInput, issues: Issue[]): number {\n const { featPlain, livingPlain, svc, featureId } = input;\n const invalid = (message: string): void => {\n issues.push({ severity: \"error\", code: \"asyncapi.baseline-invalid\", subject: svc, message: `${svc}: ${message}` });\n };\n const { record, problems } = readBaselineRecord(featPlain);\n for (const problem of problems) {\n invalid(`${problem} — the record is \\`loam rebase\\`'s bookkeeping and cannot be graded as it stands; re-run \\`loam rebase ${featureId}\\` to rebuild it`);\n }\n const surfaces = asyncapiSurfaces(featPlain);\n // An entry pinning a surface the delta does not declare is a claim about\n // nothing — usually one the author deleted after rebasing. Distinct from\n // `unfounded` below, where the surface IS declared and the LIVING side is\n // what vanished. A `pathItems` section is the same fault at the axis level:\n // an AsyncAPI document has no path items, so the whole section pins nothing,\n // and rebase's wholesale rebuild would drop it without a word.\n const declared = new Set(surfaces.map((s) => s.id));\n for (const id of Object.keys(record.components)) {\n if (declared.has(id)) continue;\n invalid(`${ASYNCAPI_BASELINES_KEY} pins component '${id}', but this feature's asyncapi.yaml does not declare it — re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n for (const path of Object.keys(record.pathItems)) {\n invalid(`${ASYNCAPI_BASELINES_KEY} pins path item '${path}', but an AsyncAPI document has no path items — the section pins nothing; re-run \\`loam rebase ${featureId}\\` to rebuild the record`);\n }\n\n let unpinned = 0;\n for (const s of surfaces) {\n const entry = entryFor(record, s);\n const living = surfaceIn(livingPlain, s);\n const verdict = classifyBaselineDigests(\n entry,\n valueDigest(s.value),\n living.found ? valueDigest(living.value) : undefined,\n );\n if (verdict === \"unpinned\") {\n // Only a surface the living contract ALREADY has can be reverted by an\n // unpinned merge; a genuinely new schema never trips the counter.\n if (living.found) unpinned += 1;\n } else if (verdict === \"stale\") {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.baseline-stale\",\n subject: svc,\n message: `${svc}: component '${s.id}' was pinned to living version ${entry}, but the living contract now holds ${valueDigest(living.value)} — somebody landed a change to it in between, and merging this would replace theirs outright. Re-read the living value, fold in what you still mean, then run \\`loam rebase ${featureId}\\`.`,\n });\n } else if (verdict === \"unfounded\") {\n invalid(`component '${s.id}' is pinned to ${entry}, but the living contract no longer has that surface — the pin is structurally unresolvable; re-read the living contract, then run \\`loam rebase ${featureId}\\``);\n }\n }\n return unpinned;\n}\n\n/** Marker exactness: the slot must exist, and a message marker must name the living declaration. */\nexport function gradeMarkers(pair: EventPair, svc: string, issues: Issue[]): void {\n const { featDoc, livingDoc, featSlots, livingSlots } = pair;\n for (const slot of featSlots) {\n if (!slot.remove) continue;\n const label = `${slot.section}.${slot.key}`;\n const living = livingSlots.get(slotKey(slot));\n if (living === undefined) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-missing\",\n subject: svc,\n message: `${svc}: removal marker at ${label} addresses no living slot — the contract has nothing there to retire; update the stale marker to the current key, or drop it if the slot is already gone`,\n });\n continue;\n }\n if (slot.section !== \"components.messages\") continue;\n // Removal is exact for messages the way it is for operations: the\n // marker names both the key and the message identity it expects to\n // delete, so a slot whose NAME moved under the delta is caught here\n // rather than deleting somebody else's message.\n const markerName = featDoc.messages.find((m) => m.remove === true && m.slot === label)?.name ?? slot.key;\n const livingName = livingDoc.messages.find((m) => m.slot === label)?.name ?? slot.key;\n if (markerName !== livingName) {\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-target-mismatch\",\n subject: svc,\n message: `${svc}: removal marker at ${label} names message '${markerName}', but the living declaration there is '${livingName}' — loam never deletes a different message occupying the slot`,\n });\n }\n }\n}\n"]}
|