@ybotok/loam 0.2.0-alpha.1 → 0.2.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3448 -0
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +47 -23
- package/QUICKSTART.md +189 -12
- package/README.md +274 -65
- package/ROADMAP.md +572 -31
- package/SCHEMA.md +1709 -155
- package/WORKFLOW.md +298 -72
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/adopt/render.js +50 -9
- package/dist/commands/adopt/render.js.map +1 -1
- package/dist/commands/archive/archive.js +16 -19
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/closing.js +42 -0
- package/dist/commands/archive/closing.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +77 -43
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/document/head.js +151 -0
- package/dist/commands/archive/plan/document/head.js.map +1 -0
- package/dist/commands/archive/plan/document/links.js +284 -0
- package/dist/commands/archive/plan/document/links.js.map +1 -0
- package/dist/commands/archive/plan/document/run.js +136 -0
- package/dist/commands/archive/plan/document/run.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +21 -19
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +167 -30
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/model/extending.js +85 -0
- package/dist/commands/archive/plan/model/extending.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +14 -19
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +137 -56
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +3 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +134 -30
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +85 -18
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +29 -7
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +102 -14
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +17 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/doctor.js +60 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +89 -13
- package/dist/commands/explain/explain.js.map +1 -1
- package/dist/commands/explore.js +16 -10
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +5 -7
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +44 -29
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +52 -9
- package/dist/commands/list/print.js.map +1 -1
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +37 -34
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +55 -26
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/plan.js +158 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +13 -11
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/feature.js +9 -5
- package/dist/commands/show/feature.js.map +1 -1
- package/dist/commands/show/introduced.js +157 -0
- package/dist/commands/show/introduced.js.map +1 -0
- package/dist/commands/show/marks.js +35 -6
- package/dist/commands/show/marks.js.map +1 -1
- package/dist/commands/show/service.js +41 -33
- package/dist/commands/show/service.js.map +1 -1
- package/dist/commands/show/show.js +17 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/fleet-next.js +90 -0
- package/dist/commands/status/fleet-next.js.map +1 -0
- package/dist/commands/status/json.js +2 -0
- package/dist/commands/status/json.js.map +1 -1
- package/dist/commands/status/print.js +89 -37
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +103 -23
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +7 -7
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +106 -46
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +289 -74
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/exclude.js +132 -0
- package/dist/commands/subsystem/txn/exclude.js.map +1 -0
- package/dist/commands/subsystem/txn/links.js +128 -0
- package/dist/commands/subsystem/txn/links.js.map +1 -0
- package/dist/commands/subsystem/txn/report.js +174 -0
- package/dist/commands/subsystem/txn/report.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +147 -45
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +200 -21
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/actions.js +107 -0
- package/dist/commands/tasks/actions.js.map +1 -0
- package/dist/commands/tasks/render.js +253 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +119 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +42 -26
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +83 -33
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +78 -27
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +34 -126
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/health-deps.js +9 -6
- package/dist/commands/validate/checks/health-deps.js.map +1 -1
- package/dist/commands/validate/checks/introduced.js +102 -0
- package/dist/commands/validate/checks/introduced.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +11 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/coverage/covers-after.js +105 -0
- package/dist/commands/validate/coverage/covers-after.js.map +1 -0
- package/dist/commands/validate/feature.js +26 -20
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +37 -13
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +144 -126
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/load.js +66 -0
- package/dist/commands/validate/fleet/load.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +98 -0
- package/dist/commands/validate/fleet/map/attest.js.map +1 -0
- package/dist/commands/validate/fleet/map/bindings.js +107 -0
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
- package/dist/commands/validate/fleet/map/consumers.js +244 -0
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
- package/dist/commands/validate/fleet/map/isolation.js +116 -0
- package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
- package/dist/commands/validate/fleet/map/usecases.js +54 -0
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +44 -14
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +10 -2
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +50 -15
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/projects.js +278 -0
- package/dist/commands/validate/fleet/views/projects.js.map +1 -0
- package/dist/commands/validate/fleet/views/root-documents.js +83 -0
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +40 -47
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/render/repeats.js +63 -0
- package/dist/commands/validate/render/repeats.js.map +1 -0
- package/dist/commands/validate/render/rollups.js +115 -0
- package/dist/commands/validate/render/rollups.js.map +1 -0
- package/dist/commands/validate/render/text.js +199 -0
- package/dist/commands/validate/render/text.js.map +1 -0
- package/dist/commands/validate/report.js +36 -160
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +21 -107
- package/dist/commands/validate/service/api.js.map +1 -1
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +36 -14
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/model/diverged.js +226 -0
- package/dist/commands/validate/service/model/diverged.js.map +1 -0
- package/dist/commands/validate/service/model/grade.js +163 -0
- package/dist/commands/validate/service/model/grade.js.map +1 -0
- package/dist/commands/validate/service/service.js +121 -61
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +88 -28
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +38 -3
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/tags.js +113 -0
- package/dist/commands/validate/service/usecases/tags.js.map +1 -0
- package/dist/commands/validate/service/usecases/usecases.js +209 -0
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/validate.js +85 -60
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +55 -52
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +81 -106
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +21 -9
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +24 -8
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +80 -8
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +88 -10
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +49 -22
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +39 -6
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/protocol.js +1 -1
- package/dist/core/agent/protocol.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/support/report.js +46 -5
- package/dist/core/agent/support/report.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +158 -11
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +78 -39
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +88 -27
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +128 -13
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +96 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +106 -4
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +6 -3
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +68 -27
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +32 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +33 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +12 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +96 -36
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/checks.js +138 -3
- package/dist/core/brief/checks.js.map +1 -1
- package/dist/core/brief/landscape.js +209 -130
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +84 -0
- package/dist/core/brief/map/extend.js.map +1 -0
- package/dist/core/brief/map/owed.js +266 -0
- package/dist/core/brief/map/owed.js.map +1 -0
- package/dist/core/brief/targets.js +109 -31
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +14 -3
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/arch.js +90 -182
- package/dist/core/c4/arch.js.map +1 -1
- package/dist/core/c4/direction/direction.js +114 -0
- package/dist/core/c4/direction/direction.js.map +1 -0
- package/dist/core/c4/likec4.js +169 -14
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +160 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/parsed/view-ids.js +39 -1
- package/dist/core/c4/parsed/view-ids.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +134 -0
- package/dist/core/c4/prefetch/fleet.js.map +1 -0
- package/dist/core/c4/project/architecture.js +123 -7
- package/dist/core/c4/project/architecture.js.map +1 -1
- package/dist/core/c4/project/batch.js +140 -0
- package/dist/core/c4/project/batch.js.map +1 -0
- package/dist/core/c4/project/documents.js +43 -12
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +79 -43
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +277 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/attested.js +0 -0
- package/dist/core/c4/resolve/attested.js.map +1 -0
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +78 -17
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/resolve/steps.js +290 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/resolve/systems.js +154 -0
- package/dist/core/c4/resolve/systems.js.map +1 -0
- package/dist/core/c4/root-project/exclude.js +352 -0
- package/dist/core/c4/root-project/exclude.js.map +1 -0
- package/dist/core/c4/seed/template.js +65 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/created/models.js +96 -0
- package/dist/core/c4/service-model/created/models.js.map +1 -0
- package/dist/core/c4/service-model/documents.js +47 -0
- package/dist/core/c4/service-model/documents.js.map +1 -0
- package/dist/core/c4/service-model/fleet/extending.js +82 -0
- package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
- package/dist/core/c4/service-model/load.js +165 -0
- package/dist/core/c4/service-model/load.js.map +1 -0
- package/dist/core/c4/service-model/renderer.js +128 -0
- package/dist/core/c4/service-model/renderer.js.map +1 -0
- package/dist/core/c4/service-model/shape.js +106 -0
- package/dist/core/c4/service-model/shape.js.map +1 -0
- package/dist/core/c4/service-model/slice.js +188 -0
- package/dist/core/c4/service-model/slice.js.map +1 -0
- package/dist/core/c4/source-mask.js +297 -10
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +222 -141
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +79 -13
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/contract.js +20 -0
- package/dist/core/c4/splice/contract.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +20 -20
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/identity/declared.js +87 -0
- package/dist/core/c4/splice/identity/declared.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +37 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -1
- package/dist/core/c4/splice/identity/existing.js +80 -0
- package/dist/core/c4/splice/identity/existing.js.map +1 -0
- package/dist/core/c4/splice/identity/interior.js +80 -0
- package/dist/core/c4/splice/identity/interior.js.map +1 -0
- package/dist/core/c4/splice/identity/owner.js +50 -0
- package/dist/core/c4/splice/identity/owner.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +182 -114
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +151 -0
- package/dist/core/c4/splice/model/merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +160 -56
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/splice/text/spot.js +131 -0
- package/dist/core/c4/splice/text/spot.js.map +1 -0
- package/dist/core/c4/workspace.js +13 -19
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/delta/uncovered.js +2 -2
- package/dist/core/capabilities/delta/uncovered.js.map +1 -1
- package/dist/core/capabilities/findings.js +18 -22
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/calls/called.js +52 -0
- package/dist/core/coherence/calls/called.js.map +1 -0
- package/dist/core/coherence/coherence.js +103 -22
- package/dist/core/coherence/coherence.js.map +1 -1
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/covers/health.js +85 -0
- package/dist/core/coherence/covers/health.js.map +1 -0
- package/dist/core/coherence/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +147 -16
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +28 -87
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/events/produced.js +100 -0
- package/dist/core/coherence/events/produced.js.map +1 -0
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/delta.js +51 -6
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/dependencies/fanin.js +2 -2
- package/dist/core/dependencies/fanin.js.map +1 -1
- package/dist/core/deployment/delta.js +191 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +49 -21
- package/dist/core/diff/base-state.js.map +1 -1
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/docs.js +31 -22
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/config.js +7 -3
- package/dist/core/doctor/config.js.map +1 -1
- package/dist/core/doctor/doctor.js +91 -11
- package/dist/core/doctor/doctor.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/doctor/reports/scan.js +242 -0
- package/dist/core/doctor/reports/scan.js.map +1 -0
- package/dist/core/document/apply.js +158 -11
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +26 -21
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/lookup.js +14 -1
- package/dist/core/explain/lookup.js.map +1 -1
- package/dist/core/explain/refusals.js +5 -5
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms/documents.js +227 -0
- package/dist/core/explain/terms/documents.js.map +1 -0
- package/dist/core/explain/terms/done-check.js +144 -0
- package/dist/core/explain/terms/done-check.js.map +1 -0
- package/dist/core/explain/terms/entry.js +14 -0
- package/dist/core/explain/terms/entry.js.map +1 -0
- package/dist/core/explain/terms/stages.js +71 -0
- package/dist/core/explain/terms/stages.js.map +1 -0
- package/dist/core/explain/terms/tasks.js +97 -0
- package/dist/core/explain/terms/tasks.js.map +1 -0
- package/dist/core/explain/terms.js +68 -109
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +40 -10
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +81 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +11 -4
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +69 -28
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/delta.js +10 -7
- package/dist/core/glossary/delta.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
- package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
- package/dist/core/kernel/path-safety.js +38 -0
- package/dist/core/kernel/path-safety.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +51 -37
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/links/findings.js.map +1 -1
- package/dist/core/links/parse.js +38 -9
- package/dist/core/links/parse.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +40 -10
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +301 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +28 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +17 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +43 -4
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/pack/pack.js +50 -6
- package/dist/core/pack/pack.js.map +1 -1
- package/dist/core/projection/arch-slice.js +2 -2
- package/dist/core/projection/arch-slice.js.map +1 -1
- package/dist/core/projection/introduced.js +14 -0
- package/dist/core/projection/introduced.js.map +1 -0
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/authored/paths.js +18 -0
- package/dist/core/repo/authored/paths.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/paths.js +92 -6
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/repo.js +74 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/render/body.js +258 -0
- package/dist/core/repo/tree/render/body.js.map +1 -0
- package/dist/core/repo/tree/render/projects.js +182 -0
- package/dist/core/repo/tree/render/projects.js.map +1 -0
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +42 -0
- package/dist/core/repo/tree/render/stale.js.map +1 -0
- package/dist/core/repo/tree/render/text.js +84 -0
- package/dist/core/repo/tree/render/text.js.map +1 -0
- package/dist/core/repo/tree/render/views.js +293 -0
- package/dist/core/repo/tree/render/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +53 -32
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +121 -13
- package/dist/core/scaffold/landscape.js.map +1 -1
- package/dist/core/scaffold/legacy.js +271 -0
- package/dist/core/scaffold/legacy.js.map +1 -0
- package/dist/core/staging/commit.js +26 -15
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +84 -6
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +188 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +24 -14
- package/dist/core/staging/txn/forward.js.map +1 -1
- package/dist/core/staging/writes.js.map +1 -1
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/contracts.js +39 -15
- package/dist/core/status/contracts.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +33 -8
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +24 -17
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/feature/next.js +20 -7
- package/dist/core/status/feature/next.js.map +1 -1
- package/dist/core/status/feature/words.js +96 -0
- package/dist/core/status/feature/words.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +43 -14
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +153 -58
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/scan.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +199 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/claims.js +31 -7
- package/dist/core/usecases/delta/claims.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/delta/modified.js +315 -0
- package/dist/core/usecases/delta/modified.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +32 -191
- package/dist/core/usecases/delta/overlay.js.map +1 -1
- package/dist/core/usecases/fleet.js +195 -36
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
- package/dist/core/usecases/grade/capability-tag.js.map +1 -0
- package/dist/core/usecases/grade/feature.js +88 -0
- package/dist/core/usecases/grade/feature.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
- package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
- package/dist/core/usecases/grade/steps.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
- package/dist/core/usecases/grade/usecases.js.map +1 -0
- package/dist/core/usecases/operations.js +26 -14
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/place.js.map +1 -1
- package/dist/core/usecases/service/exclude.js +119 -0
- package/dist/core/usecases/service/exclude.js.map +1 -0
- package/dist/core/usecases/service/flows.js +164 -0
- package/dist/core/usecases/service/flows.js.map +1 -0
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +70 -12
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +4 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +75 -4
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/dist/core/vocabulary/report.js +16 -1
- package/dist/core/vocabulary/report.js.map +1 -1
- package/examples/README.md +54 -11
- package/examples/docs/architecture/landscape.likec4 +62 -6
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +7 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/likec4.config.json +0 -1
- package/examples/docs/services/checkout-web/model.likec4 +39 -56
- package/examples/docs/services/order-service/arch.spec.md +13 -6
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/order-service/model.likec4 +58 -70
- package/examples/docs/services/payment-service/arch.spec.md +7 -4
- package/examples/docs/services/payment-service/model.likec4 +38 -37
- package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/examples/docs/services/platform/notification-service/health.yaml +8 -3
- package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -31
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -220
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -165
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -247
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/repo/tree/views.js +0 -172
- package/dist/core/repo/tree/views.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Git-at-a-ref reader for the DOCS repo — the base side of `loam diff
|
|
2
|
+
* Git-at-a-ref reader for the DOCS repo — the base side of `loam diff`, and
|
|
3
|
+
* the "what had already shipped" side of `loam gate --since`.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
+
* Five questions, all read-only: does this docs directory sit in a git
|
|
5
6
|
* repository and where (`resolveBase`), what did `services/` hold at the base
|
|
6
|
-
* commit (`listBaseTree`),
|
|
7
|
-
* (`
|
|
8
|
-
*
|
|
7
|
+
* commit (`listBaseTree`), which subdirectories did one docs directory hold
|
|
8
|
+
* there (`listBaseSubdirs` — gate's `features/archive/` question) and which of
|
|
9
|
+
* them differ in the working tree now (`subdirsChangedSince` — its second),
|
|
10
|
+
* and what were one file's bytes there (`showBaseFile`). No checkout, no temp
|
|
11
|
+
* writes — the base state is read straight out of the object store with
|
|
12
|
+
* `rev-parse`/`ls-tree`/`show`, and the comparison with `diff`/`ls-files`.
|
|
13
|
+
*
|
|
14
|
+
* Gate takes diff's doctrine below along with the mechanics: a `--since` ref
|
|
15
|
+
* git cannot answer for is a refusal, never "nothing was archived since",
|
|
16
|
+
* because that silence would pass a deploy over every feature it could not
|
|
17
|
+
* see — the same fail-open as a silently empty diff base, one command over.
|
|
9
18
|
*
|
|
10
19
|
* The doctrine here is deliberately NOT provenance's. `../provenance/git.ts`
|
|
11
20
|
* answers every git decline with "git will not say" — null, no finding —
|
|
@@ -21,6 +30,7 @@
|
|
|
21
30
|
* construction.
|
|
22
31
|
*/
|
|
23
32
|
import { spawn } from "node:child_process";
|
|
33
|
+
import { relative, sep } from "node:path";
|
|
24
34
|
import { collectStderr, declineDetail, GIT_TIMEOUT_MS, gitSaid, gitText } from "../provenance/git.js";
|
|
25
35
|
/**
|
|
26
36
|
* How many bytes one base file may be — the same generosity as
|
|
@@ -31,8 +41,8 @@ import { collectStderr, declineDetail, GIT_TIMEOUT_MS, gitSaid, gitText } from "
|
|
|
31
41
|
* killed and the file reads as unreadable for its subject, never truncated.
|
|
32
42
|
*
|
|
33
43
|
* The question-asking mechanics — `gitText`, `GitAnswer`, `gitSaid`,
|
|
34
|
-
* `declineDetail`, `collectStderr` — are the shared set
|
|
35
|
-
*
|
|
44
|
+
* `declineDetail`, `collectStderr` — are the shared set `../provenance/git.ts`
|
|
45
|
+
* exports: `../provenance/gitq/vouched-ancestor.ts` asks with the
|
|
36
46
|
* same shapes and the opposite consequence (it degrades to "full read" where
|
|
37
47
|
* this module's callers REFUSE), and the helpers stay verdict-free so the two
|
|
38
48
|
* doctrines cannot bleed into each other through a shared line.
|
|
@@ -73,15 +83,87 @@ export async function resolveBase(docsDir, ref) {
|
|
|
73
83
|
}
|
|
74
84
|
/**
|
|
75
85
|
* Every file path under `services/` at the base commit, DOCS-RELATIVE
|
|
76
|
-
* (`services/...`, prefix already stripped
|
|
77
|
-
*
|
|
78
|
-
* one prefix from `resolveBase` is the whole story; `-z` because git quotes
|
|
79
|
-
* non-ASCII paths in line output. An empty answer is git ANSWERING — the base
|
|
86
|
+
* (`services/...`, prefix already stripped — `lsTree` below holds the
|
|
87
|
+
* mechanics both listings share). An empty answer is git ANSWERING — the base
|
|
80
88
|
* commit simply has no `services/` — and only a non-zero exit is a failure.
|
|
81
89
|
*/
|
|
82
90
|
export async function listBaseTree(base) {
|
|
91
|
+
return lsTree(base, "-r", "services");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The NAMES of the immediate subdirectories of one docs directory at the base
|
|
95
|
+
* commit — `loam gate --since`'s question: which `features/archive/<FEAT>/`
|
|
96
|
+
* directories already existed at the ref, so every other archived feature
|
|
97
|
+
* shipped after it. `dir` is the absolute path a `repo/paths.ts` builder
|
|
98
|
+
* spells (`archiveDir`), made docs-relative here, so the layout keeps its one
|
|
99
|
+
* spelling; the separator is forced to `/` because git paths are slash-joined
|
|
100
|
+
* on every host. `-d` lists trees only, and the trailing slash asks for the
|
|
101
|
+
* directory's CHILDREN rather than the directory entry itself. An empty answer
|
|
102
|
+
* is git answering — the base had no such directory — and only a decline is
|
|
103
|
+
* `failed`, which the caller refuses on, as `listBaseTree`'s callers do.
|
|
104
|
+
*/
|
|
105
|
+
export async function listBaseSubdirs(base, dir) {
|
|
106
|
+
const rel = `${relative(base.docsDir, dir).split(sep).join("/")}/`;
|
|
107
|
+
const listing = await lsTree(base, "-d", rel);
|
|
108
|
+
if (listing.kind === "failed")
|
|
109
|
+
return listing;
|
|
110
|
+
return {
|
|
111
|
+
kind: "ok",
|
|
112
|
+
names: listing.paths.filter((p) => p.startsWith(rel)).map((p) => p.slice(rel.length)),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The NAMES of the immediate subdirectories of one docs directory whose
|
|
117
|
+
* contents differ between the base commit and the WORKING TREE — committed
|
|
118
|
+
* since or not — gate's second `features/archive/` question. A directory that
|
|
119
|
+
* was already there at the ref is not thereby old: `loam unarchive`, an edit
|
|
120
|
+
* and `loam archive` again leave a feature at the very path it shipped from
|
|
121
|
+
* before, and reading presence alone graded that re-shipped feature as one an
|
|
122
|
+
* earlier deploy had already answered for.
|
|
123
|
+
*
|
|
124
|
+
* Not a comparison of tree ids, though `ls-tree` has the ref's: the working
|
|
125
|
+
* tree has no id until somebody commits it, and minting one means writing
|
|
126
|
+
* objects — this module asks git, it never writes. The same two questions as
|
|
127
|
+
* `validate --base`'s scope (`./scope/changed-paths.ts`) instead, narrowed to
|
|
128
|
+
* the one directory: `git diff` for every tracked path that differs from the
|
|
129
|
+
* ref, `--no-renames` so a moved file names the place it left as well as the
|
|
130
|
+
* one it reached, and `ls-files --others` for what nobody has committed yet —
|
|
131
|
+
* the re-archive a pipeline is about to ship is exactly that. Both pathspecs
|
|
132
|
+
* are relative to `docsDir`, where git runs; both listings answer
|
|
133
|
+
* root-relative (`git diff` always, `ls-files` under `--full-name`), so the
|
|
134
|
+
* one prefix strip serves both. A decline is `failed`, and the caller decides
|
|
135
|
+
* which way that fails.
|
|
136
|
+
*/
|
|
137
|
+
export async function subdirsChangedSince(base, dir) {
|
|
138
|
+
const rel = `${relative(base.docsDir, dir).split(sep).join("/")}/`;
|
|
139
|
+
const tracked = await gitText(base.docsDir, ["diff", "--name-only", "--no-renames", "--no-ext-diff", "-z", base.commit, "--", rel]);
|
|
140
|
+
if (tracked.spawnError !== undefined || tracked.code !== 0 || tracked.overflowed) {
|
|
141
|
+
return { kind: "failed", detail: tracked.overflowed ? "the changed-path listing exceeded the output cap." : declineDetail(tracked) };
|
|
142
|
+
}
|
|
143
|
+
const untracked = await gitText(base.docsDir, ["ls-files", "--others", "--exclude-standard", "--full-name", "-z", "--", rel]);
|
|
144
|
+
if (untracked.spawnError !== undefined || untracked.code !== 0 || untracked.overflowed) {
|
|
145
|
+
return { kind: "failed", detail: untracked.overflowed ? "the untracked-path listing exceeded the output cap." : declineDetail(untracked) };
|
|
146
|
+
}
|
|
147
|
+
const under = `${base.prefix}${rel}`;
|
|
148
|
+
const names = new Set();
|
|
149
|
+
for (const path of [...tracked.out.split("\0"), ...untracked.out.split("\0")]) {
|
|
150
|
+
if (!path.startsWith(under))
|
|
151
|
+
continue;
|
|
152
|
+
const name = path.slice(under.length).split("/")[0];
|
|
153
|
+
if (name !== "")
|
|
154
|
+
names.add(name);
|
|
155
|
+
}
|
|
156
|
+
return { kind: "ok", names };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The one `ls-tree` both listings ask, DOCS-RELATIVE (prefix already
|
|
160
|
+
* stripped). `--full-tree` anchors both the pathspec and the output at the
|
|
161
|
+
* repository root regardless of cwd, so the one prefix from `resolveBase` is
|
|
162
|
+
* the whole story; `-z` because git quotes non-ASCII paths in line output.
|
|
163
|
+
*/
|
|
164
|
+
async function lsTree(base, mode, root) {
|
|
83
165
|
const answer = await gitText(base.docsDir, [
|
|
84
|
-
"ls-tree",
|
|
166
|
+
"ls-tree", mode, "-z", "--name-only", "--full-tree", base.commit, "--", `${base.prefix}${root}`,
|
|
85
167
|
]);
|
|
86
168
|
if (answer.spawnError !== undefined || answer.code !== 0 || answer.overflowed) {
|
|
87
169
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-git.js","sourceRoot":"","sources":["../../../src/core/diff/base-git.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Git-at-a-ref reader for the DOCS repo — the base side of `loam diff`.\n *\n * Three questions, all read-only: does this docs directory sit in a git\n * repository and where (`resolveBase`), what did `services/` hold at the base\n * commit (`listBaseTree`), and what were one file's bytes there\n * (`showBaseFile`). No checkout, no temp writes — the base state is read\n * straight out of the object store with `rev-parse`/`ls-tree`/`show`.\n *\n * The doctrine here is deliberately NOT provenance's. `../provenance/git.ts`\n * answers every git decline with \"git will not say\" — null, no finding —\n * because provenance has no denominator without git and nothing to report.\n * Diff is the opposite case: without the base there is no honest diff at all,\n * and a silently empty base would report the whole fleet as ADDED — the exact\n * \"green over zero services\" failure `docsRepoReady` exists to prevent, one\n * step later. So every decline travels as a typed outcome the command REFUSES\n * on, carrying git's own words (bounded) so the refusal can say why.\n *\n * What is shared with provenance is the mechanics: the one timeout and the\n * capped stdout reader, so rule 20 (every child spawn bounded) holds by\n * construction.\n */\nimport { spawn } from \"node:child_process\";\nimport { collectStderr, declineDetail, GIT_TIMEOUT_MS, gitSaid, gitText } from \"../provenance/git.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * How many bytes one base file may be — the same generosity as\n * `../provenance/git.ts`'s stdout cap, restated here because that constant is\n * private to its module and this one collects BYTES, not text (a base\n * contract's UTF-8-ness is a fact to grade, and `setEncoding(\"utf8\")` would\n * substitute U+FFFD before anyone could check). Past the cap the child is\n * killed and the file reads as unreadable for its subject, never truncated.\n *\n * The question-asking mechanics — `gitText`, `GitAnswer`, `gitSaid`,\n * `declineDetail`, `collectStderr` — are the shared set exported beside\n * `collectStdout`: `../provenance/gitq/vouched-ancestor.ts` asks with the\n * same shapes and the opposite consequence (it degrades to \"full read\" where\n * this module's callers REFUSE), and the helpers stay verdict-free so the two\n * doctrines cannot bleed into each other through a shared line.\n */\nconst MAX_BASE_FILE_BYTES = 64 * 1024 * 1024;\n\nexport type BaseResolution =\n | { kind: \"no-git\"; detail: string }\n | { kind: \"no-ref\"; detail: string }\n | { kind: \"ok\"; commit: string; prefix: string };\n\n/**\n * Resolve the base: is `docsDir` inside a git work tree, where does it sit in\n * that repository, and which commit does `ref` name?\n *\n * `--show-prefix` is asked FIRST and is not decoration: a docs repo checked\n * out as a subdirectory of a larger repository (a monorepo's `docs/`) answers\n * every later `ls-tree`/`show` only under that prefix. Dropping it makes every\n * path miss, and a diff whose base paths all miss reports the whole fleet as\n * added — silently, in exactly the repositories big enough to care.\n */\nexport async function resolveBase(docsDir: DocsDir, ref: string): Promise<BaseResolution> {\n const where = await gitText(docsDir, [\"rev-parse\", \"--show-prefix\"]);\n if (where.spawnError !== undefined || where.code !== 0) {\n return { kind: \"no-git\", detail: declineDetail(where) };\n }\n // A ref spelled like an option would be parsed as one by git, not resolved\n // as a revision — refuse it before it reaches an argv. Same reason the MCP\n // argv boundary refuses '-'-prefixed strings.\n const spelled = ref.trim();\n if (spelled === \"\" || spelled.startsWith(\"-\")) {\n return { kind: \"no-ref\", detail: \"a ref must be a revision name, not empty and not option-shaped.\" };\n }\n // `^{commit}` peels an annotated tag to the commit it tags, and refuses a\n // ref that names any other object kind — a tree or blob sha is not a base\n // state to diff against.\n const verified = await gitText(docsDir, [\"rev-parse\", \"--verify\", `${spelled}^{commit}`]);\n const commit = verified.out.trim();\n if (verified.code !== 0 || !/^[0-9a-f]{40}$/.test(commit)) {\n const said = gitSaid(verified);\n return { kind: \"no-ref\", detail: said === \"\" ? \"\" : `git said: ${said}` };\n }\n return { kind: \"ok\", commit, prefix: where.out.trim() };\n}\n\n/** The resolved base every later question is asked against. */\nexport interface ResolvedBase {\n docsDir: DocsDir;\n commit: string;\n /** The docs repo's path inside its repository, `\"\"` or `\"docs/\"`-shaped — `rev-parse --show-prefix`'s answer. */\n prefix: string;\n}\n\nexport type BaseTreeListing =\n | { kind: \"ok\"; paths: string[] }\n | { kind: \"failed\"; detail: string };\n\n/**\n * Every file path under `services/` at the base commit, DOCS-RELATIVE\n * (`services/...`, prefix already stripped). `--full-tree` anchors both the\n * pathspec and the output at the repository root regardless of cwd, so the\n * one prefix from `resolveBase` is the whole story; `-z` because git quotes\n * non-ASCII paths in line output. An empty answer is git ANSWERING — the base\n * commit simply has no `services/` — and only a non-zero exit is a failure.\n */\nexport async function listBaseTree(base: ResolvedBase): Promise<BaseTreeListing> {\n const answer = await gitText(base.docsDir, [\n \"ls-tree\", \"-r\", \"-z\", \"--name-only\", \"--full-tree\", base.commit, \"--\", `${base.prefix}services`,\n ]);\n if (answer.spawnError !== undefined || answer.code !== 0 || answer.overflowed) {\n return {\n kind: \"failed\",\n detail: answer.overflowed\n ? \"the base tree listing exceeded the output cap.\"\n : declineDetail(answer),\n };\n }\n return {\n kind: \"ok\",\n paths: answer.out\n .split(\"\\0\")\n .filter((p) => p.startsWith(base.prefix) && p !== \"\")\n .map((p) => p.slice(base.prefix.length)),\n };\n}\n\nexport type BaseFileRead =\n | { kind: \"ok\"; bytes: Buffer }\n | { kind: \"failed\"; detail: string };\n\n/**\n * One base file's bytes, by docs-relative path. Callers only ask for paths\n * `listBaseTree` returned, so there is no \"absent\" arm: a decline here is\n * abnormal and travels as `failed` for per-subject containment — that one\n * subject degrades with the detail, never the whole diff.\n */\nexport async function showBaseFile(base: ResolvedBase, rel: string): Promise<BaseFileRead> {\n return new Promise<BaseFileRead>((done) => {\n const child = spawn(\"git\", [\"show\", `${base.commit}:${base.prefix}${rel}`], {\n cwd: base.docsDir,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: GIT_TIMEOUT_MS,\n });\n // Bytes, not text: `collectStdout` decodes as UTF-8, and a base contract\n // whose bytes are NOT UTF-8 must arrive intact so base-state can grade it\n // unreadable instead of parsing a document nobody wrote.\n const chunks: Buffer[] = [];\n let held = 0;\n let overflowed = false;\n child.stdout.on(\"data\", (chunk: Buffer) => {\n if (overflowed) return;\n if (held + chunk.length > MAX_BASE_FILE_BYTES) {\n overflowed = true;\n child.kill();\n return;\n }\n chunks.push(chunk);\n held += chunk.length;\n });\n const err = collectStderr(child);\n child.on(\"error\", (e) => done({ kind: \"failed\", detail: `git could not be started: ${e.message}.` }));\n child.on(\"close\", (code) => {\n if (code !== 0 || overflowed) {\n done({\n kind: \"failed\",\n detail: overflowed\n ? \"the file exceeds the base-read byte cap.\"\n : declineDetail({ code, out: \"\", err: err(), overflowed }),\n });\n return;\n }\n done({ kind: \"ok\", bytes: Buffer.concat(chunks) });\n });\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"base-git.js","sourceRoot":"","sources":["../../../src/core/diff/base-git.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Git-at-a-ref reader for the DOCS repo — the base side of `loam diff`, and\n * the \"what had already shipped\" side of `loam gate --since`.\n *\n * Five questions, all read-only: does this docs directory sit in a git\n * repository and where (`resolveBase`), what did `services/` hold at the base\n * commit (`listBaseTree`), which subdirectories did one docs directory hold\n * there (`listBaseSubdirs` — gate's `features/archive/` question) and which of\n * them differ in the working tree now (`subdirsChangedSince` — its second),\n * and what were one file's bytes there (`showBaseFile`). No checkout, no temp\n * writes — the base state is read straight out of the object store with\n * `rev-parse`/`ls-tree`/`show`, and the comparison with `diff`/`ls-files`.\n *\n * Gate takes diff's doctrine below along with the mechanics: a `--since` ref\n * git cannot answer for is a refusal, never \"nothing was archived since\",\n * because that silence would pass a deploy over every feature it could not\n * see — the same fail-open as a silently empty diff base, one command over.\n *\n * The doctrine here is deliberately NOT provenance's. `../provenance/git.ts`\n * answers every git decline with \"git will not say\" — null, no finding —\n * because provenance has no denominator without git and nothing to report.\n * Diff is the opposite case: without the base there is no honest diff at all,\n * and a silently empty base would report the whole fleet as ADDED — the exact\n * \"green over zero services\" failure `docsRepoReady` exists to prevent, one\n * step later. So every decline travels as a typed outcome the command REFUSES\n * on, carrying git's own words (bounded) so the refusal can say why.\n *\n * What is shared with provenance is the mechanics: the one timeout and the\n * capped stdout reader, so rule 20 (every child spawn bounded) holds by\n * construction.\n */\nimport { spawn } from \"node:child_process\";\nimport { relative, sep } from \"node:path\";\nimport { collectStderr, declineDetail, GIT_TIMEOUT_MS, gitSaid, gitText } from \"../provenance/git.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\n\n/**\n * How many bytes one base file may be — the same generosity as\n * `../provenance/git.ts`'s stdout cap, restated here because that constant is\n * private to its module and this one collects BYTES, not text (a base\n * contract's UTF-8-ness is a fact to grade, and `setEncoding(\"utf8\")` would\n * substitute U+FFFD before anyone could check). Past the cap the child is\n * killed and the file reads as unreadable for its subject, never truncated.\n *\n * The question-asking mechanics — `gitText`, `GitAnswer`, `gitSaid`,\n * `declineDetail`, `collectStderr` — are the shared set `../provenance/git.ts`\n * exports: `../provenance/gitq/vouched-ancestor.ts` asks with the\n * same shapes and the opposite consequence (it degrades to \"full read\" where\n * this module's callers REFUSE), and the helpers stay verdict-free so the two\n * doctrines cannot bleed into each other through a shared line.\n */\nconst MAX_BASE_FILE_BYTES = 64 * 1024 * 1024;\n\nexport type BaseResolution =\n | { kind: \"no-git\"; detail: string }\n | { kind: \"no-ref\"; detail: string }\n | { kind: \"ok\"; commit: string; prefix: string };\n\n/**\n * Resolve the base: is `docsDir` inside a git work tree, where does it sit in\n * that repository, and which commit does `ref` name?\n *\n * `--show-prefix` is asked FIRST and is not decoration: a docs repo checked\n * out as a subdirectory of a larger repository (a monorepo's `docs/`) answers\n * every later `ls-tree`/`show` only under that prefix. Dropping it makes every\n * path miss, and a diff whose base paths all miss reports the whole fleet as\n * added — silently, in exactly the repositories big enough to care.\n */\nexport async function resolveBase(docsDir: DocsDir, ref: string): Promise<BaseResolution> {\n const where = await gitText(docsDir, [\"rev-parse\", \"--show-prefix\"]);\n if (where.spawnError !== undefined || where.code !== 0) {\n return { kind: \"no-git\", detail: declineDetail(where) };\n }\n // A ref spelled like an option would be parsed as one by git, not resolved\n // as a revision — refuse it before it reaches an argv. Same reason the MCP\n // argv boundary refuses '-'-prefixed strings.\n const spelled = ref.trim();\n if (spelled === \"\" || spelled.startsWith(\"-\")) {\n return { kind: \"no-ref\", detail: \"a ref must be a revision name, not empty and not option-shaped.\" };\n }\n // `^{commit}` peels an annotated tag to the commit it tags, and refuses a\n // ref that names any other object kind — a tree or blob sha is not a base\n // state to diff against.\n const verified = await gitText(docsDir, [\"rev-parse\", \"--verify\", `${spelled}^{commit}`]);\n const commit = verified.out.trim();\n if (verified.code !== 0 || !/^[0-9a-f]{40}$/.test(commit)) {\n const said = gitSaid(verified);\n return { kind: \"no-ref\", detail: said === \"\" ? \"\" : `git said: ${said}` };\n }\n return { kind: \"ok\", commit, prefix: where.out.trim() };\n}\n\n/** The resolved base every later question is asked against. */\nexport interface ResolvedBase {\n docsDir: DocsDir;\n commit: string;\n /** The docs repo's path inside its repository, `\"\"` or `\"docs/\"`-shaped — `rev-parse --show-prefix`'s answer. */\n prefix: string;\n}\n\nexport type BaseTreeListing =\n | { kind: \"ok\"; paths: string[] }\n | { kind: \"failed\"; detail: string };\n\n/**\n * Every file path under `services/` at the base commit, DOCS-RELATIVE\n * (`services/...`, prefix already stripped — `lsTree` below holds the\n * mechanics both listings share). An empty answer is git ANSWERING — the base\n * commit simply has no `services/` — and only a non-zero exit is a failure.\n */\nexport async function listBaseTree(base: ResolvedBase): Promise<BaseTreeListing> {\n return lsTree(base, \"-r\", \"services\");\n}\n\n/**\n * The NAMES of the immediate subdirectories of one docs directory at the base\n * commit — `loam gate --since`'s question: which `features/archive/<FEAT>/`\n * directories already existed at the ref, so every other archived feature\n * shipped after it. `dir` is the absolute path a `repo/paths.ts` builder\n * spells (`archiveDir`), made docs-relative here, so the layout keeps its one\n * spelling; the separator is forced to `/` because git paths are slash-joined\n * on every host. `-d` lists trees only, and the trailing slash asks for the\n * directory's CHILDREN rather than the directory entry itself. An empty answer\n * is git answering — the base had no such directory — and only a decline is\n * `failed`, which the caller refuses on, as `listBaseTree`'s callers do.\n */\nexport async function listBaseSubdirs(\n base: ResolvedBase,\n dir: string,\n): Promise<{ kind: \"ok\"; names: string[] } | { kind: \"failed\"; detail: string }> {\n const rel = `${relative(base.docsDir, dir).split(sep).join(\"/\")}/`;\n const listing = await lsTree(base, \"-d\", rel);\n if (listing.kind === \"failed\") return listing;\n return {\n kind: \"ok\",\n names: listing.paths.filter((p) => p.startsWith(rel)).map((p) => p.slice(rel.length)),\n };\n}\n\n/**\n * The NAMES of the immediate subdirectories of one docs directory whose\n * contents differ between the base commit and the WORKING TREE — committed\n * since or not — gate's second `features/archive/` question. A directory that\n * was already there at the ref is not thereby old: `loam unarchive`, an edit\n * and `loam archive` again leave a feature at the very path it shipped from\n * before, and reading presence alone graded that re-shipped feature as one an\n * earlier deploy had already answered for.\n *\n * Not a comparison of tree ids, though `ls-tree` has the ref's: the working\n * tree has no id until somebody commits it, and minting one means writing\n * objects — this module asks git, it never writes. The same two questions as\n * `validate --base`'s scope (`./scope/changed-paths.ts`) instead, narrowed to\n * the one directory: `git diff` for every tracked path that differs from the\n * ref, `--no-renames` so a moved file names the place it left as well as the\n * one it reached, and `ls-files --others` for what nobody has committed yet —\n * the re-archive a pipeline is about to ship is exactly that. Both pathspecs\n * are relative to `docsDir`, where git runs; both listings answer\n * root-relative (`git diff` always, `ls-files` under `--full-name`), so the\n * one prefix strip serves both. A decline is `failed`, and the caller decides\n * which way that fails.\n */\nexport async function subdirsChangedSince(\n base: ResolvedBase,\n dir: string,\n): Promise<{ kind: \"ok\"; names: Set<string> } | { kind: \"failed\"; detail: string }> {\n const rel = `${relative(base.docsDir, dir).split(sep).join(\"/\")}/`;\n const tracked = await gitText(base.docsDir, [\"diff\", \"--name-only\", \"--no-renames\", \"--no-ext-diff\", \"-z\", base.commit, \"--\", rel]);\n if (tracked.spawnError !== undefined || tracked.code !== 0 || tracked.overflowed) {\n return { kind: \"failed\", detail: tracked.overflowed ? \"the changed-path listing exceeded the output cap.\" : declineDetail(tracked) };\n }\n const untracked = await gitText(base.docsDir, [\"ls-files\", \"--others\", \"--exclude-standard\", \"--full-name\", \"-z\", \"--\", rel]);\n if (untracked.spawnError !== undefined || untracked.code !== 0 || untracked.overflowed) {\n return { kind: \"failed\", detail: untracked.overflowed ? \"the untracked-path listing exceeded the output cap.\" : declineDetail(untracked) };\n }\n const under = `${base.prefix}${rel}`;\n const names = new Set<string>();\n for (const path of [...tracked.out.split(\"\\0\"), ...untracked.out.split(\"\\0\")]) {\n if (!path.startsWith(under)) continue;\n const name = path.slice(under.length).split(\"/\")[0]!;\n if (name !== \"\") names.add(name);\n }\n return { kind: \"ok\", names };\n}\n\n/**\n * The one `ls-tree` both listings ask, DOCS-RELATIVE (prefix already\n * stripped). `--full-tree` anchors both the pathspec and the output at the\n * repository root regardless of cwd, so the one prefix from `resolveBase` is\n * the whole story; `-z` because git quotes non-ASCII paths in line output.\n */\nasync function lsTree(base: ResolvedBase, mode: \"-r\" | \"-d\", root: string): Promise<BaseTreeListing> {\n const answer = await gitText(base.docsDir, [\n \"ls-tree\", mode, \"-z\", \"--name-only\", \"--full-tree\", base.commit, \"--\", `${base.prefix}${root}`,\n ]);\n if (answer.spawnError !== undefined || answer.code !== 0 || answer.overflowed) {\n return {\n kind: \"failed\",\n detail: answer.overflowed\n ? \"the base tree listing exceeded the output cap.\"\n : declineDetail(answer),\n };\n }\n return {\n kind: \"ok\",\n paths: answer.out\n .split(\"\\0\")\n .filter((p) => p.startsWith(base.prefix) && p !== \"\")\n .map((p) => p.slice(base.prefix.length)),\n };\n}\n\nexport type BaseFileRead =\n | { kind: \"ok\"; bytes: Buffer }\n | { kind: \"failed\"; detail: string };\n\n/**\n * One base file's bytes, by docs-relative path. Callers only ask for paths\n * `listBaseTree` returned, so there is no \"absent\" arm: a decline here is\n * abnormal and travels as `failed` for per-subject containment — that one\n * subject degrades with the detail, never the whole diff.\n */\nexport async function showBaseFile(base: ResolvedBase, rel: string): Promise<BaseFileRead> {\n return new Promise<BaseFileRead>((done) => {\n const child = spawn(\"git\", [\"show\", `${base.commit}:${base.prefix}${rel}`], {\n cwd: base.docsDir,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: GIT_TIMEOUT_MS,\n });\n // Bytes, not text: `collectStdout` decodes as UTF-8, and a base contract\n // whose bytes are NOT UTF-8 must arrive intact so base-state can grade it\n // unreadable instead of parsing a document nobody wrote.\n const chunks: Buffer[] = [];\n let held = 0;\n let overflowed = false;\n child.stdout.on(\"data\", (chunk: Buffer) => {\n if (overflowed) return;\n if (held + chunk.length > MAX_BASE_FILE_BYTES) {\n overflowed = true;\n child.kill();\n return;\n }\n chunks.push(chunk);\n held += chunk.length;\n });\n const err = collectStderr(child);\n child.on(\"error\", (e) => done({ kind: \"failed\", detail: `git could not be started: ${e.message}.` }));\n child.on(\"close\", (code) => {\n if (code !== 0 || overflowed) {\n done({\n kind: \"failed\",\n detail: overflowed\n ? \"the file exceeds the base-read byte cap.\"\n : declineDetail({ code, out: \"\", err: err(), overflowed }),\n });\n return;\n }\n done({ kind: \"ok\", bytes: Buffer.concat(chunks) });\n });\n });\n}\n"]}
|
|
@@ -9,9 +9,13 @@
|
|
|
9
9
|
* rule, and that is a recorded risk, not an accident: the walk classifies a
|
|
10
10
|
* LIVE filesystem (readdir, symlinks, marker files) and cannot walk a git
|
|
11
11
|
* ref. What keeps the two honest is sharing the artifact-name table
|
|
12
|
-
* (`ARTIFACT_FILES`/`isServiceArtifactName` from `repo/paths.ts`)
|
|
13
|
-
* marker name (`SUBSYSTEM_MARKER`)
|
|
14
|
-
*
|
|
12
|
+
* (`ARTIFACT_FILES`/`isServiceArtifactName` from `repo/paths.ts`), the
|
|
13
|
+
* marker name (`SUBSYSTEM_MARKER`) and the reserved-interior set
|
|
14
|
+
* (`RESERVED_INTERIOR` from `repo/tree/walk.ts`) instead of respelling them, and restating
|
|
15
|
+
* — exactly — the walk's SERVICE branches: (0) a directory named for a
|
|
16
|
+
* service's own interior (`adrs/`, `usecases/`, `ui/`) is classified nowhere,
|
|
17
|
+
* counts as nothing beneath its parent, and hides everything under it, because
|
|
18
|
+
* the walk never descends into one; (1) a directory holding a service
|
|
15
19
|
* artifact is a service, marker present or not (artifacts win); (2) the leaf
|
|
16
20
|
* rule — neither marker nor artifact and nothing beneath (an empty or
|
|
17
21
|
* not-yet-adopted directory, a lone README.md, a lone `.gitkeep` — which is
|
|
@@ -37,12 +41,16 @@
|
|
|
37
41
|
*/
|
|
38
42
|
import { isUtf8 } from "node:buffer";
|
|
39
43
|
import { inOrder } from "../kernel/concurrency.js";
|
|
44
|
+
import { rawServiceId } from "../kernel/ids/service.js";
|
|
45
|
+
import { decodeDocument } from "../kernel/document-bytes.js";
|
|
40
46
|
import { parseRequirements } from "../document/parse.js";
|
|
41
47
|
import { openapiFromText } from "../openapi/doc.js";
|
|
42
48
|
import { asyncapiFromText } from "../asyncapi/read.js";
|
|
43
49
|
import { ARTIFACT_FILES, isServiceArtifactName } from "../repo/paths.js";
|
|
44
50
|
import { SUBSYSTEM_MARKER } from "../repo/tree/marker.js";
|
|
51
|
+
import { RESERVED_INTERIOR } from "../repo/tree/walk.js";
|
|
45
52
|
import { listBaseTree, showBaseFile } from "./base-git.js";
|
|
53
|
+
import { bytesDigest, collisions } from "../consumers/state.js";
|
|
46
54
|
/**
|
|
47
55
|
* Classify the base tree's file listing into service directories — the module
|
|
48
56
|
* banner's three restated walk branches, in depth order so every ancestor is
|
|
@@ -85,13 +93,24 @@ function classifyBaseDirs(paths) {
|
|
|
85
93
|
const hasMarker = (dir) => fileChildren.get(dir)?.has(SUBSYSTEM_MARKER) === true;
|
|
86
94
|
const hasArtifact = (dir) => [...(fileChildren.get(dir) ?? [])].some((f) => isServiceArtifactName(f, "file")) ||
|
|
87
95
|
[...(dirChildren.get(dir) ?? [])].some((d) => isServiceArtifactName(d, "dir"));
|
|
96
|
+
// The walk's `RESERVED_INTERIOR` rule, restated for a flat listing: a
|
|
97
|
+
// directory named for a service's own interior is never classified, and
|
|
98
|
+
// neither is anything beneath it, because the walk never descends into one.
|
|
99
|
+
// Segment 2 onward, matching where the walk applies it — to a directory's
|
|
100
|
+
// CHILDREN, so a directory literally at `services/usecases/` is still
|
|
101
|
+
// classified there.
|
|
102
|
+
const reserved = (dir) => dir.split("/").slice(2).some((s) => RESERVED_INTERIOR.has(s));
|
|
103
|
+
// …and, for the same reason, one of them is not the subdirectory that keeps
|
|
104
|
+
// its parent off the leaf rule: the walk filters them out of `dirs` before
|
|
105
|
+
// asking whether anything is beneath.
|
|
106
|
+
const classifiableChildren = (dir) => [...(dirChildren.get(dir) ?? [])].some((d) => !RESERVED_INTERIOR.has(d));
|
|
88
107
|
// The walk's leaf rule: neither marker nor artifact and nothing beneath is a
|
|
89
108
|
// service. (A truly EMPTY directory is unrepresentable in a git tree, so at
|
|
90
109
|
// the base this arm means "no visible entry of ours" — files none of which
|
|
91
110
|
// are artifacts, or nothing but dot-named entries, the `loam seed` shape.)
|
|
92
|
-
const isLeafService = (dir) => !hasMarker(dir) && !hasArtifact(dir) && !
|
|
111
|
+
const isLeafService = (dir) => !hasMarker(dir) && !hasArtifact(dir) && !classifiableChildren(dir);
|
|
93
112
|
const candidates = [...new Set([...fileChildren.keys(), ...dirChildren.keys(), ...bare])]
|
|
94
|
-
.filter((dir) => dir !== "services" && dir.startsWith("services/"))
|
|
113
|
+
.filter((dir) => dir !== "services" && dir.startsWith("services/") && !reserved(dir))
|
|
95
114
|
.filter((dir) => hasArtifact(dir) || isLeafService(dir))
|
|
96
115
|
.sort((a, b) => {
|
|
97
116
|
const depth = a.split("/").length - b.split("/").length;
|
|
@@ -117,44 +136,53 @@ function classifyBaseDirs(paths) {
|
|
|
117
136
|
if (interior(segments))
|
|
118
137
|
continue;
|
|
119
138
|
acceptedDirs.add(dir);
|
|
120
|
-
accepted.push({ id: segments[segments.length - 1], dir, files: fileChildren.get(dir) ?? new Set() });
|
|
139
|
+
accepted.push({ id: rawServiceId(segments[segments.length - 1]), dir, files: fileChildren.get(dir) ?? new Set() });
|
|
121
140
|
}
|
|
122
141
|
return { dirs: accepted, ambiguous: collisions(accepted) };
|
|
123
142
|
}
|
|
124
|
-
function collisions(dirs) {
|
|
125
|
-
const byId = new Map();
|
|
126
|
-
for (const d of dirs)
|
|
127
|
-
byId.set(d.id, [...(byId.get(d.id) ?? []), d.dir]);
|
|
128
|
-
return new Map([...byId].filter(([, claimants]) => claimants.length > 1));
|
|
129
|
-
}
|
|
130
143
|
/**
|
|
131
144
|
* Lift one fetched file into an axis state through its parser. Absence is the
|
|
132
145
|
* listing's answer, a `show` failure is per-subject containment, and only
|
|
133
|
-
* then do the bytes reach a parser.
|
|
146
|
+
* then do the bytes reach a parser. A parser's refusal carries the hash of
|
|
147
|
+
* the bytes it refused (`AxisState`'s `bytes`); a `show` failure has none to
|
|
148
|
+
* hash.
|
|
134
149
|
*/
|
|
135
150
|
function axisFrom(raw, parse) {
|
|
136
151
|
if (raw === null)
|
|
137
152
|
return { kind: "absent" };
|
|
138
153
|
if (raw.read.kind === "failed")
|
|
139
154
|
return { kind: "unreadable", path: raw.rel, error: raw.read.detail };
|
|
140
|
-
|
|
155
|
+
const state = parse(raw.rel, raw.read.bytes);
|
|
156
|
+
return state.kind === "unreadable" ? { ...state, bytes: bytesDigest(raw.read.bytes) } : state;
|
|
141
157
|
}
|
|
142
158
|
/**
|
|
143
|
-
* The same refusal `readOpenapi`/`readAsyncapi
|
|
144
|
-
*
|
|
145
|
-
* `toString`, and a document nobody wrote must not be graded.
|
|
159
|
+
* The same refusal `readOpenapi`/`readAsyncapi` make on the living side for a
|
|
160
|
+
* CONTRACT: non-UTF-8 bytes would be silently rewritten as U+FFFD by
|
|
161
|
+
* `toString`, and a document nobody wrote must not be graded. Contracts only:
|
|
162
|
+
* the spec axis decodes through `decodeDocument` instead (`specState`), because
|
|
163
|
+
* that is what its living side does, and each axis must refuse at the base
|
|
164
|
+
* exactly the bytes its living reader refuses.
|
|
146
165
|
*/
|
|
147
166
|
function decodeBase(path, bytes) {
|
|
148
167
|
if (!isUtf8(bytes))
|
|
149
168
|
return { unreadable: { kind: "unreadable", path, error: "file is not valid UTF-8" } };
|
|
150
169
|
return { text: bytes.toString("utf8") };
|
|
151
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* A spec-axis file decodes through `decodeDocument` — the rule the living side
|
|
173
|
+
* applies to the same file (`FleetContext.readText`) — not through
|
|
174
|
+
* `decodeBase`'s `isUtf8`-only test. The difference is the NUL test: UTF-16LE
|
|
175
|
+
* of ASCII saved WITHOUT a byte-order mark is a valid UTF-8 sequence, so
|
|
176
|
+
* `isUtf8` alone let it through here and it parsed as zero requirements while
|
|
177
|
+
* the working tree refused the identical bytes. The base then read as a
|
|
178
|
+
* readable EMPTY spec: an untouched file diffed as one-sided containment, and
|
|
179
|
+
* a branch that re-saved it as UTF-8 reported every requirement in it as
|
|
180
|
+
* added. `decodeDocument`'s `NotUtf8DocumentError` lands in the catch below
|
|
181
|
+
* and becomes the unreadable arm like any parse failure.
|
|
182
|
+
*/
|
|
152
183
|
function specState(path, bytes) {
|
|
153
|
-
const decoded = decodeBase(path, bytes);
|
|
154
|
-
if ("unreadable" in decoded)
|
|
155
|
-
return decoded.unreadable;
|
|
156
184
|
try {
|
|
157
|
-
return { kind: "read", value: parseRequirements(
|
|
185
|
+
return { kind: "read", value: parseRequirements(decodeDocument(bytes, path)) };
|
|
158
186
|
}
|
|
159
187
|
catch (e) {
|
|
160
188
|
return { kind: "unreadable", path, error: e instanceof Error ? e.message : String(e) };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-state.js","sourceRoot":"","sources":["../../../src/core/diff/base-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,EAAE,eAAe,EAAkB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAqB,MAAM,eAAe,CAAC;AAyD9E;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,KAAe;IACvC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAC;IACpD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;IACnD,yEAAyE;IACzE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,wEAAwE;QACxE,kEAAkE;QAClE,iEAAiE;QACjE,wEAAwE;QACxE,0EAA0E;QAC1E,uEAAuE;QACvE,wEAAwE;QACxE,sEAAsE;QACtE,2EAA2E;QAC3E,sEAAsE;QACtE,2DAA2D;QAC3D,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACtD,yEAAyE;QACzE,MAAM,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC;YACvD,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,GAAG,KAAK,SAAS;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;YACxD,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;QACD,IAAI,MAAM,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,SAAS,GAAG,CAAC,GAAW,EAAW,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAClG,MAAM,WAAW,GAAG,CAAC,GAAW,EAAW,EAAE,CAC3C,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAChF,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IACjF,6EAA6E;IAC7E,4EAA4E;IAC5E,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,aAAa,GAAG,CAAC,GAAW,EAAW,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9G,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;SACtF,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,UAAU,IAAI,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;SAClE,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC;SACvD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACb,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;QACxD,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IACL,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,2EAA2E;IAC3E,4EAA4E;IAC5E,kEAAkE;IAClE,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,CAAC,QAAkB,EAAW,EAAE;QAC/C,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC1C,OAAO,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACjF,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,QAAQ,CAAC,QAAQ,CAAC;YAAE,SAAS;QACjC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAE,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC7D,CAAC;AAED,SAAS,UAAU,CAAC,IAAe;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACzE,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AAC5E,CAAC;AAKD;;;;GAIG;AACH,SAAS,QAAQ,CAAI,GAAY,EAAE,KAAoD;IACrF,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5C,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;IACrG,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,IAAY,EAAE,KAAa;IAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,yBAAyB,EAAE,EAAE,CAAC;IAC1G,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,KAAa;IAC5C,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxC,IAAI,YAAY,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,UAAU,CAAC;IACvD,IAAI,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;IAClE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACzF,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,KAAa;IAC/C,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxC,IAAI,YAAY,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,UAAU,CAAC;IACvD,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,OAAO,GAAG,CAAC,UAAU;QACnB,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,iCAAiC,EAAE;QACrF,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,KAAa;IAChD,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxC,IAAI,YAAY,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,UAAU,CAAC;IACvD,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO,GAAG,CAAC,UAAU;QACnB,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,kCAAkC,EAAE;QACtF,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC;AAC7E,CAAC;AAED,wNAAwN;AACxN,KAAK,UAAU,eAAe,CAAC,IAAkB,EAAE,CAAU;IAC3D,MAAM,KAAK,GAAG,KAAK,EAAE,IAAY,EAAoB,EAAE,CACrD,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7G,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,IAAI,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;QAC3D,QAAQ,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;QACnE,OAAO,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;QACpE,QAAQ,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;KACxE,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAkB;IACpD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAC9C,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpG,6EAA6E;IAC7E,0DAA0D;IAC1D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACtE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC;AAC/F,CAAC","sourcesContent":["/**\n * The base side of `loam diff`: the fleet as the base commit held it, read\n * entirely out of git's object store (`./base-git.ts`) and parsed with the\n * SAME parsers the living files go through — `parseRequirements`,\n * `openapiFromText`, `asyncapiFromText` — so a base document can never read\n * differently from the identical bytes sitting on disk.\n *\n * CLASSIFICATION is a second, simplified spelling of `repo/tree/walk.ts`'s\n * rule, and that is a recorded risk, not an accident: the walk classifies a\n * LIVE filesystem (readdir, symlinks, marker files) and cannot walk a git\n * ref. What keeps the two honest is sharing the artifact-name table\n * (`ARTIFACT_FILES`/`isServiceArtifactName` from `repo/paths.ts`) and the\n * marker name (`SUBSYSTEM_MARKER`) instead of respelling them, and restating\n * — exactly — the walk's SERVICE branches: (1) a directory holding a service\n * artifact is a service, marker present or not (artifacts win); (2) the leaf\n * rule — neither marker nor artifact and nothing beneath (an empty or\n * not-yet-adopted directory, a lone README.md, a lone `.gitkeep` — which is\n * the whole content of a directory `loam seed` creates) is a service too; (3) nothing\n * inside a PLAIN service is classified again (`examples/openapi.yaml` two\n * levels down is interior, the walk stops there), but a service carrying the\n * marker BESIDE its artifacts is still descended — minus artifact-named\n * children like `adrs/` — so the services stranded beneath it stay\n * enumerated, the walk's marker-misplaced branch. Subsystem and unmarked\n * group directories need no restating: a flat file listing descends through\n * them by construction. What is deliberately NOT restated: the walk's\n * findings (`subsystem.marker-misplaced`, `subsystem.unmarked`,\n * `subsystem.name-collision`) — those are validate's live diagnostics; here\n * a name claimed twice suspends the subject (`ambiguous`) instead. If\n * walk.ts's rule ever changes, this module owes the same change;\n * test/diff.test.ts pins the visible agreement (subsystem move, not-adopted\n * leaf, stranded-under-marker).\n *\n * Deliberate consequence of joining by leaf directory name: a service MOVED\n * between subsystems keeps its id and diffs as UNCHANGED. Subsystem placement\n * is navigation, not identity — SCHEMA's own position, and the reason\n * `subsystem move` never touches a join key.\n */\nimport { isUtf8 } from \"node:buffer\";\nimport { inOrder } from \"../kernel/concurrency.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport type { Requirement } from \"../document/spec.js\";\nimport { openapiFromText, type Operation } from \"../openapi/doc.js\";\nimport { asyncapiFromText } from \"../asyncapi/read.js\";\nimport type { EventMessage } from \"../asyncapi/model.js\";\nimport { ARTIFACT_FILES, isServiceArtifactName } from \"../repo/paths.js\";\nimport { SUBSYSTEM_MARKER } from \"../repo/tree/marker.js\";\nimport { listBaseTree, showBaseFile, type ResolvedBase } from \"./base-git.js\";\n\n/**\n * One artifact's state on one side of the diff. `unreadable` is the\n * containment arm: the file exists but could not be read as its kind, so\n * every finding on this axis for this subject is SUSPENDED — \"nobody could\n * look\" must never grade as \"nothing is there\", in either direction.\n */\nexport type AxisState<T> =\n | { kind: \"absent\" }\n | { kind: \"read\"; value: T }\n | { kind: \"unreadable\"; path: string; error: string };\n\n/** The keys of a service state that hold requirement-bearing documents. */\nexport type SpecAxisKey = \"spec\" | \"archSpec\";\n\n/**\n * One service on one side of the diff — the same shape for the base ref and\n * the working tree (`./current-state.ts` builds the other), which is what\n * lets `./semantic.ts` join the two without caring which side came from git.\n */\nexport interface ServiceState {\n id: string;\n /** Docs-relative directory with forward slashes, e.g. `services/billing/payment-service`. */\n dir: string;\n spec: AxisState<Requirement[]>;\n /** Read on both sides for the `Consumes:` join — the outbox requirement lives here as often as in spec.md (core/coherence/events/lookups.ts's lesson). */\n archSpec: AxisState<Requirement[]>;\n /** Operations with `x-loam-remove` markers filtered out: a marker is bookkeeping, never a callable operation. */\n openapi: AxisState<Operation[]>;\n /** Messages with removal markers filtered out, for the same reason. */\n asyncapi: AxisState<EventMessage[]>;\n}\n\nexport interface BaseFleet {\n /** By service id, insertion-sorted by id — deterministic output depends on it. */\n services: Map<string, ServiceState>;\n /**\n * Ids claimed by MORE than one base directory. The live walk grades this\n * `subsystem.name-collision`; here the id's base state is unanswerable, so\n * the subject is carried with every axis suspended rather than one claimant\n * silently winning the join.\n */\n ambiguous: Map<string, string[]>;\n}\n\nexport type BaseFleetRead =\n | { kind: \"ok\"; fleet: BaseFleet }\n | { kind: \"failed\"; detail: string };\n\n/** A classified base service directory and which artifact files the base tree listed in it. */\ninterface BaseDir {\n id: string;\n dir: string;\n files: Set<string>;\n}\n\n/**\n * Classify the base tree's file listing into service directories — the module\n * banner's three restated walk branches, in depth order so every ancestor is\n * decided before anything beneath it.\n */\nfunction classifyBaseDirs(paths: string[]): { dirs: BaseDir[]; ambiguous: Map<string, string[]> } {\n const fileChildren = new Map<string, Set<string>>();\n const dirChildren = new Map<string, Set<string>>();\n // Directories nothing VISIBLE lives in, so neither map above names them.\n const bare = new Set<string>();\n for (const path of paths) {\n const all = path.split(\"/\");\n // Dot-named entries are invisible to the live walk — but the walk still\n // SEES the directory holding them, because readdir discovered the\n // directory itself and only its CONTENTS are filtered (walk.ts's\n // `subdirNames` and `visitDir`). So cut the path at its first dot-named\n // segment rather than discarding the whole path: everything above the cut\n // stays a visible directory, the cut segment and everything beneath it\n // stay invisible. Discarding it outright hid every directory whose only\n // file is dot-named — which is exactly what `loam seed` writes into a\n // service directory it creates (`.gitkeep`, and nothing else), so a seeded\n // service was absent from the base and diffed as `diff.service-added`\n // against the unchanged tree that had just been committed.\n const cut = all.findIndex((s) => s.startsWith(\".\") || s === \"\");\n const segments = cut === -1 ? all : all.slice(0, cut);\n // Only a whole path ends in a file; a cut one is a pure directory chain.\n const fileAt = cut === -1 ? segments.length - 1 : -1;\n for (let i = 1; i < segments.length; i += 1) {\n const dir = segments.slice(0, i).join(\"/\");\n const child = segments[i]!;\n const into = i === fileAt ? fileChildren : dirChildren;\n let set = into.get(dir);\n if (set === undefined) into.set(dir, (set = new Set()));\n set.add(child);\n }\n if (fileAt === -1 && segments.length > 1) bare.add(segments.join(\"/\"));\n }\n const hasMarker = (dir: string): boolean => fileChildren.get(dir)?.has(SUBSYSTEM_MARKER) === true;\n const hasArtifact = (dir: string): boolean =>\n [...(fileChildren.get(dir) ?? [])].some((f) => isServiceArtifactName(f, \"file\")) ||\n [...(dirChildren.get(dir) ?? [])].some((d) => isServiceArtifactName(d, \"dir\"));\n // The walk's leaf rule: neither marker nor artifact and nothing beneath is a\n // service. (A truly EMPTY directory is unrepresentable in a git tree, so at\n // the base this arm means \"no visible entry of ours\" — files none of which\n // are artifacts, or nothing but dot-named entries, the `loam seed` shape.)\n const isLeafService = (dir: string): boolean => !hasMarker(dir) && !hasArtifact(dir) && !dirChildren.has(dir);\n const candidates = [...new Set([...fileChildren.keys(), ...dirChildren.keys(), ...bare])]\n .filter((dir) => dir !== \"services\" && dir.startsWith(\"services/\"))\n .filter((dir) => hasArtifact(dir) || isLeafService(dir))\n .sort((a, b) => {\n const depth = a.split(\"/\").length - b.split(\"/\").length;\n return depth !== 0 ? depth : a < b ? -1 : 1;\n });\n const accepted: BaseDir[] = [];\n const acceptedDirs = new Set<string>();\n // Is this candidate INTERIOR to the nearest service already accepted above\n // it? A plain service swallows everything beneath (the walk stops there); a\n // marker-beside-artifacts service is descended except through its\n // artifact-named children (`adrs/`), so a service stranded under it stays.\n const interior = (segments: string[]): boolean => {\n for (let i = segments.length - 1; i >= 2; i -= 1) {\n const ancestor = segments.slice(0, i).join(\"/\");\n if (!acceptedDirs.has(ancestor)) continue;\n return hasMarker(ancestor) ? isServiceArtifactName(segments[i]!, \"dir\") : true;\n }\n return false;\n };\n for (const dir of candidates) {\n const segments = dir.split(\"/\");\n if (interior(segments)) continue;\n acceptedDirs.add(dir);\n accepted.push({ id: segments[segments.length - 1]!, dir, files: fileChildren.get(dir) ?? new Set() });\n }\n return { dirs: accepted, ambiguous: collisions(accepted) };\n}\n\nfunction collisions(dirs: BaseDir[]): Map<string, string[]> {\n const byId = new Map<string, string[]>();\n for (const d of dirs) byId.set(d.id, [...(byId.get(d.id) ?? []), d.dir]);\n return new Map([...byId].filter(([, claimants]) => claimants.length > 1));\n}\n\n/** One shown base file, or `null` for a file the base tree never listed. */\ntype Fetched = { rel: string; read: Awaited<ReturnType<typeof showBaseFile>> } | null;\n\n/**\n * Lift one fetched file into an axis state through its parser. Absence is the\n * listing's answer, a `show` failure is per-subject containment, and only\n * then do the bytes reach a parser.\n */\nfunction axisFrom<T>(raw: Fetched, parse: (path: string, bytes: Buffer) => AxisState<T>): AxisState<T> {\n if (raw === null) return { kind: \"absent\" };\n if (raw.read.kind === \"failed\") return { kind: \"unreadable\", path: raw.rel, error: raw.read.detail };\n return parse(raw.rel, raw.read.bytes);\n}\n\n/**\n * The same refusal `readOpenapi`/`readAsyncapi`/`decodeDocument` make on the\n * living side: non-UTF-8 bytes would be silently rewritten as U+FFFD by\n * `toString`, and a document nobody wrote must not be graded.\n */\nfunction decodeBase(path: string, bytes: Buffer): { text: string } | { unreadable: AxisState<never> } {\n if (!isUtf8(bytes)) return { unreadable: { kind: \"unreadable\", path, error: \"file is not valid UTF-8\" } };\n return { text: bytes.toString(\"utf8\") };\n}\n\nfunction specState(path: string, bytes: Buffer): AxisState<Requirement[]> {\n const decoded = decodeBase(path, bytes);\n if (\"unreadable\" in decoded) return decoded.unreadable;\n try {\n return { kind: \"read\", value: parseRequirements(decoded.text) };\n } catch (e) {\n return { kind: \"unreadable\", path, error: e instanceof Error ? e.message : String(e) };\n }\n}\n\nfunction openapiState(path: string, bytes: Buffer): AxisState<Operation[]> {\n const decoded = decodeBase(path, bytes);\n if (\"unreadable\" in decoded) return decoded.unreadable;\n const doc = openapiFromText(decoded.text);\n return doc.unreadable\n ? { kind: \"unreadable\", path, error: doc.error ?? \"not a readable OpenAPI document\" }\n : { kind: \"read\", value: doc.ops.filter((op) => !op.remove) };\n}\n\nfunction asyncapiState(path: string, bytes: Buffer): AxisState<EventMessage[]> {\n const decoded = decodeBase(path, bytes);\n if (\"unreadable\" in decoded) return decoded.unreadable;\n const doc = asyncapiFromText(decoded.text);\n return doc.unreadable\n ? { kind: \"unreadable\", path, error: doc.error ?? \"not a readable AsyncAPI document\" }\n : { kind: \"read\", value: doc.messages.filter((m) => m.remove !== true) };\n}\n\n/** One base service's four axes. Sequential INSIDE the service on purpose: the pool below rations children at the service grain, and four spawns per in-flight worker would multiply past the cap it exists to hold. */\nasync function readBaseService(base: ResolvedBase, d: BaseDir): Promise<ServiceState> {\n const fetch = async (file: string): Promise<Fetched> =>\n d.files.has(file) ? { rel: `${d.dir}/${file}`, read: await showBaseFile(base, `${d.dir}/${file}`) } : null;\n return {\n id: d.id,\n dir: d.dir,\n spec: axisFrom(await fetch(ARTIFACT_FILES.spec), specState),\n archSpec: axisFrom(await fetch(ARTIFACT_FILES.archSpec), specState),\n openapi: axisFrom(await fetch(ARTIFACT_FILES.openapi), openapiState),\n asyncapi: axisFrom(await fetch(ARTIFACT_FILES.asyncapi), asyncapiState),\n };\n}\n\n/**\n * The whole base fleet. `failed` only when git cannot LIST the tree — the\n * whole-diff refusal; a single file that cannot be shown or parsed degrades\n * its own subject's axis to `unreadable` and nothing else.\n */\nexport async function readBaseFleet(base: ResolvedBase): Promise<BaseFleetRead> {\n const listing = await listBaseTree(base);\n if (listing.kind === \"failed\") return listing;\n const { dirs, ambiguous } = classifyBaseDirs(listing.paths);\n const sorted = [...dirs].sort((a, b) => (a.id < b.id ? -1 : 1)).filter((d) => !ambiguous.has(d.id));\n // Pooled, ordered reads: bounded children in flight, results by input index,\n // so the same base state always assembles the same fleet.\n const states = await inOrder(sorted, (d) => readBaseService(base, d));\n return { kind: \"ok\", fleet: { services: new Map(states.map((s) => [s.id, s])), ambiguous } };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"base-state.js","sourceRoot":"","sources":["../../../src/core/diff/base-state.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The base side of `loam diff`: the fleet as the base commit held it, read\n * entirely out of git's object store (`./base-git.ts`) and parsed with the\n * SAME parsers the living files go through — `parseRequirements`,\n * `openapiFromText`, `asyncapiFromText` — so a base document can never read\n * differently from the identical bytes sitting on disk.\n *\n * CLASSIFICATION is a second, simplified spelling of `repo/tree/walk.ts`'s\n * rule, and that is a recorded risk, not an accident: the walk classifies a\n * LIVE filesystem (readdir, symlinks, marker files) and cannot walk a git\n * ref. What keeps the two honest is sharing the artifact-name table\n * (`ARTIFACT_FILES`/`isServiceArtifactName` from `repo/paths.ts`), the\n * marker name (`SUBSYSTEM_MARKER`) and the reserved-interior set\n * (`RESERVED_INTERIOR` from `repo/tree/walk.ts`) instead of respelling them, and restating\n * — exactly — the walk's SERVICE branches: (0) a directory named for a\n * service's own interior (`adrs/`, `usecases/`, `ui/`) is classified nowhere,\n * counts as nothing beneath its parent, and hides everything under it, because\n * the walk never descends into one; (1) a directory holding a service\n * artifact is a service, marker present or not (artifacts win); (2) the leaf\n * rule — neither marker nor artifact and nothing beneath (an empty or\n * not-yet-adopted directory, a lone README.md, a lone `.gitkeep` — which is\n * the whole content of a directory `loam seed` creates) is a service too; (3) nothing\n * inside a PLAIN service is classified again (`examples/openapi.yaml` two\n * levels down is interior, the walk stops there), but a service carrying the\n * marker BESIDE its artifacts is still descended — minus artifact-named\n * children like `adrs/` — so the services stranded beneath it stay\n * enumerated, the walk's marker-misplaced branch. Subsystem and unmarked\n * group directories need no restating: a flat file listing descends through\n * them by construction. What is deliberately NOT restated: the walk's\n * findings (`subsystem.marker-misplaced`, `subsystem.unmarked`,\n * `subsystem.name-collision`) — those are validate's live diagnostics; here\n * a name claimed twice suspends the subject (`ambiguous`) instead. If\n * walk.ts's rule ever changes, this module owes the same change;\n * test/diff.test.ts pins the visible agreement (subsystem move, not-adopted\n * leaf, stranded-under-marker).\n *\n * Deliberate consequence of joining by leaf directory name: a service MOVED\n * between subsystems keeps its id and diffs as UNCHANGED. Subsystem placement\n * is navigation, not identity — SCHEMA's own position, and the reason\n * `subsystem move` never touches a join key.\n */\nimport { isUtf8 } from \"node:buffer\";\nimport { inOrder } from \"../kernel/concurrency.js\";\nimport { rawServiceId, type RawServiceId } from \"../kernel/ids/service.js\";\nimport { decodeDocument } from \"../kernel/document-bytes.js\";\nimport { parseRequirements } from \"../document/parse.js\";\nimport type { Requirement } from \"../document/spec.js\";\nimport { openapiFromText, type Operation } from \"../openapi/doc.js\";\nimport { asyncapiFromText } from \"../asyncapi/read.js\";\nimport type { EventMessage } from \"../asyncapi/model.js\";\nimport { ARTIFACT_FILES, isServiceArtifactName } from \"../repo/paths.js\";\nimport { SUBSYSTEM_MARKER } from \"../repo/tree/marker.js\";\nimport { RESERVED_INTERIOR } from \"../repo/tree/walk.js\";\nimport { listBaseTree, showBaseFile, type ResolvedBase } from \"./base-git.js\";\nimport { bytesDigest, collisions, type AxisState, type ServiceState } from \"../consumers/state.js\";\n\nexport interface BaseFleet {\n /** By service id, insertion-sorted by id — deterministic output depends on it. */\n services: Map<string, ServiceState>;\n /**\n * Ids claimed by MORE than one base directory. The live walk grades this\n * `subsystem.name-collision`; here the id's base state is unanswerable, so\n * the subject is carried with every axis suspended rather than one claimant\n * silently winning the join.\n */\n ambiguous: Map<string, string[]>;\n}\n\nexport type BaseFleetRead =\n | { kind: \"ok\"; fleet: BaseFleet }\n | { kind: \"failed\"; detail: string };\n\n/** A classified base service directory and which artifact files the base tree listed in it. */\ninterface BaseDir {\n id: RawServiceId;\n dir: string;\n files: Set<string>;\n}\n\n/**\n * Classify the base tree's file listing into service directories — the module\n * banner's three restated walk branches, in depth order so every ancestor is\n * decided before anything beneath it.\n */\nfunction classifyBaseDirs(paths: string[]): { dirs: BaseDir[]; ambiguous: Map<string, string[]> } {\n const fileChildren = new Map<string, Set<string>>();\n const dirChildren = new Map<string, Set<string>>();\n // Directories nothing VISIBLE lives in, so neither map above names them.\n const bare = new Set<string>();\n for (const path of paths) {\n const all = path.split(\"/\");\n // Dot-named entries are invisible to the live walk — but the walk still\n // SEES the directory holding them, because readdir discovered the\n // directory itself and only its CONTENTS are filtered (walk.ts's\n // `subdirNames` and `visitDir`). So cut the path at its first dot-named\n // segment rather than discarding the whole path: everything above the cut\n // stays a visible directory, the cut segment and everything beneath it\n // stay invisible. Discarding it outright hid every directory whose only\n // file is dot-named — which is exactly what `loam seed` writes into a\n // service directory it creates (`.gitkeep`, and nothing else), so a seeded\n // service was absent from the base and diffed as `diff.service-added`\n // against the unchanged tree that had just been committed.\n const cut = all.findIndex((s) => s.startsWith(\".\") || s === \"\");\n const segments = cut === -1 ? all : all.slice(0, cut);\n // Only a whole path ends in a file; a cut one is a pure directory chain.\n const fileAt = cut === -1 ? segments.length - 1 : -1;\n for (let i = 1; i < segments.length; i += 1) {\n const dir = segments.slice(0, i).join(\"/\");\n const child = segments[i]!;\n const into = i === fileAt ? fileChildren : dirChildren;\n let set = into.get(dir);\n if (set === undefined) into.set(dir, (set = new Set()));\n set.add(child);\n }\n if (fileAt === -1 && segments.length > 1) bare.add(segments.join(\"/\"));\n }\n const hasMarker = (dir: string): boolean => fileChildren.get(dir)?.has(SUBSYSTEM_MARKER) === true;\n const hasArtifact = (dir: string): boolean =>\n [...(fileChildren.get(dir) ?? [])].some((f) => isServiceArtifactName(f, \"file\")) ||\n [...(dirChildren.get(dir) ?? [])].some((d) => isServiceArtifactName(d, \"dir\"));\n // The walk's `RESERVED_INTERIOR` rule, restated for a flat listing: a\n // directory named for a service's own interior is never classified, and\n // neither is anything beneath it, because the walk never descends into one.\n // Segment 2 onward, matching where the walk applies it — to a directory's\n // CHILDREN, so a directory literally at `services/usecases/` is still\n // classified there.\n const reserved = (dir: string): boolean => dir.split(\"/\").slice(2).some((s) => RESERVED_INTERIOR.has(s));\n // …and, for the same reason, one of them is not the subdirectory that keeps\n // its parent off the leaf rule: the walk filters them out of `dirs` before\n // asking whether anything is beneath.\n const classifiableChildren = (dir: string): boolean =>\n [...(dirChildren.get(dir) ?? [])].some((d) => !RESERVED_INTERIOR.has(d));\n // The walk's leaf rule: neither marker nor artifact and nothing beneath is a\n // service. (A truly EMPTY directory is unrepresentable in a git tree, so at\n // the base this arm means \"no visible entry of ours\" — files none of which\n // are artifacts, or nothing but dot-named entries, the `loam seed` shape.)\n const isLeafService = (dir: string): boolean =>\n !hasMarker(dir) && !hasArtifact(dir) && !classifiableChildren(dir);\n const candidates = [...new Set([...fileChildren.keys(), ...dirChildren.keys(), ...bare])]\n .filter((dir) => dir !== \"services\" && dir.startsWith(\"services/\") && !reserved(dir))\n .filter((dir) => hasArtifact(dir) || isLeafService(dir))\n .sort((a, b) => {\n const depth = a.split(\"/\").length - b.split(\"/\").length;\n return depth !== 0 ? depth : a < b ? -1 : 1;\n });\n const accepted: BaseDir[] = [];\n const acceptedDirs = new Set<string>();\n // Is this candidate INTERIOR to the nearest service already accepted above\n // it? A plain service swallows everything beneath (the walk stops there); a\n // marker-beside-artifacts service is descended except through its\n // artifact-named children (`adrs/`), so a service stranded under it stays.\n const interior = (segments: string[]): boolean => {\n for (let i = segments.length - 1; i >= 2; i -= 1) {\n const ancestor = segments.slice(0, i).join(\"/\");\n if (!acceptedDirs.has(ancestor)) continue;\n return hasMarker(ancestor) ? isServiceArtifactName(segments[i]!, \"dir\") : true;\n }\n return false;\n };\n for (const dir of candidates) {\n const segments = dir.split(\"/\");\n if (interior(segments)) continue;\n acceptedDirs.add(dir);\n accepted.push({ id: rawServiceId(segments[segments.length - 1]!), dir, files: fileChildren.get(dir) ?? new Set() });\n }\n return { dirs: accepted, ambiguous: collisions(accepted) };\n}\n\n/** One shown base file, or `null` for a file the base tree never listed. */\ntype Fetched = { rel: string; read: Awaited<ReturnType<typeof showBaseFile>> } | null;\n\n/**\n * Lift one fetched file into an axis state through its parser. Absence is the\n * listing's answer, a `show` failure is per-subject containment, and only\n * then do the bytes reach a parser. A parser's refusal carries the hash of\n * the bytes it refused (`AxisState`'s `bytes`); a `show` failure has none to\n * hash.\n */\nfunction axisFrom<T>(raw: Fetched, parse: (path: string, bytes: Buffer) => AxisState<T>): AxisState<T> {\n if (raw === null) return { kind: \"absent\" };\n if (raw.read.kind === \"failed\") return { kind: \"unreadable\", path: raw.rel, error: raw.read.detail };\n const state = parse(raw.rel, raw.read.bytes);\n return state.kind === \"unreadable\" ? { ...state, bytes: bytesDigest(raw.read.bytes) } : state;\n}\n\n/**\n * The same refusal `readOpenapi`/`readAsyncapi` make on the living side for a\n * CONTRACT: non-UTF-8 bytes would be silently rewritten as U+FFFD by\n * `toString`, and a document nobody wrote must not be graded. Contracts only:\n * the spec axis decodes through `decodeDocument` instead (`specState`), because\n * that is what its living side does, and each axis must refuse at the base\n * exactly the bytes its living reader refuses.\n */\nfunction decodeBase(path: string, bytes: Buffer): { text: string } | { unreadable: AxisState<never> } {\n if (!isUtf8(bytes)) return { unreadable: { kind: \"unreadable\", path, error: \"file is not valid UTF-8\" } };\n return { text: bytes.toString(\"utf8\") };\n}\n\n/**\n * A spec-axis file decodes through `decodeDocument` — the rule the living side\n * applies to the same file (`FleetContext.readText`) — not through\n * `decodeBase`'s `isUtf8`-only test. The difference is the NUL test: UTF-16LE\n * of ASCII saved WITHOUT a byte-order mark is a valid UTF-8 sequence, so\n * `isUtf8` alone let it through here and it parsed as zero requirements while\n * the working tree refused the identical bytes. The base then read as a\n * readable EMPTY spec: an untouched file diffed as one-sided containment, and\n * a branch that re-saved it as UTF-8 reported every requirement in it as\n * added. `decodeDocument`'s `NotUtf8DocumentError` lands in the catch below\n * and becomes the unreadable arm like any parse failure.\n */\nfunction specState(path: string, bytes: Buffer): AxisState<Requirement[]> {\n try {\n return { kind: \"read\", value: parseRequirements(decodeDocument(bytes, path)) };\n } catch (e) {\n return { kind: \"unreadable\", path, error: e instanceof Error ? e.message : String(e) };\n }\n}\n\nfunction openapiState(path: string, bytes: Buffer): AxisState<Operation[]> {\n const decoded = decodeBase(path, bytes);\n if (\"unreadable\" in decoded) return decoded.unreadable;\n const doc = openapiFromText(decoded.text);\n return doc.unreadable\n ? { kind: \"unreadable\", path, error: doc.error ?? \"not a readable OpenAPI document\" }\n : { kind: \"read\", value: doc.ops.filter((op) => !op.remove) };\n}\n\nfunction asyncapiState(path: string, bytes: Buffer): AxisState<EventMessage[]> {\n const decoded = decodeBase(path, bytes);\n if (\"unreadable\" in decoded) return decoded.unreadable;\n const doc = asyncapiFromText(decoded.text);\n return doc.unreadable\n ? { kind: \"unreadable\", path, error: doc.error ?? \"not a readable AsyncAPI document\" }\n : { kind: \"read\", value: doc.messages.filter((m) => m.remove !== true) };\n}\n\n/** One base service's four axes. Sequential INSIDE the service on purpose: the pool below rations children at the service grain, and four spawns per in-flight worker would multiply past the cap it exists to hold. */\nasync function readBaseService(base: ResolvedBase, d: BaseDir): Promise<ServiceState> {\n const fetch = async (file: string): Promise<Fetched> =>\n d.files.has(file) ? { rel: `${d.dir}/${file}`, read: await showBaseFile(base, `${d.dir}/${file}`) } : null;\n return {\n id: d.id,\n dir: d.dir,\n spec: axisFrom(await fetch(ARTIFACT_FILES.spec), specState),\n archSpec: axisFrom(await fetch(ARTIFACT_FILES.archSpec), specState),\n openapi: axisFrom(await fetch(ARTIFACT_FILES.openapi), openapiState),\n asyncapi: axisFrom(await fetch(ARTIFACT_FILES.asyncapi), asyncapiState),\n };\n}\n\n/**\n * The whole base fleet. `failed` only when git cannot LIST the tree — the\n * whole-diff refusal; a single file that cannot be shown or parsed degrades\n * its own subject's axis to `unreadable` and nothing else.\n */\nexport async function readBaseFleet(base: ResolvedBase): Promise<BaseFleetRead> {\n const listing = await listBaseTree(base);\n if (listing.kind === \"failed\") return listing;\n const { dirs, ambiguous } = classifyBaseDirs(listing.paths);\n const sorted = [...dirs].sort((a, b) => (a.id < b.id ? -1 : 1)).filter((d) => !ambiguous.has(d.id));\n // Pooled, ordered reads: bounded children in flight, results by input index,\n // so the same base state always assembles the same fleet.\n const states = await inOrder(sorted, (d) => readBaseService(base, d));\n return { kind: \"ok\", fleet: { services: new Map(states.map((s) => [s.id, s])), ambiguous } };\n}\n"]}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { ownsOperation, REQUIREMENT_AXES } from "../consumers/consumers.js";
|
|
2
|
+
/** The requirements one service contributes to a join — both spec files, a REMOVED one governing nothing. */
|
|
3
|
+
function reqsOf(state) {
|
|
4
|
+
return REQUIREMENT_AXES.flatMap((axis) => {
|
|
5
|
+
const a = state[axis];
|
|
6
|
+
return a.kind === "read" ? a.value.filter((r) => r.kind !== "REMOVED") : [];
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
/** The map key a tuple is joined on between the two states. */
|
|
10
|
+
function tupleKey(t) {
|
|
11
|
+
return `${t.kind}\0${t.consumer}\0${t.provider}\0${t.token}`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Every cross-service join one state holds, keyed for the base/current join.
|
|
15
|
+
* The rules are the consumer index's, read off it rather than restated: both
|
|
16
|
+
* spec files for `Operations:` and `Consumes:` alike (`REQUIREMENT_AXES`), and
|
|
17
|
+
* an `Operations:` line naming an id the consumer's own contract defines is
|
|
18
|
+
* that consumer's own operation, no join onto anybody else's
|
|
19
|
+
* (`ownsOperation`). An unreadable axis simply contributes nothing here — no
|
|
20
|
+
* line from a spec file nobody could read, no ownership from a contract nobody
|
|
21
|
+
* could read — and `tupleVisible` below guards the GRADING, so a join is never
|
|
22
|
+
* called added or removed off a side nobody could read, and `ownershipFlicker`
|
|
23
|
+
* keeps one from being called so off a consumer's own contract that merely
|
|
24
|
+
* stopped or started parsing.
|
|
25
|
+
*/
|
|
26
|
+
export function consumerTuples(states) {
|
|
27
|
+
const providersOf = (pick) => {
|
|
28
|
+
const map = new Map();
|
|
29
|
+
for (const id of [...states.keys()].sort()) {
|
|
30
|
+
for (const token of pick(states.get(id)))
|
|
31
|
+
map.set(token, [...(map.get(token) ?? []), id]);
|
|
32
|
+
}
|
|
33
|
+
return map;
|
|
34
|
+
};
|
|
35
|
+
const opProviders = providersOf((s) => (s.openapi.kind === "read" ? s.openapi.value.map((op) => op.id) : []));
|
|
36
|
+
const msgProviders = providersOf((s) => (s.asyncapi.kind === "read" ? s.asyncapi.value.map((m) => m.name) : []));
|
|
37
|
+
const out = new Map();
|
|
38
|
+
const add = (t) => {
|
|
39
|
+
out.set(tupleKey(t), t);
|
|
40
|
+
};
|
|
41
|
+
for (const consumer of [...states.keys()].sort()) {
|
|
42
|
+
const state = states.get(consumer);
|
|
43
|
+
for (const r of reqsOf(state)) {
|
|
44
|
+
for (const op of r.operations) {
|
|
45
|
+
if (ownsOperation(state, op))
|
|
46
|
+
continue;
|
|
47
|
+
for (const p of opProviders.get(op) ?? [])
|
|
48
|
+
if (p !== consumer)
|
|
49
|
+
add({ kind: "op", consumer, provider: p, token: op });
|
|
50
|
+
}
|
|
51
|
+
for (const name of r.consumes) {
|
|
52
|
+
for (const p of msgProviders.get(name) ?? [])
|
|
53
|
+
if (p !== consumer)
|
|
54
|
+
add({ kind: "message", consumer, provider: p, token: name });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
/** May a tuple be graded off this side? Absence answers; unreadable does not; an ambiguous base identity answers nothing. */
|
|
61
|
+
function tupleVisible(states, ambiguous, t) {
|
|
62
|
+
if (ambiguous.has(t.consumer) || ambiguous.has(t.provider))
|
|
63
|
+
return false;
|
|
64
|
+
const readable = (id, axes) => {
|
|
65
|
+
const state = states.get(id);
|
|
66
|
+
return state === undefined || axes.every((axis) => state[axis].kind !== "unreadable");
|
|
67
|
+
};
|
|
68
|
+
// Both spec files on the consumer's side, for either kind: a line in the file
|
|
69
|
+
// nobody could read may be the very one the join came or went by. NOT the
|
|
70
|
+
// consumer's own contract, although an operation join reads it: it is asked
|
|
71
|
+
// only whether the line names the consumer's OWN operation, and an
|
|
72
|
+
// unreadable contract proves no ownership (`ownsOperation`), so the line
|
|
73
|
+
// counts on that side — the index's own rule. A side holding it unread can
|
|
74
|
+
// only have MORE joins, never hide one, and suppressing the join there kept
|
|
75
|
+
// `diff.consumer-added` / `-removed` off a report whose victims named that
|
|
76
|
+
// very consumer (the contract review, finding 4). Those extra joins are a
|
|
77
|
+
// change of their own only when the readable side does not prove the
|
|
78
|
+
// ownership the unread one could not — `ownershipFlicker` below.
|
|
79
|
+
return readable(t.consumer, ["spec", "archSpec"]) && readable(t.provider, [t.kind === "op" ? "openapi" : "asyncapi"]);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Is this operation join held only because the consumer's own contract did not
|
|
83
|
+
* parse on the side that holds it, while on the other side it reads and
|
|
84
|
+
* defines the id? Then the line names the consumer's OWN operation — the
|
|
85
|
+
* readable side says so — and nothing about the join moved but the contract's
|
|
86
|
+
* readability: the line that never changed "went away" when the contract was
|
|
87
|
+
* fixed and "appeared" when it broke, once per provider of a shared id such as
|
|
88
|
+
* `healthCheck` (the verification of finding 4, N1). The unreadable contract
|
|
89
|
+
* is already its own `unreadable[]` entry on the consumer, which carries the
|
|
90
|
+
* run to exit 1; a second, false finding on every provider said nothing true.
|
|
91
|
+
*
|
|
92
|
+
* Only that shape is skipped. A readable side that defines no such id proves
|
|
93
|
+
* no ownership either, so the join is the line's and is graded as before; a
|
|
94
|
+
* message join asks no ownership at all (`ownsOperation` says why).
|
|
95
|
+
*/
|
|
96
|
+
function ownershipFlicker(sides, t) {
|
|
97
|
+
if (t.kind !== "op" || sides.holding.get(t.consumer)?.openapi.kind !== "unreadable")
|
|
98
|
+
return false;
|
|
99
|
+
const other = sides.other.get(t.consumer);
|
|
100
|
+
return other !== undefined && ownsOperation(other, t.token);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The joins that appeared or went away between base and current, sorted for
|
|
104
|
+
* deterministic output (provider, kind, token, consumer). The visibility
|
|
105
|
+
* guard is the containment rule: a join is never called "added" off a base
|
|
106
|
+
* side nobody could read, nor "removed" off an unreadable working file — the
|
|
107
|
+
* suspended axis already carries that fact. The flicker guard is its mirror on
|
|
108
|
+
* the side that HOLDS the join: one held only because the consumer's own
|
|
109
|
+
* contract did not read there, over an id the other side shows it owns, is no
|
|
110
|
+
* change either.
|
|
111
|
+
*/
|
|
112
|
+
export function consumerJoinChanges(input) {
|
|
113
|
+
const baseTuples = consumerTuples(input.base);
|
|
114
|
+
const currentTuples = consumerTuples(input.current);
|
|
115
|
+
const graded = [];
|
|
116
|
+
const added = { holding: input.current, other: input.base };
|
|
117
|
+
const removed = { holding: input.base, other: input.current };
|
|
118
|
+
for (const [key, t] of currentTuples) {
|
|
119
|
+
if (baseTuples.has(key) || ownershipFlicker(added, t))
|
|
120
|
+
continue;
|
|
121
|
+
if (tupleVisible(input.base, input.baseAmbiguous, t))
|
|
122
|
+
graded.push({ t, change: "added" });
|
|
123
|
+
}
|
|
124
|
+
for (const [key, t] of baseTuples) {
|
|
125
|
+
if (currentTuples.has(key) || ownershipFlicker(removed, t))
|
|
126
|
+
continue;
|
|
127
|
+
if (tupleVisible(input.current, input.currentAmbiguous, t))
|
|
128
|
+
graded.push({ t, change: "removed" });
|
|
129
|
+
}
|
|
130
|
+
return graded.sort((a, b) => {
|
|
131
|
+
const ka = `${a.t.provider}\0${a.t.kind}\0${a.t.token}\0${a.t.consumer}\0${a.change}`;
|
|
132
|
+
const kb = `${b.t.provider}\0${b.t.kind}\0${b.t.token}\0${b.t.consumer}\0${b.change}`;
|
|
133
|
+
return ka < kb ? -1 : 1;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=joins.js.map
|