@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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The revival candidates: every identity — an operationId, or a message
|
|
3
|
+
* name — that the contract the archive would WRITE answers at an address
|
|
4
|
+
* the living contract does not. Split from ./revived.ts, which grades them,
|
|
5
|
+
* along the seam between the two questions: what the merge would leave, and
|
|
6
|
+
* which of it an archived feature took out.
|
|
7
|
+
*
|
|
8
|
+
* The question is asked of the contract the archive would WRITE: the
|
|
9
|
+
* archive's own merge runs over the two texts (../merged.ts), and where the
|
|
10
|
+
* result answers each operationId and message name is compared with where
|
|
11
|
+
* the living contract answers it now. This once re-derived the merge from
|
|
12
|
+
* the delta, case by case — any removal marker naming the id excused a quote,
|
|
13
|
+
* another NAMED operation at the id's address made a swap — and every review
|
|
14
|
+
* found a shape the derivation got wrong: a marker at an address the id never
|
|
15
|
+
* lived at (it deletes nothing, so the second address stays), a replacement
|
|
16
|
+
* with no operationId (it replaces all the same, so there is no second
|
|
17
|
+
* address), a retirement whose address became a second one when a later
|
|
18
|
+
* feature re-added the id elsewhere. The merged contract answers each of
|
|
19
|
+
* them by construction.
|
|
20
|
+
*
|
|
21
|
+
* The same comparison read the other way round answers the removal gate's
|
|
22
|
+
* blind spot: a living operationId the merged contract answers nowhere,
|
|
23
|
+
* with no marker naming it, was taken out by an overwrite (`Displacement`).
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync } from "node:fs";
|
|
26
|
+
import { readOr } from "../../../openapi/contract-text.js";
|
|
27
|
+
import { slotKey } from "../../../asyncapi/digest.js";
|
|
28
|
+
import { operationAddress } from "../archived.js";
|
|
29
|
+
import { mergedOpenapiDoc } from "../merged.js";
|
|
30
|
+
const opsPlaced = (ops) => ops.filter((op) => !op.remove).map((op) => [op.id, operationAddress(op)]);
|
|
31
|
+
const messagesPlaced = (all) => all.filter((m) => m.remove !== true).map((m) => [m.name, m.slot]);
|
|
32
|
+
/**
|
|
33
|
+
* Every (identity, address) the merged contract has and the living one lacks,
|
|
34
|
+
* in the merged contract's order. `unnamed` holds the living addresses an
|
|
35
|
+
* entry with no identity stands at — an operation with no operationId; the
|
|
36
|
+
* event axis has none.
|
|
37
|
+
*/
|
|
38
|
+
function arrivals(living, merged, markable, unnamed = new Set()) {
|
|
39
|
+
const where = (all, id) => all.filter(([i]) => i === id).map(([, at]) => at);
|
|
40
|
+
return merged.flatMap(([id, at]) => {
|
|
41
|
+
const livesAt = where(living, id);
|
|
42
|
+
if (livesAt.includes(at))
|
|
43
|
+
return [];
|
|
44
|
+
const besides = where(merged, id).filter((b) => b !== at);
|
|
45
|
+
const replaces = living.find(([, address]) => address === at)?.[0];
|
|
46
|
+
const occupant = replaces !== undefined ? { replaces } : unnamed.has(at) ? { replacesUnnamed: true } : {};
|
|
47
|
+
return [{ id, at, livesAt, besides, markable: besides.every((b) => markable(b, livesAt)), ...occupant }];
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The OpenAPI candidates: the merged contract against the living one. An
|
|
52
|
+
* unreadable side proves nothing (`openapi.invalid` owns it), nor does a text
|
|
53
|
+
* that does not decode, so either yields none; neither does a merge the
|
|
54
|
+
* archive refuses (`merge-failed`, which `--approve` cannot pass). The merge
|
|
55
|
+
* runs only when some definition in the delta is not living at its own
|
|
56
|
+
* address already: nothing else the merge writes can be new, so a delta that
|
|
57
|
+
* only restates what stands has nothing to compare and never pays for it.
|
|
58
|
+
* The displacements ride the same run, and need one more trigger: a named
|
|
59
|
+
* operation replacing a living one is itself a definition not living there,
|
|
60
|
+
* but one written with no operationId at a living operation's address is
|
|
61
|
+
* not, and it replaces that operation all the same.
|
|
62
|
+
*/
|
|
63
|
+
export async function operationArrivals(sides, context) {
|
|
64
|
+
const none = { operations: [], displaced: [] };
|
|
65
|
+
const { featPath, featDoc, livingPath, livingDoc, service } = sides;
|
|
66
|
+
if (featDoc.unreadable || livingDoc.unreadable)
|
|
67
|
+
return none;
|
|
68
|
+
const living = opsPlaced(livingDoc.ops);
|
|
69
|
+
const named = new Set(living.map(([, at]) => at));
|
|
70
|
+
const overNamed = featDoc.anonymousOps.some((op) => named.has(operationAddress(op)));
|
|
71
|
+
if (arrivals(living, opsPlaced(featDoc.ops), () => true).length === 0 && !overNamed)
|
|
72
|
+
return none;
|
|
73
|
+
const featureText = sides.featText ?? (await readOr(featPath, context));
|
|
74
|
+
const livingExists = existsSync(livingPath);
|
|
75
|
+
const livingText = livingExists ? await readOr(livingPath, context) : undefined;
|
|
76
|
+
if (featureText === undefined || (livingExists && livingText === undefined))
|
|
77
|
+
return none;
|
|
78
|
+
const merged = mergedOpenapiDoc({ livingText, livingDoc, featureText, service });
|
|
79
|
+
if (merged === undefined)
|
|
80
|
+
return none;
|
|
81
|
+
// Every address the delta spells — a removal marker, an operation, named
|
|
82
|
+
// or not — holds what the delta spells there, so no marker can stand
|
|
83
|
+
// beside it.
|
|
84
|
+
const spelled = new Set([...featDoc.ops, ...featDoc.anonymousRemovals, ...featDoc.anonymousOps].map(operationAddress));
|
|
85
|
+
const placed = opsPlaced(merged.ops);
|
|
86
|
+
const unnamed = new Set(livingDoc.anonymousOps.map(operationAddress));
|
|
87
|
+
return {
|
|
88
|
+
operations: arrivals(living, placed, (b, livesAt) => livesAt.includes(b) && !spelled.has(b), unnamed),
|
|
89
|
+
displaced: displacements(living, placed, new Set(featDoc.ops.filter((op) => op.remove).map((op) => op.id))),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The living identities the merged contract answers nowhere, minus every one
|
|
94
|
+
* a removal marker names: those are the removal gate's already — retired,
|
|
95
|
+
* relocated, or a marker the exactness checks refuse — and grading them here
|
|
96
|
+
* too would file one loss under two findings.
|
|
97
|
+
*/
|
|
98
|
+
function displacements(living, merged, marked) {
|
|
99
|
+
const answered = new Set(merged.map(([id]) => id));
|
|
100
|
+
const gone = [...new Set(living.map(([id]) => id))].filter((id) => !answered.has(id) && !marked.has(id));
|
|
101
|
+
return gone.map((id) => ({
|
|
102
|
+
id,
|
|
103
|
+
at: living.filter(([i]) => i === id).map(([, address]) => {
|
|
104
|
+
const by = merged.find(([, at]) => at === address)?.[0];
|
|
105
|
+
return by === undefined ? { address } : { address, by };
|
|
106
|
+
}),
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The event axis's candidates. A message is graded by its NAME, not by its
|
|
111
|
+
* key: the name is what every join reads, so a retired message redeclared
|
|
112
|
+
* under a new key is the same message back, and a new message reusing a dead
|
|
113
|
+
* key is not. A living side that does not answer (./event-pair.ts) proves
|
|
114
|
+
* nothing, so it yields none; a merge the pair refuses grades no message.
|
|
115
|
+
* The slots are read off the delta, and they are the merged contract's new
|
|
116
|
+
* slots exactly: the merge writes every slot the delta declares, bar a quote,
|
|
117
|
+
* which has a living counterpart.
|
|
118
|
+
*/
|
|
119
|
+
export function eventArrivals(read) {
|
|
120
|
+
const { featDoc, featSlots, livingDoc, livingSlots } = read;
|
|
121
|
+
if (!read.livingAnswers || featDoc.unreadable || livingDoc.unreadable)
|
|
122
|
+
return { slots: [], messages: [] };
|
|
123
|
+
const slots = featSlots.filter((s) => !s.remove && s.section !== "components.messages" && !livingSlots.has(slotKey(s)));
|
|
124
|
+
const merged = read.merged();
|
|
125
|
+
if (merged === undefined)
|
|
126
|
+
return { slots, messages: [] };
|
|
127
|
+
const spelled = new Set(featSlots.filter((s) => s.section === "components.messages").map((s) => `${s.section}.${s.key}`));
|
|
128
|
+
const markable = (b, livesAt) => b.startsWith("channels.") || (livesAt.includes(b) && !spelled.has(b));
|
|
129
|
+
return { slots, messages: arrivals(messagesPlaced(livingDoc.messages), messagesPlaced(merged.doc.messages), markable), mergedRoot: merged.root };
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=arrivals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrivals.js","sourceRoot":"","sources":["../../../../../src/core/coherence/retired/revival/arrivals.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The revival candidates: every identity — an operationId, or a message\n * name — that the contract the archive would WRITE answers at an address\n * the living contract does not. Split from ./revived.ts, which grades them,\n * along the seam between the two questions: what the merge would leave, and\n * which of it an archived feature took out.\n *\n * The question is asked of the contract the archive would WRITE: the\n * archive's own merge runs over the two texts (../merged.ts), and where the\n * result answers each operationId and message name is compared with where\n * the living contract answers it now. This once re-derived the merge from\n * the delta, case by case — any removal marker naming the id excused a quote,\n * another NAMED operation at the id's address made a swap — and every review\n * found a shape the derivation got wrong: a marker at an address the id never\n * lived at (it deletes nothing, so the second address stays), a replacement\n * with no operationId (it replaces all the same, so there is no second\n * address), a retirement whose address became a second one when a later\n * feature re-added the id elsewhere. The merged contract answers each of\n * them by construction.\n *\n * The same comparison read the other way round answers the removal gate's\n * blind spot: a living operationId the merged contract answers nowhere,\n * with no marker naming it, was taken out by an overwrite (`Displacement`).\n */\nimport { existsSync } from \"node:fs\";\nimport type { OpenapiDoc, Operation } from \"../../../openapi/doc.js\";\nimport { readOr } from \"../../../openapi/contract-text.js\";\nimport { slotKey, type AsyncapiSlot } from \"../../../asyncapi/digest.js\";\nimport type { EventMessage } from \"../../../asyncapi/model.js\";\nimport type { FleetContext } from \"../../../fleet-context.js\";\nimport { operationAddress } from \"../archived.js\";\nimport type { EventPairRead, PairSides } from \"../event-pair.js\";\nimport { mergedOpenapiDoc } from \"../merged.js\";\n\n/**\n * One identity — an operationId, or a message name — that the merged\n * contract answers at an address the living contract does not. The only\n * shape a revival can take: everything else the merge writes is already\n * living where it stands.\n */\nexport interface Arrival {\n id: string;\n /** The new address: `METHOD /path`, or the slot a message is declared in. */\n at: string;\n /** Where the living contract answers the identity now — empty for nowhere. */\n livesAt: string[];\n /** Every OTHER address the merged contract answers it at. */\n besides: string[];\n /**\n * What the living contract answers at `at` now — another operationId, or\n * another message name — which the merge replaces there: a slot holds one\n * entry. Absent where nothing does. A finding that called such an address\n * one \"the living contract no longer has\" sent its reader to a remedy that\n * deleted the other entry with nothing naming it.\n */\n replaces?: string;\n /**\n * Set where what the living contract answers at `at` is an operation\n * written with NO operationId: the merge replaces it there all the same,\n * and no id can name it, so `replaces` stays absent. The reader dropped\n * such operations outright once, and the finding called their address one\n * \"the living contract no longer has\" — the very defect `replaces` was\n * added for, through the one door it left open.\n */\n replacesUnnamed?: true;\n /**\n * Can this same delta take it out of every one of `besides`? A living\n * address the delta spells nothing at takes a removal marker; a declaration\n * inline under a channel is taken out by restating or retiring the channel\n * (`moveBackRemedy`). A key the delta already spells holds what it spells,\n * and a marker cannot be written beside it — the remedy that once named\n * one there could not be followed.\n */\n markable: boolean;\n}\n\n/**\n * A living operationId the merged contract answers NOWHERE although no\n * removal marker in the delta names it: another operation now stands at\n * every address it held, so the merge — which writes per method and path —\n * replaced it there. The removal gate's own question\n * (`openapi.remove-op-consumed`, ../../consumed/operations.ts) asked of the one way to\n * take an operation out of a contract that carries no marker for it to read:\n * a delta defining\n * `fetchOrder` where `getOrder` lives archived at exit 0 while a landscape\n * edge still called `getOrder`, and the next `validate` reported the\n * contract broken (`spine.op-undefined`) on a repository nobody in the\n * feature touched. Read off the merged contract, never re-derived from the\n * delta, for this module's reason.\n */\nexport interface Displacement {\n /** The living operationId the contract the archive would write no longer defines. */\n id: string;\n /**\n * Every address it lives at now, with what the merged contract answers\n * there — the operationId that replaces it, or nothing for an operation\n * written without one, which the reader cannot name.\n */\n at: ReadonlyArray<{ address: string; by?: string }>;\n}\n\n/** What the merged contract answers that the living one does not, and what it stops answering. */\nexport interface OperationArrivals {\n /** Every (operationId, address) the merged contract has and the living one lacks — the revival candidates. */\n operations: Arrival[];\n /** Every living operationId the merged contract answers nowhere, no removal marker naming it. */\n displaced: Displacement[];\n}\n\n/** An identity and one address a contract answers it at. */\ntype Placed = readonly [id: string, at: string];\n\nconst opsPlaced = (ops: readonly Operation[]): Placed[] => ops.filter((op) => !op.remove).map((op) => [op.id, operationAddress(op)]);\nconst messagesPlaced = (all: readonly EventMessage[]): Placed[] => all.filter((m) => m.remove !== true).map((m) => [m.name, m.slot]);\n\n/**\n * Every (identity, address) the merged contract has and the living one lacks,\n * in the merged contract's order. `unnamed` holds the living addresses an\n * entry with no identity stands at — an operation with no operationId; the\n * event axis has none.\n */\nfunction arrivals(\n living: readonly Placed[],\n merged: readonly Placed[],\n markable: (address: string, livesAt: readonly string[]) => boolean,\n unnamed: ReadonlySet<string> = new Set(),\n): Arrival[] {\n const where = (all: readonly Placed[], id: string): string[] => all.filter(([i]) => i === id).map(([, at]) => at);\n return merged.flatMap(([id, at]) => {\n const livesAt = where(living, id);\n if (livesAt.includes(at)) return [];\n const besides = where(merged, id).filter((b) => b !== at);\n const replaces = living.find(([, address]) => address === at)?.[0];\n const occupant = replaces !== undefined ? { replaces } : unnamed.has(at) ? { replacesUnnamed: true as const } : {};\n return [{ id, at, livesAt, besides, markable: besides.every((b) => markable(b, livesAt)), ...occupant }];\n });\n}\n\n/**\n * The OpenAPI candidates: the merged contract against the living one. An\n * unreadable side proves nothing (`openapi.invalid` owns it), nor does a text\n * that does not decode, so either yields none; neither does a merge the\n * archive refuses (`merge-failed`, which `--approve` cannot pass). The merge\n * runs only when some definition in the delta is not living at its own\n * address already: nothing else the merge writes can be new, so a delta that\n * only restates what stands has nothing to compare and never pays for it.\n * The displacements ride the same run, and need one more trigger: a named\n * operation replacing a living one is itself a definition not living there,\n * but one written with no operationId at a living operation's address is\n * not, and it replaces that operation all the same.\n */\nexport async function operationArrivals(sides: PairSides<OpenapiDoc>, context?: FleetContext): Promise<OperationArrivals> {\n const none: OperationArrivals = { operations: [], displaced: [] };\n const { featPath, featDoc, livingPath, livingDoc, service } = sides;\n if (featDoc.unreadable || livingDoc.unreadable) return none;\n const living = opsPlaced(livingDoc.ops);\n const named = new Set(living.map(([, at]) => at));\n const overNamed = featDoc.anonymousOps.some((op) => named.has(operationAddress(op)));\n if (arrivals(living, opsPlaced(featDoc.ops), () => true).length === 0 && !overNamed) return none;\n const featureText = sides.featText ?? (await readOr(featPath, context));\n const livingExists = existsSync(livingPath);\n const livingText = livingExists ? await readOr(livingPath, context) : undefined;\n if (featureText === undefined || (livingExists && livingText === undefined)) return none;\n const merged = mergedOpenapiDoc({ livingText, livingDoc, featureText, service });\n if (merged === undefined) return none;\n // Every address the delta spells — a removal marker, an operation, named\n // or not — holds what the delta spells there, so no marker can stand\n // beside it.\n const spelled = new Set([...featDoc.ops, ...featDoc.anonymousRemovals, ...featDoc.anonymousOps].map(operationAddress));\n const placed = opsPlaced(merged.ops);\n const unnamed = new Set(livingDoc.anonymousOps.map(operationAddress));\n return {\n operations: arrivals(living, placed, (b, livesAt) => livesAt.includes(b) && !spelled.has(b), unnamed),\n displaced: displacements(living, placed, new Set(featDoc.ops.filter((op) => op.remove).map((op) => op.id))),\n };\n}\n\n/**\n * The living identities the merged contract answers nowhere, minus every one\n * a removal marker names: those are the removal gate's already — retired,\n * relocated, or a marker the exactness checks refuse — and grading them here\n * too would file one loss under two findings.\n */\nfunction displacements(living: readonly Placed[], merged: readonly Placed[], marked: ReadonlySet<string>): Displacement[] {\n const answered = new Set(merged.map(([id]) => id));\n const gone = [...new Set(living.map(([id]) => id))].filter((id) => !answered.has(id) && !marked.has(id));\n return gone.map((id) => ({\n id,\n at: living.filter(([i]) => i === id).map(([, address]) => {\n const by = merged.find(([, at]) => at === address)?.[0];\n return by === undefined ? { address } : { address, by };\n }),\n }));\n}\n\n/** What an event delta brings that the living contract does not have. */\nexport interface EventArrivals {\n /** `channels` and `operations` slots whose key the living contract lacks — their identity IS the key. */\n slots: AsyncapiSlot[];\n /** Message names at slots the living contract does not declare them in — the operation axis's arrivals. */\n messages: Arrival[];\n /**\n * The merged contract's tree, where the move-back remedy follows a `$ref`\n * back to the channel holding it (`moveBackRemedy`) — absent where the\n * merge refused, and then no message is graded.\n */\n mergedRoot?: unknown;\n}\n\n/**\n * The event axis's candidates. A message is graded by its NAME, not by its\n * key: the name is what every join reads, so a retired message redeclared\n * under a new key is the same message back, and a new message reusing a dead\n * key is not. A living side that does not answer (./event-pair.ts) proves\n * nothing, so it yields none; a merge the pair refuses grades no message.\n * The slots are read off the delta, and they are the merged contract's new\n * slots exactly: the merge writes every slot the delta declares, bar a quote,\n * which has a living counterpart.\n */\nexport function eventArrivals(read: EventPairRead): EventArrivals {\n const { featDoc, featSlots, livingDoc, livingSlots } = read;\n if (!read.livingAnswers || featDoc.unreadable || livingDoc.unreadable) return { slots: [], messages: [] };\n const slots = featSlots.filter((s) => !s.remove && s.section !== \"components.messages\" && !livingSlots.has(slotKey(s)));\n const merged = read.merged();\n if (merged === undefined) return { slots, messages: [] };\n const spelled = new Set(featSlots.filter((s) => s.section === \"components.messages\").map((s) => `${s.section}.${s.key}`));\n const markable = (b: string, livesAt: readonly string[]): boolean =>\n b.startsWith(\"channels.\") || (livesAt.includes(b) && !spelled.has(b));\n return { slots, messages: arrivals(messagesPlaced(livingDoc.messages), messagesPlaced(merged.doc.messages), markable), mergedRoot: merged.root };\n}\n"]}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The revival findings: a feature delta that would bring back what an
|
|
3
|
+
* ARCHIVED feature took out of the living contract — an operation, slot or
|
|
4
|
+
* message it retired, or an address it moved one away from, which would put
|
|
5
|
+
* it back there beside where it lives now.
|
|
6
|
+
*
|
|
7
|
+
* `openapi.op-revived` and `asyncapi.slot-revived` — one code per axis, because
|
|
8
|
+
* a caller acts the same way whichever slot section came back. Both are a
|
|
9
|
+
* warning that GATES archive, `openapi.baseline-missing`'s doctrine: the
|
|
10
|
+
* document is legal, and re-adding a retired operation can be exactly what a
|
|
11
|
+
* feature means (a rollback of a retirement, a deprecation reversed), so
|
|
12
|
+
* `--approve` carries the call. What it must never be again is SILENT — the
|
|
13
|
+
* merge cannot tell a quote of a dead operation from an addition, and treated
|
|
14
|
+
* every one as an addition.
|
|
15
|
+
*
|
|
16
|
+
* The candidates — what the contract the archive would WRITE answers that
|
|
17
|
+
* the living one does not (./arrivals.ts) — are computed where both
|
|
18
|
+
* documents are already in hand, in the feature gate's two walks
|
|
19
|
+
* (../../declared.ts, ../../events/declared.ts), and only graded here, so
|
|
20
|
+
* the archive walk (../archived.ts) is paid for only when the merged
|
|
21
|
+
* contract answers something at an address the living one does not.
|
|
22
|
+
* `serviceRevivals` asks the same two functions per axis standalone, for
|
|
23
|
+
* `loam rebase`, which reads no gate — on the event axis through the gate's
|
|
24
|
+
* own reading of the pair (../event-pair.ts) — so the two can never grade one
|
|
25
|
+
* tree two ways.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync } from "node:fs";
|
|
28
|
+
import { featureSpecPaths } from "../../../repo/paths.js";
|
|
29
|
+
import { locateServicePaths } from "../../../repo/service-target.js";
|
|
30
|
+
import { readOpenapi } from "../../../openapi/doc.js";
|
|
31
|
+
import { readAsyncapi } from "../../../asyncapi/read.js";
|
|
32
|
+
import { entriesOf } from "../../../asyncapi/digest.js";
|
|
33
|
+
import { collectRefs } from "../../../openapi/depth.js";
|
|
34
|
+
import { retiredKey } from "../archived.js";
|
|
35
|
+
import { readEventPair } from "../event-pair.js";
|
|
36
|
+
import { eventArrivals, operationArrivals } from "./arrivals.js";
|
|
37
|
+
/** The two remedies, spelled once: which one applies is the human's call, never loam's. */
|
|
38
|
+
const REMEDY = "Delete the quote if you only restated it; archive with --approve if the feature brings it back on purpose.";
|
|
39
|
+
/**
|
|
40
|
+
* The vacations that make one arrival a revival — undefined when it is none.
|
|
41
|
+
* Either the living contract answers the identity nowhere, and an archived
|
|
42
|
+
* feature retired it (anywhere: a retired operation re-added at a new path is
|
|
43
|
+
* the same operation back, and a message is its name wherever it is
|
|
44
|
+
* declared) or moved it away from this very address; or the merged contract
|
|
45
|
+
* answers it at two or more addresses, and an archived feature retired it at,
|
|
46
|
+
* or moved it away from, this very address — an old address resurrected
|
|
47
|
+
* beside the current one. An identity the living contract still holds that
|
|
48
|
+
* the merged contract answers at exactly one address — a move back, a swap,
|
|
49
|
+
* a replacement with no operationId — is never a revival, and neither is an
|
|
50
|
+
* address no archived feature vacated. One the living contract holds nowhere
|
|
51
|
+
* is the first case at one address too: this once said "exactly one address
|
|
52
|
+
* is never a revival" unqualified, and a quote of an address a move vacated,
|
|
53
|
+
* after a hand edit took the id out of where the move put it, is one.
|
|
54
|
+
*
|
|
55
|
+
* Any archived feature — the one asking is never among them: coherence
|
|
56
|
+
* grades only features in flight (status/feature/feature.ts), so a vacation
|
|
57
|
+
* bearing the asking feature's id is another directory carrying the same
|
|
58
|
+
* id, and as real as anyone's. Excluding it by id, as this once did, could
|
|
59
|
+
* only hide a revival.
|
|
60
|
+
*/
|
|
61
|
+
function revivalOf({ at, livesAt, besides }, vacations) {
|
|
62
|
+
if (vacations === undefined)
|
|
63
|
+
return undefined;
|
|
64
|
+
const [first, ...rest] = vacations.filter((v) => livesAt.length === 0 ? v.to.length === 0 || v.at === at : besides.length > 0 && v.at === at);
|
|
65
|
+
return first === undefined ? undefined : [first, ...rest];
|
|
66
|
+
}
|
|
67
|
+
/** The operation arrivals that are revivals — each named for what happened, retired or vacated. */
|
|
68
|
+
export function revivedOperations(service, arrived, index) {
|
|
69
|
+
const issues = [];
|
|
70
|
+
for (const arrival of arrived) {
|
|
71
|
+
const by = revivalOf(arrival, index.operations.get(retiredKey(service, arrival.id)));
|
|
72
|
+
if (by === undefined)
|
|
73
|
+
continue;
|
|
74
|
+
const { id, at, livesAt, besides } = arrival;
|
|
75
|
+
const what = livesAt.length === 0
|
|
76
|
+
? `'${id}' (${at}), ${broughtBack(by, replacing(arrival, "holds"))}`
|
|
77
|
+
// A second address, not the operation back: it is living, so the
|
|
78
|
+
// remedy is which address to drop — never "bring it back".
|
|
79
|
+
: `'${id}' at ${at}, ${occupant(arrival) === undefined ? "an address the living contract no longer has" : "an address it no longer lives at"} — ${vacators(by, " there")}${present(by, livesAt, "lives")}, and archiving this delta would put it back at ${at} beside ${besides.join(" and ")}: one operationId at ${count(besides.length + 1)} addresses.${replacing(arrival, "holds")} ${dropOne(arrival, `add a removal marker for '${id}' at ${besides.join(" and ")} to this same delta${keeping(arrival, "address")}`)}`;
|
|
80
|
+
issues.push({ severity: "warn", gates: true, code: "openapi.op-revived", subject: service, message: `${service}: this feature's openapi.yaml spells ${what}` });
|
|
81
|
+
}
|
|
82
|
+
return issues;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* How a delta takes a message name OUT of the places it is declared — the
|
|
86
|
+
* move-back remedy, per place, toward wherever the message is moved back to
|
|
87
|
+
* (`to`). A `components.messages` key is a slot, and a removal marker there
|
|
88
|
+
* retires what it declares. A declaration INLINE under a channel is not: it
|
|
89
|
+
* is the channel slot's interior (SCHEMA.md), so a marker nested on it
|
|
90
|
+
* retires nothing — `asyncapi.remove-marker-inline`, and the deep strip
|
|
91
|
+
* publishes the message without the marker. This once said "retire it at
|
|
92
|
+
* channels.<ck>.messages.<mk>", a remedy that, followed exactly, changed
|
|
93
|
+
* nothing and drew a second refusal. The channel is the unit: restated with
|
|
94
|
+
* the message as a `$ref` (an upsert replaces the interior), or retired. The
|
|
95
|
+
* channel key is read the way the archive plan's remove-marker-inline
|
|
96
|
+
* message reads it, so the two name the same channel.
|
|
97
|
+
*
|
|
98
|
+
* A `components.messages` key is retired by its marker, but a channel (or an
|
|
99
|
+
* operation) of the contract the merge would leave (`root`) that still
|
|
100
|
+
* `$ref`s it would point at nothing: its send stops sending the message,
|
|
101
|
+
* `asyncapi.remove-message-consumed` where the fleet consumes it. This once
|
|
102
|
+
* said only "retire it at components.messages.<K2>", a remedy that, followed
|
|
103
|
+
* exactly, drew that error — so every slot still pointing there is named,
|
|
104
|
+
* to restate with the `$ref` pointed at `to`.
|
|
105
|
+
*/
|
|
106
|
+
export function moveBackRemedy(declaredAt, to, root) {
|
|
107
|
+
const steps = declaredAt.map((slot) => {
|
|
108
|
+
if (!slot.startsWith("channels.")) {
|
|
109
|
+
const holders = pointingAt(root, slot);
|
|
110
|
+
return holders.length === 0 ? `retire it at ${slot}` : `retire it at ${slot}, and restate ${holders.join(" and ")} with every $ref to ${slot} pointed at ${to}`;
|
|
111
|
+
}
|
|
112
|
+
const channel = slot.slice("channels.".length, slot.lastIndexOf(".messages."));
|
|
113
|
+
return `restate channel ${channel} with a $ref to ${to}, or retire channel ${channel}`;
|
|
114
|
+
});
|
|
115
|
+
return [...new Set(steps)].join("; and ");
|
|
116
|
+
}
|
|
117
|
+
/** The channels and operations of `root` holding a `$ref` to one `components.messages` slot, at any depth. */
|
|
118
|
+
function pointingAt(root, slot) {
|
|
119
|
+
const key = slot.slice("components.messages.".length);
|
|
120
|
+
// JSON Pointer escaping, `~` before `/`, so a key spelling either is the pointer that reaches it.
|
|
121
|
+
const pointer = `#/components/messages/${key.replace(/~/g, "~0").replace(/\//g, "~1")}`;
|
|
122
|
+
const sections = root !== null && typeof root === "object" ? root : {};
|
|
123
|
+
return ["channels", "operations"].flatMap((section) => entriesOf(sections[section])
|
|
124
|
+
.filter(([, node]) => collectRefs(node).includes(pointer))
|
|
125
|
+
.map(([key]) => `${section === "channels" ? "channel" : "operation"} ${key}`));
|
|
126
|
+
}
|
|
127
|
+
/** The event arrivals that are revivals — `revivedOperations`' rule, on the slot and the message name. */
|
|
128
|
+
export function revivedEvents(service, arrived, index) {
|
|
129
|
+
const issues = [];
|
|
130
|
+
const revive = (what) => {
|
|
131
|
+
issues.push({ severity: "warn", gates: true, code: "asyncapi.slot-revived", subject: service, message: `${service}: this feature's asyncapi.yaml declares ${what}` });
|
|
132
|
+
};
|
|
133
|
+
const revivedChannels = new Set();
|
|
134
|
+
for (const slot of arrived.slots) {
|
|
135
|
+
const by = index.slots.get(retiredKey(service, slot.section, slot.key));
|
|
136
|
+
if (by === undefined)
|
|
137
|
+
continue;
|
|
138
|
+
if (slot.section === "channels")
|
|
139
|
+
revivedChannels.add(slot.key);
|
|
140
|
+
revive(`${slot.section}.${slot.key}, ${broughtBack(by)}`);
|
|
141
|
+
}
|
|
142
|
+
for (const arrival of arrived.messages) {
|
|
143
|
+
// A message declared inline under a channel that is itself coming back is
|
|
144
|
+
// that channel's interior, and the channel's finding already names the
|
|
145
|
+
// revival — one act, one finding. Only while the merged contract declares
|
|
146
|
+
// the name nowhere else: where it does, the channel's finding says nothing
|
|
147
|
+
// of the second place, and this was once skipped all the same — a stale
|
|
148
|
+
// delta restating a channel whose inline message an archived feature
|
|
149
|
+
// re-declared under components archived under --approve into
|
|
150
|
+
// `asyncapi.duplicate-message`.
|
|
151
|
+
if (arrival.besides.length === 0 && [...revivedChannels].some((ck) => arrival.at.startsWith(`channels.${ck}.messages.`)))
|
|
152
|
+
continue;
|
|
153
|
+
const by = revivalOf(arrival, index.messages.get(retiredKey(service, arrival.id)));
|
|
154
|
+
if (by === undefined)
|
|
155
|
+
continue;
|
|
156
|
+
const { id, at, livesAt, besides } = arrival;
|
|
157
|
+
revive(livesAt.length === 0
|
|
158
|
+
? `message '${id}' (${at}), ${broughtBack(by, replacing(arrival, "declares"))}`
|
|
159
|
+
: `message '${id}' at ${at}, ${arrival.replaces === undefined ? "a key the living contract no longer has" : "a key it is no longer declared at"} — ${vacators(by, " there")}${present(by, livesAt, "is declared")}, and archiving this delta would declare it at ${at} beside ${besides.join(" and ")}: one message name in ${count(besides.length + 1)} places.${replacing(arrival, "declares")} ${dropOne(arrival, `${moveBackRemedy(besides, at, arrived.mergedRoot)} in this same delta${keeping(arrival, "key")}`)}`);
|
|
160
|
+
}
|
|
161
|
+
return issues;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Both axes' revival findings for one service, read standalone — the gate's
|
|
165
|
+
* two walks compute the same arrivals from the documents they already hold;
|
|
166
|
+
* `loam rebase` holds none, and must not pin a revived operation as if this
|
|
167
|
+
* feature were adding it. One detection, the gate's: `operationArrivals` on
|
|
168
|
+
* the contract axis, and on the event axis `readEventPair` and
|
|
169
|
+
* `eventArrivals`, the walk's own reader and candidates. Asked of the text
|
|
170
|
+
* rebase will WRITE (`planned`), not the text it found: its repin can turn a
|
|
171
|
+
* skipped quote into an edit, and the revival that edit makes is the one the
|
|
172
|
+
* next validate reports — this once ran over the delta as on disk, before
|
|
173
|
+
* the repin, and stayed silent about it.
|
|
174
|
+
*/
|
|
175
|
+
export async function serviceRevivals(request) {
|
|
176
|
+
const { docsDir, featureDir, service, retired, context, planned } = request;
|
|
177
|
+
const feature = featureSpecPaths(featureDir, service);
|
|
178
|
+
const living = await locateServicePaths(docsDir, service, context);
|
|
179
|
+
const issues = [];
|
|
180
|
+
if (existsSync(feature.openapi)) {
|
|
181
|
+
const featDoc = await readOpenapi(feature.openapi, context);
|
|
182
|
+
const livingDoc = await readOpenapi(living.openapi, context);
|
|
183
|
+
const sides = { featPath: feature.openapi, featDoc, livingPath: living.openapi, livingDoc, service, featText: planned?.get(feature.openapi) };
|
|
184
|
+
const ops = (await operationArrivals(sides, context)).operations;
|
|
185
|
+
if (ops.length > 0)
|
|
186
|
+
issues.push(...revivedOperations(service, ops, await retired()));
|
|
187
|
+
}
|
|
188
|
+
if (!existsSync(feature.asyncapi))
|
|
189
|
+
return issues;
|
|
190
|
+
const featDoc = await readAsyncapi(feature.asyncapi, context);
|
|
191
|
+
// An unreadable delta is `asyncapi.invalid` in the gate, which grades
|
|
192
|
+
// nothing else against it; the guards on the living side are the pair's.
|
|
193
|
+
if (featDoc.unreadable)
|
|
194
|
+
return issues;
|
|
195
|
+
const livingDoc = await readAsyncapi(living.asyncapi, context);
|
|
196
|
+
const sides = { featPath: feature.asyncapi, featDoc, livingPath: living.asyncapi, livingDoc, service, featText: planned?.get(feature.asyncapi) };
|
|
197
|
+
const events = eventArrivals(await readEventPair(sides, context));
|
|
198
|
+
if (events.slots.length > 0 || events.messages.length > 0)
|
|
199
|
+
issues.push(...revivedEvents(service, events, await retired()));
|
|
200
|
+
return issues;
|
|
201
|
+
}
|
|
202
|
+
/** The retired shape: absent from living, so the merge reads it as an addition. */
|
|
203
|
+
function broughtBack(by, replaced = "") {
|
|
204
|
+
return `which the living contract no longer has — ${vacators(by, "")}, and archiving this delta would put it back.${replaced} ${REMEDY}`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* How a finding names the entry living at the arrival's address — its
|
|
208
|
+
* identity quoted, or, for an operation written with no operationId, what it
|
|
209
|
+
* is; undefined where nothing lives there.
|
|
210
|
+
*/
|
|
211
|
+
function occupant({ replaces, replacesUnnamed }) {
|
|
212
|
+
return replaces !== undefined ? `'${replaces}'` : replacesUnnamed === true ? "an operation with no operationId" : undefined;
|
|
213
|
+
}
|
|
214
|
+
/** The entry the merge would replace at the arrival's address, said as a sentence of its own — or nothing. */
|
|
215
|
+
function replacing(arrival, verb) {
|
|
216
|
+
const there = occupant(arrival);
|
|
217
|
+
return there === undefined ? "" : ` ${arrival.at} now ${verb} ${there}, and archiving this delta replaces it there.`;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The duplicate shape's remedy, naming the address to drop: the quote, if it
|
|
221
|
+
* only restated the entry; to move it back on purpose, every other address —
|
|
222
|
+
* through `moveBack` when this delta can take the entry out of all of them,
|
|
223
|
+
* and otherwise only by keeping both under `--approve`, never a marker at a
|
|
224
|
+
* key the delta already spells.
|
|
225
|
+
*/
|
|
226
|
+
function dropOne({ at, besides, markable }, moveBack) {
|
|
227
|
+
const keep = `Delete the quote at ${at} if you only restated it, and make any change at ${besides.join(" and ")}`;
|
|
228
|
+
return markable
|
|
229
|
+
? `${keep}; to move it back on purpose, ${moveBack}.`
|
|
230
|
+
: `${keep}; archive with --approve if the feature brings it back there on purpose.`;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* A move back over another living entry takes that entry out too, unless the
|
|
234
|
+
* same delta puts it elsewhere — said in the remedy, because followed as it
|
|
235
|
+
* once read, the move back reached a clean gate and an archive that deleted
|
|
236
|
+
* the other entry with nothing naming it.
|
|
237
|
+
*/
|
|
238
|
+
function keeping(arrival, place) {
|
|
239
|
+
const there = occupant(arrival);
|
|
240
|
+
if (there === undefined)
|
|
241
|
+
return "";
|
|
242
|
+
// An operation nobody can name is still what stands there, and the delta
|
|
243
|
+
// can restate it elsewhere all the same — said by what it is, not by an id.
|
|
244
|
+
return arrival.replaces === undefined
|
|
245
|
+
? ` — and, if the operation with no operationId is to stay, define it at another ${place} in it`
|
|
246
|
+
: ` — and define ${there} at another ${place} in it if it is to stay`;
|
|
247
|
+
}
|
|
248
|
+
function count(n) {
|
|
249
|
+
return n === 2 ? "two" : String(n);
|
|
250
|
+
}
|
|
251
|
+
/** "FEAT-112", "FEAT-112 and FEAT-140", "FEAT-1, FEAT-2 and FEAT-3". */
|
|
252
|
+
function idList([first, ...rest]) {
|
|
253
|
+
return rest.reduce((said, id, i) => `${said}${i === rest.length - 1 ? " and" : ","} ${id}`, first);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Who took an entry out, and how — "archived FEAT-112 retired it",
|
|
257
|
+
* "archived FEAT-200 moved it to POST /y" — each feature once, in id order,
|
|
258
|
+
* movers that put it in the same place sharing one clause and retirers
|
|
259
|
+
* last. `there` places a retirement at the address the finding names, where
|
|
260
|
+
* the entry lives on elsewhere and the difference matters.
|
|
261
|
+
*/
|
|
262
|
+
function vacators(by, there) {
|
|
263
|
+
const byDestination = new Map();
|
|
264
|
+
const retirers = [];
|
|
265
|
+
for (const { by: id, to } of by) {
|
|
266
|
+
if (to.length === 0) {
|
|
267
|
+
if (!retirers.includes(id))
|
|
268
|
+
retirers.push(id);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const where = to.join(" and ");
|
|
272
|
+
const ids = byDestination.get(where);
|
|
273
|
+
if (ids === undefined)
|
|
274
|
+
byDestination.set(where, [id]);
|
|
275
|
+
else if (!ids.includes(id))
|
|
276
|
+
ids.push(id);
|
|
277
|
+
}
|
|
278
|
+
const clauses = [...byDestination].map(([where, ids], i) => `${idList(ids)} ${i === 0 ? "moved it " : ""}to ${where}`);
|
|
279
|
+
const [first, ...rest] = retirers;
|
|
280
|
+
if (first !== undefined)
|
|
281
|
+
clauses.push(`${idList([first, ...rest])} retired it${there}`);
|
|
282
|
+
return `archived ${clauses.reduce((all, clause, i) => `${all}${i === clauses.length - 1 ? ", and" : ","} ${clause}`)}`;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Where the entry stands now — "…, where it lives now" when every vacation
|
|
286
|
+
* was a move to exactly there, and said apart otherwise ("; it lives at
|
|
287
|
+
* POST /z now"): the two part ways once anything lands after a move, and
|
|
288
|
+
* this once credited a later feature's address to the mover (`Vacated`).
|
|
289
|
+
* `verb` is how the axis says it — an operation "lives", a message "is
|
|
290
|
+
* declared".
|
|
291
|
+
*/
|
|
292
|
+
function present(by, livesAt, verb) {
|
|
293
|
+
const now = new Set(livesAt);
|
|
294
|
+
const unchanged = by.every(({ to }) => to.length > 0 && to.length === now.size && to.every((where) => now.has(where)));
|
|
295
|
+
return unchanged ? `, where it ${verb} now` : `; it ${verb} at ${livesAt.join(" and ")} now`;
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=revived.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revived.js","sourceRoot":"","sources":["../../../../../src/core/coherence/retired/revival/revived.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The revival findings: a feature delta that would bring back what an\n * ARCHIVED feature took out of the living contract — an operation, slot or\n * message it retired, or an address it moved one away from, which would put\n * it back there beside where it lives now.\n *\n * `openapi.op-revived` and `asyncapi.slot-revived` — one code per axis, because\n * a caller acts the same way whichever slot section came back. Both are a\n * warning that GATES archive, `openapi.baseline-missing`'s doctrine: the\n * document is legal, and re-adding a retired operation can be exactly what a\n * feature means (a rollback of a retirement, a deprecation reversed), so\n * `--approve` carries the call. What it must never be again is SILENT — the\n * merge cannot tell a quote of a dead operation from an addition, and treated\n * every one as an addition.\n *\n * The candidates — what the contract the archive would WRITE answers that\n * the living one does not (./arrivals.ts) — are computed where both\n * documents are already in hand, in the feature gate's two walks\n * (../../declared.ts, ../../events/declared.ts), and only graded here, so\n * the archive walk (../archived.ts) is paid for only when the merged\n * contract answers something at an address the living one does not.\n * `serviceRevivals` asks the same two functions per axis standalone, for\n * `loam rebase`, which reads no gate — on the event axis through the gate's\n * own reading of the pair (../event-pair.ts) — so the two can never grade one\n * tree two ways.\n */\nimport { existsSync } from \"node:fs\";\nimport type { Issue } from \"../../../vocabulary/issue.js\";\nimport type { PathableService } from \"../../../kernel/ids/service.js\";\nimport type { DocsDir, FeatureDir } from \"../../../kernel/ids/dirs.js\";\nimport { featureSpecPaths } from \"../../../repo/paths.js\";\nimport { locateServicePaths } from \"../../../repo/service-target.js\";\nimport { readOpenapi } from \"../../../openapi/doc.js\";\nimport { readAsyncapi } from \"../../../asyncapi/read.js\";\nimport { entriesOf } from \"../../../asyncapi/digest.js\";\nimport { collectRefs } from \"../../../openapi/depth.js\";\nimport type { FleetContext } from \"../../../fleet-context.js\";\nimport { retiredKey, type RetiredIndex, type RetiredLookup, type Vacated, type Vacations } from \"../archived.js\";\nimport { readEventPair } from \"../event-pair.js\";\nimport { eventArrivals, operationArrivals, type Arrival, type EventArrivals } from \"./arrivals.js\";\n\n/** The two remedies, spelled once: which one applies is the human's call, never loam's. */\nconst REMEDY =\n \"Delete the quote if you only restated it; archive with --approve if the feature brings it back on purpose.\";\n\n/**\n * The vacations that make one arrival a revival — undefined when it is none.\n * Either the living contract answers the identity nowhere, and an archived\n * feature retired it (anywhere: a retired operation re-added at a new path is\n * the same operation back, and a message is its name wherever it is\n * declared) or moved it away from this very address; or the merged contract\n * answers it at two or more addresses, and an archived feature retired it at,\n * or moved it away from, this very address — an old address resurrected\n * beside the current one. An identity the living contract still holds that\n * the merged contract answers at exactly one address — a move back, a swap,\n * a replacement with no operationId — is never a revival, and neither is an\n * address no archived feature vacated. One the living contract holds nowhere\n * is the first case at one address too: this once said \"exactly one address\n * is never a revival\" unqualified, and a quote of an address a move vacated,\n * after a hand edit took the id out of where the move put it, is one.\n *\n * Any archived feature — the one asking is never among them: coherence\n * grades only features in flight (status/feature/feature.ts), so a vacation\n * bearing the asking feature's id is another directory carrying the same\n * id, and as real as anyone's. Excluding it by id, as this once did, could\n * only hide a revival.\n */\nfunction revivalOf({ at, livesAt, besides }: Arrival, vacations: Vacations | undefined): Vacations | undefined {\n if (vacations === undefined) return undefined;\n const [first, ...rest] = vacations.filter((v) =>\n livesAt.length === 0 ? v.to.length === 0 || v.at === at : besides.length > 0 && v.at === at,\n );\n return first === undefined ? undefined : [first, ...rest];\n}\n\n/** The operation arrivals that are revivals — each named for what happened, retired or vacated. */\nexport function revivedOperations(service: string, arrived: readonly Arrival[], index: RetiredIndex): Issue[] {\n const issues: Issue[] = [];\n for (const arrival of arrived) {\n const by = revivalOf(arrival, index.operations.get(retiredKey(service, arrival.id)));\n if (by === undefined) continue;\n const { id, at, livesAt, besides } = arrival;\n const what = livesAt.length === 0\n ? `'${id}' (${at}), ${broughtBack(by, replacing(arrival, \"holds\"))}`\n // A second address, not the operation back: it is living, so the\n // remedy is which address to drop — never \"bring it back\".\n : `'${id}' at ${at}, ${occupant(arrival) === undefined ? \"an address the living contract no longer has\" : \"an address it no longer lives at\"} — ${vacators(by, \" there\")}${present(by, livesAt, \"lives\")}, and archiving this delta would put it back at ${at} beside ${besides.join(\" and \")}: one operationId at ${count(besides.length + 1)} addresses.${replacing(arrival, \"holds\")} ${dropOne(arrival, `add a removal marker for '${id}' at ${besides.join(\" and \")} to this same delta${keeping(arrival, \"address\")}`)}`;\n issues.push({ severity: \"warn\", gates: true, code: \"openapi.op-revived\", subject: service, message: `${service}: this feature's openapi.yaml spells ${what}` });\n }\n return issues;\n}\n\n/**\n * How a delta takes a message name OUT of the places it is declared — the\n * move-back remedy, per place, toward wherever the message is moved back to\n * (`to`). A `components.messages` key is a slot, and a removal marker there\n * retires what it declares. A declaration INLINE under a channel is not: it\n * is the channel slot's interior (SCHEMA.md), so a marker nested on it\n * retires nothing — `asyncapi.remove-marker-inline`, and the deep strip\n * publishes the message without the marker. This once said \"retire it at\n * channels.<ck>.messages.<mk>\", a remedy that, followed exactly, changed\n * nothing and drew a second refusal. The channel is the unit: restated with\n * the message as a `$ref` (an upsert replaces the interior), or retired. The\n * channel key is read the way the archive plan's remove-marker-inline\n * message reads it, so the two name the same channel.\n *\n * A `components.messages` key is retired by its marker, but a channel (or an\n * operation) of the contract the merge would leave (`root`) that still\n * `$ref`s it would point at nothing: its send stops sending the message,\n * `asyncapi.remove-message-consumed` where the fleet consumes it. This once\n * said only \"retire it at components.messages.<K2>\", a remedy that, followed\n * exactly, drew that error — so every slot still pointing there is named,\n * to restate with the `$ref` pointed at `to`.\n */\nexport function moveBackRemedy(declaredAt: readonly string[], to: string, root: unknown): string {\n const steps = declaredAt.map((slot) => {\n if (!slot.startsWith(\"channels.\")) {\n const holders = pointingAt(root, slot);\n return holders.length === 0 ? `retire it at ${slot}` : `retire it at ${slot}, and restate ${holders.join(\" and \")} with every $ref to ${slot} pointed at ${to}`;\n }\n const channel = slot.slice(\"channels.\".length, slot.lastIndexOf(\".messages.\"));\n return `restate channel ${channel} with a $ref to ${to}, or retire channel ${channel}`;\n });\n return [...new Set(steps)].join(\"; and \");\n}\n\n/** The channels and operations of `root` holding a `$ref` to one `components.messages` slot, at any depth. */\nfunction pointingAt(root: unknown, slot: string): string[] {\n const key = slot.slice(\"components.messages.\".length);\n // JSON Pointer escaping, `~` before `/`, so a key spelling either is the pointer that reaches it.\n const pointer = `#/components/messages/${key.replace(/~/g, \"~0\").replace(/\\//g, \"~1\")}`;\n const sections = root !== null && typeof root === \"object\" ? (root as Record<string, unknown>) : {};\n return ([\"channels\", \"operations\"] as const).flatMap((section) =>\n entriesOf(sections[section])\n .filter(([, node]) => collectRefs(node).includes(pointer))\n .map(([key]) => `${section === \"channels\" ? \"channel\" : \"operation\"} ${key}`),\n );\n}\n\n/** The event arrivals that are revivals — `revivedOperations`' rule, on the slot and the message name. */\nexport function revivedEvents(service: string, arrived: EventArrivals, index: RetiredIndex): Issue[] {\n const issues: Issue[] = [];\n const revive = (what: string): void => {\n issues.push({ severity: \"warn\", gates: true, code: \"asyncapi.slot-revived\", subject: service, message: `${service}: this feature's asyncapi.yaml declares ${what}` });\n };\n const revivedChannels = new Set<string>();\n for (const slot of arrived.slots) {\n const by = index.slots.get(retiredKey(service, slot.section, slot.key));\n if (by === undefined) continue;\n if (slot.section === \"channels\") revivedChannels.add(slot.key);\n revive(`${slot.section}.${slot.key}, ${broughtBack(by)}`);\n }\n for (const arrival of arrived.messages) {\n // A message declared inline under a channel that is itself coming back is\n // that channel's interior, and the channel's finding already names the\n // revival — one act, one finding. Only while the merged contract declares\n // the name nowhere else: where it does, the channel's finding says nothing\n // of the second place, and this was once skipped all the same — a stale\n // delta restating a channel whose inline message an archived feature\n // re-declared under components archived under --approve into\n // `asyncapi.duplicate-message`.\n if (arrival.besides.length === 0 && [...revivedChannels].some((ck) => arrival.at.startsWith(`channels.${ck}.messages.`))) continue;\n const by = revivalOf(arrival, index.messages.get(retiredKey(service, arrival.id)));\n if (by === undefined) continue;\n const { id, at, livesAt, besides } = arrival;\n revive(livesAt.length === 0\n ? `message '${id}' (${at}), ${broughtBack(by, replacing(arrival, \"declares\"))}`\n : `message '${id}' at ${at}, ${arrival.replaces === undefined ? \"a key the living contract no longer has\" : \"a key it is no longer declared at\"} — ${vacators(by, \" there\")}${present(by, livesAt, \"is declared\")}, and archiving this delta would declare it at ${at} beside ${besides.join(\" and \")}: one message name in ${count(besides.length + 1)} places.${replacing(arrival, \"declares\")} ${dropOne(arrival, `${moveBackRemedy(besides, at, arrived.mergedRoot)} in this same delta${keeping(arrival, \"key\")}`)}`);\n }\n return issues;\n}\n\n/** What `serviceRevivals` needs: the feature, the service, and the run's reads. */\nexport interface ServiceRevivalRequest {\n docsDir: DocsDir;\n featureDir: FeatureDir;\n service: PathableService;\n retired: RetiredLookup;\n context?: FleetContext;\n /**\n * The text rebase will write, by the path it writes it to — a contract\n * delta absent here is graded as it stands on disk (`PairSides.featText`).\n */\n planned?: ReadonlyMap<string, string>;\n}\n\n/**\n * Both axes' revival findings for one service, read standalone — the gate's\n * two walks compute the same arrivals from the documents they already hold;\n * `loam rebase` holds none, and must not pin a revived operation as if this\n * feature were adding it. One detection, the gate's: `operationArrivals` on\n * the contract axis, and on the event axis `readEventPair` and\n * `eventArrivals`, the walk's own reader and candidates. Asked of the text\n * rebase will WRITE (`planned`), not the text it found: its repin can turn a\n * skipped quote into an edit, and the revival that edit makes is the one the\n * next validate reports — this once ran over the delta as on disk, before\n * the repin, and stayed silent about it.\n */\nexport async function serviceRevivals(request: ServiceRevivalRequest): Promise<Issue[]> {\n const { docsDir, featureDir, service, retired, context, planned } = request;\n const feature = featureSpecPaths(featureDir, service);\n const living = await locateServicePaths(docsDir, service, context);\n const issues: Issue[] = [];\n\n if (existsSync(feature.openapi)) {\n const featDoc = await readOpenapi(feature.openapi, context);\n const livingDoc = await readOpenapi(living.openapi, context);\n const sides = { featPath: feature.openapi, featDoc, livingPath: living.openapi, livingDoc, service, featText: planned?.get(feature.openapi) };\n const ops = (await operationArrivals(sides, context)).operations;\n if (ops.length > 0) issues.push(...revivedOperations(service, ops, await retired()));\n }\n\n if (!existsSync(feature.asyncapi)) return issues;\n const featDoc = await readAsyncapi(feature.asyncapi, context);\n // An unreadable delta is `asyncapi.invalid` in the gate, which grades\n // nothing else against it; the guards on the living side are the pair's.\n if (featDoc.unreadable) return issues;\n const livingDoc = await readAsyncapi(living.asyncapi, context);\n const sides = { featPath: feature.asyncapi, featDoc, livingPath: living.asyncapi, livingDoc, service, featText: planned?.get(feature.asyncapi) };\n const events = eventArrivals(await readEventPair(sides, context));\n if (events.slots.length > 0 || events.messages.length > 0) issues.push(...revivedEvents(service, events, await retired()));\n return issues;\n}\n\n/** The retired shape: absent from living, so the merge reads it as an addition. */\nfunction broughtBack(by: Vacations, replaced = \"\"): string {\n return `which the living contract no longer has — ${vacators(by, \"\")}, and archiving this delta would put it back.${replaced} ${REMEDY}`;\n}\n\n/**\n * How a finding names the entry living at the arrival's address — its\n * identity quoted, or, for an operation written with no operationId, what it\n * is; undefined where nothing lives there.\n */\nfunction occupant({ replaces, replacesUnnamed }: Arrival): string | undefined {\n return replaces !== undefined ? `'${replaces}'` : replacesUnnamed === true ? \"an operation with no operationId\" : undefined;\n}\n\n/** The entry the merge would replace at the arrival's address, said as a sentence of its own — or nothing. */\nfunction replacing(arrival: Arrival, verb: \"holds\" | \"declares\"): string {\n const there = occupant(arrival);\n return there === undefined ? \"\" : ` ${arrival.at} now ${verb} ${there}, and archiving this delta replaces it there.`;\n}\n\n/**\n * The duplicate shape's remedy, naming the address to drop: the quote, if it\n * only restated the entry; to move it back on purpose, every other address —\n * through `moveBack` when this delta can take the entry out of all of them,\n * and otherwise only by keeping both under `--approve`, never a marker at a\n * key the delta already spells.\n */\nfunction dropOne({ at, besides, markable }: Arrival, moveBack: string): string {\n const keep = `Delete the quote at ${at} if you only restated it, and make any change at ${besides.join(\" and \")}`;\n return markable\n ? `${keep}; to move it back on purpose, ${moveBack}.`\n : `${keep}; archive with --approve if the feature brings it back there on purpose.`;\n}\n\n/**\n * A move back over another living entry takes that entry out too, unless the\n * same delta puts it elsewhere — said in the remedy, because followed as it\n * once read, the move back reached a clean gate and an archive that deleted\n * the other entry with nothing naming it.\n */\nfunction keeping(arrival: Arrival, place: \"address\" | \"key\"): string {\n const there = occupant(arrival);\n if (there === undefined) return \"\";\n // An operation nobody can name is still what stands there, and the delta\n // can restate it elsewhere all the same — said by what it is, not by an id.\n return arrival.replaces === undefined\n ? ` — and, if the operation with no operationId is to stay, define it at another ${place} in it`\n : ` — and define ${there} at another ${place} in it if it is to stay`;\n}\n\nfunction count(n: number): string {\n return n === 2 ? \"two\" : String(n);\n}\n\n/** \"FEAT-112\", \"FEAT-112 and FEAT-140\", \"FEAT-1, FEAT-2 and FEAT-3\". */\nfunction idList([first, ...rest]: readonly [string, ...string[]]): string {\n return rest.reduce((said, id, i) => `${said}${i === rest.length - 1 ? \" and\" : \",\"} ${id}`, first);\n}\n\n/**\n * Who took an entry out, and how — \"archived FEAT-112 retired it\",\n * \"archived FEAT-200 moved it to POST /y\" — each feature once, in id order,\n * movers that put it in the same place sharing one clause and retirers\n * last. `there` places a retirement at the address the finding names, where\n * the entry lives on elsewhere and the difference matters.\n */\nfunction vacators(by: Vacations, there: string): string {\n const byDestination = new Map<string, [string, ...string[]]>();\n const retirers: string[] = [];\n for (const { by: id, to } of by) {\n if (to.length === 0) {\n if (!retirers.includes(id)) retirers.push(id);\n continue;\n }\n const where = to.join(\" and \");\n const ids = byDestination.get(where);\n if (ids === undefined) byDestination.set(where, [id]);\n else if (!ids.includes(id)) ids.push(id);\n }\n const clauses = [...byDestination].map(([where, ids], i) => `${idList(ids)} ${i === 0 ? \"moved it \" : \"\"}to ${where}`);\n const [first, ...rest] = retirers;\n if (first !== undefined) clauses.push(`${idList([first, ...rest])} retired it${there}`);\n return `archived ${clauses.reduce((all, clause, i) => `${all}${i === clauses.length - 1 ? \", and\" : \",\"} ${clause}`)}`;\n}\n\n/**\n * Where the entry stands now — \"…, where it lives now\" when every vacation\n * was a move to exactly there, and said apart otherwise (\"; it lives at\n * POST /z now\"): the two part ways once anything lands after a move, and\n * this once credited a later feature's address to the mover (`Vacated`).\n * `verb` is how the axis says it — an operation \"lives\", a message \"is\n * declared\".\n */\nfunction present(by: readonly Vacated[], livesAt: readonly string[], verb: \"lives\" | \"is declared\"): string {\n const now = new Set(livesAt);\n const unchanged = by.every(({ to }) => to.length > 0 && to.length === now.size && to.every((where) => now.has(where)));\n return unchanged ? `, where it ${verb} now` : `; it ${verb} at ${livesAt.join(\" and \")} now`;\n}\n"]}
|