@ybotok/loam 0.2.0-alpha.6 → 0.2.0-alpha.9
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 +1477 -1
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +40 -22
- package/QUICKSTART.md +181 -9
- package/README.md +256 -57
- package/ROADMAP.md +108 -11
- package/SCHEMA.md +721 -130
- package/WORKFLOW.md +272 -62
- 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/archive/archive.js +15 -18
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
- 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 +25 -42
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +3 -7
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +4 -4
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/refusal.js +8 -18
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +134 -25
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +1 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +82 -33
- 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 +4 -7
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +2 -2
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +44 -12
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +1 -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/explore.js +4 -8
- 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 +0 -4
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +38 -23
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +38 -6
- 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 +28 -33
- 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 +64 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +6 -9
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/show.js +3 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/print.js +13 -4
- 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 +3 -7
- 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 +60 -25
- 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 +141 -71
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/report.js +55 -5
- package/dist/commands/subsystem/txn/report.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +121 -45
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +82 -12
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/render.js +116 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +113 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +6 -9
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +17 -28
- 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 +6 -15
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +4 -49
- 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/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +6 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/feature.js +23 -19
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +26 -3
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +35 -32
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +7 -12
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +16 -5
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
- package/dist/commands/validate/fleet/map/consumers.js +43 -122
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -1
- package/dist/commands/validate/fleet/map/usecases.js +5 -11
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/obligations.js +1 -5
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +5 -1
- 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/usecases.js +6 -4
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
- package/dist/commands/validate/fleet/views/ids.js +33 -7
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +17 -35
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
- package/dist/commands/validate/fleet/views/stale.js +31 -14
- 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/report.js +5 -4
- 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 +16 -104
- 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 +1 -1
- 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/service.js +16 -26
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +31 -38
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +8 -11
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/usecases.js +3 -3
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/validate.js +44 -26
- 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 +54 -51
- 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 +57 -102
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +4 -3
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
- 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 +9 -3
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +31 -17
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +24 -6
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +37 -21
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +25 -10
- 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/scaffold.js +10 -15
- package/dist/core/agent/scaffold.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 +5 -3
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +25 -22
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +54 -21
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +79 -11
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +91 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +3 -1
- 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/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +62 -24
- 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 +30 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +31 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +10 -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 +30 -25
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/landscape.js +8 -12
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +8 -5
- package/dist/core/brief/map/extend.js.map +1 -1
- package/dist/core/brief/map/owed.js +8 -26
- package/dist/core/brief/map/owed.js.map +1 -1
- package/dist/core/brief/targets.js +79 -5
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +1 -1
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/likec4.js +147 -16
- 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/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +1 -9
- package/dist/core/c4/prefetch/fleet.js.map +1 -1
- package/dist/core/c4/project/architecture.js +31 -9
- package/dist/core/c4/project/architecture.js.map +1 -1
- package/dist/core/c4/project/documents.js +36 -10
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +14 -21
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +7 -3
- package/dist/core/c4/project/staged.js.map +1 -1
- 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 +25 -1
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/seed/template.js +15 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/shape.js +2 -22
- package/dist/core/c4/service-model/shape.js.map +1 -1
- package/dist/core/c4/source-mask.js +288 -4
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +203 -147
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +19 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +5 -18
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/landscape-merge.js +13 -8
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +10 -6
- package/dist/core/c4/splice/model/merge.js.map +1 -1
- package/dist/core/c4/splice/placement.js +108 -18
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/workspace.js +13 -22
- 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/findings.js +15 -20
- 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/coherence.js +30 -16
- 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/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 +119 -8
- 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 +14 -82
- package/dist/core/coherence/events/lookups.js.map +1 -1
- 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 +1 -1
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/deployment/delta.js +15 -16
- package/dist/core/deployment/delta.js.map +1 -1
- 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 +28 -16
- 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/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/document/apply.js +10 -2
- 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 +17 -12
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/refusals.js +3 -3
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms.js +18 -9
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +2 -7
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +34 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +10 -3
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +64 -19
- 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/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/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 +5 -36
- package/dist/core/links/corpus.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 +22 -8
- 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 +56 -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 +26 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +15 -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 +2 -16
- package/dist/core/pack/living.js.map +1 -1
- 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/entries.js.map +1 -1
- package/dist/core/repo/paths.js +34 -29
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/repo.js +24 -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 +45 -6
- package/dist/core/repo/tree/render/body.js.map +1 -1
- 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 +19 -2
- package/dist/core/repo/tree/render/stale.js.map +1 -1
- package/dist/core/repo/tree/render/views.js +102 -33
- package/dist/core/repo/tree/render/views.js.map +1 -1
- package/dist/core/repo/tree/walk.js +8 -30
- 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/staging/commit.js +10 -12
- 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 +60 -1
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +152 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +2 -2
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +5 -11
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/fleet/fleet.js +6 -2
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +26 -3
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.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 +174 -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/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/fleet.js +178 -31
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/core/usecases/operations.js +5 -4
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/service/flows.js +9 -6
- package/dist/core/usecases/service/flows.js.map +1 -1
- 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 +45 -10
- 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 +1 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +53 -3
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +28 -9
- package/examples/docs/architecture/landscape.likec4 +21 -7
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +3 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
- 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/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/order-service/model.likec4 +10 -8
- package/examples/docs/services/payment-service/model.likec4 +6 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +9 -7
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/examples/docs/services/platform/notification-service/model.likec4 +4 -5
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -16
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -221
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -180
- 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 -256
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* feature document's readability (`asyncapi.invalid` suspends the rest, the
|
|
7
7
|
* unreadableApis discipline), the baseline pins and removal-marker exactness
|
|
8
8
|
* (./grades.ts, slot-keyed), the removal↔REMOVED-requirement justification
|
|
9
|
-
* join, and the merge simulation (
|
|
10
|
-
* against. What it cannot answer alone — who still consumes a message, what
|
|
9
|
+
* join, and the merge simulation (../retired/merged.ts) the cross-axis
|
|
10
|
+
* checks grade against. What it cannot answer alone — who still consumes a message, what
|
|
11
11
|
* other features are adding — lives in ./lookups.ts, and the cross-axis
|
|
12
12
|
* grading of edges and requirement lines in ./events.ts.
|
|
13
13
|
*
|
|
@@ -23,16 +23,15 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { existsSync } from "node:fs";
|
|
25
25
|
import { readFile } from "node:fs/promises";
|
|
26
|
-
import { parse } from "yaml";
|
|
27
|
-
import { NotUtf8DocumentError } from "../../kernel/document-bytes.js";
|
|
28
26
|
import {} from "../../kernel/ids/service.js";
|
|
29
27
|
import { featureSpecPaths, SPEC_AXES } from "../../repo/paths.js";
|
|
30
28
|
import { locateServicePaths } from "../../repo/service-target.js";
|
|
31
29
|
import { parseRequirements } from "../../document/parse.js";
|
|
32
|
-
import { asyncapiSlots } from "../../asyncapi/digest.js";
|
|
33
30
|
import { readAsyncapi } from "../../asyncapi/read.js";
|
|
34
31
|
import { gradeBaselines, gradeMarkers } from "./grades.js";
|
|
35
|
-
import {
|
|
32
|
+
import { messageRetirements } from "../retired/retirement.js";
|
|
33
|
+
import { eventArrivals } from "../retired/revival/arrivals.js";
|
|
34
|
+
import { readEventPair } from "../retired/event-pair.js";
|
|
36
35
|
/**
|
|
37
36
|
* The delta's `Publishes:`/`Consumes:` lines for one service, split by
|
|
38
37
|
* requirement fate — read from BOTH spec files, because the outbox
|
|
@@ -62,25 +61,16 @@ export async function deltaEventLines(scope, service, context) {
|
|
|
62
61
|
}
|
|
63
62
|
return { removed, links };
|
|
64
63
|
}
|
|
65
|
-
/** Read a document's text, answering undefined for bytes that do not decode — ../declared.ts's guard. */
|
|
66
|
-
async function readOr(p, context) {
|
|
67
|
-
try {
|
|
68
|
-
return context === undefined ? await readFile(p, "utf8") : await context.readText(p);
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
if (err instanceof NotUtf8DocumentError)
|
|
72
|
-
return undefined;
|
|
73
|
-
throw err;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
64
|
export async function declaredEvents(scope, svcNames, issues, context) {
|
|
77
65
|
const { docsDir, featureDir, featureId } = scope;
|
|
78
66
|
const featureDocs = new Map();
|
|
79
67
|
const mergedDocs = new Map();
|
|
80
68
|
const netRemoved = new Map();
|
|
81
69
|
const stoppedSending = new Map();
|
|
70
|
+
const sentAfter = new Map();
|
|
82
71
|
const links = new Map();
|
|
83
72
|
const unreadable = new Set();
|
|
73
|
+
const revivalCandidates = new Map();
|
|
84
74
|
for (const svc of svcNames) {
|
|
85
75
|
const featPath = featureSpecPaths(featureDir, svc).asyncapi;
|
|
86
76
|
const { removed: justifiedMsgs, links: svcLinks } = await deltaEventLines(scope, svc, context);
|
|
@@ -125,53 +115,39 @@ export async function declaredEvents(scope, svcNames, issues, context) {
|
|
|
125
115
|
severity: "error",
|
|
126
116
|
code: "asyncapi.invalid",
|
|
127
117
|
subject: svc,
|
|
128
|
-
message: `${svc}: this feature's asyncapi.yaml does not
|
|
118
|
+
message: `${svc}: this feature's asyncapi.yaml does not read as AsyncAPI 3.0${featDoc.error === undefined ? "" : ` (${featDoc.error})`} — the event axis is unchecked and the merge would have nothing true to write. Fix the document first.`,
|
|
129
119
|
});
|
|
130
120
|
continue;
|
|
131
121
|
}
|
|
132
122
|
featureDocs.set(svc, featDoc);
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// every restatement new and every marker stale, so both checks stand
|
|
145
|
-
// down and the (later) merge refuses the unreadable side by name.
|
|
146
|
-
const livingText = !existsSync(livingPath) || livingDoc.unreadable ? undefined : await readOr(livingPath, context);
|
|
147
|
-
const livingPlain = livingText === undefined ? {} : parse(livingText);
|
|
148
|
-
const livingSlots = new Map(asyncapiSlots(livingPlain).map((s) => [`${s.section}\0${s.key}`, s]));
|
|
123
|
+
// Both sides' texts, trees and slots, read the one way rebase's
|
|
124
|
+
// standalone revival read reads them too (../retired/event-pair.ts owns
|
|
125
|
+
// the guards: absent living is an answer, unreadable living is not).
|
|
126
|
+
const read = await readEventPair({ featPath, featDoc, livingPath, livingDoc, service: svc }, context);
|
|
127
|
+
const { featSlots, livingText } = read;
|
|
128
|
+
revivalCandidates.set(svc, eventArrivals(read));
|
|
129
|
+
// The pair's one simulation, which the revival candidates above have
|
|
130
|
+
// already asked: absent living included, where it is the create branch.
|
|
131
|
+
const sends = read.merged()?.doc.sent;
|
|
132
|
+
if (sends !== undefined)
|
|
133
|
+
sentAfter.set(svc, sends);
|
|
149
134
|
if (livingText !== undefined) {
|
|
150
|
-
gradeBaselines({
|
|
151
|
-
gradeMarkers(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
135
|
+
gradeBaselines({ ...read, svc, featureId }, issues);
|
|
136
|
+
gradeMarkers(read, svc, issues);
|
|
137
|
+
// The pair's one simulation — the revival candidates above have already
|
|
138
|
+
// asked it, comparing it with the living contract.
|
|
139
|
+
const merged = read.merged()?.doc;
|
|
140
|
+
if (merged !== undefined) {
|
|
141
|
+
mergedDocs.set(svc, merged);
|
|
142
|
+
stoppedSending.set(svc, livingDoc.sent.filter((m) => !merged.sent.includes(m)));
|
|
158
143
|
}
|
|
159
144
|
}
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
// exactness only (requirements join on message names, never channel
|
|
167
|
-
// keys — SCHEMA.md) and its living inline interior leaves with it.
|
|
168
|
-
const markerNames = new Set(featDoc.messages.filter((m) => m.remove === true && m.slot.startsWith("components.messages.")).map((m) => m.name));
|
|
169
|
-
const channelRemovals = featSlots.filter((s) => s.section === "channels" && s.remove).map((s) => s.key);
|
|
170
|
-
const inlineRetired = new Set(livingDoc.messages
|
|
171
|
-
.filter((m) => channelRemovals.some((ck) => m.slot.startsWith(`channels.${ck}.messages.`)))
|
|
172
|
-
.map((m) => m.name));
|
|
173
|
-
const redeclared = new Set(featDoc.messages.filter((m) => m.remove !== true).map((m) => m.name));
|
|
174
|
-
const net = new Set([...markerNames, ...inlineRetired].filter((name) => !redeclared.has(name)));
|
|
145
|
+
// The raw marker sets and the NET one (minus relocations) —
|
|
146
|
+
// ../retired/retirement.ts owns the rule and says why each set exists;
|
|
147
|
+
// the retired index reads every archived feature through the same
|
|
148
|
+
// function, against that feature's snapshot pre-image instead of the
|
|
149
|
+
// living contract on disk.
|
|
150
|
+
const { markerNames, inlineRetired, redeclared, net } = messageRetirements(featDoc, featSlots, livingDoc);
|
|
175
151
|
netRemoved.set(svc, net);
|
|
176
152
|
for (const name of markerNames) {
|
|
177
153
|
if (redeclared.has(name) || justifiedMsgs.has(name))
|
|
@@ -188,11 +164,20 @@ export async function declaredEvents(scope, svcNames, issues, context) {
|
|
|
188
164
|
// and satisfied by ANY marker taking it out, relocation included: a
|
|
189
165
|
// message-slot marker naming it, or a channel-slot removal whose
|
|
190
166
|
// living inline interior declares it (the SCHEMA-sanctioned way to
|
|
191
|
-
// retire an inline declaration).
|
|
167
|
+
// retire an inline declaration). And owed only while the MERGE would
|
|
168
|
+
// still declare it: a slot re-keyed to another name takes the message
|
|
169
|
+
// out with no marker, and none could be written there — the key holds
|
|
170
|
+
// the re-keyed declaration — so the route `asyncapi.remove-message-
|
|
171
|
+
// consumed` names (REMOVE the requirement whose Publishes: line names
|
|
172
|
+
// it) ended in this error, `openapi.remove-marker-missing`'s deadlock
|
|
173
|
+
// one axis over.
|
|
192
174
|
if (markerNames.has(m) || inlineRetired.has(m))
|
|
193
175
|
continue;
|
|
194
176
|
if (livingDoc.unreadable || !livingDoc.messages.some((msg) => msg.name === m))
|
|
195
177
|
continue;
|
|
178
|
+
const after = mergedDocs.get(svc);
|
|
179
|
+
if (after !== undefined && !after.messages.some((msg) => msg.name === m))
|
|
180
|
+
continue;
|
|
196
181
|
issues.push({
|
|
197
182
|
severity: "error",
|
|
198
183
|
code: "asyncapi.remove-marker-missing",
|
|
@@ -201,6 +186,6 @@ export async function declaredEvents(scope, svcNames, issues, context) {
|
|
|
201
186
|
});
|
|
202
187
|
}
|
|
203
188
|
}
|
|
204
|
-
return { featureDocs, mergedDocs, netRemoved, stoppedSending, links, unreadable };
|
|
189
|
+
return { featureDocs, mergedDocs, netRemoved, stoppedSending, sentAfter, links, unreadable, revivalCandidates };
|
|
205
190
|
}
|
|
206
191
|
//# sourceMappingURL=declared.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"declared.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/declared.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a feature's asyncapi deltas DECLARE, per service — ../declared.ts's\n * mirror on the event axis, indexed in one walk and graded as it goes.\n *\n * The walk owns every check that reads ONE service's pair of contracts: the\n * feature document's readability (`asyncapi.invalid` suspends the rest, the\n * unreadableApis discipline), the baseline pins and removal-marker exactness\n * (./grades.ts, slot-keyed), the removal↔REMOVED-requirement justification\n * join, and the merge simulation (./merged.ts) the cross-axis checks grade\n * against. What it cannot answer alone — who still consumes a message, what\n * other features are adding — lives in ./lookups.ts, and the cross-axis\n * grading of edges and requirement lines in ./events.ts.\n *\n * Slot identity is core/asyncapi/digest.ts's: three sections, and an inline\n * channel message is channel-slot interior — a marker NESTED on one makes\n * the channel an EDIT against the channel's pin, never a message removal,\n * so nothing here reads it as one. A CHANNEL-slot removal, by contrast,\n * takes its living inline interior with it (SCHEMA.md's decision), so the\n * justification join counts those inline names as marked: without that, a\n * message declared only inline was a deadlock — the sanctioned channel\n * marker graded remove-marker-missing, and the components.messages marker\n * the error suggested graded remove-target-missing.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { parse } from \"yaml\";\nimport { NotUtf8DocumentError } from \"../../kernel/document-bytes.js\";\nimport { type PathableService } from \"../../kernel/ids/service.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { featureSpecPaths, SPEC_AXES } from \"../../repo/paths.js\";\nimport { locateServicePaths } from \"../../repo/service-target.js\";\nimport { parseRequirements } from \"../../document/parse.js\";\nimport { asyncapiSlots, type AsyncapiSlot } from \"../../asyncapi/digest.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport type { DocsDir, FeatureDir } from \"../../kernel/ids/dirs.js\";\nimport { gradeBaselines, gradeMarkers } from \"./grades.js\";\nimport { mergedAsyncapiDoc } from \"./merged.js\";\n\n/** The three facts this walk needs about the feature it is reading. */\nexport interface EventDeltaScope {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n featureId: string;\n}\n\n/** One delta requirement's `Publishes:`/`Consumes:` claim — what ./events.ts grades against the contract. */\nexport interface EventLink {\n direction: \"publishes\" | \"consumes\";\n message: string;\n requirement: string;\n}\n\n/** Everything one feature's asyncapi deltas say about their own services. */\nexport interface DeclaredEvents {\n /** Per touched service: the feature delta's parse — present only where the file exists and reads. */\n featureDocs: Map<PathableService, AsyncapiDoc>;\n /**\n * Per touched service: the living contract as the MERGE would leave it\n * (./merged.ts) — present where a feature delta sits over a readable\n * living contract and the simulation had an answer. What `declares()` and\n * the consumer gate's wire half read, so the gate grades the post-archive\n * contract rather than the union of two documents one of which is leaving.\n */\n mergedDocs: Map<PathableService, AsyncapiDoc>;\n /**\n * Per service: message names this feature genuinely retires — message-slot\n * markers plus a removed channel's living INLINE interior (SCHEMA.md:\n * retiring a whole channel retires its inline messages with it), minus\n * redeclarations.\n */\n netRemoved: Map<string, Set<string>>;\n /**\n * Per service: messages the living contract SENDS that the merged one\n * would not — a channel or operation removal, or an edit, stopping\n * production while the declaration may well survive. The wire half of the\n * consumer question (./events.ts); empty where no simulation ran.\n */\n stoppedSending: Map<string, string[]>;\n /**\n * Per service in the walk: the delta's non-REMOVED `Publishes:`/`Consumes:`\n * lines — read once HERE and graded in ./events.ts, the indexed-in-one-walk\n * rule this record exists for (the same lines used to be re-read and\n * re-parsed per question on the context-less archive-gate path).\n */\n links: Map<PathableService, EventLink[]>;\n /** Services whose FEATURE asyncapi.yaml exists but does not parse — every event check suspends for them. */\n unreadable: Set<string>;\n}\n\n/**\n * The delta's `Publishes:`/`Consumes:` lines for one service, split by\n * requirement fate — read from BOTH spec files, because the outbox\n * requirement in arch.spec.md is the canonical home of an event line. A\n * REMOVED requirement's lines justify removals; everything else's are\n * claims ./events.ts grades against the contract.\n */\nexport async function deltaEventLines(\n scope: EventDeltaScope,\n service: PathableService,\n context?: FleetContext,\n): Promise<{ removed: Set<string>; links: EventLink[] }> {\n const removed = new Set<string>();\n const links: EventLink[] = [];\n for (const axis of SPEC_AXES) {\n const p = featureSpecPaths(scope.featureDir, service)[axis.key];\n if (!existsSync(p)) continue;\n const reqs = context === undefined\n ? parseRequirements(await readFile(p, \"utf8\"))\n : await context.readRequirements(p);\n for (const r of reqs) {\n if (r.kind === \"REMOVED\") {\n for (const m of [...r.publishes, ...r.consumes]) removed.add(m);\n continue;\n }\n links.push(...r.publishes.map((m) => ({ direction: \"publishes\" as const, message: m, requirement: r.name })));\n links.push(...r.consumes.map((m) => ({ direction: \"consumes\" as const, message: m, requirement: r.name })));\n }\n }\n return { removed, links };\n}\n\n/** Read a document's text, answering undefined for bytes that do not decode — ../declared.ts's guard. */\nasync function readOr(p: string, context?: FleetContext): Promise<string | undefined> {\n try {\n return context === undefined ? await readFile(p, \"utf8\") : await context.readText(p);\n } catch (err) {\n if (err instanceof NotUtf8DocumentError) return undefined;\n throw err;\n }\n}\n\nexport async function declaredEvents(\n scope: EventDeltaScope,\n svcNames: PathableService[],\n issues: Issue[],\n context?: FleetContext,\n): Promise<DeclaredEvents> {\n const { docsDir, featureDir, featureId } = scope;\n const featureDocs = new Map<PathableService, AsyncapiDoc>();\n const mergedDocs = new Map<PathableService, AsyncapiDoc>();\n const netRemoved = new Map<string, Set<string>>();\n const stoppedSending = new Map<string, string[]>();\n const links = new Map<PathableService, EventLink[]>();\n const unreadable = new Set<string>();\n for (const svc of svcNames) {\n const featPath = featureSpecPaths(featureDir, svc).asyncapi;\n const { removed: justifiedMsgs, links: svcLinks } = await deltaEventLines(scope, svc, context);\n links.set(svc, svcLinks);\n // The living contract is read only where a question will be asked of it:\n // no event delta and no REMOVED line means no marker debt and no slot to\n // grade. The unguarded read was an eager parse per touched service —\n // paid on every archive, whose gate runs without a FleetContext — for an\n // answer nothing consumed.\n if (!existsSync(featPath) && justifiedMsgs.size === 0) continue;\n const livingPath = (await locateServicePaths(docsDir, svc, context)).asyncapi;\n const livingDoc = await readAsyncapi(livingPath, context);\n if (!existsSync(featPath)) {\n // No event delta — but a REMOVED requirement may still be walking away\n // from a message the LIVING contract declares, and the marker that\n // retires the declaration has to ride in the same feature. Only\n // messages the living contract actually carries are owed one: an event\n // contract is optional, and a fleet retiring a requirement over a\n // message no contract declares owes no file.\n for (const m of justifiedMsgs) {\n if (livingDoc.unreadable || !livingDoc.messages.some((msg) => msg.name === m)) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-marker-missing\",\n subject: svc,\n message: `${svc}: a REMOVED requirement's Publishes:/Consumes: line names '${m}', which the living asyncapi.yaml still declares, but this feature carries no matching x-loam-remove: true marker in specs/${svc}/asyncapi.yaml`,\n });\n }\n continue;\n }\n const featDoc = await readAsyncapi(featPath, context);\n // A feature contract that EXISTS but does not read is a broken document,\n // not an empty one — openapi.invalid's discipline (../declared.ts),\n // applied to the event axis: the rest of this service's event checks are\n // suspended, because every one of them would be an opinion about a\n // document nobody could open.\n if (featDoc.unreadable) {\n unreadable.add(svc);\n issues.push({\n severity: \"error\",\n code: \"asyncapi.invalid\",\n subject: svc,\n message: `${svc}: this feature's asyncapi.yaml does not parse${featDoc.error === undefined ? \"\" : ` (${featDoc.error})`} — the event axis is unchecked and the merge would have nothing true to write. Fix the YAML first.`,\n });\n continue;\n }\n featureDocs.set(svc, featDoc);\n\n const featText = await readOr(featPath, context);\n // Parsed ONCE and kept: the slot walk and the surface half of the baseline\n // grade read the same resolved tree, and a second `parse` of the same\n // bytes is a second chance for the two halves to disagree about what the\n // delta declares.\n const featPlain: unknown = featText === undefined ? {} : parse(featText);\n const featSlots = asyncapiSlots(featPlain);\n // The living side of every slot judgement. Absent is a real answer —\n // everything the delta spells is new, and a baseline only means\n // something for a slot that already exists. Unreadable is NOT an\n // answer: grading pins or markers against an empty parse would call\n // every restatement new and every marker stale, so both checks stand\n // down and the (later) merge refuses the unreadable side by name.\n const livingText = !existsSync(livingPath) || livingDoc.unreadable ? undefined : await readOr(livingPath, context);\n const livingPlain: unknown = livingText === undefined ? {} : parse(livingText);\n const livingSlots = new Map<string, AsyncapiSlot>(\n asyncapiSlots(livingPlain).map((s) => [`${s.section}\\0${s.key}`, s]),\n );\n\n if (livingText !== undefined) {\n gradeBaselines({ featSlots, livingSlots, featPlain, livingPlain, svc, featureId }, issues);\n gradeMarkers({ featDoc, livingDoc, featSlots, livingSlots }, svc, issues);\n if (featText !== undefined) {\n const merged = mergedAsyncapiDoc({ livingText, livingDoc, featureText: featText, service: svc });\n if (merged !== undefined) {\n mergedDocs.set(svc, merged);\n stoppedSending.set(svc, livingDoc.sent.filter((m) => !merged.sent.includes(m)));\n }\n }\n }\n\n // A relocation — same message name, marker on the old key, declaration\n // at a new one — retires nothing. \"Is this message going away\" asks the\n // NET set; \"did the author write a marker\" asks the raw one,\n // ../declared.ts's distinction verbatim. Two raw sets, because the two\n // marker shapes carry different debts: a message-slot marker must be\n // justified by a REMOVED requirement, while a channel-slot removal needs\n // exactness only (requirements join on message names, never channel\n // keys — SCHEMA.md) and its living inline interior leaves with it.\n const markerNames = new Set(\n featDoc.messages.filter((m) => m.remove === true && m.slot.startsWith(\"components.messages.\")).map((m) => m.name),\n );\n const channelRemovals = featSlots.filter((s) => s.section === \"channels\" && s.remove).map((s) => s.key);\n const inlineRetired = new Set(\n livingDoc.messages\n .filter((m) => channelRemovals.some((ck) => m.slot.startsWith(`channels.${ck}.messages.`)))\n .map((m) => m.name),\n );\n const redeclared = new Set(featDoc.messages.filter((m) => m.remove !== true).map((m) => m.name));\n const net = new Set([...markerNames, ...inlineRetired].filter((name) => !redeclared.has(name)));\n netRemoved.set(svc, net);\n\n for (const name of markerNames) {\n if (redeclared.has(name) || justifiedMsgs.has(name)) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-marker-unjustified\",\n subject: svc,\n message: `${svc}: removal marker for message '${name}' is not justified by a REMOVED requirement's Publishes:/Consumes: line`,\n });\n }\n for (const m of justifiedMsgs) {\n // Owed only while the living contract still declares the message —\n // and satisfied by ANY marker taking it out, relocation included: a\n // message-slot marker naming it, or a channel-slot removal whose\n // living inline interior declares it (the SCHEMA-sanctioned way to\n // retire an inline declaration).\n if (markerNames.has(m) || inlineRetired.has(m)) continue;\n if (livingDoc.unreadable || !livingDoc.messages.some((msg) => msg.name === m)) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-marker-missing\",\n subject: svc,\n message: `${svc}: a REMOVED requirement's Publishes:/Consumes: line names '${m}', which the living asyncapi.yaml still declares, but this feature's asyncapi.yaml has no matching x-loam-remove: true marker`,\n });\n }\n }\n return { featureDocs, mergedDocs, netRemoved, stoppedSending, links, unreadable };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"declared.js","sourceRoot":"","sources":["../../../../src/core/coherence/events/declared.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a feature's asyncapi deltas DECLARE, per service — ../declared.ts's\n * mirror on the event axis, indexed in one walk and graded as it goes.\n *\n * The walk owns every check that reads ONE service's pair of contracts: the\n * feature document's readability (`asyncapi.invalid` suspends the rest, the\n * unreadableApis discipline), the baseline pins and removal-marker exactness\n * (./grades.ts, slot-keyed), the removal↔REMOVED-requirement justification\n * join, and the merge simulation (../retired/merged.ts) the cross-axis\n * checks grade against. What it cannot answer alone — who still consumes a message, what\n * other features are adding — lives in ./lookups.ts, and the cross-axis\n * grading of edges and requirement lines in ./events.ts.\n *\n * Slot identity is core/asyncapi/digest.ts's: three sections, and an inline\n * channel message is channel-slot interior — a marker NESTED on one makes\n * the channel an EDIT against the channel's pin, never a message removal,\n * so nothing here reads it as one. A CHANNEL-slot removal, by contrast,\n * takes its living inline interior with it (SCHEMA.md's decision), so the\n * justification join counts those inline names as marked: without that, a\n * message declared only inline was a deadlock — the sanctioned channel\n * marker graded remove-marker-missing, and the components.messages marker\n * the error suggested graded remove-target-missing.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { type PathableService } from \"../../kernel/ids/service.js\";\nimport type { Issue } from \"../../vocabulary/issue.js\";\nimport { featureSpecPaths, SPEC_AXES } from \"../../repo/paths.js\";\nimport { locateServicePaths } from \"../../repo/service-target.js\";\nimport { parseRequirements } from \"../../document/parse.js\";\nimport { readAsyncapi } from \"../../asyncapi/read.js\";\nimport type { AsyncapiDoc } from \"../../asyncapi/model.js\";\nimport type { FleetContext } from \"../../fleet-context.js\";\nimport type { DocsDir, FeatureDir } from \"../../kernel/ids/dirs.js\";\nimport { gradeBaselines, gradeMarkers } from \"./grades.js\";\nimport { messageRetirements } from \"../retired/retirement.js\";\nimport { eventArrivals, type EventArrivals } from \"../retired/revival/arrivals.js\";\nimport { readEventPair } from \"../retired/event-pair.js\";\n\n/** The three facts this walk needs about the feature it is reading. */\nexport interface EventDeltaScope {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n featureId: string;\n}\n\n/** One delta requirement's `Publishes:`/`Consumes:` claim — what ./events.ts grades against the contract. */\nexport interface EventLink {\n direction: \"publishes\" | \"consumes\";\n message: string;\n requirement: string;\n}\n\n/** Everything one feature's asyncapi deltas say about their own services. */\nexport interface DeclaredEvents {\n /** Per touched service: the feature delta's parse — present only where the file exists and reads. */\n featureDocs: Map<PathableService, AsyncapiDoc>;\n /**\n * Per touched service: the living contract as the MERGE would leave it\n * (../retired/merged.ts) — present where a feature delta sits over a\n * readable living contract and the simulation had an answer. What `declares()` and\n * the consumer gate's wire half read, so the gate grades the post-archive\n * contract rather than the union of two documents one of which is leaving.\n */\n mergedDocs: Map<PathableService, AsyncapiDoc>;\n /**\n * Per service: message names this feature genuinely retires — message-slot\n * markers plus a removed channel's living INLINE interior (SCHEMA.md:\n * retiring a whole channel retires its inline messages with it), minus\n * redeclarations.\n */\n netRemoved: Map<string, Set<string>>;\n /**\n * Per service: messages the living contract SENDS that the merged one\n * would not — a channel or operation removal, or an edit, stopping\n * production while the declaration may well survive. The wire half of the\n * consumer question (./events.ts); empty where no simulation ran.\n */\n stoppedSending: Map<string, string[]>;\n /**\n * Per touched service whose feature asyncapi.yaml reads: the names its\n * contract would SEND once the archive writes it — the merged contract over a\n * readable living one, and the create branch's document where none exists\n * (a feature may move production to a service with no contract yet). Absent\n * where the simulation stands down: an unreadable living contract, or a pair\n * the merge refuses. What the consumer gate asks for the senders that keep a\n * message produced after this merge (./events.ts), so a service absent here\n * that this feature carries a delta for vouches for nothing.\n */\n sentAfter: Map<PathableService, readonly string[]>;\n /**\n * Per service in the walk: the delta's non-REMOVED `Publishes:`/`Consumes:`\n * lines — read once HERE and graded in ./events.ts, the indexed-in-one-walk\n * rule this record exists for (the same lines used to be re-read and\n * re-parsed per question on the context-less archive-gate path).\n */\n links: Map<PathableService, EventLink[]>;\n /** Services whose FEATURE asyncapi.yaml exists but does not parse — every event check suspends for them. */\n unreadable: Set<string>;\n /**\n * Per touched service: the slots the delta declares that the living\n * contract lacks, and the message names the contract the archive would\n * write declares where the living one does not — new, or a revival of what\n * an archived feature retired or moved away, which the living contract\n * alone cannot tell apart. ./events.ts asks the retired index (../retired/)\n * only when one of these is non-empty.\n */\n revivalCandidates: Map<PathableService, EventArrivals>;\n}\n\n/**\n * The delta's `Publishes:`/`Consumes:` lines for one service, split by\n * requirement fate — read from BOTH spec files, because the outbox\n * requirement in arch.spec.md is the canonical home of an event line. A\n * REMOVED requirement's lines justify removals; everything else's are\n * claims ./events.ts grades against the contract.\n */\nexport async function deltaEventLines(\n scope: EventDeltaScope,\n service: PathableService,\n context?: FleetContext,\n): Promise<{ removed: Set<string>; links: EventLink[] }> {\n const removed = new Set<string>();\n const links: EventLink[] = [];\n for (const axis of SPEC_AXES) {\n const p = featureSpecPaths(scope.featureDir, service)[axis.key];\n if (!existsSync(p)) continue;\n const reqs = context === undefined\n ? parseRequirements(await readFile(p, \"utf8\"))\n : await context.readRequirements(p);\n for (const r of reqs) {\n if (r.kind === \"REMOVED\") {\n for (const m of [...r.publishes, ...r.consumes]) removed.add(m);\n continue;\n }\n links.push(...r.publishes.map((m) => ({ direction: \"publishes\" as const, message: m, requirement: r.name })));\n links.push(...r.consumes.map((m) => ({ direction: \"consumes\" as const, message: m, requirement: r.name })));\n }\n }\n return { removed, links };\n}\n\nexport async function declaredEvents(\n scope: EventDeltaScope,\n svcNames: PathableService[],\n issues: Issue[],\n context?: FleetContext,\n): Promise<DeclaredEvents> {\n const { docsDir, featureDir, featureId } = scope;\n const featureDocs = new Map<PathableService, AsyncapiDoc>();\n const mergedDocs = new Map<PathableService, AsyncapiDoc>();\n const netRemoved = new Map<string, Set<string>>();\n const stoppedSending = new Map<string, string[]>();\n const sentAfter = new Map<PathableService, readonly string[]>();\n const links = new Map<PathableService, EventLink[]>();\n const unreadable = new Set<string>();\n const revivalCandidates = new Map<PathableService, EventArrivals>();\n for (const svc of svcNames) {\n const featPath = featureSpecPaths(featureDir, svc).asyncapi;\n const { removed: justifiedMsgs, links: svcLinks } = await deltaEventLines(scope, svc, context);\n links.set(svc, svcLinks);\n // The living contract is read only where a question will be asked of it:\n // no event delta and no REMOVED line means no marker debt and no slot to\n // grade. The unguarded read was an eager parse per touched service —\n // paid on every archive, whose gate runs without a FleetContext — for an\n // answer nothing consumed.\n if (!existsSync(featPath) && justifiedMsgs.size === 0) continue;\n const livingPath = (await locateServicePaths(docsDir, svc, context)).asyncapi;\n const livingDoc = await readAsyncapi(livingPath, context);\n if (!existsSync(featPath)) {\n // No event delta — but a REMOVED requirement may still be walking away\n // from a message the LIVING contract declares, and the marker that\n // retires the declaration has to ride in the same feature. Only\n // messages the living contract actually carries are owed one: an event\n // contract is optional, and a fleet retiring a requirement over a\n // message no contract declares owes no file.\n for (const m of justifiedMsgs) {\n if (livingDoc.unreadable || !livingDoc.messages.some((msg) => msg.name === m)) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-marker-missing\",\n subject: svc,\n message: `${svc}: a REMOVED requirement's Publishes:/Consumes: line names '${m}', which the living asyncapi.yaml still declares, but this feature carries no matching x-loam-remove: true marker in specs/${svc}/asyncapi.yaml`,\n });\n }\n continue;\n }\n const featDoc = await readAsyncapi(featPath, context);\n // A feature contract that EXISTS but does not read is a broken document,\n // not an empty one — openapi.invalid's discipline (../declared.ts),\n // applied to the event axis: the rest of this service's event checks are\n // suspended, because every one of them would be an opinion about a\n // document nobody could open.\n if (featDoc.unreadable) {\n unreadable.add(svc);\n issues.push({\n severity: \"error\",\n code: \"asyncapi.invalid\",\n subject: svc,\n message: `${svc}: this feature's asyncapi.yaml does not read as AsyncAPI 3.0${featDoc.error === undefined ? \"\" : ` (${featDoc.error})`} — the event axis is unchecked and the merge would have nothing true to write. Fix the document first.`,\n });\n continue;\n }\n featureDocs.set(svc, featDoc);\n\n // Both sides' texts, trees and slots, read the one way rebase's\n // standalone revival read reads them too (../retired/event-pair.ts owns\n // the guards: absent living is an answer, unreadable living is not).\n const read = await readEventPair({ featPath, featDoc, livingPath, livingDoc, service: svc }, context);\n const { featSlots, livingText } = read;\n revivalCandidates.set(svc, eventArrivals(read));\n // The pair's one simulation, which the revival candidates above have\n // already asked: absent living included, where it is the create branch.\n const sends = read.merged()?.doc.sent;\n if (sends !== undefined) sentAfter.set(svc, sends);\n\n if (livingText !== undefined) {\n gradeBaselines({ ...read, svc, featureId }, issues);\n gradeMarkers(read, svc, issues);\n // The pair's one simulation — the revival candidates above have already\n // asked it, comparing it with the living contract.\n const merged = read.merged()?.doc;\n if (merged !== undefined) {\n mergedDocs.set(svc, merged);\n stoppedSending.set(svc, livingDoc.sent.filter((m) => !merged.sent.includes(m)));\n }\n }\n\n // The raw marker sets and the NET one (minus relocations) —\n // ../retired/retirement.ts owns the rule and says why each set exists;\n // the retired index reads every archived feature through the same\n // function, against that feature's snapshot pre-image instead of the\n // living contract on disk.\n const { markerNames, inlineRetired, redeclared, net } = messageRetirements(featDoc, featSlots, livingDoc);\n netRemoved.set(svc, net);\n\n for (const name of markerNames) {\n if (redeclared.has(name) || justifiedMsgs.has(name)) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-marker-unjustified\",\n subject: svc,\n message: `${svc}: removal marker for message '${name}' is not justified by a REMOVED requirement's Publishes:/Consumes: line`,\n });\n }\n for (const m of justifiedMsgs) {\n // Owed only while the living contract still declares the message —\n // and satisfied by ANY marker taking it out, relocation included: a\n // message-slot marker naming it, or a channel-slot removal whose\n // living inline interior declares it (the SCHEMA-sanctioned way to\n // retire an inline declaration). And owed only while the MERGE would\n // still declare it: a slot re-keyed to another name takes the message\n // out with no marker, and none could be written there — the key holds\n // the re-keyed declaration — so the route `asyncapi.remove-message-\n // consumed` names (REMOVE the requirement whose Publishes: line names\n // it) ended in this error, `openapi.remove-marker-missing`'s deadlock\n // one axis over.\n if (markerNames.has(m) || inlineRetired.has(m)) continue;\n if (livingDoc.unreadable || !livingDoc.messages.some((msg) => msg.name === m)) continue;\n const after = mergedDocs.get(svc);\n if (after !== undefined && !after.messages.some((msg) => msg.name === m)) continue;\n issues.push({\n severity: \"error\",\n code: \"asyncapi.remove-marker-missing\",\n subject: svc,\n message: `${svc}: a REMOVED requirement's Publishes:/Consumes: line names '${m}', which the living asyncapi.yaml still declares, but this feature's asyncapi.yaml has no matching x-loam-remove: true marker`,\n });\n }\n }\n return { featureDocs, mergedDocs, netRemoved, stoppedSending, sentAfter, links, unreadable, revivalCandidates };\n}\n"]}
|
|
@@ -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
|
|
@@ -22,11 +23,92 @@ import { enumeratedServiceIndex } from "../../repo/service-target.js";
|
|
|
22
23
|
import { readAsyncapi } from "../../asyncapi/read.js";
|
|
23
24
|
import { declaredEvents } from "./declared.js";
|
|
24
25
|
import { eventLookups } from "./lookups.js";
|
|
26
|
+
import { revivedEvents } from "../retired/revival/revived.js";
|
|
27
|
+
import { nothingStill, refusalDetails, refusalMessage, stillNamed } from "../consumed/refusal.js";
|
|
25
28
|
export async function eventCoherence(request) {
|
|
26
|
-
const { scope, svcNames, taggedRels, svcOf, context } = request;
|
|
29
|
+
const { scope, svcNames, taggedRels, svcOf, context, consumers } = request;
|
|
27
30
|
const issues = [];
|
|
28
31
|
const declared = await declaredEvents(scope, svcNames, issues, context);
|
|
29
32
|
const lookups = eventLookups({ docsDir: scope.docsDir, featureId: scope.featureId }, context);
|
|
33
|
+
// Who would still name a message after this merge: the consumer index's
|
|
34
|
+
// consumers, and its `own` — the provider's own `Publishes:` lines as the
|
|
35
|
+
// merge leaves them. That is the event twin of the overwrite's own-requirement
|
|
36
|
+
// scan: a message re-keyed or no longer sent while the provider's living
|
|
37
|
+
// requirement still says it `Publishes:` it archived at exit 0, and the next
|
|
38
|
+
// `validate --all` reported `spec-event.message-undefined` against the
|
|
39
|
+
// provider (review 2026-10-12, finding 3) — a REMOVED requirement justifies a
|
|
40
|
+
// marker, but nothing asks a re-key or a turned-around operation for one.
|
|
41
|
+
// A consumer document nobody could read or place refuses too, as on the
|
|
42
|
+
// contract axis (../consumed/refusal.ts).
|
|
43
|
+
//
|
|
44
|
+
// But a CONSUMER is stranded only when nothing would send the message after
|
|
45
|
+
// this merge — no service, and no #external element whose edge publishes it.
|
|
46
|
+
// That is the rule `spine.message-unproduced` grades a consumer by — it stays
|
|
47
|
+
// quiet over an outside producer (`externalProducerOf`,
|
|
48
|
+
// core/c4/resolve/external.ts) — and the one `loam diff` asks first
|
|
49
|
+
// (`producers`, then `externalProducer`, core/diff/semantic.ts).
|
|
50
|
+
// Counting every consumer "whoever its producer is" refused a retirement from
|
|
51
|
+
// one of two senders over the other sender's own consumes edge, and told the
|
|
52
|
+
// author to drop it, while `loam diff` called the same removal a warning
|
|
53
|
+
// ("still sent by …") and the next `validate --all` was clean (the
|
|
54
|
+
// verification of the consumer fixes, N2). Counting the fleet's senders alone
|
|
55
|
+
// then refused a retirement from a message's only fleet sender while an
|
|
56
|
+
// #external element still published it, and `loam diff` called the archived
|
|
57
|
+
// removal `diff.message-removed-consumed`, while the next `validate --all` was
|
|
58
|
+
// clean again (the verification of N1–N3, D1). The provider's OWN documents are
|
|
59
|
+
// another matter: the spine and validate grade them against the provider's
|
|
60
|
+
// own contract, so they strand whoever else sends the message, and `own`
|
|
61
|
+
// still refuses beside a surviving sender.
|
|
62
|
+
//
|
|
63
|
+
// The senders are asked of the MERGE, never of the living fleet alone:
|
|
64
|
+
// `producers` reads the fleet as it stands, so a feature retiring the message
|
|
65
|
+
// from both senders would let each one pass on the other's living contract.
|
|
66
|
+
// A service this feature carries an event delta for sends what the
|
|
67
|
+
// simulation leaves it (`declared.sentAfter`), or vouches for nothing; every
|
|
68
|
+
// other service sends what its living contract does. Positive evidence only,
|
|
69
|
+
// as `producers` is: an unreadable contract, or a merge that stood down,
|
|
70
|
+
// never keeps a message produced. The outside producer is the living map's
|
|
71
|
+
// edge and tag, which the landscape merge cannot take away (it only adds to
|
|
72
|
+
// the map), asked over the directories the merge leaves: each `specs/<svc>/`
|
|
73
|
+
// of this feature becomes a `services/<svc>/`, and a directory the #external
|
|
74
|
+
// element resolves to outranks its tag, so a feature onboarding the outside
|
|
75
|
+
// system as a service takes the producer away. Asked over the directories
|
|
76
|
+
// as they stood, the gate passed that retirement while the next `loam diff`
|
|
77
|
+
// reported `diff.message-removed-consumed` and `validate --all`
|
|
78
|
+
// `spine.message-unproduced` (the verification of D1, X2). One this
|
|
79
|
+
// feature's own C4 delta would draw is not counted, so that shape errs
|
|
80
|
+
// toward refusing — `--approve`, or draw the edge in the living map.
|
|
81
|
+
const touched = new Set([...declared.featureDocs.keys(), ...declared.unreadable]);
|
|
82
|
+
const sendersAfter = async (svc, message) => {
|
|
83
|
+
const living = (await consumers.producers(message)).filter((id) => !touched.has(id));
|
|
84
|
+
const merged = [...declared.sentAfter].filter(([, sent]) => sent.includes(message)).map(([id]) => id);
|
|
85
|
+
const fleet = [...new Set([...living, ...merged])].filter((id) => id !== svc).sort();
|
|
86
|
+
const outside = await consumers.externalProducer(message);
|
|
87
|
+
return outside === null ? fleet : [...fleet, `${outside} (#external)`];
|
|
88
|
+
};
|
|
89
|
+
const still = async (svc, message) => {
|
|
90
|
+
const answer = await consumers.messageConsumers(svc, message);
|
|
91
|
+
const senders = await sendersAfter(svc, message);
|
|
92
|
+
return { senders, found: stillNamed(senders.length === 0 ? answer : { ...answer, victims: [], suspended: [] }) };
|
|
93
|
+
};
|
|
94
|
+
// The landscape merge only ADDS edges, so an edge is dropped in the living map
|
|
95
|
+
// itself — a consumer's consumes edge, or the provider's own publishes edge,
|
|
96
|
+
// which the spine grades on its source (`own`, core/consumers/consumers.ts).
|
|
97
|
+
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`;
|
|
98
|
+
// With a sender left only the provider's own documents are named, so the
|
|
99
|
+
// remedy is theirs alone — telling the author to drop a consumer's edge
|
|
100
|
+
// there is the false route this gate used to give.
|
|
101
|
+
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`;
|
|
102
|
+
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`;
|
|
103
|
+
// The route a consumer's refusal gains: another sender keeps the message
|
|
104
|
+
// produced, and one this feature adds counts (`declared.sentAfter`); so does
|
|
105
|
+
// the #external element that publishes it, drawn in the living map — the one
|
|
106
|
+
// place `sendersAfter` reads an outside producer from — unless this feature
|
|
107
|
+
// makes it a service. The route says so: a feature onboarding the gateway is
|
|
108
|
+
// told no #external element publishes the message while its living map
|
|
109
|
+
// draws one doing exactly that, and the way out is the first route, the new
|
|
110
|
+
// service's own send operation in its specs/<service>/ delta.
|
|
111
|
+
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
112
|
// Retiring a message the fleet still consumes — openapi.remove-op-consumed's
|
|
31
113
|
// twin, and like it a question neither exactness nor justification asks:
|
|
32
114
|
// the merge deletes the declaration while the consumes edge and the other
|
|
@@ -34,14 +116,23 @@ export async function eventCoherence(request) {
|
|
|
34
116
|
// repository whose author was never in this feature.
|
|
35
117
|
for (const [svc, net] of declared.netRemoved) {
|
|
36
118
|
for (const message of net) {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
119
|
+
const { found, senders } = await still(svc, message);
|
|
120
|
+
if (nothingStill(found))
|
|
39
121
|
continue;
|
|
122
|
+
const lost = `this feature retires message '${message}'`;
|
|
40
123
|
issues.push({
|
|
41
124
|
severity: "error",
|
|
42
125
|
code: "asyncapi.remove-message-consumed",
|
|
43
126
|
subject: svc,
|
|
44
|
-
|
|
127
|
+
details: refusalDetails(found),
|
|
128
|
+
message: refusalMessage(found, {
|
|
129
|
+
subject: svc,
|
|
130
|
+
lost,
|
|
131
|
+
verb: "name",
|
|
132
|
+
named: (list) => senders.length === 0
|
|
133
|
+
? `${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.`
|
|
134
|
+
: `${svc}: ${lost}; ${ownOnly(svc, senders)} — ${list}. Retire each in the same change (${ownRoute(svc)}), or archive with --approve to break it deliberately.`,
|
|
135
|
+
}),
|
|
45
136
|
});
|
|
46
137
|
}
|
|
47
138
|
}
|
|
@@ -56,17 +147,37 @@ export async function eventCoherence(request) {
|
|
|
56
147
|
for (const message of stopped) {
|
|
57
148
|
if (net?.has(message) === true)
|
|
58
149
|
continue;
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
150
|
+
const { found, senders } = await still(svc, message);
|
|
151
|
+
if (nothingStill(found))
|
|
61
152
|
continue;
|
|
153
|
+
const lost = `after this feature's merge, ${svc}'s asyncapi.yaml would no longer declare an action: send operation for '${message}'`;
|
|
62
154
|
issues.push({
|
|
63
155
|
severity: "error",
|
|
64
156
|
code: "asyncapi.remove-message-consumed",
|
|
65
157
|
subject: svc,
|
|
66
|
-
|
|
158
|
+
details: refusalDetails(found),
|
|
159
|
+
message: refusalMessage(found, {
|
|
160
|
+
subject: svc,
|
|
161
|
+
lost,
|
|
162
|
+
verb: "name",
|
|
163
|
+
named: (list) => senders.length === 0
|
|
164
|
+
? `${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.`
|
|
165
|
+
: `${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.`,
|
|
166
|
+
}),
|
|
67
167
|
});
|
|
68
168
|
}
|
|
69
169
|
}
|
|
170
|
+
// A revival: a slot or message this delta declares that the living
|
|
171
|
+
// contract no longer has because an ARCHIVED feature retired it, or a
|
|
172
|
+
// message name declared again at a key an archived feature took it out of
|
|
173
|
+
// — the contract axis's `openapi.op-revived`, on the slot and the message
|
|
174
|
+
// name (../retired/revival/revived.ts). Asked of the index only when the merged
|
|
175
|
+
// contract declares something the living one does not.
|
|
176
|
+
for (const [svc, arrived] of declared.revivalCandidates) {
|
|
177
|
+
if (arrived.slots.length === 0 && arrived.messages.length === 0)
|
|
178
|
+
continue;
|
|
179
|
+
issues.push(...revivedEvents(svc, arrived, await request.retired()));
|
|
180
|
+
}
|
|
70
181
|
// Two features declaring one (service, message): both deltas apply
|
|
71
182
|
// cleanly, so whichever archives second replaces the other's declaration
|
|
72
183
|
// wholesale — delta.added-conflict's shape on the message join.
|
|
@@ -1 +1 @@
|
|
|
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 * 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 { 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\";\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\";\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 /** 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}\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.\n const touched = new Set<string>([...declared.featureDocs.keys(), ...declared.unreadable]);\n const sendersAfter = async (svc: string, message: string): Promise<string[]> => {\n const living = (await consumers.producers(message)).filter((id) => !touched.has(id));\n const merged = [...declared.sentAfter].filter(([, sent]) => sent.includes(message)).map(([id]) => id);\n const fleet = [...new Set([...living, ...merged])].filter((id) => id !== svc).sort();\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.\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"]}
|