@ybotok/loam 0.2.0-alpha.1 → 0.2.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3448 -0
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +47 -23
- package/QUICKSTART.md +189 -12
- package/README.md +274 -65
- package/ROADMAP.md +572 -31
- package/SCHEMA.md +1709 -155
- package/WORKFLOW.md +298 -72
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/adopt/render.js +50 -9
- package/dist/commands/adopt/render.js.map +1 -1
- package/dist/commands/archive/archive.js +16 -19
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/closing.js +42 -0
- package/dist/commands/archive/closing.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +77 -43
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/document/head.js +151 -0
- package/dist/commands/archive/plan/document/head.js.map +1 -0
- package/dist/commands/archive/plan/document/links.js +284 -0
- package/dist/commands/archive/plan/document/links.js.map +1 -0
- package/dist/commands/archive/plan/document/run.js +136 -0
- package/dist/commands/archive/plan/document/run.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +21 -19
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +167 -30
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/model/extending.js +85 -0
- package/dist/commands/archive/plan/model/extending.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +14 -19
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +137 -56
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +3 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +134 -30
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +85 -18
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +29 -7
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +102 -14
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +17 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/doctor.js +60 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +89 -13
- package/dist/commands/explain/explain.js.map +1 -1
- package/dist/commands/explore.js +16 -10
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +5 -7
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +44 -29
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +52 -9
- package/dist/commands/list/print.js.map +1 -1
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +37 -34
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +55 -26
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/plan.js +158 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +13 -11
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/feature.js +9 -5
- package/dist/commands/show/feature.js.map +1 -1
- package/dist/commands/show/introduced.js +157 -0
- package/dist/commands/show/introduced.js.map +1 -0
- package/dist/commands/show/marks.js +35 -6
- package/dist/commands/show/marks.js.map +1 -1
- package/dist/commands/show/service.js +41 -33
- package/dist/commands/show/service.js.map +1 -1
- package/dist/commands/show/show.js +17 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/fleet-next.js +90 -0
- package/dist/commands/status/fleet-next.js.map +1 -0
- package/dist/commands/status/json.js +2 -0
- package/dist/commands/status/json.js.map +1 -1
- package/dist/commands/status/print.js +89 -37
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +103 -23
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +7 -7
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +106 -46
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +289 -74
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/exclude.js +132 -0
- package/dist/commands/subsystem/txn/exclude.js.map +1 -0
- package/dist/commands/subsystem/txn/links.js +128 -0
- package/dist/commands/subsystem/txn/links.js.map +1 -0
- package/dist/commands/subsystem/txn/report.js +174 -0
- package/dist/commands/subsystem/txn/report.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +147 -45
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +200 -21
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/actions.js +107 -0
- package/dist/commands/tasks/actions.js.map +1 -0
- package/dist/commands/tasks/render.js +253 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +119 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +42 -26
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +83 -33
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +78 -27
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +34 -126
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/health-deps.js +9 -6
- package/dist/commands/validate/checks/health-deps.js.map +1 -1
- package/dist/commands/validate/checks/introduced.js +102 -0
- package/dist/commands/validate/checks/introduced.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +11 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/coverage/covers-after.js +105 -0
- package/dist/commands/validate/coverage/covers-after.js.map +1 -0
- package/dist/commands/validate/feature.js +26 -20
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +37 -13
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +144 -126
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/load.js +66 -0
- package/dist/commands/validate/fleet/load.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +98 -0
- package/dist/commands/validate/fleet/map/attest.js.map +1 -0
- package/dist/commands/validate/fleet/map/bindings.js +107 -0
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
- package/dist/commands/validate/fleet/map/consumers.js +244 -0
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
- package/dist/commands/validate/fleet/map/isolation.js +116 -0
- package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
- package/dist/commands/validate/fleet/map/usecases.js +54 -0
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +44 -14
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +10 -2
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +50 -15
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/projects.js +278 -0
- package/dist/commands/validate/fleet/views/projects.js.map +1 -0
- package/dist/commands/validate/fleet/views/root-documents.js +83 -0
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +40 -47
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/render/repeats.js +63 -0
- package/dist/commands/validate/render/repeats.js.map +1 -0
- package/dist/commands/validate/render/rollups.js +115 -0
- package/dist/commands/validate/render/rollups.js.map +1 -0
- package/dist/commands/validate/render/text.js +199 -0
- package/dist/commands/validate/render/text.js.map +1 -0
- package/dist/commands/validate/report.js +36 -160
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +21 -107
- package/dist/commands/validate/service/api.js.map +1 -1
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +36 -14
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/model/diverged.js +226 -0
- package/dist/commands/validate/service/model/diverged.js.map +1 -0
- package/dist/commands/validate/service/model/grade.js +163 -0
- package/dist/commands/validate/service/model/grade.js.map +1 -0
- package/dist/commands/validate/service/service.js +121 -61
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +88 -28
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +38 -3
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/tags.js +113 -0
- package/dist/commands/validate/service/usecases/tags.js.map +1 -0
- package/dist/commands/validate/service/usecases/usecases.js +209 -0
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/validate.js +85 -60
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +55 -52
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +81 -106
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +21 -9
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +24 -8
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +80 -8
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +88 -10
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +49 -22
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +39 -6
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/protocol.js +1 -1
- package/dist/core/agent/protocol.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/support/report.js +46 -5
- package/dist/core/agent/support/report.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +158 -11
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +78 -39
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +88 -27
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +128 -13
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +96 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +106 -4
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +6 -3
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +68 -27
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +32 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +33 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +12 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +96 -36
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/checks.js +138 -3
- package/dist/core/brief/checks.js.map +1 -1
- package/dist/core/brief/landscape.js +209 -130
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +84 -0
- package/dist/core/brief/map/extend.js.map +1 -0
- package/dist/core/brief/map/owed.js +266 -0
- package/dist/core/brief/map/owed.js.map +1 -0
- package/dist/core/brief/targets.js +109 -31
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +14 -3
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/arch.js +90 -182
- package/dist/core/c4/arch.js.map +1 -1
- package/dist/core/c4/direction/direction.js +114 -0
- package/dist/core/c4/direction/direction.js.map +1 -0
- package/dist/core/c4/likec4.js +169 -14
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +160 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/parsed/view-ids.js +39 -1
- package/dist/core/c4/parsed/view-ids.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +134 -0
- package/dist/core/c4/prefetch/fleet.js.map +1 -0
- package/dist/core/c4/project/architecture.js +123 -7
- package/dist/core/c4/project/architecture.js.map +1 -1
- package/dist/core/c4/project/batch.js +140 -0
- package/dist/core/c4/project/batch.js.map +1 -0
- package/dist/core/c4/project/documents.js +43 -12
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +79 -43
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +277 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/attested.js +0 -0
- package/dist/core/c4/resolve/attested.js.map +1 -0
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +78 -17
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/resolve/steps.js +290 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/resolve/systems.js +154 -0
- package/dist/core/c4/resolve/systems.js.map +1 -0
- package/dist/core/c4/root-project/exclude.js +352 -0
- package/dist/core/c4/root-project/exclude.js.map +1 -0
- package/dist/core/c4/seed/template.js +65 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/created/models.js +96 -0
- package/dist/core/c4/service-model/created/models.js.map +1 -0
- package/dist/core/c4/service-model/documents.js +47 -0
- package/dist/core/c4/service-model/documents.js.map +1 -0
- package/dist/core/c4/service-model/fleet/extending.js +82 -0
- package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
- package/dist/core/c4/service-model/load.js +165 -0
- package/dist/core/c4/service-model/load.js.map +1 -0
- package/dist/core/c4/service-model/renderer.js +128 -0
- package/dist/core/c4/service-model/renderer.js.map +1 -0
- package/dist/core/c4/service-model/shape.js +106 -0
- package/dist/core/c4/service-model/shape.js.map +1 -0
- package/dist/core/c4/service-model/slice.js +188 -0
- package/dist/core/c4/service-model/slice.js.map +1 -0
- package/dist/core/c4/source-mask.js +297 -10
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +222 -141
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +79 -13
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/contract.js +20 -0
- package/dist/core/c4/splice/contract.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +20 -20
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/identity/declared.js +87 -0
- package/dist/core/c4/splice/identity/declared.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +37 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -1
- package/dist/core/c4/splice/identity/existing.js +80 -0
- package/dist/core/c4/splice/identity/existing.js.map +1 -0
- package/dist/core/c4/splice/identity/interior.js +80 -0
- package/dist/core/c4/splice/identity/interior.js.map +1 -0
- package/dist/core/c4/splice/identity/owner.js +50 -0
- package/dist/core/c4/splice/identity/owner.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +182 -114
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +151 -0
- package/dist/core/c4/splice/model/merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +160 -56
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/splice/text/spot.js +131 -0
- package/dist/core/c4/splice/text/spot.js.map +1 -0
- package/dist/core/c4/workspace.js +13 -19
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/delta/uncovered.js +2 -2
- package/dist/core/capabilities/delta/uncovered.js.map +1 -1
- package/dist/core/capabilities/findings.js +18 -22
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/calls/called.js +52 -0
- package/dist/core/coherence/calls/called.js.map +1 -0
- package/dist/core/coherence/coherence.js +103 -22
- package/dist/core/coherence/coherence.js.map +1 -1
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/covers/health.js +85 -0
- package/dist/core/coherence/covers/health.js.map +1 -0
- package/dist/core/coherence/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +147 -16
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +28 -87
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/events/produced.js +100 -0
- package/dist/core/coherence/events/produced.js.map +1 -0
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/delta.js +51 -6
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/dependencies/fanin.js +2 -2
- package/dist/core/dependencies/fanin.js.map +1 -1
- package/dist/core/deployment/delta.js +191 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +49 -21
- package/dist/core/diff/base-state.js.map +1 -1
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/docs.js +31 -22
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/config.js +7 -3
- package/dist/core/doctor/config.js.map +1 -1
- package/dist/core/doctor/doctor.js +91 -11
- package/dist/core/doctor/doctor.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/doctor/reports/scan.js +242 -0
- package/dist/core/doctor/reports/scan.js.map +1 -0
- package/dist/core/document/apply.js +158 -11
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +26 -21
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/lookup.js +14 -1
- package/dist/core/explain/lookup.js.map +1 -1
- package/dist/core/explain/refusals.js +5 -5
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms/documents.js +227 -0
- package/dist/core/explain/terms/documents.js.map +1 -0
- package/dist/core/explain/terms/done-check.js +144 -0
- package/dist/core/explain/terms/done-check.js.map +1 -0
- package/dist/core/explain/terms/entry.js +14 -0
- package/dist/core/explain/terms/entry.js.map +1 -0
- package/dist/core/explain/terms/stages.js +71 -0
- package/dist/core/explain/terms/stages.js.map +1 -0
- package/dist/core/explain/terms/tasks.js +97 -0
- package/dist/core/explain/terms/tasks.js.map +1 -0
- package/dist/core/explain/terms.js +68 -109
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +40 -10
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +81 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +11 -4
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +69 -28
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/delta.js +10 -7
- package/dist/core/glossary/delta.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
- package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
- package/dist/core/kernel/path-safety.js +38 -0
- package/dist/core/kernel/path-safety.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +51 -37
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/links/findings.js.map +1 -1
- package/dist/core/links/parse.js +38 -9
- package/dist/core/links/parse.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +40 -10
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +301 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +28 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +17 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +43 -4
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/pack/pack.js +50 -6
- package/dist/core/pack/pack.js.map +1 -1
- package/dist/core/projection/arch-slice.js +2 -2
- package/dist/core/projection/arch-slice.js.map +1 -1
- package/dist/core/projection/introduced.js +14 -0
- package/dist/core/projection/introduced.js.map +1 -0
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/authored/paths.js +18 -0
- package/dist/core/repo/authored/paths.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/paths.js +92 -6
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/repo.js +74 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/render/body.js +258 -0
- package/dist/core/repo/tree/render/body.js.map +1 -0
- package/dist/core/repo/tree/render/projects.js +182 -0
- package/dist/core/repo/tree/render/projects.js.map +1 -0
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +42 -0
- package/dist/core/repo/tree/render/stale.js.map +1 -0
- package/dist/core/repo/tree/render/text.js +84 -0
- package/dist/core/repo/tree/render/text.js.map +1 -0
- package/dist/core/repo/tree/render/views.js +293 -0
- package/dist/core/repo/tree/render/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +53 -32
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +121 -13
- package/dist/core/scaffold/landscape.js.map +1 -1
- package/dist/core/scaffold/legacy.js +271 -0
- package/dist/core/scaffold/legacy.js.map +1 -0
- package/dist/core/staging/commit.js +26 -15
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +84 -6
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +188 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +24 -14
- package/dist/core/staging/txn/forward.js.map +1 -1
- package/dist/core/staging/writes.js.map +1 -1
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/contracts.js +39 -15
- package/dist/core/status/contracts.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +33 -8
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +24 -17
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/feature/next.js +20 -7
- package/dist/core/status/feature/next.js.map +1 -1
- package/dist/core/status/feature/words.js +96 -0
- package/dist/core/status/feature/words.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +43 -14
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +153 -58
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/scan.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +199 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/claims.js +31 -7
- package/dist/core/usecases/delta/claims.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/delta/modified.js +315 -0
- package/dist/core/usecases/delta/modified.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +32 -191
- package/dist/core/usecases/delta/overlay.js.map +1 -1
- package/dist/core/usecases/fleet.js +195 -36
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
- package/dist/core/usecases/grade/capability-tag.js.map +1 -0
- package/dist/core/usecases/grade/feature.js +88 -0
- package/dist/core/usecases/grade/feature.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
- package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
- package/dist/core/usecases/grade/steps.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
- package/dist/core/usecases/grade/usecases.js.map +1 -0
- package/dist/core/usecases/operations.js +26 -14
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/place.js.map +1 -1
- package/dist/core/usecases/service/exclude.js +119 -0
- package/dist/core/usecases/service/exclude.js.map +1 -0
- package/dist/core/usecases/service/flows.js +164 -0
- package/dist/core/usecases/service/flows.js.map +1 -0
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +70 -12
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +4 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +75 -4
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/dist/core/vocabulary/report.js +16 -1
- package/dist/core/vocabulary/report.js.map +1 -1
- package/examples/README.md +54 -11
- package/examples/docs/architecture/landscape.likec4 +62 -6
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +7 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/likec4.config.json +0 -1
- package/examples/docs/services/checkout-web/model.likec4 +39 -56
- package/examples/docs/services/order-service/arch.spec.md +13 -6
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/order-service/model.likec4 +58 -70
- package/examples/docs/services/payment-service/arch.spec.md +7 -4
- package/examples/docs/services/payment-service/model.likec4 +38 -37
- package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/examples/docs/services/platform/notification-service/health.yaml +8 -3
- package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -31
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -220
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -165
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -247
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/repo/tree/views.js +0 -172
- package/dist/core/repo/tree/views.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What a feature's own delta DECLARES, per service, indexed once.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Nine indexes over the same four files (the delta spec.md, arch.spec.md,
|
|
5
5
|
* openapi.yaml, and the living contract they are graded against), built in one
|
|
6
6
|
* walk because every check downstream joins on the service id and would
|
|
7
7
|
* otherwise re-read the delta per question. The distinctions here are the ones
|
|
8
8
|
* the checks cannot make for themselves: an operation a REMOVED requirement
|
|
9
9
|
* governs is being retired, so it neither claims the contract nor governs
|
|
10
10
|
* anything after the merge; a removal marker that names an id also defined
|
|
11
|
-
* elsewhere in the delta is a RELOCATION, not a retirement;
|
|
11
|
+
* elsewhere in the delta is a RELOCATION, not a retirement; a living operation
|
|
12
|
+
* another one replaces at its address, with no marker naming it, leaves the
|
|
13
|
+
* contract all the same; and a marker with
|
|
12
14
|
* no `operationId` at all is a shape only this walk can see. The capability
|
|
13
15
|
* gate rides the same loop: both delta spec documents are graded against the
|
|
14
16
|
* fleet vocabulary here, because coherence.ts and this package's file count
|
|
15
17
|
* are both at their limits and the walk already holds the parsed documents.
|
|
16
18
|
*/
|
|
17
19
|
import { existsSync } from "node:fs";
|
|
18
|
-
import {
|
|
20
|
+
import { readOr } from "../openapi/contract-text.js";
|
|
19
21
|
import { readFile } from "node:fs/promises";
|
|
20
22
|
import {} from "../kernel/ids/service.js";
|
|
21
23
|
import { featureSpecPaths } from "../repo/paths.js";
|
|
@@ -29,6 +31,8 @@ import { realizesUnknownIssues } from "../capabilities/realizes/findings.js";
|
|
|
29
31
|
import { capabilityUnknownIssues } from "../capabilities/findings.js";
|
|
30
32
|
import { openapiBaselineIssues } from "../openapi/baseline/gate.js";
|
|
31
33
|
import { readOpenapi } from "../openapi/doc.js";
|
|
34
|
+
import { operationRetirements } from "./retired/retirement.js";
|
|
35
|
+
import { operationArrivals } from "./retired/revival/arrivals.js";
|
|
32
36
|
export async function declaredByService(scope, svcNames, issues, context) {
|
|
33
37
|
const { docsDir, featureDir, featureId } = scope;
|
|
34
38
|
const reqOps = new Map();
|
|
@@ -41,6 +45,10 @@ export async function declaredByService(scope, svcNames, issues, context) {
|
|
|
41
45
|
const anonymousMarkers = new Map();
|
|
42
46
|
const featureApiOps = new Set();
|
|
43
47
|
const unreadableApis = new Set();
|
|
48
|
+
const revivalCandidates = new Map();
|
|
49
|
+
const displacedOps = new Map();
|
|
50
|
+
/** Per service read here: the operationIds its living contract holds, or null when it does not read. */
|
|
51
|
+
const livingIds = new Map();
|
|
44
52
|
// The capability gate's vocabulary, once per call. Absent or invalid stays
|
|
45
53
|
// SILENT here — capability.invalid is validate --all's one finding about a
|
|
46
54
|
// broken file, and an absent file means the fleet never opted in — so with
|
|
@@ -108,8 +116,12 @@ export async function declaredByService(scope, svcNames, issues, context) {
|
|
|
108
116
|
issues.push(...capabilityUnknownIssues(archReqs, archTarget, capabilityVocab));
|
|
109
117
|
issues.push(...realizesUnknownIssues(archReqs, archTarget, await capabilityReqs()));
|
|
110
118
|
}
|
|
111
|
-
// Only operations
|
|
112
|
-
//
|
|
119
|
+
// Only operations the living contract does not hold count as feature-added:
|
|
120
|
+
// a delta may restate living operations it does not change (a quote), and
|
|
121
|
+
// a quote adds nothing. "Not in living" is not quite "new", though — an
|
|
122
|
+
// operation an archived feature RETIRED is absent too, and adding it back
|
|
123
|
+
// is `openapi.op-revived`'s question (./retired/revival/revived.ts), asked of the
|
|
124
|
+
// `revivalCandidates` collected below.
|
|
113
125
|
const featDoc = await readOpenapi(paths.openapi, context);
|
|
114
126
|
// A feature contract that EXISTS but does not read is a broken document,
|
|
115
127
|
// not an empty one — the living-side `openapi.invalid` discipline
|
|
@@ -131,15 +143,10 @@ export async function declaredByService(scope, svcNames, issues, context) {
|
|
|
131
143
|
}
|
|
132
144
|
const featOps = featDoc.ops;
|
|
133
145
|
const removals = featOps.filter((op) => op.remove);
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
// Conflating them made moving an endpoint fail three checks at once for a
|
|
139
|
-
// change that removes nothing.
|
|
140
|
-
const redefined = new Set(featOps.filter((op) => !op.remove).map((op) => op.id));
|
|
141
|
-
const markerIds = new Set(removals.map((op) => op.id));
|
|
142
|
-
const netRemoved = new Set([...markerIds].filter((id) => !redefined.has(id)));
|
|
146
|
+
// The raw marker set and the NET one (markers minus relocations) —
|
|
147
|
+
// ./retired/retirement.ts owns the rule and says why the two differ; the
|
|
148
|
+
// retired index reads every archived feature through the same function.
|
|
149
|
+
const { markerIds, net: netRemoved } = operationRetirements(featOps);
|
|
143
150
|
removingOps.set(svc, netRemoved);
|
|
144
151
|
allMarkerIds.set(svc, markerIds);
|
|
145
152
|
anonymousMarkers.set(svc, featDoc.anonymousRemovals.length > 0);
|
|
@@ -155,8 +162,15 @@ export async function declaredByService(scope, svcNames, issues, context) {
|
|
|
155
162
|
message: `${svc}: ${marker.method} ${marker.path} carries x-loam-remove: true but declares no operationId — loam cannot tell which operation it retires; name the operation the living contract has at that slot`,
|
|
156
163
|
});
|
|
157
164
|
}
|
|
158
|
-
const
|
|
165
|
+
const livingOpenapiPath = (await locateServicePaths(docsDir, svc, context)).openapi;
|
|
166
|
+
const livingDoc = await readOpenapi(livingOpenapiPath, context);
|
|
159
167
|
const livingOps = livingDoc.ops;
|
|
168
|
+
livingIds.set(svc, livingDoc.unreadable ? null : new Set(livingOps.filter((op) => !op.remove).map((op) => op.id)));
|
|
169
|
+
const sides = { featPath: paths.openapi, featDoc, livingPath: livingOpenapiPath, livingDoc, service: svc };
|
|
170
|
+
const moved = await operationArrivals(sides, context);
|
|
171
|
+
revivalCandidates.set(svc, moved.operations);
|
|
172
|
+
if (moved.displaced.length > 0)
|
|
173
|
+
displacedOps.set(svc, moved.displaced);
|
|
160
174
|
for (const id of livingDoc.duplicateIds) {
|
|
161
175
|
const slots = livingOps.filter((op) => op.id === id).map((op) => `${op.method} ${op.path}`);
|
|
162
176
|
issues.push({
|
|
@@ -172,27 +186,14 @@ export async function declaredByService(scope, svcNames, issues, context) {
|
|
|
172
186
|
// read when there is one, mirroring the readRequirements pattern above;
|
|
173
187
|
// the gate itself never touches the filesystem.
|
|
174
188
|
if (existsSync(paths.openapi)) {
|
|
175
|
-
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
// flag the document unreadable. Uncaught, one service's UTF-16 living
|
|
179
|
-
// contract took down the whole feature target under validate's
|
|
180
|
-
// context while archive's no-context path sailed past — the exact
|
|
189
|
+
// `readOr` (../openapi/contract-text.ts, which says why): `context.readText`
|
|
190
|
+
// throws on non-UTF-8 bytes where the no-context `readFile` lets
|
|
191
|
+
// `readOpenapi` flag the document unreadable — the exact
|
|
181
192
|
// context/no-context divergence doc.ts documents as paid for once.
|
|
182
193
|
// Either text failing to decode gets the same answer the gate gives
|
|
183
194
|
// an unreadable parse: that side's surface checks are skipped.
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
return context === undefined ? await readFile(p, "utf8") : await context.readText(p);
|
|
187
|
-
}
|
|
188
|
-
catch (err) {
|
|
189
|
-
if (err instanceof NotUtf8DocumentError)
|
|
190
|
-
return undefined;
|
|
191
|
-
throw err;
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
const featureText = await readOr(paths.openapi);
|
|
195
|
-
const livingText = !existsSync(livingOpenapiPath) ? undefined : await readOr(livingOpenapiPath);
|
|
195
|
+
const featureText = await readOr(paths.openapi, context);
|
|
196
|
+
const livingText = !existsSync(livingOpenapiPath) ? undefined : await readOr(livingOpenapiPath, context);
|
|
196
197
|
if (featureText !== undefined) {
|
|
197
198
|
issues.push(...openapiBaselineIssues({ featDoc, livingDoc, featureText, livingText, service: svc, featureId }));
|
|
198
199
|
}
|
|
@@ -246,9 +247,31 @@ export async function declaredByService(scope, svcNames, issues, context) {
|
|
|
246
247
|
if (unreadableApis.has(svc))
|
|
247
248
|
continue;
|
|
248
249
|
const marked = allMarkerIds.get(svc) ?? new Set();
|
|
250
|
+
const replaced = new Set((displacedOps.get(svc) ?? []).map((d) => d.id));
|
|
251
|
+
const living = livingIds.get(svc);
|
|
249
252
|
for (const op of required) {
|
|
250
253
|
if (marked.has(op))
|
|
251
254
|
continue;
|
|
255
|
+
// A marker is owed only while there is a living operation for it to
|
|
256
|
+
// retire — the event axis's rule (events/declared.ts) on the contract
|
|
257
|
+
// axis. An operation this delta already REPLACES at its address leaves
|
|
258
|
+
// the contract without one, and the marker could not even be written:
|
|
259
|
+
// the slot holds the replacement. So the route the overwrite's
|
|
260
|
+
// `openapi.remove-op-consumed` names — REMOVE the requirement governing
|
|
261
|
+
// the old id — ended in this error, followable only by deleting the
|
|
262
|
+
// requirement's Operations: line, which nothing said (review
|
|
263
|
+
// 2026-10-12, finding 4). And an id the service's living contract does
|
|
264
|
+
// not define is not this service's to mark at all: a REMOVED consumer
|
|
265
|
+
// requirement naming ANOTHER service's operation was told to retire it
|
|
266
|
+
// from its own contract. Both exemptions are facts about a contract
|
|
267
|
+
// loam READ: a living openapi.yaml that does not parse proves neither,
|
|
268
|
+
// so the marker stays owed — the closed direction, and the answer this
|
|
269
|
+
// check gave before either exemption existed. Skipping it there let a
|
|
270
|
+
// REMOVED requirement with no marker pass at exit 0 (the contract
|
|
271
|
+
// review, finding 3); `openapi.invalid` is still validate's finding
|
|
272
|
+
// about the file itself.
|
|
273
|
+
if (replaced.has(op) || (living !== undefined && living !== null && !living.has(op)))
|
|
274
|
+
continue;
|
|
252
275
|
// Distinguish "no marker at all" from "a marker is there but loam cannot
|
|
253
276
|
// read which operation it names" — the first asks the author to write the
|
|
254
277
|
// marker, the second to write the operationId, and telling somebody who
|
|
@@ -264,6 +287,9 @@ export async function declaredByService(scope, svcNames, issues, context) {
|
|
|
264
287
|
});
|
|
265
288
|
}
|
|
266
289
|
}
|
|
267
|
-
return {
|
|
290
|
+
return {
|
|
291
|
+
reqOps, removedReqOps, removingOps, allMarkerIds, anonymousMarkers, featureApiOps, unreadableApis, revivalCandidates,
|
|
292
|
+
displacedOps,
|
|
293
|
+
};
|
|
268
294
|
}
|
|
269
295
|
//# sourceMappingURL=declared.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"declared.js","sourceRoot":"","sources":["../../../src/core/coherence/declared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAwB,MAAM,0BAA0B,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AACpG,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAmChD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAiB,EACjB,QAA2B,EAC3B,MAAe,EACf,OAAsB;IAEtB,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAC;IACpD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAuB,CAAC;IACrD,0FAA0F;IAC1F,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;IACnD,0EAA0E;IAC1E,MAAM,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAC;IACpD,iFAAiF;IACjF,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAmB,CAAC;IACpD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,2EAA2E;IAC3E,2EAA2E;IAC3E,2EAA2E;IAC3E,kEAAkE;IAClE,yEAAyE;IACzE,2EAA2E;IAC3E,yBAAyB;IACzB,EAAE;IACF,yEAAyE;IACzE,2EAA2E;IAC3E,wEAAwE;IACxE,2EAA2E;IAC3E,sEAAsE;IACtE,uEAAuE;IACvE,yEAAyE;IACzE,2EAA2E;IAC3E,4BAA4B;IAC5B,MAAM,gBAAgB,GACpB,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC;IACxH,MAAM,eAAe,GAAG,uBAAuB,CAC7C,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,EACrG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACvC,CAAC;IACF,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,IAAI,eAAe,CAAC,OAAO,KAAK,SAAS,CAAC;IACjF,yEAAyE;IACzE,uEAAuE;IACvE,uEAAuE;IACvE,yEAAyE;IACzE,oEAAoE;IACpE,4CAA4C;IAC5C,IAAI,YAAY,GAA+C,IAAI,CAAC;IACpE,MAAM,cAAc,GAAG,GAAwC,EAAE;QAC/D,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS;YAChC,CAAC,CAAC,KAAK,EAAE,CAAS,EAA0B,EAAE,CAAC,iBAAiB,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAC3F,CAAC,CAAC,CAAC,CAAS,EAA0B,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;QACvE,yEAAyE;QACzE,oEAAoE;QACpE,0EAA0E;QAC1E,YAAY,KAAK,0BAA0B,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CACtF,uBAAuB,CACrB,KAAK,EACL,MAAM,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CACpG,CACF,CAAC;QACF,OAAO,YAAY,CAAC;IACtB,CAAC,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,gBAAgB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAChD,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS;gBAChC,CAAC,CAAC,iBAAiB,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBACvD,CAAC,CAAC,MAAM,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/C,6EAA6E;YAC7E,2EAA2E;YAC3E,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;YACvF,aAAa,CAAC,GAAG,CACf,GAAG,EACH,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAC/E,CAAC;YACF,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;gBAC1D,MAAM,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;gBACvE,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,EAAE,CAAC,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QACD,sEAAsE;QACtE,yEAAyE;QACzE,oEAAoE;QACpE,yEAAyE;QACzE,yEAAyE;QACzE,mEAAmE;QACnE,IAAI,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,OAAO,KAAK,SAAS;gBACpC,CAAC,CAAC,iBAAiB,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC3D,CAAC,CAAC,MAAM,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACnD,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,gBAAgB,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;YACnE,MAAM,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;YAC/E,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,EAAE,CAAC,CAAC,CAAC;QACtF,CAAC;QACD,gFAAgF;QAChF,0FAA0F;QAC1F,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1D,yEAAyE;QACzE,kEAAkE;QAClE,sEAAsE;QACtE,qEAAqE;QACrE,sEAAsE;QACtE,qEAAqE;QACrE,wEAAwE;QACxE,sEAAsE;QACtE,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,+CAA+C,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,GAAG,uGAAuG;aAC9N,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;QAC5B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QACnD,0EAA0E;QAC1E,4EAA4E;QAC5E,4EAA4E;QAC5E,4EAA4E;QAC5E,0EAA0E;QAC1E,+BAA+B;QAC/B,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACjF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9E,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACjC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACjC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEhE,oEAAoE;QACpE,2EAA2E;QAC3E,4EAA4E;QAC5E,oCAAoC;QACpC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,iCAAiC;gBACvC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,iKAAiK;aAClN,CAAC,CAAC;QACL,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,CAAC,MAAM,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxG,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC;QAChC,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,YAAY,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5F,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,+BAA+B;gBACrC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,6CAA6C,EAAE,QAAQ,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,8IAA8I;aACxO,CAAC,CAAC;QACL,CAAC;QACD,sEAAsE;QACtE,mEAAmE;QACnE,yEAAyE;QACzE,wEAAwE;QACxE,gDAAgD;QAChD,IAAI,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,MAAM,iBAAiB,GAAG,CAAC,MAAM,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YACpF,sEAAsE;YACtE,kEAAkE;YAClE,sEAAsE;YACtE,+DAA+D;YAC/D,kEAAkE;YAClE,mEAAmE;YACnE,oEAAoE;YACpE,+DAA+D;YAC/D,MAAM,MAAM,GAAG,KAAK,EAAE,CAAS,EAA+B,EAAE;gBAC9D,IAAI,CAAC;oBACH,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACvF,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,IAAI,GAAG,YAAY,oBAAoB;wBAAE,OAAO,SAAS,CAAC;oBAC1D,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC,CAAC;YACF,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,UAAU,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;YAChG,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,CAAC,IAAI,CACT,GAAG,qBAAqB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CACnG,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAChF,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBACzB,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACjE,CAAC;YAED,kEAAkE;YAClE,yEAAyE;YACzE,uEAAuE;YACvE,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;YAC9D,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;gBAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC9F,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,MAAM,CAAC,IAAI,CAAC;wBACV,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,+BAA+B;wBACrC,OAAO,EAAE,GAAG;wBACZ,OAAO,EAAE,GAAG,GAAG,yBAAyB,MAAM,CAAC,EAAE,eAAe,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,wCAAwC;qBACrI,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC;oBACnC,MAAM,CAAC,IAAI,CAAC;wBACV,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,gCAAgC;wBACtC,OAAO,EAAE,GAAG;wBACZ,OAAO,EAAE,GAAG,GAAG,2BAA2B,MAAM,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,wCAAwC,MAAM,CAAC,EAAE,GAAG;qBAC5I,CAAC,CAAC;gBACL,CAAC;gBACD,uEAAuE;gBACvE,2DAA2D;gBAC3D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBAAE,SAAS;gBACzC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC9B,MAAM,CAAC,IAAI,CAAC;wBACV,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,mCAAmC;wBACzC,OAAO,EAAE,GAAG;wBACZ,OAAO,EAAE,GAAG,GAAG,yBAAyB,MAAM,CAAC,EAAE,+DAA+D;qBACjH,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC;QAC5C,uEAAuE;QACvE,iDAAiD;QACjD,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACtC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QAC1D,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,SAAS;YAC7B,yEAAyE;YACzE,0EAA0E;YAC1E,wEAAwE;YACxE,uEAAuE;YACvE,4BAA4B;YAC5B,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,+BAA+B;gBACrC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI;oBACzC,CAAC,CAAC,GAAG,GAAG,kCAAkC,EAAE,qGAAqG,EAAE,kBAAkB;oBACrK,CAAC,CAAC,GAAG,GAAG,kCAAkC,EAAE,4EAA4E;aAC3H,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC;AAC/G,CAAC","sourcesContent":["/**\n * What a feature's own delta DECLARES, per service, indexed once.\n *\n * Seven indexes over the same four files (the delta spec.md, arch.spec.md,\n * openapi.yaml, and the living contract they are graded against), built in one\n * walk because every check downstream joins on the service id and would\n * otherwise re-read the delta per question. The distinctions here are the ones\n * the checks cannot make for themselves: an operation a REMOVED requirement\n * governs is being retired, so it neither claims the contract nor governs\n * anything after the merge; a removal marker that names an id also defined\n * elsewhere in the delta is a RELOCATION, not a retirement; and a marker with\n * no `operationId` at all is a shape only this walk can see. The capability\n * gate rides the same loop: both delta spec documents are graded against the\n * fleet vocabulary here, because coherence.ts and this package's file count\n * are both at their limits and the walk already holds the parsed documents.\n */\nimport { existsSync } from \"node:fs\";\nimport { NotUtf8DocumentError } from \"../kernel/document-bytes.js\";\nimport { readFile } from \"node:fs/promises\";\nimport { type PathableService } from \"../kernel/ids/service.js\";\nimport type { Issue } from \"../vocabulary/issue.js\";\nimport { featureSpecPaths } from \"../repo/paths.js\";\nimport { locateServicePaths } from \"../repo/service-target.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport { readCapabilityVocabulary } from \"../capabilities/capabilities.js\";\nimport { capabilityRequirementIndex } from \"../capabilities/findings.js\";\nimport { withFeatureCapabilities, withFeatureRequirements } from \"../capabilities/delta/overlay.js\";\nimport { featureCapabilityDeltas } from \"../capabilities/delta/tree.js\";\nimport { realizesUnknownIssues } from \"../capabilities/realizes/findings.js\";\nimport type { CapabilityRequirementIndex } from \"../capabilities/realizes/join.js\";\nimport { capabilityUnknownIssues } from \"../capabilities/findings.js\";\nimport type { Requirement } from \"../document/spec.js\";\nimport { openapiBaselineIssues } from \"../openapi/baseline/gate.js\";\nimport { readOpenapi } from \"../openapi/doc.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\n\n/** Everything one feature's delta says about its own services. */\nexport interface Declared {\n /** Per service: the operations its non-REMOVED requirements govern. */\n reqOps: Map<PathableService, string[]>;\n /** Per service: the operations its REMOVED requirements governed. */\n removedReqOps: Map<string, Set<string>>;\n /** Per service: operations this feature genuinely retires (markers minus relocations). */\n removingOps: Map<string, Set<string>>;\n /** Per service: every id a removal marker names, relocations included. */\n allMarkerIds: Map<string, Set<string>>;\n /** Per service: does the feature contract carry a marker with no operationId? */\n anonymousMarkers: Map<string, boolean>;\n /** The operationIds genuinely NEW to their service across the whole feature. */\n featureApiOps: Set<string>;\n /**\n * Services whose FEATURE openapi.yaml exists but does not parse. Every check\n * that joins on the feature contract must suspend itself for these — an\n * unreadable document yields the same empty op set as an absent one, and\n * grading against that emptiness produced false errors pointing everywhere\n * but at the broken file.\n */\n unreadableApis: Set<string>;\n}\n\n/** The three facts this walk needs about the feature it is reading. */\nexport interface DeltaScope {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n featureId: string;\n}\n\nexport async function declaredByService(\n scope: DeltaScope,\n svcNames: PathableService[],\n issues: Issue[],\n context?: FleetContext,\n): Promise<Declared> {\n const { docsDir, featureDir, featureId } = scope;\n const reqOps = new Map<PathableService, string[]>();\n const removedReqOps = new Map<string, Set<string>>();\n /** Per service: operations this feature genuinely retires (markers minus relocations). */\n const removingOps = new Map<string, Set<string>>();\n /** Per service: every id a removal marker names, relocations included. */\n const allMarkerIds = new Map<string, Set<string>>();\n /** Per service: does the feature contract carry a marker with no operationId? */\n const anonymousMarkers = new Map<string, boolean>();\n const featureApiOps = new Set<string>();\n const unreadableApis = new Set<string>();\n // The capability gate's vocabulary, once per call. Absent or invalid stays\n // SILENT here — capability.invalid is validate --all's one finding about a\n // broken file, and an absent file means the fleet never opted in — so with\n // either, nothing new gates archive (the roadmap's own trade; the\n // /loam-check row tells agents to fix the YAML first). `grading` is that\n // silence hoisted into a guard, so a silent axis also skips the reads that\n // exist only to feed it.\n //\n // The vocabulary is the one this feature's MERGE would leave behind, not\n // the one on disk: a feature that adds a capability requirement in its own\n // capability delta and `Realizes:` it from its own service delta is the\n // flow the whole axis exists for, and graded against the living tree alone\n // it is refused with `capability.realizes-unknown` — an error, gating\n // archive, for a target the same archive creates. `capabilities/delta/\n // overlay.ts` states the whole judgement. One `existsSync` for a feature\n // that carries no capability delta, which is every feature in a fleet that\n // has not adopted the axis.\n const capabilityDeltas =\n context === undefined ? await featureCapabilityDeltas(featureDir) : await context.featureCapabilityDeltas(featureDir);\n const capabilityVocab = withFeatureCapabilities(\n context === undefined ? await readCapabilityVocabulary(docsDir) : await context.capabilities(docsDir),\n capabilityDeltas.docs.map((d) => d.id),\n );\n const grading = capabilityVocab.present && capabilityVocab.invalid === undefined;\n // The `Realizes:` index, built at most ONCE per feature and only if some\n // delta document actually reaches the grading branch. Lazy rather than\n // eager because it reads every capability document in the fleet, and a\n // feature whose deltas carry no requirement file at all must not pay for\n // that — the same \"a silent axis skips the reads that feed it\" rule\n // `grading` above states, one level deeper.\n let pendingIndex: Promise<CapabilityRequirementIndex> | null = null;\n const capabilityReqs = (): Promise<CapabilityRequirementIndex> => {\n const read = context === undefined\n ? async (p: string): Promise<Requirement[]> => parseRequirements(await readFile(p, \"utf8\"))\n : (p: string): Promise<Requirement[]> => context.readRequirements(p);\n // The living index first, then widened by what this feature's capability\n // deltas ADD or MODIFY. Both halves are inside the lazy thunk, so a\n // feature whose deltas carry no requirement file at all pays for neither.\n pendingIndex ??= capabilityRequirementIndex(capabilityVocab, read).then(async (index) =>\n withFeatureRequirements(\n index,\n await Promise.all(capabilityDeltas.docs.map(async (d) => ({ id: d.id, reqs: await read(d.spec) }))),\n ),\n );\n return pendingIndex;\n };\n for (const svc of svcNames) {\n const paths = featureSpecPaths(featureDir, svc);\n if (existsSync(paths.spec)) {\n const reqs = context === undefined\n ? parseRequirements(await readFile(paths.spec, \"utf8\"))\n : await context.readRequirements(paths.spec);\n // REMOVED requirements are being retired along with their operations — their\n // ops neither claim the contract (E1) nor govern anything after the merge.\n reqOps.set(svc, reqs.filter((r) => r.kind !== \"REMOVED\").flatMap((r) => r.operations));\n removedReqOps.set(\n svc,\n new Set(reqs.filter((r) => r.kind === \"REMOVED\").flatMap((r) => r.operations)),\n );\n if (grading) {\n const target = { where: `${svc}: spec.md`, subject: svc };\n issues.push(...capabilityUnknownIssues(reqs, target, capabilityVocab));\n issues.push(...realizesUnknownIssues(reqs, target, await capabilityReqs()));\n }\n }\n // The delta arch.spec.md carries the same grammar and merges the same\n // way, so an undeclared capability in it gates identically. Nothing else\n // in this walk needs the arch delta, so the read itself sits behind\n // `grading` — and it is NOT always cached: `validate --feature` passes a\n // context, but archive's plan gate (commands/archive/plan/gate.ts) calls\n // featureCoherence without one, so this parse is paid fresh there.\n if (grading && existsSync(paths.archSpec)) {\n const archReqs = context === undefined\n ? parseRequirements(await readFile(paths.archSpec, \"utf8\"))\n : await context.readRequirements(paths.archSpec);\n const archTarget = { where: `${svc}: arch.spec.md`, subject: svc };\n issues.push(...capabilityUnknownIssues(archReqs, archTarget, capabilityVocab));\n issues.push(...realizesUnknownIssues(archReqs, archTarget, await capabilityReqs()));\n }\n // Only operations genuinely NEW to this service count as feature-added: authors\n // restate the full living API in the delta file (it is a complete document, not a patch).\n const featDoc = await readOpenapi(paths.openapi, context);\n // A feature contract that EXISTS but does not read is a broken document,\n // not an empty one — the living-side `openapi.invalid` discipline\n // (validate/service/api.ts), applied to the delta. Before this check,\n // `validate --feature` said nothing about the file at all: the empty\n // parse silently skipped every baseline pin and removal marker in it,\n // and E1 graded requirements against a contract that was never read.\n // The rest of this service's contract-axis checks are suspended — every\n // one of them would be an opinion about a document nobody could open.\n if (featDoc.unreadable) {\n unreadableApis.add(svc);\n issues.push({\n severity: \"error\",\n code: \"openapi.invalid\",\n subject: svc,\n message: `${svc}: this feature's openapi.yaml does not parse${featDoc.error === undefined ? \"\" : ` (${featDoc.error})`} — the contract axis is unchecked and the merge would have nothing true to write. Fix the YAML first.`,\n });\n continue;\n }\n const featOps = featDoc.ops;\n const removals = featOps.filter((op) => op.remove);\n // A relocation — same operationId, removal marker on the old slot, upsert\n // on the new one — retires nothing. Every rule that asks \"is this operation\n // going away\" must therefore ask the NET set (markers this feature does not\n // redefine), while \"did the author write a marker at all\" asks the raw one.\n // Conflating them made moving an endpoint fail three checks at once for a\n // change that removes nothing.\n const redefined = new Set(featOps.filter((op) => !op.remove).map((op) => op.id));\n const markerIds = new Set(removals.map((op) => op.id));\n const netRemoved = new Set([...markerIds].filter((id) => !redefined.has(id)));\n removingOps.set(svc, netRemoved);\n allMarkerIds.set(svc, markerIds);\n anonymousMarkers.set(svc, featDoc.anonymousRemovals.length > 0);\n\n // A marker with no operationId names a slot but no operation. Every\n // id-keyed check is blind to it, so it used to travel all the way into the\n // living contract as a literal `x-loam-remove: true` — the one feature-only\n // key that must never be published.\n for (const marker of featDoc.anonymousRemovals) {\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-marker-anonymous\",\n subject: svc,\n message: `${svc}: ${marker.method} ${marker.path} carries x-loam-remove: true but declares no operationId — loam cannot tell which operation it retires; name the operation the living contract has at that slot`,\n });\n }\n\n const livingDoc = await readOpenapi((await locateServicePaths(docsDir, svc, context)).openapi, context);\n const livingOps = livingDoc.ops;\n for (const id of livingDoc.duplicateIds) {\n const slots = livingOps.filter((op) => op.id === id).map((op) => `${op.method} ${op.path}`);\n issues.push({\n severity: \"warn\",\n code: \"openapi.duplicate-operationid\",\n subject: svc,\n message: `${svc}: the living OpenAPI defines operationId '${id}' at ${slots.join(\" and \")} — every join on the id (a requirement's Operations: line, an edge's metadata { op }, a removal marker) picks one of those slots arbitrarily`,\n });\n }\n // The baseline axis — operation pins, path-item keys and components —\n // graded in its own package (openapi/baseline/gate.ts), slot-keyed\n // exactly as the merge writes. Texts come through the context's memoized\n // read when there is one, mirroring the readRequirements pattern above;\n // the gate itself never touches the filesystem.\n if (existsSync(paths.openapi)) {\n const livingOpenapiPath = (await locateServicePaths(docsDir, svc, context)).openapi;\n // `context.readText` DECODES and THROWS on non-UTF-8 bytes, where the\n // no-context `readFile` substitutes U+FFFD and lets `readOpenapi`\n // flag the document unreadable. Uncaught, one service's UTF-16 living\n // contract took down the whole feature target under validate's\n // context while archive's no-context path sailed past — the exact\n // context/no-context divergence doc.ts documents as paid for once.\n // Either text failing to decode gets the same answer the gate gives\n // an unreadable parse: that side's surface checks are skipped.\n const readOr = async (p: string): 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 const featureText = await readOr(paths.openapi);\n const livingText = !existsSync(livingOpenapiPath) ? undefined : await readOr(livingOpenapiPath);\n if (featureText !== undefined) {\n issues.push(\n ...openapiBaselineIssues({ featDoc, livingDoc, featureText, livingText, service: svc, featureId }),\n );\n }\n }\n\n if (featOps.length > 0) {\n const living = new Set(livingOps.filter((op) => !op.remove).map((op) => op.id));\n for (const op of featOps) {\n if (!op.remove && !living.has(op.id)) featureApiOps.add(op.id);\n }\n\n // Removal is exact: the feature names both an operationId and the\n // path+method slot it expects to delete. That catches stale deltas whose\n // target moved or was already retired before the archive planner runs.\n const justified = removedReqOps.get(svc) ?? new Set<string>();\n for (const marker of removals) {\n const target = livingOps.find((op) => op.path === marker.path && op.method === marker.method);\n if (target === undefined) {\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-target-missing\",\n subject: svc,\n message: `${svc}: removal marker for '${marker.id}' addresses ${marker.method} ${marker.path}, but no living operation exists there`,\n });\n } else if (target.id !== marker.id) {\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-target-mismatch\",\n subject: svc,\n message: `${svc}: removal marker names '${marker.id}' at ${marker.method} ${marker.path}, but the living operation there is '${target.id}'`,\n });\n }\n // A relocation needs no REMOVED requirement: the requirement governing\n // the operation stays, the operation only changes address.\n if (!netRemoved.has(marker.id)) continue;\n if (!justified.has(marker.id)) {\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-marker-unjustified\",\n subject: svc,\n message: `${svc}: removal marker for '${marker.id}' is not governed by a REMOVED requirement's Operations: line`,\n });\n }\n }\n }\n }\n for (const [svc, required] of removedReqOps) {\n // The markers live in the unreadable file — \"no matching marker\" would\n // be a claim about a document nobody could open.\n if (unreadableApis.has(svc)) continue;\n const marked = allMarkerIds.get(svc) ?? new Set<string>();\n for (const op of required) {\n if (marked.has(op)) continue;\n // Distinguish \"no marker at all\" from \"a marker is there but loam cannot\n // read which operation it names\" — the first asks the author to write the\n // marker, the second to write the operationId, and telling somebody who\n // already wrote the marker that there is none sends them looking for a\n // file they are staring at.\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-marker-missing\",\n subject: svc,\n message: anonymousMarkers.get(svc) === true\n ? `${svc}: REMOVED requirement governs '${op}', and its feature openapi.yaml carries an x-loam-remove: true marker with no operationId — name '${op}' on that marker`\n : `${svc}: REMOVED requirement governs '${op}', but its feature openapi.yaml has no matching x-loam-remove: true marker`,\n });\n }\n }\n return { reqOps, removedReqOps, removingOps, allMarkerIds, anonymousMarkers, featureApiOps, unreadableApis };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"declared.js","sourceRoot":"","sources":["../../../src/core/coherence/declared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAwB,MAAM,0BAA0B,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AACpG,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAmC,MAAM,+BAA+B,CAAC;AAmDnG,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAiB,EACjB,QAA2B,EAC3B,MAAe,EACf,OAAsB;IAEtB,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAC;IACpD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAgC,CAAC;IAC9D,0FAA0F;IAC1F,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;IACnD,0EAA0E;IAC1E,MAAM,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAC;IACpD,iFAAiF;IACjF,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAmB,CAAC;IACpD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAA8B,CAAC;IAChE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAmC,CAAC;IAChE,wGAAwG;IACxG,MAAM,SAAS,GAAG,IAAI,GAAG,EAAsC,CAAC;IAChE,2EAA2E;IAC3E,2EAA2E;IAC3E,2EAA2E;IAC3E,kEAAkE;IAClE,yEAAyE;IACzE,2EAA2E;IAC3E,yBAAyB;IACzB,EAAE;IACF,yEAAyE;IACzE,2EAA2E;IAC3E,wEAAwE;IACxE,2EAA2E;IAC3E,sEAAsE;IACtE,uEAAuE;IACvE,yEAAyE;IACzE,2EAA2E;IAC3E,4BAA4B;IAC5B,MAAM,gBAAgB,GACpB,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC;IACxH,MAAM,eAAe,GAAG,uBAAuB,CAC7C,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,EACrG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACvC,CAAC;IACF,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,IAAI,eAAe,CAAC,OAAO,KAAK,SAAS,CAAC;IACjF,yEAAyE;IACzE,uEAAuE;IACvE,uEAAuE;IACvE,yEAAyE;IACzE,oEAAoE;IACpE,4CAA4C;IAC5C,IAAI,YAAY,GAA+C,IAAI,CAAC;IACpE,MAAM,cAAc,GAAG,GAAwC,EAAE;QAC/D,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS;YAChC,CAAC,CAAC,KAAK,EAAE,CAAS,EAA0B,EAAE,CAAC,iBAAiB,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAC3F,CAAC,CAAC,CAAC,CAAS,EAA0B,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;QACvE,yEAAyE;QACzE,oEAAoE;QACpE,0EAA0E;QAC1E,YAAY,KAAK,0BAA0B,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CACtF,uBAAuB,CACrB,KAAK,EACL,MAAM,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CACpG,CACF,CAAC;QACF,OAAO,YAAY,CAAC;IACtB,CAAC,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,gBAAgB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAChD,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS;gBAChC,CAAC,CAAC,iBAAiB,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBACvD,CAAC,CAAC,MAAM,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/C,6EAA6E;YAC7E,2EAA2E;YAC3E,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;YACvF,aAAa,CAAC,GAAG,CACf,GAAG,EACH,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAC/E,CAAC;YACF,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;gBAC1D,MAAM,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;gBACvE,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,EAAE,CAAC,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QACD,sEAAsE;QACtE,yEAAyE;QACzE,oEAAoE;QACpE,yEAAyE;QACzE,yEAAyE;QACzE,mEAAmE;QACnE,IAAI,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,OAAO,KAAK,SAAS;gBACpC,CAAC,CAAC,iBAAiB,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC3D,CAAC,CAAC,MAAM,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACnD,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,gBAAgB,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;YACnE,MAAM,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;YAC/E,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,EAAE,CAAC,CAAC,CAAC;QACtF,CAAC;QACD,4EAA4E;QAC5E,0EAA0E;QAC1E,wEAAwE;QACxE,0EAA0E;QAC1E,kFAAkF;QAClF,uCAAuC;QACvC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1D,yEAAyE;QACzE,kEAAkE;QAClE,sEAAsE;QACtE,qEAAqE;QACrE,sEAAsE;QACtE,qEAAqE;QACrE,wEAAwE;QACxE,sEAAsE;QACtE,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,+CAA+C,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,GAAG,uGAAuG;aAC9N,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;QAC5B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QACnD,mEAAmE;QACnE,yEAAyE;QACzE,wEAAwE;QACxE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACrE,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACjC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACjC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEhE,oEAAoE;QACpE,2EAA2E;QAC3E,4EAA4E;QAC5E,oCAAoC;QACpC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,iCAAiC;gBACvC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,iKAAiK;aAClN,CAAC,CAAC;QACL,CAAC;QAED,MAAM,iBAAiB,GAAG,CAAC,MAAM,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QACpF,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC;QAChC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnH,MAAM,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAC3G,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACtD,iBAAiB,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACvE,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,YAAY,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5F,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,+BAA+B;gBACrC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,6CAA6C,EAAE,QAAQ,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,8IAA8I;aACxO,CAAC,CAAC;QACL,CAAC;QACD,sEAAsE;QACtE,mEAAmE;QACnE,yEAAyE;QACzE,wEAAwE;QACxE,gDAAgD;QAChD,IAAI,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,6EAA6E;YAC7E,iEAAiE;YACjE,yDAAyD;YACzD,mEAAmE;YACnE,oEAAoE;YACpE,+DAA+D;YAC/D,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzD,MAAM,UAAU,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;YACzG,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,CAAC,IAAI,CACT,GAAG,qBAAqB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CACnG,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAChF,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBACzB,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACjE,CAAC;YAED,kEAAkE;YAClE,yEAAyE;YACzE,uEAAuE;YACvE,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;YAC9D,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;gBAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC9F,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,MAAM,CAAC,IAAI,CAAC;wBACV,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,+BAA+B;wBACrC,OAAO,EAAE,GAAG;wBACZ,OAAO,EAAE,GAAG,GAAG,yBAAyB,MAAM,CAAC,EAAE,eAAe,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,wCAAwC;qBACrI,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC;oBACnC,MAAM,CAAC,IAAI,CAAC;wBACV,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,gCAAgC;wBACtC,OAAO,EAAE,GAAG;wBACZ,OAAO,EAAE,GAAG,GAAG,2BAA2B,MAAM,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,wCAAwC,MAAM,CAAC,EAAE,GAAG;qBAC5I,CAAC,CAAC;gBACL,CAAC;gBACD,uEAAuE;gBACvE,2DAA2D;gBAC3D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBAAE,SAAS;gBACzC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC9B,MAAM,CAAC,IAAI,CAAC;wBACV,QAAQ,EAAE,OAAO;wBACjB,IAAI,EAAE,mCAAmC;wBACzC,OAAO,EAAE,GAAG;wBACZ,OAAO,EAAE,GAAG,GAAG,yBAAyB,MAAM,CAAC,EAAE,+DAA+D;qBACjH,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC;QAC5C,uEAAuE;QACvE,iDAAiD;QACjD,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACtC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QAC1D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,SAAS;YAC7B,oEAAoE;YACpE,sEAAsE;YACtE,uEAAuE;YACvE,sEAAsE;YACtE,+DAA+D;YAC/D,wEAAwE;YACxE,oEAAoE;YACpE,6DAA6D;YAC7D,uEAAuE;YACvE,sEAAsE;YACtE,uEAAuE;YACvE,oEAAoE;YACpE,uEAAuE;YACvE,uEAAuE;YACvE,sEAAsE;YACtE,kEAAkE;YAClE,oEAAoE;YACpE,yBAAyB;YACzB,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAAE,SAAS;YAC/F,yEAAyE;YACzE,0EAA0E;YAC1E,wEAAwE;YACxE,uEAAuE;YACvE,4BAA4B;YAC5B,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,+BAA+B;gBACrC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI;oBACzC,CAAC,CAAC,GAAG,GAAG,kCAAkC,EAAE,qGAAqG,EAAE,kBAAkB;oBACrK,CAAC,CAAC,GAAG,GAAG,kCAAkC,EAAE,4EAA4E;aAC3H,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACH,OAAO;QACL,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB;QACpH,YAAY;KACb,CAAC;AACJ,CAAC","sourcesContent":["/**\n * What a feature's own delta DECLARES, per service, indexed once.\n *\n * Nine indexes over the same four files (the delta spec.md, arch.spec.md,\n * openapi.yaml, and the living contract they are graded against), built in one\n * walk because every check downstream joins on the service id and would\n * otherwise re-read the delta per question. The distinctions here are the ones\n * the checks cannot make for themselves: an operation a REMOVED requirement\n * governs is being retired, so it neither claims the contract nor governs\n * anything after the merge; a removal marker that names an id also defined\n * elsewhere in the delta is a RELOCATION, not a retirement; a living operation\n * another one replaces at its address, with no marker naming it, leaves the\n * contract all the same; and a marker with\n * no `operationId` at all is a shape only this walk can see. The capability\n * gate rides the same loop: both delta spec documents are graded against the\n * fleet vocabulary here, because coherence.ts and this package's file count\n * are both at their limits and the walk already holds the parsed documents.\n */\nimport { existsSync } from \"node:fs\";\nimport { readOr } from \"../openapi/contract-text.js\";\nimport { readFile } from \"node:fs/promises\";\nimport { type PathableService } from \"../kernel/ids/service.js\";\nimport type { Issue } from \"../vocabulary/issue.js\";\nimport { featureSpecPaths } from \"../repo/paths.js\";\nimport { locateServicePaths } from \"../repo/service-target.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport { readCapabilityVocabulary } from \"../capabilities/capabilities.js\";\nimport { capabilityRequirementIndex } from \"../capabilities/findings.js\";\nimport { withFeatureCapabilities, withFeatureRequirements } from \"../capabilities/delta/overlay.js\";\nimport { featureCapabilityDeltas } from \"../capabilities/delta/tree.js\";\nimport { realizesUnknownIssues } from \"../capabilities/realizes/findings.js\";\nimport type { CapabilityRequirementIndex } from \"../capabilities/realizes/join.js\";\nimport { capabilityUnknownIssues } from \"../capabilities/findings.js\";\nimport type { Requirement } from \"../document/spec.js\";\nimport { openapiBaselineIssues } from \"../openapi/baseline/gate.js\";\nimport { readOpenapi } from \"../openapi/doc.js\";\nimport { operationRetirements } from \"./retired/retirement.js\";\nimport { operationArrivals, type Arrival, type Displacement } from \"./retired/revival/arrivals.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\n\n/** Everything one feature's delta says about its own services. */\nexport interface Declared {\n /** Per service: the operations its non-REMOVED requirements govern. */\n reqOps: Map<PathableService, string[]>;\n /** Per service: the operations its REMOVED requirements governed. */\n removedReqOps: Map<PathableService, Set<string>>;\n /** Per service: operations this feature genuinely retires (markers minus relocations). */\n removingOps: Map<string, Set<string>>;\n /** Per service: every id a removal marker names, relocations included. */\n allMarkerIds: Map<string, Set<string>>;\n /** Per service: does the feature contract carry a marker with no operationId? */\n anonymousMarkers: Map<string, boolean>;\n /** The operationIds the living contract of their service does not hold, across the whole feature — new, or revived (see `revivalCandidates`). */\n featureApiOps: Set<string>;\n /**\n * Services whose FEATURE openapi.yaml exists but does not parse. Every check\n * that joins on the feature contract must suspend itself for these — an\n * unreadable document yields the same empty op set as an absent one, and\n * grading against that emptiness produced false errors pointing everywhere\n * but at the broken file.\n */\n unreadableApis: Set<string>;\n /**\n * Per service: every (operationId, address) the contract the archive would\n * write has and the living one lacks — an addition, or a revival of what an\n * archived feature retired or moved away, which the living contract alone\n * cannot tell apart. coherence.ts asks the retired index (./retired/) only\n * when one of these lists is non-empty.\n */\n revivalCandidates: Map<PathableService, Arrival[]>;\n /**\n * Per service: every living operationId the contract the archive would\n * write answers nowhere while no removal marker names it — replaced at its\n * address by another operation (./retired/revival/arrivals.ts). The\n * removal gate's consumers are asked of these too: the loss is the same,\n * only the spelling differs.\n */\n displacedOps: Map<PathableService, Displacement[]>;\n}\n\n/** The three facts this walk needs about the feature it is reading. */\nexport interface DeltaScope {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n featureId: string;\n}\n\nexport async function declaredByService(\n scope: DeltaScope,\n svcNames: PathableService[],\n issues: Issue[],\n context?: FleetContext,\n): Promise<Declared> {\n const { docsDir, featureDir, featureId } = scope;\n const reqOps = new Map<PathableService, string[]>();\n const removedReqOps = new Map<PathableService, Set<string>>();\n /** Per service: operations this feature genuinely retires (markers minus relocations). */\n const removingOps = new Map<string, Set<string>>();\n /** Per service: every id a removal marker names, relocations included. */\n const allMarkerIds = new Map<string, Set<string>>();\n /** Per service: does the feature contract carry a marker with no operationId? */\n const anonymousMarkers = new Map<string, boolean>();\n const featureApiOps = new Set<string>();\n const unreadableApis = new Set<string>();\n const revivalCandidates = new Map<PathableService, Arrival[]>();\n const displacedOps = new Map<PathableService, Displacement[]>();\n /** Per service read here: the operationIds its living contract holds, or null when it does not read. */\n const livingIds = new Map<string, ReadonlySet<string> | null>();\n // The capability gate's vocabulary, once per call. Absent or invalid stays\n // SILENT here — capability.invalid is validate --all's one finding about a\n // broken file, and an absent file means the fleet never opted in — so with\n // either, nothing new gates archive (the roadmap's own trade; the\n // /loam-check row tells agents to fix the YAML first). `grading` is that\n // silence hoisted into a guard, so a silent axis also skips the reads that\n // exist only to feed it.\n //\n // The vocabulary is the one this feature's MERGE would leave behind, not\n // the one on disk: a feature that adds a capability requirement in its own\n // capability delta and `Realizes:` it from its own service delta is the\n // flow the whole axis exists for, and graded against the living tree alone\n // it is refused with `capability.realizes-unknown` — an error, gating\n // archive, for a target the same archive creates. `capabilities/delta/\n // overlay.ts` states the whole judgement. One `existsSync` for a feature\n // that carries no capability delta, which is every feature in a fleet that\n // has not adopted the axis.\n const capabilityDeltas =\n context === undefined ? await featureCapabilityDeltas(featureDir) : await context.featureCapabilityDeltas(featureDir);\n const capabilityVocab = withFeatureCapabilities(\n context === undefined ? await readCapabilityVocabulary(docsDir) : await context.capabilities(docsDir),\n capabilityDeltas.docs.map((d) => d.id),\n );\n const grading = capabilityVocab.present && capabilityVocab.invalid === undefined;\n // The `Realizes:` index, built at most ONCE per feature and only if some\n // delta document actually reaches the grading branch. Lazy rather than\n // eager because it reads every capability document in the fleet, and a\n // feature whose deltas carry no requirement file at all must not pay for\n // that — the same \"a silent axis skips the reads that feed it\" rule\n // `grading` above states, one level deeper.\n let pendingIndex: Promise<CapabilityRequirementIndex> | null = null;\n const capabilityReqs = (): Promise<CapabilityRequirementIndex> => {\n const read = context === undefined\n ? async (p: string): Promise<Requirement[]> => parseRequirements(await readFile(p, \"utf8\"))\n : (p: string): Promise<Requirement[]> => context.readRequirements(p);\n // The living index first, then widened by what this feature's capability\n // deltas ADD or MODIFY. Both halves are inside the lazy thunk, so a\n // feature whose deltas carry no requirement file at all pays for neither.\n pendingIndex ??= capabilityRequirementIndex(capabilityVocab, read).then(async (index) =>\n withFeatureRequirements(\n index,\n await Promise.all(capabilityDeltas.docs.map(async (d) => ({ id: d.id, reqs: await read(d.spec) }))),\n ),\n );\n return pendingIndex;\n };\n for (const svc of svcNames) {\n const paths = featureSpecPaths(featureDir, svc);\n if (existsSync(paths.spec)) {\n const reqs = context === undefined\n ? parseRequirements(await readFile(paths.spec, \"utf8\"))\n : await context.readRequirements(paths.spec);\n // REMOVED requirements are being retired along with their operations — their\n // ops neither claim the contract (E1) nor govern anything after the merge.\n reqOps.set(svc, reqs.filter((r) => r.kind !== \"REMOVED\").flatMap((r) => r.operations));\n removedReqOps.set(\n svc,\n new Set(reqs.filter((r) => r.kind === \"REMOVED\").flatMap((r) => r.operations)),\n );\n if (grading) {\n const target = { where: `${svc}: spec.md`, subject: svc };\n issues.push(...capabilityUnknownIssues(reqs, target, capabilityVocab));\n issues.push(...realizesUnknownIssues(reqs, target, await capabilityReqs()));\n }\n }\n // The delta arch.spec.md carries the same grammar and merges the same\n // way, so an undeclared capability in it gates identically. Nothing else\n // in this walk needs the arch delta, so the read itself sits behind\n // `grading` — and it is NOT always cached: `validate --feature` passes a\n // context, but archive's plan gate (commands/archive/plan/gate.ts) calls\n // featureCoherence without one, so this parse is paid fresh there.\n if (grading && existsSync(paths.archSpec)) {\n const archReqs = context === undefined\n ? parseRequirements(await readFile(paths.archSpec, \"utf8\"))\n : await context.readRequirements(paths.archSpec);\n const archTarget = { where: `${svc}: arch.spec.md`, subject: svc };\n issues.push(...capabilityUnknownIssues(archReqs, archTarget, capabilityVocab));\n issues.push(...realizesUnknownIssues(archReqs, archTarget, await capabilityReqs()));\n }\n // Only operations the living contract does not hold count as feature-added:\n // a delta may restate living operations it does not change (a quote), and\n // a quote adds nothing. \"Not in living\" is not quite \"new\", though — an\n // operation an archived feature RETIRED is absent too, and adding it back\n // is `openapi.op-revived`'s question (./retired/revival/revived.ts), asked of the\n // `revivalCandidates` collected below.\n const featDoc = await readOpenapi(paths.openapi, context);\n // A feature contract that EXISTS but does not read is a broken document,\n // not an empty one — the living-side `openapi.invalid` discipline\n // (validate/service/api.ts), applied to the delta. Before this check,\n // `validate --feature` said nothing about the file at all: the empty\n // parse silently skipped every baseline pin and removal marker in it,\n // and E1 graded requirements against a contract that was never read.\n // The rest of this service's contract-axis checks are suspended — every\n // one of them would be an opinion about a document nobody could open.\n if (featDoc.unreadable) {\n unreadableApis.add(svc);\n issues.push({\n severity: \"error\",\n code: \"openapi.invalid\",\n subject: svc,\n message: `${svc}: this feature's openapi.yaml does not parse${featDoc.error === undefined ? \"\" : ` (${featDoc.error})`} — the contract axis is unchecked and the merge would have nothing true to write. Fix the YAML first.`,\n });\n continue;\n }\n const featOps = featDoc.ops;\n const removals = featOps.filter((op) => op.remove);\n // The raw marker set and the NET one (markers minus relocations) —\n // ./retired/retirement.ts owns the rule and says why the two differ; the\n // retired index reads every archived feature through the same function.\n const { markerIds, net: netRemoved } = operationRetirements(featOps);\n removingOps.set(svc, netRemoved);\n allMarkerIds.set(svc, markerIds);\n anonymousMarkers.set(svc, featDoc.anonymousRemovals.length > 0);\n\n // A marker with no operationId names a slot but no operation. Every\n // id-keyed check is blind to it, so it used to travel all the way into the\n // living contract as a literal `x-loam-remove: true` — the one feature-only\n // key that must never be published.\n for (const marker of featDoc.anonymousRemovals) {\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-marker-anonymous\",\n subject: svc,\n message: `${svc}: ${marker.method} ${marker.path} carries x-loam-remove: true but declares no operationId — loam cannot tell which operation it retires; name the operation the living contract has at that slot`,\n });\n }\n\n const livingOpenapiPath = (await locateServicePaths(docsDir, svc, context)).openapi;\n const livingDoc = await readOpenapi(livingOpenapiPath, context);\n const livingOps = livingDoc.ops;\n livingIds.set(svc, livingDoc.unreadable ? null : new Set(livingOps.filter((op) => !op.remove).map((op) => op.id)));\n const sides = { featPath: paths.openapi, featDoc, livingPath: livingOpenapiPath, livingDoc, service: svc };\n const moved = await operationArrivals(sides, context);\n revivalCandidates.set(svc, moved.operations);\n if (moved.displaced.length > 0) displacedOps.set(svc, moved.displaced);\n for (const id of livingDoc.duplicateIds) {\n const slots = livingOps.filter((op) => op.id === id).map((op) => `${op.method} ${op.path}`);\n issues.push({\n severity: \"warn\",\n code: \"openapi.duplicate-operationid\",\n subject: svc,\n message: `${svc}: the living OpenAPI defines operationId '${id}' at ${slots.join(\" and \")} — every join on the id (a requirement's Operations: line, an edge's metadata { op }, a removal marker) picks one of those slots arbitrarily`,\n });\n }\n // The baseline axis — operation pins, path-item keys and components —\n // graded in its own package (openapi/baseline/gate.ts), slot-keyed\n // exactly as the merge writes. Texts come through the context's memoized\n // read when there is one, mirroring the readRequirements pattern above;\n // the gate itself never touches the filesystem.\n if (existsSync(paths.openapi)) {\n // `readOr` (../openapi/contract-text.ts, which says why): `context.readText`\n // throws on non-UTF-8 bytes where the no-context `readFile` lets\n // `readOpenapi` flag the document unreadable — the exact\n // context/no-context divergence doc.ts documents as paid for once.\n // Either text failing to decode gets the same answer the gate gives\n // an unreadable parse: that side's surface checks are skipped.\n const featureText = await readOr(paths.openapi, context);\n const livingText = !existsSync(livingOpenapiPath) ? undefined : await readOr(livingOpenapiPath, context);\n if (featureText !== undefined) {\n issues.push(\n ...openapiBaselineIssues({ featDoc, livingDoc, featureText, livingText, service: svc, featureId }),\n );\n }\n }\n\n if (featOps.length > 0) {\n const living = new Set(livingOps.filter((op) => !op.remove).map((op) => op.id));\n for (const op of featOps) {\n if (!op.remove && !living.has(op.id)) featureApiOps.add(op.id);\n }\n\n // Removal is exact: the feature names both an operationId and the\n // path+method slot it expects to delete. That catches stale deltas whose\n // target moved or was already retired before the archive planner runs.\n const justified = removedReqOps.get(svc) ?? new Set<string>();\n for (const marker of removals) {\n const target = livingOps.find((op) => op.path === marker.path && op.method === marker.method);\n if (target === undefined) {\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-target-missing\",\n subject: svc,\n message: `${svc}: removal marker for '${marker.id}' addresses ${marker.method} ${marker.path}, but no living operation exists there`,\n });\n } else if (target.id !== marker.id) {\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-target-mismatch\",\n subject: svc,\n message: `${svc}: removal marker names '${marker.id}' at ${marker.method} ${marker.path}, but the living operation there is '${target.id}'`,\n });\n }\n // A relocation needs no REMOVED requirement: the requirement governing\n // the operation stays, the operation only changes address.\n if (!netRemoved.has(marker.id)) continue;\n if (!justified.has(marker.id)) {\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-marker-unjustified\",\n subject: svc,\n message: `${svc}: removal marker for '${marker.id}' is not governed by a REMOVED requirement's Operations: line`,\n });\n }\n }\n }\n }\n for (const [svc, required] of removedReqOps) {\n // The markers live in the unreadable file — \"no matching marker\" would\n // be a claim about a document nobody could open.\n if (unreadableApis.has(svc)) continue;\n const marked = allMarkerIds.get(svc) ?? new Set<string>();\n const replaced = new Set((displacedOps.get(svc) ?? []).map((d) => d.id));\n const living = livingIds.get(svc);\n for (const op of required) {\n if (marked.has(op)) continue;\n // A marker is owed only while there is a living operation for it to\n // retire — the event axis's rule (events/declared.ts) on the contract\n // axis. An operation this delta already REPLACES at its address leaves\n // the contract without one, and the marker could not even be written:\n // the slot holds the replacement. So the route the overwrite's\n // `openapi.remove-op-consumed` names — REMOVE the requirement governing\n // the old id — ended in this error, followable only by deleting the\n // requirement's Operations: line, which nothing said (review\n // 2026-10-12, finding 4). And an id the service's living contract does\n // not define is not this service's to mark at all: a REMOVED consumer\n // requirement naming ANOTHER service's operation was told to retire it\n // from its own contract. Both exemptions are facts about a contract\n // loam READ: a living openapi.yaml that does not parse proves neither,\n // so the marker stays owed — the closed direction, and the answer this\n // check gave before either exemption existed. Skipping it there let a\n // REMOVED requirement with no marker pass at exit 0 (the contract\n // review, finding 3); `openapi.invalid` is still validate's finding\n // about the file itself.\n if (replaced.has(op) || (living !== undefined && living !== null && !living.has(op))) continue;\n // Distinguish \"no marker at all\" from \"a marker is there but loam cannot\n // read which operation it names\" — the first asks the author to write the\n // marker, the second to write the operationId, and telling somebody who\n // already wrote the marker that there is none sends them looking for a\n // file they are staring at.\n issues.push({\n severity: \"error\",\n code: \"openapi.remove-marker-missing\",\n subject: svc,\n message: anonymousMarkers.get(svc) === true\n ? `${svc}: REMOVED requirement governs '${op}', and its feature openapi.yaml carries an x-loam-remove: true marker with no operationId — name '${op}' on that marker`\n : `${svc}: REMOVED requirement governs '${op}', but its feature openapi.yaml has no matching x-loam-remove: true marker`,\n });\n }\n }\n return {\n reqOps, removedReqOps, removingOps, allMarkerIds, anonymousMarkers, featureApiOps, unreadableApis, revivalCandidates,\n displacedOps,\n };\n}\n"]}
|
|
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAwB,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAqB,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAItD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqDhD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAsB,EACtB,OAAwB,EACxB,OAAsB;IAEtB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,SAAS;QAC7B,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS;YAChC,CAAC,CAAC,iBAAiB,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAC9C,CAAC,CAAC,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;QACtC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBACzB,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC;oBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAChE,SAAS;YACX,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,WAAoB,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9G,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,UAAmB,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9G,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,yGAAyG;AACzG,KAAK,UAAU,MAAM,CAAC,CAAS,EAAE,OAAsB;IACrD,IAAI,CAAC;QACH,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,oBAAoB;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAsB,EACtB,QAA2B,EAC3B,MAAe,EACf,OAAsB;IAEtB,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACjD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAgC,CAAC;IAC5D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAgC,CAAC;IAC3D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuB,CAAC;IAClD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAoB,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgC,CAAC;IACtD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC;QAC5D,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/F,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzB,yEAAyE;QACzE,yEAAyE;QACzE,qEAAqE;QACrE,yEAAyE;QACzE,2BAA2B;QAC3B,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC,IAAI,KAAK,CAAC;YAAE,SAAS;QAChE,MAAM,UAAU,GAAG,CAAC,MAAM,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC9E,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC1D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,uEAAuE;YACvE,mEAAmE;YACnE,gEAAgE;YAChE,uEAAuE;YACvE,kEAAkE;YAClE,6CAA6C;YAC7C,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;gBAC9B,IAAI,SAAS,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC;oBAAE,SAAS;gBACxF,MAAM,CAAC,IAAI,CAAC;oBACV,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,gCAAgC;oBACtC,OAAO,EAAE,GAAG;oBACZ,OAAO,EAAE,GAAG,GAAG,8DAA8D,CAAC,8HAA8H,GAAG,gBAAgB;iBAChO,CAAC,CAAC;YACL,CAAC;YACD,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACtD,yEAAyE;QACzE,oEAAoE;QACpE,yEAAyE;QACzE,mEAAmE;QACnE,8BAA8B;QAC9B,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,gDAAgD,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,GAAG,oGAAoG;aAC5N,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAE9B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACjD,2EAA2E;QAC3E,sEAAsE;QACtE,yEAAyE;QACzE,kBAAkB;QAClB,MAAM,SAAS,GAAY,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACzE,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QAC3C,qEAAqE;QACrE,gEAAgE;QAChE,iEAAiE;QACjE,oEAAoE;QACpE,qEAAqE;QACrE,kEAAkE;QAClE,MAAM,UAAU,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACnH,MAAM,WAAW,GAAY,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC/E,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,aAAa,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CACrE,CAAC;QAEF,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,cAAc,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;YAC3F,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1E,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;gBACjG,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;oBAC5B,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAClF,CAAC;YACH,CAAC;QACH,CAAC;QAED,uEAAuE;QACvE,wEAAwE;QACxE,6DAA6D;QAC7D,uEAAuE;QACvE,qEAAqE;QACrE,yEAAyE;QACzE,oEAAoE;QACpE,mEAAmE;QACnE,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAClH,CAAC;QACF,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,UAAU,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACxG,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,SAAS,CAAC,QAAQ;aACf,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;aAC1F,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CACtB,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACjG,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,WAAW,EAAE,GAAG,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChG,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAEzB,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YAC9D,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,oCAAoC;gBAC1C,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,iCAAiC,IAAI,yEAAyE;aAC9H,CAAC,CAAC;QACL,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;YAC9B,mEAAmE;YACnE,oEAAoE;YACpE,iEAAiE;YACjE,mEAAmE;YACnE,iCAAiC;YACjC,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YACzD,IAAI,SAAS,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC;gBAAE,SAAS;YACxF,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,gCAAgC;gBACtC,OAAO,EAAE,GAAG;gBACZ,OAAO,EAAE,GAAG,GAAG,8DAA8D,CAAC,+HAA+H;aAC9M,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AACpF,CAAC","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"]}
|