@ybotok/loam 0.2.0-alpha.1 → 0.2.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3448 -0
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +47 -23
- package/QUICKSTART.md +189 -12
- package/README.md +274 -65
- package/ROADMAP.md +572 -31
- package/SCHEMA.md +1709 -155
- package/WORKFLOW.md +298 -72
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/adopt/render.js +50 -9
- package/dist/commands/adopt/render.js.map +1 -1
- package/dist/commands/archive/archive.js +16 -19
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/closing.js +42 -0
- package/dist/commands/archive/closing.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +36 -42
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +77 -43
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +103 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/document/head.js +151 -0
- package/dist/commands/archive/plan/document/head.js.map +1 -0
- package/dist/commands/archive/plan/document/links.js +284 -0
- package/dist/commands/archive/plan/document/links.js.map +1 -0
- package/dist/commands/archive/plan/document/run.js +136 -0
- package/dist/commands/archive/plan/document/run.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +21 -19
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +167 -30
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/model/extending.js +85 -0
- package/dist/commands/archive/plan/model/extending.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +14 -19
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +137 -56
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +3 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +134 -30
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +85 -18
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +29 -7
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +102 -14
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +17 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/doctor.js +60 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +89 -13
- package/dist/commands/explain/explain.js.map +1 -1
- package/dist/commands/explore.js +16 -10
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +5 -7
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +44 -29
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +52 -9
- package/dist/commands/list/print.js.map +1 -1
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +37 -34
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +55 -26
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/plan.js +158 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +13 -11
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/feature.js +9 -5
- package/dist/commands/show/feature.js.map +1 -1
- package/dist/commands/show/introduced.js +157 -0
- package/dist/commands/show/introduced.js.map +1 -0
- package/dist/commands/show/marks.js +35 -6
- package/dist/commands/show/marks.js.map +1 -1
- package/dist/commands/show/service.js +41 -33
- package/dist/commands/show/service.js.map +1 -1
- package/dist/commands/show/show.js +17 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/fleet-next.js +90 -0
- package/dist/commands/status/fleet-next.js.map +1 -0
- package/dist/commands/status/json.js +2 -0
- package/dist/commands/status/json.js.map +1 -1
- package/dist/commands/status/print.js +89 -37
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +103 -23
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +7 -7
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +106 -46
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +289 -74
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/exclude.js +132 -0
- package/dist/commands/subsystem/txn/exclude.js.map +1 -0
- package/dist/commands/subsystem/txn/links.js +128 -0
- package/dist/commands/subsystem/txn/links.js.map +1 -0
- package/dist/commands/subsystem/txn/report.js +174 -0
- package/dist/commands/subsystem/txn/report.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +147 -45
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +200 -21
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/actions.js +107 -0
- package/dist/commands/tasks/actions.js.map +1 -0
- package/dist/commands/tasks/render.js +253 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +119 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +42 -26
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +83 -33
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +78 -27
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +34 -126
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/health-deps.js +9 -6
- package/dist/commands/validate/checks/health-deps.js.map +1 -1
- package/dist/commands/validate/checks/introduced.js +102 -0
- package/dist/commands/validate/checks/introduced.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +11 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/coverage/covers-after.js +105 -0
- package/dist/commands/validate/coverage/covers-after.js.map +1 -0
- package/dist/commands/validate/feature.js +26 -20
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +37 -13
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +144 -126
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/load.js +66 -0
- package/dist/commands/validate/fleet/load.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +98 -0
- package/dist/commands/validate/fleet/map/attest.js.map +1 -0
- package/dist/commands/validate/fleet/map/bindings.js +107 -0
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
- package/dist/commands/validate/fleet/map/consumers.js +244 -0
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -0
- package/dist/commands/validate/fleet/map/isolation.js +116 -0
- package/dist/commands/validate/fleet/map/isolation.js.map +1 -0
- package/dist/commands/validate/fleet/map/usecases.js +54 -0
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +44 -14
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +10 -2
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js +217 -0
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +50 -15
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/projects.js +278 -0
- package/dist/commands/validate/fleet/views/projects.js.map +1 -0
- package/dist/commands/validate/fleet/views/root-documents.js +83 -0
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +40 -47
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/render/repeats.js +63 -0
- package/dist/commands/validate/render/repeats.js.map +1 -0
- package/dist/commands/validate/render/rollups.js +115 -0
- package/dist/commands/validate/render/rollups.js.map +1 -0
- package/dist/commands/validate/render/text.js +199 -0
- package/dist/commands/validate/render/text.js.map +1 -0
- package/dist/commands/validate/report.js +36 -160
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +21 -107
- package/dist/commands/validate/service/api.js.map +1 -1
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +36 -14
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/model/diverged.js +226 -0
- package/dist/commands/validate/service/model/diverged.js.map +1 -0
- package/dist/commands/validate/service/model/grade.js +163 -0
- package/dist/commands/validate/service/model/grade.js.map +1 -0
- package/dist/commands/validate/service/service.js +121 -61
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +88 -28
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +38 -3
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/tags.js +113 -0
- package/dist/commands/validate/service/usecases/tags.js.map +1 -0
- package/dist/commands/validate/service/usecases/usecases.js +209 -0
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/validate.js +85 -60
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +55 -52
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +81 -106
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +21 -9
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +17 -8
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +24 -8
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +80 -8
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +88 -10
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +49 -22
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +39 -6
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/protocol.js +1 -1
- package/dist/core/agent/protocol.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/support/report.js +46 -5
- package/dist/core/agent/support/report.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +158 -11
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +78 -39
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +88 -27
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +128 -13
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +96 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +106 -4
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/drawing/drawing.js +253 -0
- package/dist/core/agent/workflows/reference/drawing/drawing.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +6 -3
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +68 -27
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +32 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +33 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +12 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +96 -36
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/checks.js +138 -3
- package/dist/core/brief/checks.js.map +1 -1
- package/dist/core/brief/landscape.js +209 -130
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +84 -0
- package/dist/core/brief/map/extend.js.map +1 -0
- package/dist/core/brief/map/owed.js +266 -0
- package/dist/core/brief/map/owed.js.map +1 -0
- package/dist/core/brief/targets.js +109 -31
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +14 -3
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/arch.js +90 -182
- package/dist/core/c4/arch.js.map +1 -1
- package/dist/core/c4/direction/direction.js +114 -0
- package/dist/core/c4/direction/direction.js.map +1 -0
- package/dist/core/c4/likec4.js +169 -14
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/deployment.js +160 -0
- package/dist/core/c4/parsed/deployment.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -1
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/parsed/view-ids.js +39 -1
- package/dist/core/c4/parsed/view-ids.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +134 -0
- package/dist/core/c4/prefetch/fleet.js.map +1 -0
- package/dist/core/c4/project/architecture.js +123 -7
- package/dist/core/c4/project/architecture.js.map +1 -1
- package/dist/core/c4/project/batch.js +140 -0
- package/dist/core/c4/project/batch.js.map +1 -0
- package/dist/core/c4/project/documents.js +43 -12
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +79 -43
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +277 -0
- package/dist/core/c4/project/staged.js.map +1 -0
- package/dist/core/c4/resolve/attested.js +0 -0
- package/dist/core/c4/resolve/attested.js.map +1 -0
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +78 -17
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/resolve/steps.js +290 -0
- package/dist/core/c4/resolve/steps.js.map +1 -0
- package/dist/core/c4/resolve/systems.js +154 -0
- package/dist/core/c4/resolve/systems.js.map +1 -0
- package/dist/core/c4/root-project/exclude.js +352 -0
- package/dist/core/c4/root-project/exclude.js.map +1 -0
- package/dist/core/c4/seed/template.js +65 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/created/models.js +96 -0
- package/dist/core/c4/service-model/created/models.js.map +1 -0
- package/dist/core/c4/service-model/documents.js +47 -0
- package/dist/core/c4/service-model/documents.js.map +1 -0
- package/dist/core/c4/service-model/fleet/extending.js +82 -0
- package/dist/core/c4/service-model/fleet/extending.js.map +1 -0
- package/dist/core/c4/service-model/load.js +165 -0
- package/dist/core/c4/service-model/load.js.map +1 -0
- package/dist/core/c4/service-model/renderer.js +128 -0
- package/dist/core/c4/service-model/renderer.js.map +1 -0
- package/dist/core/c4/service-model/shape.js +106 -0
- package/dist/core/c4/service-model/shape.js.map +1 -0
- package/dist/core/c4/service-model/slice.js +188 -0
- package/dist/core/c4/service-model/slice.js.map +1 -0
- package/dist/core/c4/source-mask.js +297 -10
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +222 -141
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +79 -13
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/contract.js +20 -0
- package/dist/core/c4/splice/contract.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +20 -20
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/identity/declared.js +87 -0
- package/dist/core/c4/splice/identity/declared.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +37 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -1
- package/dist/core/c4/splice/identity/existing.js +80 -0
- package/dist/core/c4/splice/identity/existing.js.map +1 -0
- package/dist/core/c4/splice/identity/interior.js +80 -0
- package/dist/core/c4/splice/identity/interior.js.map +1 -0
- package/dist/core/c4/splice/identity/owner.js +50 -0
- package/dist/core/c4/splice/identity/owner.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +182 -114
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +151 -0
- package/dist/core/c4/splice/model/merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +160 -56
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/splice/text/spot.js +131 -0
- package/dist/core/c4/splice/text/spot.js.map +1 -0
- package/dist/core/c4/workspace.js +13 -19
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/delta/uncovered.js +2 -2
- package/dist/core/capabilities/delta/uncovered.js.map +1 -1
- package/dist/core/capabilities/findings.js +18 -22
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/calls/called.js +52 -0
- package/dist/core/coherence/calls/called.js.map +1 -0
- package/dist/core/coherence/coherence.js +103 -22
- package/dist/core/coherence/coherence.js.map +1 -1
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/covers/health.js +85 -0
- package/dist/core/coherence/covers/health.js.map +1 -0
- package/dist/core/coherence/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +147 -16
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +28 -87
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/events/produced.js +100 -0
- package/dist/core/coherence/events/produced.js.map +1 -0
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/delta.js +51 -6
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/dependencies/fanin.js +2 -2
- package/dist/core/dependencies/fanin.js.map +1 -1
- package/dist/core/deployment/delta.js +191 -0
- package/dist/core/deployment/delta.js.map +1 -0
- package/dist/core/deployment/objects.js +145 -0
- package/dist/core/deployment/objects.js.map +1 -0
- package/dist/core/diff/base-git.js +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +49 -21
- package/dist/core/diff/base-state.js.map +1 -1
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/docs.js +31 -22
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/config.js +7 -3
- package/dist/core/doctor/config.js.map +1 -1
- package/dist/core/doctor/doctor.js +91 -11
- package/dist/core/doctor/doctor.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/doctor/reports/scan.js +242 -0
- package/dist/core/doctor/reports/scan.js.map +1 -0
- package/dist/core/document/apply.js +158 -11
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +26 -21
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/lookup.js +14 -1
- package/dist/core/explain/lookup.js.map +1 -1
- package/dist/core/explain/refusals.js +5 -5
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms/documents.js +227 -0
- package/dist/core/explain/terms/documents.js.map +1 -0
- package/dist/core/explain/terms/done-check.js +144 -0
- package/dist/core/explain/terms/done-check.js.map +1 -0
- package/dist/core/explain/terms/entry.js +14 -0
- package/dist/core/explain/terms/entry.js.map +1 -0
- package/dist/core/explain/terms/stages.js +71 -0
- package/dist/core/explain/terms/stages.js.map +1 -0
- package/dist/core/explain/terms/tasks.js +97 -0
- package/dist/core/explain/terms/tasks.js.map +1 -0
- package/dist/core/explain/terms.js +68 -109
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +40 -10
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +81 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +11 -4
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +69 -28
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/delta.js +10 -7
- package/dist/core/glossary/delta.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/ids/fqn/ancestors.js +65 -0
- package/dist/core/kernel/ids/fqn/ancestors.js.map +1 -0
- package/dist/core/kernel/path-safety.js +38 -0
- package/dist/core/kernel/path-safety.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +51 -37
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/links/findings.js.map +1 -1
- package/dist/core/links/parse.js +38 -9
- package/dist/core/links/parse.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +40 -10
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +301 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +28 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +17 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +43 -4
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/pack/pack.js +50 -6
- package/dist/core/pack/pack.js.map +1 -1
- package/dist/core/projection/arch-slice.js +2 -2
- package/dist/core/projection/arch-slice.js.map +1 -1
- package/dist/core/projection/introduced.js +14 -0
- package/dist/core/projection/introduced.js.map +1 -0
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/authored/paths.js +18 -0
- package/dist/core/repo/authored/paths.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/paths.js +92 -6
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/repo.js +74 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/render/body.js +258 -0
- package/dist/core/repo/tree/render/body.js.map +1 -0
- package/dist/core/repo/tree/render/projects.js +182 -0
- package/dist/core/repo/tree/render/projects.js.map +1 -0
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +42 -0
- package/dist/core/repo/tree/render/stale.js.map +1 -0
- package/dist/core/repo/tree/render/text.js +84 -0
- package/dist/core/repo/tree/render/text.js.map +1 -0
- package/dist/core/repo/tree/render/views.js +293 -0
- package/dist/core/repo/tree/render/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +53 -32
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +121 -13
- package/dist/core/scaffold/landscape.js.map +1 -1
- package/dist/core/scaffold/legacy.js +271 -0
- package/dist/core/scaffold/legacy.js.map +1 -0
- package/dist/core/staging/commit.js +26 -15
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +84 -6
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +188 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +39 -45
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +24 -14
- package/dist/core/staging/txn/forward.js.map +1 -1
- package/dist/core/staging/writes.js.map +1 -1
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/contracts.js +39 -15
- package/dist/core/status/contracts.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +33 -8
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +24 -17
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/feature/next.js +20 -7
- package/dist/core/status/feature/next.js.map +1 -1
- package/dist/core/status/feature/words.js +96 -0
- package/dist/core/status/feature/words.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +43 -14
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +153 -58
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/scan.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +199 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/claims.js +31 -7
- package/dist/core/usecases/delta/claims.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/delta/modified.js +315 -0
- package/dist/core/usecases/delta/modified.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +32 -191
- package/dist/core/usecases/delta/overlay.js.map +1 -1
- package/dist/core/usecases/fleet.js +195 -36
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/capability-tag.js +8 -9
- package/dist/core/usecases/grade/capability-tag.js.map +1 -0
- package/dist/core/usecases/grade/feature.js +88 -0
- package/dist/core/usecases/grade/feature.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/requirement-tag.js +3 -4
- package/dist/core/usecases/grade/requirement-tag.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/steps.js +102 -33
- package/dist/core/usecases/grade/steps.js.map +1 -0
- package/dist/{commands/validate/fleet/usecases → core/usecases/grade}/usecases.js +23 -10
- package/dist/core/usecases/grade/usecases.js.map +1 -0
- package/dist/core/usecases/operations.js +26 -14
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/place.js.map +1 -1
- package/dist/core/usecases/service/exclude.js +119 -0
- package/dist/core/usecases/service/exclude.js.map +1 -0
- package/dist/core/usecases/service/flows.js +164 -0
- package/dist/core/usecases/service/flows.js.map +1 -0
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +70 -12
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +4 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +75 -4
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/dist/core/vocabulary/report.js +16 -1
- package/dist/core/vocabulary/report.js.map +1 -1
- package/examples/README.md +54 -11
- package/examples/docs/architecture/landscape.likec4 +62 -6
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +7 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +16 -7
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +4 -1
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/likec4.config.json +0 -1
- package/examples/docs/services/checkout-web/model.likec4 +39 -56
- package/examples/docs/services/order-service/arch.spec.md +13 -6
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/order-service/model.likec4 +58 -70
- package/examples/docs/services/payment-service/arch.spec.md +7 -4
- package/examples/docs/services/payment-service/model.likec4 +38 -37
- package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +35 -37
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/examples/docs/services/platform/notification-service/health.yaml +8 -3
- package/examples/docs/services/platform/notification-service/model.likec4 +43 -64
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -31
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/steps.js.map +0 -1
- package/dist/commands/validate/fleet/usecases/usecases.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -220
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -165
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -247
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/repo/tree/views.js +0 -172
- package/dist/core/repo/tree/views.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the parsed model declares and the splice map cannot locate — named the
|
|
3
|
+
* way an author can act on it.
|
|
4
|
+
*
|
|
5
|
+
* The archive splices the landscape by its text scan (`../source-scan.ts`) and
|
|
6
|
+
* refuses rather than splice around a declaration the scan cannot see. That
|
|
7
|
+
* refusal used to guess its cause: "most often two declarations sharing one
|
|
8
|
+
* line", whatever the map held. A fleet on 0.2.0-alpha.7 read it over a
|
|
9
|
+
* standalone declaration on a line of its own — the scan was blind to a second
|
|
10
|
+
* `model { }` block — and was asked to rewrite a valid map to satisfy a message
|
|
11
|
+
* that described something else. So the cause is no longer guessed where it can
|
|
12
|
+
* be read: LikeC4's own locator says on which line the declaration is, the
|
|
13
|
+
* line itself says whether anything but the declaration's own lead (`Lead`)
|
|
14
|
+
* precedes it there, and the scan says what it read in that place instead.
|
|
15
|
+
*
|
|
16
|
+
* A second parse of the landscape, and it is paid only on the refusal path —
|
|
17
|
+
* the merge decides THAT something is unlocated from the parse it already has,
|
|
18
|
+
* and asks this module only for the words.
|
|
19
|
+
*/
|
|
20
|
+
import { LikeC4 } from "likec4";
|
|
21
|
+
import { maskSource } from "../source-mask.js";
|
|
22
|
+
/**
|
|
23
|
+
* The first declaration of `text` that `scan` does not locate — elements in
|
|
24
|
+
* the parser's order, then relationships in document order; null when every
|
|
25
|
+
* one is located.
|
|
26
|
+
*
|
|
27
|
+
* An element is located when the scan holds its id. A relationship has no id
|
|
28
|
+
* the text can carry, so it is located by POSITION: LikeC4 places each edge at
|
|
29
|
+
* its arrow, and each scanned statement may stand for one edge — the second
|
|
30
|
+
* arrow inside one scanned span is the edge the scan swallowed.
|
|
31
|
+
*/
|
|
32
|
+
export async function firstUnlocated(text, scan) {
|
|
33
|
+
const likec4 = await LikeC4.fromSource(text, { logger: false });
|
|
34
|
+
try {
|
|
35
|
+
const model = await likec4.parsedModel();
|
|
36
|
+
const where = placer(text, scan);
|
|
37
|
+
const scanned = new Set(scan.elements.map((e) => e.id));
|
|
38
|
+
for (const e of model.elements()) {
|
|
39
|
+
if (scanned.has(e.id))
|
|
40
|
+
continue;
|
|
41
|
+
return { kind: "element", name: e.id, ...where.place(likec4.languageServices.locate({ element: e.id })?.range.start, { kind: e.kind }) };
|
|
42
|
+
}
|
|
43
|
+
const edges = [...model.relationships()].map((r) => {
|
|
44
|
+
const start = likec4.languageServices.locate({ relation: r.id })?.range.start;
|
|
45
|
+
return { name: `${r.source.id} -> ${r.target.id}`, start, offset: start === undefined ? -1 : where.offset(start) };
|
|
46
|
+
});
|
|
47
|
+
const claimed = new Set();
|
|
48
|
+
for (const edge of edges.sort((a, b) => a.offset - b.offset)) {
|
|
49
|
+
if (edge.offset === -1)
|
|
50
|
+
continue;
|
|
51
|
+
const span = scan.rels.findIndex((s) => edge.offset >= s.start && edge.offset < s.end);
|
|
52
|
+
if (span !== -1 && !claimed.has(span)) {
|
|
53
|
+
claimed.add(span);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
return { kind: "relationship", name: edge.name, ...where.place(edge.start, "edge") };
|
|
57
|
+
}
|
|
58
|
+
// Every located edge has a statement of its own: an unlocated one, if the
|
|
59
|
+
// count says there is one, is among those the locator could not place.
|
|
60
|
+
const lost = edges.find((edge) => edge.offset === -1);
|
|
61
|
+
return lost === undefined ? null : { kind: "relationship", name: lost.name };
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await likec4.dispose();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** A whole token — not the tail of a longer id — directly before the anchor, the kind written there. */
|
|
68
|
+
const ELEMENT_LEAD = /(?<![\w.-])([A-Za-z_][\w-]*)\s*$/;
|
|
69
|
+
/** An optional source reference and an optional `-[`, directly before the anchor; always matches, possibly empty. */
|
|
70
|
+
const EDGE_LEAD = /(?<![\w.-])(?:[A-Za-z_][\w.-]*\s*)?(?:-\[\s*)?$/;
|
|
71
|
+
/**
|
|
72
|
+
* Where the declaration anchored at `at` starts, in the masked `code` of the
|
|
73
|
+
* line starting at `from`: before its lead when the code there ends in one,
|
|
74
|
+
* else `at`. Masked, a literal or a URI is blank, so the word before one reads
|
|
75
|
+
* as an edge's source here exactly as `relationHead` reads it in the scan
|
|
76
|
+
* (`link https://… -> zulu` is `link -> zulu` to both). Such a line is a
|
|
77
|
+
* scanned edge of its own, so its edge is unlocated only when an earlier
|
|
78
|
+
* statement's span ran on over it — and then the line-break explanation is
|
|
79
|
+
* the one to give, which is what dropping that word leads to.
|
|
80
|
+
*/
|
|
81
|
+
function leadStart(code, span, lead) {
|
|
82
|
+
const before = code.slice(span.from, span.at);
|
|
83
|
+
const m = lead === "edge" ? EDGE_LEAD.exec(before) : ELEMENT_LEAD.exec(before);
|
|
84
|
+
return m === null || (lead !== "edge" && m[1] !== lead.kind) ? span.at : span.from + m.index;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Reads a position against the text and the scan. Lines are split the way LSP
|
|
88
|
+
* counts them — `\r\n`, `\r` or `\n` — because that is the count LikeC4's
|
|
89
|
+
* positions are in. A bare carriage return used to be the one break the scan
|
|
90
|
+
* did not make, and so the cause the "read it as part of" words were written
|
|
91
|
+
* for; `statementHead` breaks there too now, and the words stay for whatever
|
|
92
|
+
* run-on the scan has next.
|
|
93
|
+
*/
|
|
94
|
+
function placer(text, scan) {
|
|
95
|
+
const starts = [0];
|
|
96
|
+
for (const m of text.matchAll(/\r\n|\r|\n/g))
|
|
97
|
+
starts.push(m.index + m[0].length);
|
|
98
|
+
const lineOf = (offset) => starts.findLastIndex((s) => s <= offset) + 1;
|
|
99
|
+
const { code } = maskSource(text);
|
|
100
|
+
const offset = (start) => (starts[start.line] ?? text.length) + start.character;
|
|
101
|
+
const heads = [
|
|
102
|
+
...scan.elements
|
|
103
|
+
.filter((e) => e.extend !== true)
|
|
104
|
+
.map((e) => ({ name: `'${e.id}'`, start: e.start, end: e.bodyOpen === -1 ? e.end : e.bodyOpen })),
|
|
105
|
+
...scan.rels.map((r) => ({ name: `the edge '${r.source} -> ${r.target}'`, start: r.start, end: r.end })),
|
|
106
|
+
];
|
|
107
|
+
const place = (start, lead) => {
|
|
108
|
+
if (start === undefined)
|
|
109
|
+
return {};
|
|
110
|
+
const at = offset(start);
|
|
111
|
+
const from = starts[start.line] ?? at;
|
|
112
|
+
// Measured from where the declaration starts, not from the anchor: its own
|
|
113
|
+
// kind keyword or source is no other statement (`Lead`).
|
|
114
|
+
const before = code.slice(from, leadStart(code, { from, at }, lead));
|
|
115
|
+
// Innermost first: a nested declaration's span lies inside its parent's.
|
|
116
|
+
const cover = heads.filter((h) => at >= h.start && at < h.end).sort((a, b) => b.start - a.start)[0];
|
|
117
|
+
return {
|
|
118
|
+
line: start.line + 1,
|
|
119
|
+
...(/\S/.test(before) ? { sharesLine: true } : {}),
|
|
120
|
+
...(cover === undefined ? {} : { readAs: { name: cover.name, line: lineOf(cover.start) } }),
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
return { place, offset };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The refusal's words for one unlocated declaration of `file`. A shared line
|
|
127
|
+
* is named only when the line shows one, and the statement the scan read
|
|
128
|
+
* instead only when it read one: a guess at the cause is the message this
|
|
129
|
+
* replaced.
|
|
130
|
+
*/
|
|
131
|
+
export function unlocatedReason(file, gap) {
|
|
132
|
+
const remedy = "nothing was written; rewrite it on a line of its own as `name = kind 'title' {` (an edge as `a -> b 'title'`) and re-run — " +
|
|
133
|
+
"and report the original shape: LikeC4 parses it, so it is a form the splice map should read";
|
|
134
|
+
if (gap === null)
|
|
135
|
+
return `${file} declares more than placement can locate, and LikeC4's locator could not say where — ${remedy}`;
|
|
136
|
+
const what = `${file} declares ${gap.kind === "element" ? `'${gap.name}'` : `the relationship '${gap.name}'`}`;
|
|
137
|
+
const where = gap.line === undefined ? "" : ` at line ${gap.line}`;
|
|
138
|
+
const read = gap.readAs;
|
|
139
|
+
if (gap.sharesLine === true) {
|
|
140
|
+
const other = read === undefined ? "the statement before it" : read.name;
|
|
141
|
+
return (`${what}${where} in a form placement cannot locate: it shares that line with ${other}, and the splice map reads ` +
|
|
142
|
+
`one statement per line where LikeC4 accepts two — give each its own line, then re-run`);
|
|
143
|
+
}
|
|
144
|
+
if (read !== undefined) {
|
|
145
|
+
return (`${what}${where} in a form placement cannot locate: the splice map read it as part of ${read.name}, which starts at ` +
|
|
146
|
+
`line ${read.line} — it ran that statement on past a line break LikeC4 makes — ${remedy}`);
|
|
147
|
+
}
|
|
148
|
+
return `${what}${where} in a form placement cannot locate, and the splice map read no statement there — ${remedy}`;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=unlocated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unlocated.js","sourceRoot":"","sources":["../../../../src/core/c4/locate/unlocated.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What the parsed model declares and the splice map cannot locate — named the\n * way an author can act on it.\n *\n * The archive splices the landscape by its text scan (`../source-scan.ts`) and\n * refuses rather than splice around a declaration the scan cannot see. That\n * refusal used to guess its cause: \"most often two declarations sharing one\n * line\", whatever the map held. A fleet on 0.2.0-alpha.7 read it over a\n * standalone declaration on a line of its own — the scan was blind to a second\n * `model { }` block — and was asked to rewrite a valid map to satisfy a message\n * that described something else. So the cause is no longer guessed where it can\n * be read: LikeC4's own locator says on which line the declaration is, the\n * line itself says whether anything but the declaration's own lead (`Lead`)\n * precedes it there, and the scan says what it read in that place instead.\n *\n * A second parse of the landscape, and it is paid only on the refusal path —\n * the merge decides THAT something is unlocated from the parse it already has,\n * and asks this module only for the words.\n */\nimport { LikeC4 } from \"likec4\";\nimport { maskSource } from \"../source-mask.js\";\nimport type { ScannedModel } from \"../source-scan.js\";\n\n/** One declaration the parser has and the scan cannot place. */\nexport interface Unlocated {\n kind: \"element\" | \"relationship\";\n /** The element's id, or the relationship as `source -> target`, as the parser resolved them. */\n name: string;\n /** 1-based line LikeC4's locator puts the declaration on; absent when it has no answer. */\n line?: number;\n /**\n * Code other than the declaration's own lead precedes it on its own line —\n * the shared line is then a FACT, not a guess.\n */\n sharesLine?: true;\n /** The scanned statement that covers where the declaration sits, and the line that statement starts on. */\n readAs?: { name: string; line: number };\n}\n\n/**\n * The first declaration of `text` that `scan` does not locate — elements in\n * the parser's order, then relationships in document order; null when every\n * one is located.\n *\n * An element is located when the scan holds its id. A relationship has no id\n * the text can carry, so it is located by POSITION: LikeC4 places each edge at\n * its arrow, and each scanned statement may stand for one edge — the second\n * arrow inside one scanned span is the edge the scan swallowed.\n */\nexport async function firstUnlocated(text: string, scan: ScannedModel): Promise<Unlocated | null> {\n const likec4 = await LikeC4.fromSource(text, { logger: false });\n try {\n const model = await likec4.parsedModel();\n const where = placer(text, scan);\n const scanned = new Set(scan.elements.map((e) => e.id));\n for (const e of model.elements()) {\n if (scanned.has(e.id)) continue;\n return { kind: \"element\", name: e.id, ...where.place(likec4.languageServices.locate({ element: e.id })?.range.start, { kind: e.kind }) };\n }\n const edges = [...model.relationships()].map((r) => {\n const start = likec4.languageServices.locate({ relation: r.id })?.range.start;\n return { name: `${r.source.id} -> ${r.target.id}`, start, offset: start === undefined ? -1 : where.offset(start) };\n });\n const claimed = new Set<number>();\n for (const edge of edges.sort((a, b) => a.offset - b.offset)) {\n if (edge.offset === -1) continue;\n const span = scan.rels.findIndex((s) => edge.offset >= s.start && edge.offset < s.end);\n if (span !== -1 && !claimed.has(span)) {\n claimed.add(span);\n continue;\n }\n return { kind: \"relationship\", name: edge.name, ...where.place(edge.start, \"edge\") };\n }\n // Every located edge has a statement of its own: an unlocated one, if the\n // count says there is one, is among those the locator could not place.\n const lost = edges.find((edge) => edge.offset === -1);\n return lost === undefined ? null : { kind: \"relationship\", name: lost.name };\n } finally {\n await likec4.dispose();\n }\n}\n\n/** An LSP position: a 0-based line and a character within it. */\ninterface Position {\n line: number;\n character: number;\n}\n\n/**\n * The part of a declaration LikeC4's locator leaves BEFORE its anchor. It\n * points an element at its NAME and a relationship at its ARROW — inside\n * `-[…]->`, at the kind — so `container api 'API'` has its kind keyword in\n * front of the anchor and `zulu -> apple` its source. Read as \"code before\n * it\", every unlocated edge and every `kind name` element shared its line with\n * itself, and the line-break explanation `unlocatedReason` holds below the\n * shared-line one was never reached. An element's lead is a token equal to\n * its parsed kind; an edge's is its source reference (`it`, a dotted path or\n * none) and the `-[` opening a bracketed kind.\n */\ntype Lead = { kind: string } | \"edge\";\n\n/** A whole token — not the tail of a longer id — directly before the anchor, the kind written there. */\nconst ELEMENT_LEAD = /(?<![\\w.-])([A-Za-z_][\\w-]*)\\s*$/;\n/** An optional source reference and an optional `-[`, directly before the anchor; always matches, possibly empty. */\nconst EDGE_LEAD = /(?<![\\w.-])(?:[A-Za-z_][\\w.-]*\\s*)?(?:-\\[\\s*)?$/;\n\n/**\n * Where the declaration anchored at `at` starts, in the masked `code` of the\n * line starting at `from`: before its lead when the code there ends in one,\n * else `at`. Masked, a literal or a URI is blank, so the word before one reads\n * as an edge's source here exactly as `relationHead` reads it in the scan\n * (`link https://… -> zulu` is `link -> zulu` to both). Such a line is a\n * scanned edge of its own, so its edge is unlocated only when an earlier\n * statement's span ran on over it — and then the line-break explanation is\n * the one to give, which is what dropping that word leads to.\n */\nfunction leadStart(code: string, span: { from: number; at: number }, lead: Lead): number {\n const before = code.slice(span.from, span.at);\n const m = lead === \"edge\" ? EDGE_LEAD.exec(before) : ELEMENT_LEAD.exec(before);\n return m === null || (lead !== \"edge\" && m[1] !== lead.kind) ? span.at : span.from + m.index;\n}\n\n/** Everything a position tells about a declaration, and the offset arithmetic that is built on. */\ninterface Placer {\n place: (start: Position | undefined, lead: Lead) => Pick<Unlocated, \"line\" | \"sharesLine\" | \"readAs\">;\n offset: (start: Position) => number;\n}\n\n/**\n * Reads a position against the text and the scan. Lines are split the way LSP\n * counts them — `\\r\\n`, `\\r` or `\\n` — because that is the count LikeC4's\n * positions are in. A bare carriage return used to be the one break the scan\n * did not make, and so the cause the \"read it as part of\" words were written\n * for; `statementHead` breaks there too now, and the words stay for whatever\n * run-on the scan has next.\n */\nfunction placer(text: string, scan: ScannedModel): Placer {\n const starts = [0];\n for (const m of text.matchAll(/\\r\\n|\\r|\\n/g)) starts.push(m.index + m[0].length);\n const lineOf = (offset: number): number => starts.findLastIndex((s) => s <= offset) + 1;\n const { code } = maskSource(text);\n const offset = (start: Position): number => (starts[start.line] ?? text.length) + start.character;\n const heads = [\n ...scan.elements\n .filter((e) => e.extend !== true)\n .map((e) => ({ name: `'${e.id}'`, start: e.start, end: e.bodyOpen === -1 ? e.end : e.bodyOpen })),\n ...scan.rels.map((r) => ({ name: `the edge '${r.source} -> ${r.target}'`, start: r.start, end: r.end })),\n ];\n const place = (start: Position | undefined, lead: Lead): Pick<Unlocated, \"line\" | \"sharesLine\" | \"readAs\"> => {\n if (start === undefined) return {};\n const at = offset(start);\n const from = starts[start.line] ?? at;\n // Measured from where the declaration starts, not from the anchor: its own\n // kind keyword or source is no other statement (`Lead`).\n const before = code.slice(from, leadStart(code, { from, at }, lead));\n // Innermost first: a nested declaration's span lies inside its parent's.\n const cover = heads.filter((h) => at >= h.start && at < h.end).sort((a, b) => b.start - a.start)[0];\n return {\n line: start.line + 1,\n ...(/\\S/.test(before) ? { sharesLine: true as const } : {}),\n ...(cover === undefined ? {} : { readAs: { name: cover.name, line: lineOf(cover.start) } }),\n };\n };\n return { place, offset };\n}\n\n/**\n * The refusal's words for one unlocated declaration of `file`. A shared line\n * is named only when the line shows one, and the statement the scan read\n * instead only when it read one: a guess at the cause is the message this\n * replaced.\n */\nexport function unlocatedReason(file: string, gap: Unlocated | null): string {\n const remedy =\n \"nothing was written; rewrite it on a line of its own as `name = kind 'title' {` (an edge as `a -> b 'title'`) and re-run — \" +\n \"and report the original shape: LikeC4 parses it, so it is a form the splice map should read\";\n if (gap === null) return `${file} declares more than placement can locate, and LikeC4's locator could not say where — ${remedy}`;\n const what = `${file} declares ${gap.kind === \"element\" ? `'${gap.name}'` : `the relationship '${gap.name}'`}`;\n const where = gap.line === undefined ? \"\" : ` at line ${gap.line}`;\n const read = gap.readAs;\n if (gap.sharesLine === true) {\n const other = read === undefined ? \"the statement before it\" : read.name;\n return (\n `${what}${where} in a form placement cannot locate: it shares that line with ${other}, and the splice map reads ` +\n `one statement per line where LikeC4 accepts two — give each its own line, then re-run`\n );\n }\n if (read !== undefined) {\n return (\n `${what}${where} in a form placement cannot locate: the splice map read it as part of ${read.name}, which starts at ` +\n `line ${read.line} — it ran that statement on past a line break LikeC4 makes — ${remedy}`\n );\n }\n return `${what}${where} in a form placement cannot locate, and the splice map read no statement there — ${remedy}`;\n}\n"]}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DEPLOYMENT model a document declares — which physical nodes exist, which
|
|
3
|
+
* containers are instanced where, and what talks to what across them.
|
|
4
|
+
*
|
|
5
|
+
* A `deployment { }` block has been legal in a docs repo since LikeC4 could
|
|
6
|
+
* parse one, and until this reader existed loam saw none of it. The parser
|
|
7
|
+
* resolved every `instanceOf` — so a container renamed out from under a
|
|
8
|
+
* deployment node already failed the gate — and after that no requirement could
|
|
9
|
+
* name a node, no `#obl-` tag on a datacenter was graded, and the context pack
|
|
10
|
+
* an agent implements from did not mention topology at all. Integrity held at
|
|
11
|
+
* the level of syntax and was absent at the level of requirements. ROADMAP's
|
|
12
|
+
* deployment axis and `meta/docs/features/FEAT-1-the-deployment-axis/` are the
|
|
13
|
+
* item and the specification; this module is its first half.
|
|
14
|
+
*
|
|
15
|
+
* ## Why this is a `parsed/` reader and not a package of its own
|
|
16
|
+
*
|
|
17
|
+
* It is the same job the three modules beside it do: take one corner of
|
|
18
|
+
* LikeC4's parse output and hand back a loam-neutral record, defensively, with
|
|
19
|
+
* its shape assumptions owned outright. `readSpecification`, `readDynamicViews`
|
|
20
|
+
* and `readViewIds` are all called on the line above `flattenModel(model)` in
|
|
21
|
+
* each of loam's three loaders, and so is this. FEAT-1's delta said
|
|
22
|
+
* `src/core/c4/deploy/`; a package holding this alone would be a directory
|
|
23
|
+
* pretending to be a subject (docs/DESIGN.md), and the delta was corrected
|
|
24
|
+
* rather than the code bent to it — which is what an unarchived feature is for.
|
|
25
|
+
*
|
|
26
|
+
* **`parsed/` is still AT its five-file limit.** The global style-id census
|
|
27
|
+
* rode into `view-ids.ts` because it IS that module's subject — one more
|
|
28
|
+
* table asked the same question — and the next reader that is not a census
|
|
29
|
+
* still needs the split first. The seam to split on is the one this file does
|
|
30
|
+
* not share: `dynamic-views.ts` and `view-ids.ts` read `$data`, this one and
|
|
31
|
+
* `specification.ts` read the model surface.
|
|
32
|
+
*
|
|
33
|
+
* ## What is read, and what is deliberately left
|
|
34
|
+
*
|
|
35
|
+
* Nodes, instances and relationships, with the ids, titles and tags each
|
|
36
|
+
* carries. MEASURED at the 1.59.2 pin, and every line of it needed measuring
|
|
37
|
+
* rather than assuming:
|
|
38
|
+
*
|
|
39
|
+
* - `nodes()` returns deployment NODES only — an `instanceOf` is not among
|
|
40
|
+
* them, which is why instances are a second list rather than a filter.
|
|
41
|
+
* - an instance carries the LOGICAL element it deploys as `element.id`, and
|
|
42
|
+
* that is the whole join back to the C4 model: `eu.a.dbA -> orders.db`.
|
|
43
|
+
* - both nodes and instances carry `tags`, so `#obl-geo` on a datacenter is
|
|
44
|
+
* readable — the fail-open case that made this an item.
|
|
45
|
+
* - a relationship carries `tags` and `metadata` DIRECTLY (`r.metadata`, no
|
|
46
|
+
* `$relationship` indirection), and both model stages return byte-identical
|
|
47
|
+
* results, which is what lets `test/likec4-model-parity.test.ts` cover this
|
|
48
|
+
* read with the substitution it already pins.
|
|
49
|
+
* - a document with NO `deployment { }` block still returns a deployment
|
|
50
|
+
* object whose three iterators are empty. Absent and empty are therefore the
|
|
51
|
+
* same answer here, deliberately: no reader may distinguish "declares none"
|
|
52
|
+
* from "could not be read", because the second is what the defensive returns
|
|
53
|
+
* below produce and a fleet that draws no topology owes loam nothing.
|
|
54
|
+
*
|
|
55
|
+
* `metadata` is NOT carried on the records below, and neither is an instance's
|
|
56
|
+
* `kind`. Both are readable; no check reads them yet, and a field carried
|
|
57
|
+
* before a caller needs it is a field nobody notices going wrong. Add one with
|
|
58
|
+
* the check that consumes it.
|
|
59
|
+
*/
|
|
60
|
+
/** No topology — what an absent block and an unreadable one both come back as. */
|
|
61
|
+
export const NO_DEPLOYMENT = { nodes: [], instances: [], relationships: [] };
|
|
62
|
+
/** Is there anything here? The opt-in every deployment check is gated on. */
|
|
63
|
+
export function hasDeployment(d) {
|
|
64
|
+
return d.nodes.length > 0 || d.instances.length > 0 || d.relationships.length > 0;
|
|
65
|
+
}
|
|
66
|
+
/** A record, or nothing — the parse surface hands back `unknown`. */
|
|
67
|
+
function record(v) {
|
|
68
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
|
|
69
|
+
}
|
|
70
|
+
/** An iterable produced by `name()`, or an empty one when the surface is not what we measured. */
|
|
71
|
+
function iterate(source, name) {
|
|
72
|
+
const fn = source[name];
|
|
73
|
+
if (typeof fn !== "function")
|
|
74
|
+
return [];
|
|
75
|
+
try {
|
|
76
|
+
const out = fn.call(source);
|
|
77
|
+
// A generator satisfies this; so does an array. Anything else is a shape
|
|
78
|
+
// change upstream, and the honest answer to one is "no topology read".
|
|
79
|
+
return out !== null && typeof out === "object" && Symbol.iterator in out
|
|
80
|
+
? [...out]
|
|
81
|
+
: [];
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** A string field, or the empty string — never `undefined` leaking into a record. */
|
|
88
|
+
function str(o, key) {
|
|
89
|
+
const v = o[key];
|
|
90
|
+
return typeof v === "string" ? v : "";
|
|
91
|
+
}
|
|
92
|
+
/** Tags as loam models them: a plain array of strings, always present. */
|
|
93
|
+
function tags(o) {
|
|
94
|
+
const v = o["tags"];
|
|
95
|
+
return Array.isArray(v) ? v.filter((t) => typeof t === "string") : [];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Read the deployment model off an awaited `parsedModel()`.
|
|
99
|
+
*
|
|
100
|
+
* Defensive throughout, in the shape `readViewIds` established: an entry
|
|
101
|
+
* missing the one field that identifies it is DROPPED rather than reported with
|
|
102
|
+
* a hole, and a surface that is not what was measured yields an empty model
|
|
103
|
+
* instead of throwing. A check that cannot see the topology must say
|
|
104
|
+
* could-not-look through its own silence, never fail the fleet's parse.
|
|
105
|
+
*/
|
|
106
|
+
export function readDeployment(model) {
|
|
107
|
+
const root = record(model);
|
|
108
|
+
if (root === undefined)
|
|
109
|
+
return NO_DEPLOYMENT;
|
|
110
|
+
const deployment = record(root["deployment"]);
|
|
111
|
+
if (deployment === undefined)
|
|
112
|
+
return NO_DEPLOYMENT;
|
|
113
|
+
const nodes = [];
|
|
114
|
+
for (const raw of iterate(deployment, "nodes")) {
|
|
115
|
+
const n = record(raw);
|
|
116
|
+
if (n === undefined)
|
|
117
|
+
continue;
|
|
118
|
+
const id = str(n, "id");
|
|
119
|
+
if (id === "")
|
|
120
|
+
continue;
|
|
121
|
+
nodes.push({ id, kind: str(n, "kind"), title: str(n, "title"), tags: tags(n) });
|
|
122
|
+
}
|
|
123
|
+
const instances = [];
|
|
124
|
+
for (const raw of iterate(deployment, "instances")) {
|
|
125
|
+
const i = record(raw);
|
|
126
|
+
if (i === undefined)
|
|
127
|
+
continue;
|
|
128
|
+
const id = str(i, "id");
|
|
129
|
+
// The logical element is the whole point of the record. An instance whose
|
|
130
|
+
// element cannot be read joins to nothing, so reporting it would put a
|
|
131
|
+
// deployed thing on the map that no requirement could ever cover.
|
|
132
|
+
const element = record(i["element"]);
|
|
133
|
+
if (id === "" || element === undefined)
|
|
134
|
+
continue;
|
|
135
|
+
const elementId = str(element, "id");
|
|
136
|
+
if (elementId === "")
|
|
137
|
+
continue;
|
|
138
|
+
instances.push({ id, element: elementId, title: str(i, "title"), tags: tags(i) });
|
|
139
|
+
}
|
|
140
|
+
const relationships = [];
|
|
141
|
+
for (const raw of iterate(deployment, "relationships")) {
|
|
142
|
+
const r = record(raw);
|
|
143
|
+
if (r === undefined)
|
|
144
|
+
continue;
|
|
145
|
+
const source = record(r["source"]);
|
|
146
|
+
const target = record(r["target"]);
|
|
147
|
+
if (source === undefined || target === undefined)
|
|
148
|
+
continue;
|
|
149
|
+
const from = str(source, "id");
|
|
150
|
+
const to = str(target, "id");
|
|
151
|
+
if (from === "" || to === "")
|
|
152
|
+
continue;
|
|
153
|
+
// LikeC4 reports an untitled edge as `title: null` — normalized to the
|
|
154
|
+
// declared optional exactly as `flattenModel` does for the logical model.
|
|
155
|
+
const title = typeof r["title"] === "string" ? r["title"] : undefined;
|
|
156
|
+
relationships.push({ source: from, target: to, ...(title === undefined ? {} : { title }), tags: tags(r) });
|
|
157
|
+
}
|
|
158
|
+
return { nodes, instances, relationships };
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=deployment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/deployment.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The DEPLOYMENT model a document declares — which physical nodes exist, which\n * containers are instanced where, and what talks to what across them.\n *\n * A `deployment { }` block has been legal in a docs repo since LikeC4 could\n * parse one, and until this reader existed loam saw none of it. The parser\n * resolved every `instanceOf` — so a container renamed out from under a\n * deployment node already failed the gate — and after that no requirement could\n * name a node, no `#obl-` tag on a datacenter was graded, and the context pack\n * an agent implements from did not mention topology at all. Integrity held at\n * the level of syntax and was absent at the level of requirements. ROADMAP's\n * deployment axis and `meta/docs/features/FEAT-1-the-deployment-axis/` are the\n * item and the specification; this module is its first half.\n *\n * ## Why this is a `parsed/` reader and not a package of its own\n *\n * It is the same job the three modules beside it do: take one corner of\n * LikeC4's parse output and hand back a loam-neutral record, defensively, with\n * its shape assumptions owned outright. `readSpecification`, `readDynamicViews`\n * and `readViewIds` are all called on the line above `flattenModel(model)` in\n * each of loam's three loaders, and so is this. FEAT-1's delta said\n * `src/core/c4/deploy/`; a package holding this alone would be a directory\n * pretending to be a subject (docs/DESIGN.md), and the delta was corrected\n * rather than the code bent to it — which is what an unarchived feature is for.\n *\n * **`parsed/` is still AT its five-file limit.** The global style-id census\n * rode into `view-ids.ts` because it IS that module's subject — one more\n * table asked the same question — and the next reader that is not a census\n * still needs the split first. The seam to split on is the one this file does\n * not share: `dynamic-views.ts` and `view-ids.ts` read `$data`, this one and\n * `specification.ts` read the model surface.\n *\n * ## What is read, and what is deliberately left\n *\n * Nodes, instances and relationships, with the ids, titles and tags each\n * carries. MEASURED at the 1.59.2 pin, and every line of it needed measuring\n * rather than assuming:\n *\n * - `nodes()` returns deployment NODES only — an `instanceOf` is not among\n * them, which is why instances are a second list rather than a filter.\n * - an instance carries the LOGICAL element it deploys as `element.id`, and\n * that is the whole join back to the C4 model: `eu.a.dbA -> orders.db`.\n * - both nodes and instances carry `tags`, so `#obl-geo` on a datacenter is\n * readable — the fail-open case that made this an item.\n * - a relationship carries `tags` and `metadata` DIRECTLY (`r.metadata`, no\n * `$relationship` indirection), and both model stages return byte-identical\n * results, which is what lets `test/likec4-model-parity.test.ts` cover this\n * read with the substitution it already pins.\n * - a document with NO `deployment { }` block still returns a deployment\n * object whose three iterators are empty. Absent and empty are therefore the\n * same answer here, deliberately: no reader may distinguish \"declares none\"\n * from \"could not be read\", because the second is what the defensive returns\n * below produce and a fleet that draws no topology owes loam nothing.\n *\n * `metadata` is NOT carried on the records below, and neither is an instance's\n * `kind`. Both are readable; no check reads them yet, and a field carried\n * before a caller needs it is a field nobody notices going wrong. Add one with\n * the check that consumes it.\n */\n\n/** One deployment node — a region, a datacenter, a cluster; the kinds are the fleet's own. */\nexport interface DeployNode {\n /** The dotted path LikeC4 gives it: `eu.dcA.k8sA`. */\n id: string;\n /** The `deploymentNode` kind the specification declares. */\n kind: string;\n title: string;\n tags: string[];\n}\n\n/** One deployed instance of a logical element. */\nexport interface DeployInstance {\n id: string;\n /**\n * The id of the C4 element this instance deploys — the join back to the\n * logical model, and the only reason an instance is worth reading at all.\n */\n element: string;\n title: string;\n tags: string[];\n}\n\n/** One edge between deployment objects — replication, failover, a network hop. */\nexport interface DeployRel {\n source: string;\n target: string;\n title?: string;\n tags: string[];\n}\n\n/** A document's whole deployment model, flattened. */\nexport interface DeploymentModel {\n nodes: DeployNode[];\n instances: DeployInstance[];\n relationships: DeployRel[];\n}\n\n/** No topology — what an absent block and an unreadable one both come back as. */\nexport const NO_DEPLOYMENT: DeploymentModel = { nodes: [], instances: [], relationships: [] };\n\n/** Is there anything here? The opt-in every deployment check is gated on. */\nexport function hasDeployment(d: DeploymentModel): boolean {\n return d.nodes.length > 0 || d.instances.length > 0 || d.relationships.length > 0;\n}\n\n/** A record, or nothing — the parse surface hands back `unknown`. */\nfunction record(v: unknown): Record<string, unknown> | undefined {\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;\n}\n\n/** An iterable produced by `name()`, or an empty one when the surface is not what we measured. */\nfunction iterate(source: Record<string, unknown>, name: string): unknown[] {\n const fn = source[name];\n if (typeof fn !== \"function\") return [];\n try {\n const out: unknown = (fn as () => unknown).call(source);\n // A generator satisfies this; so does an array. Anything else is a shape\n // change upstream, and the honest answer to one is \"no topology read\".\n return out !== null && typeof out === \"object\" && Symbol.iterator in (out as object)\n ? [...(out as Iterable<unknown>)]\n : [];\n } catch {\n return [];\n }\n}\n\n/** A string field, or the empty string — never `undefined` leaking into a record. */\nfunction str(o: Record<string, unknown>, key: string): string {\n const v = o[key];\n return typeof v === \"string\" ? v : \"\";\n}\n\n/** Tags as loam models them: a plain array of strings, always present. */\nfunction tags(o: Record<string, unknown>): string[] {\n const v = o[\"tags\"];\n return Array.isArray(v) ? v.filter((t): t is string => typeof t === \"string\") : [];\n}\n\n/**\n * Read the deployment model off an awaited `parsedModel()`.\n *\n * Defensive throughout, in the shape `readViewIds` established: an entry\n * missing the one field that identifies it is DROPPED rather than reported with\n * a hole, and a surface that is not what was measured yields an empty model\n * instead of throwing. A check that cannot see the topology must say\n * could-not-look through its own silence, never fail the fleet's parse.\n */\nexport function readDeployment(model: unknown): DeploymentModel {\n const root = record(model);\n if (root === undefined) return NO_DEPLOYMENT;\n const deployment = record(root[\"deployment\"]);\n if (deployment === undefined) return NO_DEPLOYMENT;\n\n const nodes: DeployNode[] = [];\n for (const raw of iterate(deployment, \"nodes\")) {\n const n = record(raw);\n if (n === undefined) continue;\n const id = str(n, \"id\");\n if (id === \"\") continue;\n nodes.push({ id, kind: str(n, \"kind\"), title: str(n, \"title\"), tags: tags(n) });\n }\n\n const instances: DeployInstance[] = [];\n for (const raw of iterate(deployment, \"instances\")) {\n const i = record(raw);\n if (i === undefined) continue;\n const id = str(i, \"id\");\n // The logical element is the whole point of the record. An instance whose\n // element cannot be read joins to nothing, so reporting it would put a\n // deployed thing on the map that no requirement could ever cover.\n const element = record(i[\"element\"]);\n if (id === \"\" || element === undefined) continue;\n const elementId = str(element, \"id\");\n if (elementId === \"\") continue;\n instances.push({ id, element: elementId, title: str(i, \"title\"), tags: tags(i) });\n }\n\n const relationships: DeployRel[] = [];\n for (const raw of iterate(deployment, \"relationships\")) {\n const r = record(raw);\n if (r === undefined) continue;\n const source = record(r[\"source\"]);\n const target = record(r[\"target\"]);\n if (source === undefined || target === undefined) continue;\n const from = str(source, \"id\");\n const to = str(target, \"id\");\n if (from === \"\" || to === \"\") continue;\n // LikeC4 reports an untitled edge as `title: null` — normalized to the\n // declared optional exactly as `flattenModel` does for the logical model.\n const title = typeof r[\"title\"] === \"string\" ? r[\"title\"] : undefined;\n relationships.push({ source: from, target: to, ...(title === undefined ? {} : { title }), tags: tags(r) });\n }\n\n return { nodes, instances, relationships };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-views.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/dynamic-views.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `dynamic view`s a document DECLARES, read off LikeC4's parsed model.\n *\n * This is the only module in loam permitted to touch `$data`, and\n * `scripts/arch-check.mjs` enforces that rather than trusting it (docs/DESIGN.md\n * rule 26). The confinement is the point: `$data` is public and typed but thinly\n * documented for this use, so an upstream shape change has a blast radius of one\n * file, and `test/likec4-view-shape.test.ts` is the tripwire that fires before\n * the change reaches a check.\n *\n * It takes the WHOLE model as `unknown` rather than a `$data` slice handed in by\n * its caller, and that is not squeamishness — naming `$data` in `../likec4.ts`\n * would violate the containment scan this module exists behind. Callers pass the\n * model; every assumption about LikeC4's record shape lives here.\n *\n * Everything is defensive, in the same posture as `./specification.ts`: a shape\n * this reader cannot make sense of degrades to ZERO views, never to a wrong one\n * and never to a throw inside a validate run. That asymmetry is deliberate and\n * is loam's standing rule for a suspended axis — a check with no views to grade\n * reports could-not-look, while a check fed a half-read view would report\n * nothing-wrong about a flow it never saw.\n */\nimport { descText } from \"./values.js\";\n\n/** One hop of a declared use case: an ordered edge between two elements. */\nexport interface ParsedStep {\n /**\n * 1-based pre-order position among the view's LEAF steps — what lets a finding\n * say \"step 4 of Checkout\". Group wrappers do not consume an ordinal: a\n * `loop` is a bracket around hops, not a hop, and counting it would shift\n * every number after it away from what the author sees on the diagram.\n */\n ordinal: number;\n /**\n * The step's endpoints, ALREADY ORIENTED — see `isBackward`. Dotted FQN\n * strings naming elements, never relationship ids: a step says who talks to\n * whom, and which relationship backs that is a join loam performs later.\n */\n source: string;\n target: string;\n /** The step's label, absent when the author wrote none. */\n title?: string;\n /** A `notes '...'` block on the step, decoded from LikeC4's rich-text shape. */\n notes?: string;\n /**\n * True when the author wrote the hop as a reply — `a <- b` rather than\n * `b -> a`. LOAD-BEARING, and measured: LikeC4 records `a <- b 'reply'` as\n * `{source: b, target: a, isBackward: true}` — already reversed AND flagged —\n * while a forward step carries no such key at all. `source`/`target` above are\n * therefore correct as they stand and need no flipping; the flag is carried so\n * a check can tell a return hop from a call, and so a message naming the\n * failing step can suggest the `<-` spelling to an author who wrote the arrow\n * the long way round.\n */\n isBackward: boolean;\n /**\n * LikeC4's own path to the step inside the view (`/steps@1/steps@0`), carried\n * verbatim. It is the only stable handle on a nested step — two hops between\n * the same pair inside different `loop` blocks are otherwise indistinguishable\n * — and it is message text, never identity: nothing joins on it.\n *\n * OPTIONAL, and that is a deliberate refusal to make it load-bearing. Every\n * leaf carries one at the 1.59.2 pin, so requiring it would look free — and\n * would mean that an upstream release which stopped emitting it turned every\n * use case in the fleet into a view with zero steps. That is the silent wrong\n * answer this module exists to avoid: a hop loam cannot cite is still a hop it\n * must grade.\n */\n astPath?: string;\n}\n\n/** One declared `dynamic view`, flattened to what rule 26 permits reading. */\nexport interface ParsedView {\n id: string;\n /**\n * The name LikeC4 knows the document by, and it is only a PATH under\n * `loadProject`: there it is relative to the project root, so\n * `usecases/checkout.likec4` inside `architecture/`. Under the per-file\n * loaders it is whatever that loader called the document — `source.c4` for\n * `loadSource`, the basename for `loadBatch` — because there is no project\n * for it to be relative to.\n *\n * So: name a file with it only when the views came from `loadProject`. That\n * is the only reader that has one, which is also the only reader that needs\n * one — a use case lives in a file of its own now, and a finding that cannot\n * name that file sends its reader to the landscape to look for something\n * that is not there.\n *\n * Absent for anything LikeC4 synthesized rather than read off a file, which\n * is how the census reader tells the two apart.\n */\n sourcePath?: string;\n /**\n * The view's declared tags, `[]` when it declares none. Normalized here\n * because LikeC4 reads an untagged view's tags as `null`, and a caller\n * testing `.length` on that would throw inside a validate run.\n *\n * Tags live on the VIEW and nowhere else: a step cannot carry tags, and\n * `metadata` on a step is a hard parse error. That is measured, and it is why\n * a use case is opted in as a whole rather than hop by hop.\n */\n tags: string[];\n title?: string;\n description?: string;\n /** The declared hops, in author order, groups flattened away. */\n steps: ParsedStep[];\n}\n\n/** A record, or nothing — `v` is `unknown` off an untyped parse. */\nfunction record(v: unknown): Record<string, unknown> | undefined {\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;\n}\n\n/** The value at `key`, when it is a non-empty string. */\nfunction str(rec: Record<string, unknown>, key: string): string | undefined {\n const v = rec[key];\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n\n/**\n * Every leaf step under `entries`, depth-first in author order.\n *\n * Steps NEST: a `loop 'until accepted' { ... }` arrives as an entry with\n * `_type: \"loop\"`, a title, its own `steps[]`, and NO endpoints and NO `astPath`\n * of its own (measured). A walk that treated every entry as a hop would read the\n * bracket as a step and then fail to find a relationship backing it — inventing\n * a finding out of a grouping the author drew for legibility.\n *\n * The test is structural rather than a list of group keywords (`loop`, `par`,\n * ...): an entry with a nested `steps` array is a group whatever LikeC4 calls\n * it, and an entry with two string endpoints is a hop. A future group kind\n * therefore flattens correctly on the day it ships instead of on the day\n * somebody notices.\n */\nfunction leaves(entries: unknown, out: Record<string, unknown>[]): void {\n if (!Array.isArray(entries)) return;\n for (const entry of entries) {\n const rec = record(entry);\n if (!rec) continue;\n if (Array.isArray(rec[\"steps\"])) {\n leaves(rec[\"steps\"], out);\n continue;\n }\n if (typeof rec[\"source\"] === \"string\" && typeof rec[\"target\"] === \"string\") out.push(rec);\n }\n}\n\n/** One leaf entry as a `ParsedStep`. Every leaf yields one; nothing is dropped. */\nfunction step(rec: Record<string, unknown>, ordinal: number): ParsedStep {\n const astPath = str(rec, \"astPath\");\n const title = str(rec, \"title\");\n const notes = descText(rec[\"notes\"]);\n return {\n ordinal,\n source: rec[\"source\"] as string,\n target: rec[\"target\"] as string,\n ...(title === undefined ? {} : { title }),\n ...(notes === undefined ? {} : { notes }),\n // Present-and-true is the only truthy form LikeC4 emits; absent means\n // forward. Read as a boolean rather than carried as `true | undefined` so a\n // caller cannot accidentally test `=== false` against a key that is never\n // written.\n isBackward: rec[\"isBackward\"] === true,\n ...(astPath === undefined ? {} : { astPath }),\n };\n}\n\n/**\n * The dynamic views one parsed document declares, in the record's own order.\n *\n * `model` is the awaited `parsedModel()` result. The `await` matters and is the\n * mistake every draft of this made: `$data` is `undefined` on the unresolved\n * promise, so a reader handed the promise reports a document with views as\n * having none — a silent zero, which is the one wrong answer this module is\n * written to avoid.\n *\n * Two entries are dropped on the way out, for different reasons:\n *\n * - Anything whose `_type` is not `\"dynamic\"`. LikeC4 synthesises an `index`\n * view into EVERY document, including one with no `views` block at all\n * (measured), so reporting element views would be reporting a fiction the\n * author never wrote. A census that must see authored element views wants the\n * other filter rule 26 states — `sourcePath !== undefined` — and must not\n * reuse this function to get it.\n * - Anything with no `id`. There is nothing to name in a finding, and a view\n * loam cannot name is a view it cannot ask anybody to fix.\n */\nexport function readDynamicViews(model: unknown): ParsedView[] {\n const data = record(record(model)?.[\"$data\"]);\n const views = record(data?.[\"views\"]);\n if (!views) return [];\n\n const out: ParsedView[] = [];\n for (const raw of Object.values(views)) {\n const rec = record(raw);\n if (!rec || rec[\"_type\"] !== \"dynamic\") continue;\n const id = str(rec, \"id\");\n if (id === undefined) continue;\n\n const found: Record<string, unknown>[] = [];\n leaves(rec[\"steps\"], found);\n const steps = found.map((leaf, i) => step(leaf, i + 1));\n\n const title = str(rec, \"title\");\n const description = descText(rec[\"description\"]);\n const sourcePath = str(rec, \"sourcePath\");\n out.push({\n id,\n ...(sourcePath === undefined ? {} : { sourcePath }),\n tags: Array.isArray(rec[\"tags\"]) ? rec[\"tags\"].filter((t): t is string => typeof t === \"string\") : [],\n ...(title === undefined ? {} : { title }),\n ...(description === undefined ? {} : { description }),\n steps,\n });\n }\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dynamic-views.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/dynamic-views.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `dynamic view`s a document DECLARES, read off LikeC4's parsed model.\n *\n * This is the only module in loam permitted to touch `$data`, and\n * `scripts/arch-check.mjs` enforces that rather than trusting it (docs/DESIGN.md\n * rule 26). The confinement is the point: `$data` is public and typed but thinly\n * documented for this use, so an upstream shape change has a blast radius of one\n * file, and `test/likec4-view-shape.test.ts` is the tripwire that fires before\n * the change reaches a check.\n *\n * It takes the WHOLE model as `unknown` rather than a `$data` slice handed in by\n * its caller, and that is not squeamishness — naming `$data` in `../likec4.ts`\n * would violate the containment scan this module exists behind. Callers pass the\n * model; every assumption about LikeC4's record shape lives here.\n *\n * Everything is defensive, in the same posture as `./specification.ts`: a shape\n * this reader cannot make sense of degrades to ZERO views, never to a wrong one\n * and never to a throw inside a validate run. That asymmetry is deliberate and\n * is loam's standing rule for a suspended axis — a check with no views to grade\n * reports could-not-look, while a check fed a half-read view would report\n * nothing-wrong about a flow it never saw.\n */\nimport { descText } from \"./values.js\";\n\n/** One hop of a declared use case: an ordered edge between two elements. */\nexport interface ParsedStep {\n /**\n * 1-based pre-order position among the view's LEAF steps — what lets a finding\n * say \"step 4 of Checkout\". Group wrappers do not consume an ordinal: a\n * `loop` is a bracket around hops, not a hop, and counting it would shift\n * every number after it away from what the author sees on the diagram.\n */\n ordinal: number;\n /**\n * The step's endpoints, ALREADY ORIENTED — see `isBackward`. Dotted FQN\n * strings naming elements, never relationship ids: a step says who talks to\n * whom, and which relationship backs that is a join loam performs later.\n */\n source: string;\n target: string;\n /**\n * The step's label, absent when the author wrote none. Carried verbatim,\n * because one join reads its opening characters: a title that opens with a\n * candidate relationship's operation id or message name, set off by\n * punctuation, picks that relationship where the map joins the pair by\n * several (`pickedByTitle` in `../resolve/steps.ts`).\n */\n title?: string;\n /** A `notes '...'` block on the step, decoded from LikeC4's rich-text shape. */\n notes?: string;\n /**\n * True when the author wrote the hop as a reply — `a <- b` rather than\n * `b -> a`. LOAD-BEARING, and measured: LikeC4 records `a <- b 'reply'` as\n * `{source: b, target: a, isBackward: true}` — already reversed AND flagged —\n * while a forward step carries no such key at all. `source`/`target` above are\n * therefore correct as they stand and need no flipping; the flag is carried so\n * a check can tell a return hop from a call, and so a message naming the\n * failing step can suggest the `<-` spelling to an author who wrote the arrow\n * the long way round.\n */\n isBackward: boolean;\n /**\n * LikeC4's own path to the step inside the view (`/steps@1/steps@0`), carried\n * verbatim. It is the only stable handle on a nested step — two hops between\n * the same pair inside different `loop` blocks are otherwise indistinguishable\n * — and it is message text, never identity: nothing joins on it.\n *\n * OPTIONAL, and that is a deliberate refusal to make it load-bearing. Every\n * leaf carries one at the 1.59.2 pin, so requiring it would look free — and\n * would mean that an upstream release which stopped emitting it turned every\n * use case in the fleet into a view with zero steps. That is the silent wrong\n * answer this module exists to avoid: a hop loam cannot cite is still a hop it\n * must grade.\n */\n astPath?: string;\n}\n\n/** One declared `dynamic view`, flattened to what rule 26 permits reading. */\nexport interface ParsedView {\n id: string;\n /**\n * The name LikeC4 knows the document by, and it is only a PATH under\n * `loadProject`: there it is relative to the project root, so\n * `usecases/checkout.likec4` inside `architecture/`. Under the per-file\n * loaders it is whatever that loader called the document — `source.c4` for\n * `loadSource`, the basename for `loadBatch` — because there is no project\n * for it to be relative to.\n *\n * So: name a file with it only when the views came from `loadProject`. That\n * is the only reader that has one, which is also the only reader that needs\n * one — a use case lives in a file of its own now, and a finding that cannot\n * name that file sends its reader to the landscape to look for something\n * that is not there.\n *\n * Absent for anything LikeC4 synthesized rather than read off a file, which\n * is how the census reader tells the two apart.\n */\n sourcePath?: string;\n /**\n * The view's declared tags, `[]` when it declares none. Normalized here\n * because LikeC4 reads an untagged view's tags as `null`, and a caller\n * testing `.length` on that would throw inside a validate run.\n *\n * Tags live on the VIEW and nowhere else: a step cannot carry tags, and\n * `metadata` on a step is a hard parse error. That is measured, and it is why\n * a use case is opted in as a whole rather than hop by hop.\n */\n tags: string[];\n title?: string;\n description?: string;\n /** The declared hops, in author order, groups flattened away. */\n steps: ParsedStep[];\n}\n\n/** A record, or nothing — `v` is `unknown` off an untyped parse. */\nfunction record(v: unknown): Record<string, unknown> | undefined {\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;\n}\n\n/** The value at `key`, when it is a non-empty string. */\nfunction str(rec: Record<string, unknown>, key: string): string | undefined {\n const v = rec[key];\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n\n/**\n * Every leaf step under `entries`, depth-first in author order.\n *\n * Steps NEST: a `loop 'until accepted' { ... }` arrives as an entry with\n * `_type: \"loop\"`, a title, its own `steps[]`, and NO endpoints and NO `astPath`\n * of its own (measured). A walk that treated every entry as a hop would read the\n * bracket as a step and then fail to find a relationship backing it — inventing\n * a finding out of a grouping the author drew for legibility.\n *\n * The test is structural rather than a list of group keywords (`loop`, `par`,\n * ...): an entry with a nested `steps` array is a group whatever LikeC4 calls\n * it, and an entry with two string endpoints is a hop. A future group kind\n * therefore flattens correctly on the day it ships instead of on the day\n * somebody notices.\n */\nfunction leaves(entries: unknown, out: Record<string, unknown>[]): void {\n if (!Array.isArray(entries)) return;\n for (const entry of entries) {\n const rec = record(entry);\n if (!rec) continue;\n if (Array.isArray(rec[\"steps\"])) {\n leaves(rec[\"steps\"], out);\n continue;\n }\n if (typeof rec[\"source\"] === \"string\" && typeof rec[\"target\"] === \"string\") out.push(rec);\n }\n}\n\n/** One leaf entry as a `ParsedStep`. Every leaf yields one; nothing is dropped. */\nfunction step(rec: Record<string, unknown>, ordinal: number): ParsedStep {\n const astPath = str(rec, \"astPath\");\n const title = str(rec, \"title\");\n const notes = descText(rec[\"notes\"]);\n return {\n ordinal,\n source: rec[\"source\"] as string,\n target: rec[\"target\"] as string,\n ...(title === undefined ? {} : { title }),\n ...(notes === undefined ? {} : { notes }),\n // Present-and-true is the only truthy form LikeC4 emits; absent means\n // forward. Read as a boolean rather than carried as `true | undefined` so a\n // caller cannot accidentally test `=== false` against a key that is never\n // written.\n isBackward: rec[\"isBackward\"] === true,\n ...(astPath === undefined ? {} : { astPath }),\n };\n}\n\n/**\n * The dynamic views one parsed document declares, in the record's own order.\n *\n * `model` is the awaited `parsedModel()` result. The `await` matters and is the\n * mistake every draft of this made: `$data` is `undefined` on the unresolved\n * promise, so a reader handed the promise reports a document with views as\n * having none — a silent zero, which is the one wrong answer this module is\n * written to avoid.\n *\n * Two entries are dropped on the way out, for different reasons:\n *\n * - Anything whose `_type` is not `\"dynamic\"`. LikeC4 synthesises an `index`\n * view into EVERY document, including one with no `views` block at all\n * (measured), so reporting element views would be reporting a fiction the\n * author never wrote. A census that must see authored element views wants the\n * other filter rule 26 states — `sourcePath !== undefined` — and must not\n * reuse this function to get it.\n * - Anything with no `id`. There is nothing to name in a finding, and a view\n * loam cannot name is a view it cannot ask anybody to fix.\n */\nexport function readDynamicViews(model: unknown): ParsedView[] {\n const data = record(record(model)?.[\"$data\"]);\n const views = record(data?.[\"views\"]);\n if (!views) return [];\n\n const out: ParsedView[] = [];\n for (const raw of Object.values(views)) {\n const rec = record(raw);\n if (!rec || rec[\"_type\"] !== \"dynamic\") continue;\n const id = str(rec, \"id\");\n if (id === undefined) continue;\n\n const found: Record<string, unknown>[] = [];\n leaves(rec[\"steps\"], found);\n const steps = found.map((leaf, i) => step(leaf, i + 1));\n\n const title = str(rec, \"title\");\n const description = descText(rec[\"description\"]);\n const sourcePath = str(rec, \"sourcePath\");\n out.push({\n id,\n ...(sourcePath === undefined ? {} : { sourcePath }),\n tags: Array.isArray(rec[\"tags\"]) ? rec[\"tags\"].filter((t): t is string => typeof t === \"string\") : [],\n ...(title === undefined ? {} : { title }),\n ...(description === undefined ? {} : { description }),\n steps,\n });\n }\n return out;\n}\n"]}
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* (`../likec4.ts`) that calls them, because the adapter is at its line limit and
|
|
10
10
|
* because "decode a value LikeC4 left untyped" is a subject of its own: the
|
|
11
11
|
* specification reader beside this file is the same subject on a different
|
|
12
|
-
* corner of the same object.
|
|
12
|
+
* corner of the same object. The ORDER relationships come back in is one more
|
|
13
|
+
* such corner — LikeC4 promises none, and the one it happens to give changes
|
|
14
|
+
* between runs — so `compareRels` lives here too.
|
|
13
15
|
*/
|
|
14
16
|
/**
|
|
15
17
|
* Read one string key out of a LikeC4 `metadata { ... }` block. Four keys carry
|
|
@@ -54,4 +56,55 @@ export function descText(d) {
|
|
|
54
56
|
}
|
|
55
57
|
return undefined;
|
|
56
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* The ONE order every loader hands relationships back in: by source, then
|
|
61
|
+
* target, title, `op`, `publishes`, `consumes` and tags — plain code-unit
|
|
62
|
+
* compares, so the order is a function of the documents' text alone and never
|
|
63
|
+
* of the locale, the machine or the run. Two edges equal on every key are equal
|
|
64
|
+
* in every field `Rel` (`../likec4.ts`) carries, so which of them comes first
|
|
65
|
+
* cannot be seen. `flattenModel` there sorts with it; it lives here, beside the
|
|
66
|
+
* other reads of LikeC4's parse output that cannot be taken at face value,
|
|
67
|
+
* because the adapter is at its line limit.
|
|
68
|
+
*
|
|
69
|
+
* LikeC4 does not give one, for two measured reasons (1.59.2). Its parsed
|
|
70
|
+
* model indexes relations in a plain object keyed by an id HASHED FROM THE
|
|
71
|
+
* DOCUMENT'S URI — and every loader stages documents under a fresh
|
|
72
|
+
* crypto-random folder per invocation (`../workspace.ts` says why), so the ids
|
|
73
|
+
* change on every run. A JavaScript object enumerates integer-like keys
|
|
74
|
+
* numerically ahead of every other key, so whenever a hash comes out all
|
|
75
|
+
* digits (about one hash in 2,200) that edge jumps to the front of the list,
|
|
76
|
+
* on that run only. And inside a project of several documents the base order
|
|
77
|
+
* is whichever file Langium's concurrent folder walk finished reading first.
|
|
78
|
+
* The first reason is what failed a scheduled CI run on 2026-09-28:
|
|
79
|
+
* `loadProjectBatch` and `loadProject` returned one service's two edges in
|
|
80
|
+
* opposite orders on an unchanged commit — and on a fleet map of a few hundred
|
|
81
|
+
* edges it would reorder some run's findings far more often than that.
|
|
82
|
+
*
|
|
83
|
+
* Declaration order is therefore NOT what a reader gets, and nothing may read
|
|
84
|
+
* meaning into the position of an edge: what the `Rel` list promises is the
|
|
85
|
+
* same order for the same documents, which is what the text report and the
|
|
86
|
+
* `--json` envelope being diffed between runs and machines needs.
|
|
87
|
+
*/
|
|
88
|
+
export function compareRels(a, b) {
|
|
89
|
+
// An absent field sorts ahead of every present one, the empty string
|
|
90
|
+
// included, so "no title" and "an empty title" cannot tie. Tags are LikeC4
|
|
91
|
+
// identifiers, which cannot contain the comma that joins them.
|
|
92
|
+
const opt = (value) => (value === undefined ? "" : `=${value}`);
|
|
93
|
+
const keys = (r) => [
|
|
94
|
+
r.source,
|
|
95
|
+
r.target,
|
|
96
|
+
opt(r.title),
|
|
97
|
+
opt(r.op),
|
|
98
|
+
opt(r.publishes),
|
|
99
|
+
opt(r.consumes),
|
|
100
|
+
r.tags.join(","),
|
|
101
|
+
];
|
|
102
|
+
const ka = keys(a);
|
|
103
|
+
const kb = keys(b);
|
|
104
|
+
for (let i = 0; i < ka.length; i++) {
|
|
105
|
+
if (ka[i] !== kb[i])
|
|
106
|
+
return ka[i] < kb[i] ? -1 : 1;
|
|
107
|
+
}
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
57
110
|
//# sourceMappingURL=values.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"values.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/values.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"values.js","sourceRoot":"","sources":["../../../../src/core/c4/parsed/values.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,OAAO,CAAC,CAAU,EAAE,GAAW;IAC7C,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAI,CAA6B,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC;QACpC,4EAA4E;QAC5E,2EAA2E;QAC3E,8EAA8E;QAC9E,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACxE,kDAAkD;QAClD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,QAAQ,CAAC,CAAU;IACjC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,CAA4B,CAAC;QACvC,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACjB,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,CAAU;IAChD,qEAAqE;IACrE,2EAA2E;IAC3E,+DAA+D;IAC/D,MAAM,GAAG,GAAG,CAAC,KAAyB,EAAU,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IAC5F,MAAM,IAAI,GAAG,CAAC,CAAU,EAAY,EAAE,CAAC;QACrC,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,MAAM;QACR,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;QACZ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACT,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;QAChB,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;QACf,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;KACjB,CAAC;IACF,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC,CAAC,CAAE,GAAG,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC","sourcesContent":["/**\n * The corners of LikeC4's parse output that are typed `unknown`, decoded.\n *\n * LikeC4 models `metadata { ... }` values and a `description` as open shapes —\n * a value may be a string, an array, or a rich-text object, and the declared\n * type says none of that. Every one of those crossings needs a defensive read\n * that answers \"not present\" rather than throwing, and each read encodes a\n * defect somebody hit. They live together here, one module below the adapter\n * (`../likec4.ts`) that calls them, because the adapter is at its line limit and\n * because \"decode a value LikeC4 left untyped\" is a subject of its own: the\n * specification reader beside this file is the same subject on a different\n * corner of the same object. The ORDER relationships come back in is one more\n * such corner — LikeC4 promises none, and the one it happens to give changes\n * between runs — so `compareRels` lives here too.\n */\n\n/**\n * Read one string key out of a LikeC4 `metadata { ... }` block. Four keys carry\n * loam's spines: `op` on a relationship (the OpenAPI operationId it calls),\n * `publishes`/`consumes` on a relationship (the AsyncAPI message it produces or\n * receives), and `service` on an element (the services/<id> directory it stands\n * for). Every one of them is read by BOTH model readers — the parsed one\n * (`flattenModel` in `../likec4.ts`, which calls this) and the text scanner\n * `scanModel` uses for archive's splice map — because a key only one of them\n * sees is a key the merge silently drops. Elements with no metadata come back as\n * `{}`, so a missing key is indistinguishable from a missing block — both mean\n * \"not bound\".\n */\nexport function metaKey(m: unknown, key: string): string | undefined {\n if (m && typeof m === \"object\") {\n const v = (m as Record<string, unknown>)[key];\n if (typeof v === \"string\") return v;\n // A key written TWICE in one block comes back as an array, accepted with no\n // error, and reading only the string form dropped every value — so an edge\n // naming two operations reported as naming none, `c4.op-link-missing` telling\n // the author the opposite of what they wrote. First wins, matching the text\n // scanner's `keyedLiteral`: the two readers disagreeing about a binding is\n // exactly what the paragraph above exists to prevent. Later values stay\n // dropped — one id per key per edge is the model.\n if (Array.isArray(v) && typeof v[0] === \"string\") return v[0];\n }\n return undefined;\n}\n\n/** LikeC4 descriptions can be a string or a rich-text object ({ txt } / { text } / { md }). */\nexport function descText(d: unknown): string | undefined {\n if (typeof d === \"string\") return d;\n if (d && typeof d === \"object\") {\n const o = d as Record<string, unknown>;\n for (const key of [\"txt\", \"text\", \"md\", \"value\"]) {\n const v = o[key];\n if (typeof v === \"string\") return v;\n }\n }\n return undefined;\n}\n\n/**\n * The fields of a relationship its order is decided on — `Rel` in\n * `../likec4.ts`, stated structurally because that module imports this one and\n * a type imported back would close a cycle.\n */\nexport interface RelKeys {\n readonly source: string;\n readonly target: string;\n readonly title?: string;\n readonly op?: string;\n readonly publishes?: string;\n readonly consumes?: string;\n readonly tags: readonly string[];\n}\n\n/**\n * The ONE order every loader hands relationships back in: by source, then\n * target, title, `op`, `publishes`, `consumes` and tags — plain code-unit\n * compares, so the order is a function of the documents' text alone and never\n * of the locale, the machine or the run. Two edges equal on every key are equal\n * in every field `Rel` (`../likec4.ts`) carries, so which of them comes first\n * cannot be seen. `flattenModel` there sorts with it; it lives here, beside the\n * other reads of LikeC4's parse output that cannot be taken at face value,\n * because the adapter is at its line limit.\n *\n * LikeC4 does not give one, for two measured reasons (1.59.2). Its parsed\n * model indexes relations in a plain object keyed by an id HASHED FROM THE\n * DOCUMENT'S URI — and every loader stages documents under a fresh\n * crypto-random folder per invocation (`../workspace.ts` says why), so the ids\n * change on every run. A JavaScript object enumerates integer-like keys\n * numerically ahead of every other key, so whenever a hash comes out all\n * digits (about one hash in 2,200) that edge jumps to the front of the list,\n * on that run only. And inside a project of several documents the base order\n * is whichever file Langium's concurrent folder walk finished reading first.\n * The first reason is what failed a scheduled CI run on 2026-09-28:\n * `loadProjectBatch` and `loadProject` returned one service's two edges in\n * opposite orders on an unchanged commit — and on a fleet map of a few hundred\n * edges it would reorder some run's findings far more often than that.\n *\n * Declaration order is therefore NOT what a reader gets, and nothing may read\n * meaning into the position of an edge: what the `Rel` list promises is the\n * same order for the same documents, which is what the text report and the\n * `--json` envelope being diffed between runs and machines needs.\n */\nexport function compareRels(a: RelKeys, b: RelKeys): number {\n // An absent field sorts ahead of every present one, the empty string\n // included, so \"no title\" and \"an empty title\" cannot tie. Tags are LikeC4\n // identifiers, which cannot contain the comma that joins them.\n const opt = (value: string | undefined): string => (value === undefined ? \"\" : `=${value}`);\n const keys = (r: RelKeys): string[] => [\n r.source,\n r.target,\n opt(r.title),\n opt(r.op),\n opt(r.publishes),\n opt(r.consumes),\n r.tags.join(\",\"),\n ];\n const ka = keys(a);\n const kb = keys(b);\n for (let i = 0; i < ka.length; i++) {\n if (ka[i] !== kb[i]) return ka[i]! < kb[i]! ? -1 : 1;\n }\n return 0;\n}\n"]}
|
|
@@ -16,8 +16,17 @@
|
|
|
16
16
|
* safer when each one owns its shape assumptions outright — the same reason
|
|
17
17
|
* `./specification.ts` states.
|
|
18
18
|
*
|
|
19
|
+
* Two censuses over one record now: view ids off `$data.views`, and since
|
|
20
|
+
* 2026-09-03 the global style ids off `$data.globals.styles` — the same
|
|
21
|
+
* question (which authored ids exist, for a collision or a reference) asked of
|
|
22
|
+
* a second corner, so the generated subsystem views can reference a style
|
|
23
|
+
* group only when the project actually declares it. The second rode into this
|
|
24
|
+
* module rather than a file of its own because `parsed/` sits AT its five-file
|
|
25
|
+
* cap and the census IS this module's subject; `./deployment.ts`'s banner
|
|
26
|
+
* records the seam the next non-census reader still needs.
|
|
27
|
+
*
|
|
19
28
|
* Ids only, each with the file that claims it. What the view SHOWS stays
|
|
20
|
-
* unread, in any document, forever.
|
|
29
|
+
* unread, in any document, forever — and so does what a style group SAYS.
|
|
21
30
|
*/
|
|
22
31
|
/**
|
|
23
32
|
* The shape LikeC4 mints for a view its author never named — `view of svc { }`
|
|
@@ -72,4 +81,33 @@ export function readViewIds(model) {
|
|
|
72
81
|
}
|
|
73
82
|
return out;
|
|
74
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Every global style id the document declares, sorted.
|
|
86
|
+
*
|
|
87
|
+
* `$data.globals.styles` is keyed by id and holds one entry per `styleGroup`
|
|
88
|
+
* OR single-rule `style` declared under `global { }` — both forms land in the
|
|
89
|
+
* one table, measured at the 1.59.2 pin and pinned in
|
|
90
|
+
* test/likec4-view-shape.test.ts. Keys only: what a group SAYS — its targets
|
|
91
|
+
* and colours — is a rendering instruction, and docs/DESIGN.md rule 26 keeps
|
|
92
|
+
* loam out of those on purpose; the one consumer (the generated subsystem
|
|
93
|
+
* views) asks whether an id is declared, nothing more. Sorted so a document
|
|
94
|
+
* declaring two groups yields one order under every loader — the parity rule
|
|
95
|
+
* `readViewIds` states for the minted-id case, applied here to `Object.keys`
|
|
96
|
+
* order, which is the record's and not the author's.
|
|
97
|
+
*
|
|
98
|
+
* Defensive like everything here: a shape this cannot read is ZERO ids, so the
|
|
99
|
+
* generated views then reference nothing — the same file they rendered before
|
|
100
|
+
* the read existed, never a reference to a name loam guessed. That direction
|
|
101
|
+
* is the safe one: a missed declaration costs a palette, while an invented
|
|
102
|
+
* reference is a parse error that blanks the whole `architecture/` project in
|
|
103
|
+
* the renderer.
|
|
104
|
+
*/
|
|
105
|
+
export function readGlobalStyleIds(model) {
|
|
106
|
+
const styles = record(record(record(record(model)?.["$data"])?.["globals"])?.["styles"]);
|
|
107
|
+
if (!styles)
|
|
108
|
+
return [];
|
|
109
|
+
return Object.keys(styles)
|
|
110
|
+
.filter((id) => id.length > 0)
|
|
111
|
+
.sort();
|
|
112
|
+
}
|
|
75
113
|
//# sourceMappingURL=view-ids.js.map
|