@ybotok/loam 0.2.0-alpha.5 → 0.2.0-alpha.6
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 +901 -0
- package/MIGRATING-from-OpenSpec.md +7 -4
- package/QUICKSTART.md +8 -3
- package/README.md +13 -7
- package/ROADMAP.md +264 -11
- package/SCHEMA.md +439 -38
- package/WORKFLOW.md +23 -10
- package/dist/commands/adopt/render.js +47 -8
- package/dist/commands/adopt/render.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +45 -1
- 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/run.js +5 -2
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/context/print.js +11 -1
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/doctor.js +46 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explore.js +12 -2
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/list/views.js +5 -1
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/seed/plan.js +97 -3
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/seed/seed.js +7 -2
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/feature.js +2 -1
- package/dist/commands/show/feature.js.map +1 -1
- package/dist/commands/show/marks.js +3 -5
- package/dist/commands/show/marks.js.map +1 -1
- package/dist/commands/show/service.js +39 -18
- package/dist/commands/show/service.js.map +1 -1
- package/dist/commands/subsystem/sync.js +128 -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/report.js +124 -0
- package/dist/commands/subsystem/txn/report.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +10 -3
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +113 -16
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +16 -9
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +30 -77
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/vocabulary.js +4 -19
- package/dist/commands/validate/checks/vocabulary.js.map +1 -1
- package/dist/commands/validate/feature.js +1 -1
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +11 -10
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +127 -119
- 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 +103 -0
- package/dist/commands/validate/fleet/map/attest.js.map +1 -0
- package/dist/commands/validate/fleet/map/bindings.js +96 -0
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -0
- package/dist/commands/validate/fleet/map/consumers.js +298 -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 +56 -0
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/steps.js +49 -17
- package/dist/commands/validate/fleet/usecases/steps.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 +13 -9
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/projects.js +258 -70
- package/dist/commands/validate/fleet/views/projects.js.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +97 -0
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +2 -2
- package/dist/commands/validate/fleet/views/stale.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 +110 -34
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +39 -5
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +41 -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 +10 -24
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/core/agent/agents-md/artifacts.js +1 -0
- package/dist/core/agent/agents-md/artifacts.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +18 -4
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/cycle.js +24 -9
- package/dist/core/agent/agents-md/cycle.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +51 -11
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +45 -3
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md.js +14 -4
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/support/report.js +39 -5
- package/dist/core/agent/support/report.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +123 -14
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +35 -20
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +3 -1
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/brief/brief.js +63 -11
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/checks.js +133 -7
- package/dist/core/brief/checks.js.map +1 -1
- package/dist/core/brief/landscape.js +212 -129
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +81 -0
- package/dist/core/brief/map/extend.js.map +1 -0
- package/dist/core/brief/map/owed.js +284 -0
- package/dist/core/brief/map/owed.js.map +1 -0
- package/dist/core/brief/targets.js +30 -29
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +12 -2
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/likec4.js +23 -1
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/parsed/deployment.js +5 -3
- package/dist/core/c4/parsed/deployment.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 +142 -0
- package/dist/core/c4/prefetch/fleet.js.map +1 -0
- package/dist/core/c4/project/architecture.js +52 -4
- 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 +9 -4
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +77 -38
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +46 -12
- package/dist/core/c4/project/staged.js.map +1 -1
- package/dist/core/c4/resolve/attested.js +0 -0
- package/dist/core/c4/resolve/attested.js.map +1 -0
- package/dist/core/c4/resolve/service.js +8 -17
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/resolve/steps.js +18 -0
- package/dist/core/c4/resolve/steps.js.map +1 -1
- 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/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 +126 -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-scan.js +26 -0
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +60 -13
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/contract.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 +17 -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 +154 -100
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +140 -0
- package/dist/core/c4/splice/model/merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +14 -4
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/workspace.js +2 -1
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/coherence/coherence.js +7 -0
- package/dist/core/coherence/coherence.js.map +1 -1
- package/dist/core/delta/delta.js +9 -0
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/deployment/delta.js +4 -0
- package/dist/core/deployment/delta.js.map +1 -1
- package/dist/core/diff/base-state.js +21 -5
- package/dist/core/diff/base-state.js.map +1 -1
- package/dist/core/diff/victims.js +10 -1
- package/dist/core/diff/victims.js.map +1 -1
- package/dist/core/docs.js +29 -24
- package/dist/core/docs.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 +36 -8
- 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/explain/refusals.js +1 -1
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explore/explore.js +44 -9
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +47 -0
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/partners.js +5 -9
- package/dist/core/gate/partners.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/pack/living.js +47 -13
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/pack/pack.js +9 -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/repo/paths.js +11 -9
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/tree/render/projects.js +93 -35
- package/dist/core/repo/tree/render/projects.js.map +1 -1
- package/dist/core/repo/tree/render/stale.js +2 -3
- package/dist/core/repo/tree/render/stale.js.map +1 -1
- package/dist/core/repo/tree/render/views.js +51 -3
- package/dist/core/repo/tree/render/views.js.map +1 -1
- package/dist/core/repo/tree/walk.js +45 -2
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/scaffold/landscape.js +219 -9
- package/dist/core/scaffold/landscape.js.map +1 -1
- package/dist/core/staging/writes.js.map +1 -1
- package/dist/core/usecases/delta/claims.js +1 -0
- package/dist/core/usecases/delta/claims.js.map +1 -1
- package/dist/core/usecases/delta/overlay.js +4 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -1
- package/dist/core/usecases/fleet.js +18 -6
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/core/usecases/operations.js +7 -0
- package/dist/core/usecases/operations.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 +161 -0
- package/dist/core/usecases/service/flows.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +10 -0
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +24 -6
- package/examples/docs/architecture/landscape.likec4 +41 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +5 -2
- 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/model.likec4 +53 -67
- package/examples/docs/services/payment-service/arch.spec.md +7 -4
- package/examples/docs/services/payment-service/model.likec4 +32 -35
- package/examples/docs/services/platform/identity-service/arch.spec.md +7 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +29 -33
- package/examples/docs/services/platform/notification-service/health.yaml +8 -3
- package/examples/docs/services/platform/notification-service/model.likec4 +39 -59
- package/package.json +1 -1
- package/examples/docs/services/checkout-web/likec4.config.json +0 -4
- package/examples/docs/services/order-service/likec4.config.json +0 -4
- package/examples/docs/services/payment-service/likec4.config.json +0 -4
- package/examples/docs/services/platform/identity-service/likec4.config.json +0 -4
- package/examples/docs/services/platform/notification-service/likec4.config.json +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../../src/core/brief/checks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The checks half of the adoption brief: every check `loam validate` will run\n * against a baseline, each naming the invocation that surfaces it. Its\n * counterpart — the UNCHECKED list, the checks that do not exist — lives in\n * unchecked.ts: honesty no validator will ever supply, and an agent that does\n * not know where the checking stops will assume it never does.\n */\n\n/* ------------------------------------------------------------------ */\n/* The checks */\n/* ------------------------------------------------------------------ */\n\n/**\n * The two invocations a baseline meets. `<id>` is a placeholder for the service\n * id — the JSON contract keeps it symbolic, the text renderer substitutes it.\n */\nconst VIA_SERVICE = \"loam validate --service <id>\";\nexport const VIA_ALL = \"loam validate --all\";\n\nexport interface BriefCheck {\n code: string;\n severity: \"error\" | \"warn\";\n /**\n * The invocation that surfaces this check. Most run under `--service`, but\n * the fleet cross-check does not — attributing it there taught agents to\n * expect a finding that run never reports.\n */\n via: typeof VIA_SERVICE | typeof VIA_ALL;\n what: string;\n}\n\n/** What `loam validate` will run against the result — each check names the invocation that surfaces it. */\nexport const VALIDATE_CHECKS: BriefCheck[] = [\n { code: \"service.no-model\", severity: \"error\", via: VIA_SERVICE, what: \"there is no model.likec4 — every other check stops here\" },\n { code: \"c4.invalid\", severity: \"error\", via: VIA_SERVICE, what: \"model.likec4 does not parse as LikeC4\" },\n {\n code: \"requirements.missing-scenarios\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a requirement in spec.md or arch.spec.md has no `#### Scenario:`\",\n },\n {\n code: \"spec.duplicate-requirement\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one `### Requirement:` name defined twice in one living spec.md or arch.spec.md — a later merge edits only the first, the rest live on as stale copies\",\n },\n {\n code: \"spec.requirement-id-invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"an optional `Requirement-ID:` violates `[A-Za-z][A-Za-z0-9._-]{0,127}` — stable identities must be portable and unambiguous\",\n },\n {\n code: \"spec.requirement-id-repeated\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one requirement declares `Requirement-ID:` more than once — keep exactly one identity line\",\n },\n {\n code: \"spec.requirement-id-duplicate\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one stable Requirement-ID identifies multiple requirements in one spec.md or arch.spec.md\",\n },\n // The keep-last pair: a second `Operations:` / `Covers:` line in one\n // requirement body REPLACES the first — assignment, not append — so the\n // author's two-line list silently loses its first line. The semantics stay;\n // the loss no longer happens in silence.\n {\n code: \"spec.repeated-operations\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a second `Operations:` line in one requirement body — the last replaces the earlier ones, whose list is silently dropped; merge into one comma-separated line\",\n },\n {\n code: \"spec.repeated-covers\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a second `Covers:` line in one requirement body — same keep-last rule, same silent loss; merge into one comma-separated line\",\n },\n // The graded absences: the brief marks spec.md and openapi.yaml required, and\n // validate agrees they belong — as warns, because partial adoption is a\n // supported state and these are its honest progress meter, not a gate.\n {\n code: \"service.no-spec\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"spec.md does not exist yet — legal mid-adoption, but requirement coverage and API governance are unchecked until it does\",\n },\n {\n // `warn` is the floor, not the whole story, and the difference matters to a\n // baseline: the absence is graded an ERROR once something already written\n // down joins into the file — a living requirement's `Operations:` line, or\n // an op-linked landscape edge (`landscape.expects`, landscape.ts). For a\n // service the fleet already calls, `required: true` on this target and this\n // row are the same fact stated twice.\n code: \"service.no-openapi\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"openapi.yaml does not exist yet — quiet only when the landscape proves no other service calls an operation on this one, and an ERROR rather than a warning once a living `Operations:` line or a landscape edge already names an operation it would have defined\",\n },\n {\n code: \"openapi.invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"openapi.yaml exists but does not parse — an unreadable contract proves nothing, so the `api.*` checks and the spine's op resolution are suspended until it reads\",\n },\n {\n code: \"api.ungoverned\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"an operationId in openapi.yaml that no requirement's `Operations:` line names\",\n },\n {\n code: \"api.ops-unlinked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"openapi.yaml defines operations and spec.md has requirements, but no `Operations:` line joins them — every cross-axis check is vacuously green\",\n },\n // The `Operations:` spine, checked against the LIVING contract. It used to\n // fire only inside a feature delta, so a baseline could ship a requirement\n // governing an operation nobody defines and stay green until the first\n // feature touched it.\n {\n code: \"spec-api.op-undefined\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a living requirement's `Operations:` line names an operationId this service's openapi.yaml does not define — the two axes disagree about what the service exposes\",\n },\n {\n code: \"spec.no-requirements\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"spec.md exists but holds no `### Requirement:` block at all — every requirement-driven check below it is vacuous rather than passing\",\n },\n // The deprecation pair fires on a fresh baseline too — a legacy service is\n // exactly where ops marked `deprecated: true` coexist with their\n // replacements, and documenting that lifecycle honestly is part of the\n // adoption. Mark the flag where the code retires an op; loam reads it, and\n // has no removal semantics beyond it.\n {\n code: \"api.requirement-deprecated\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a requirement whose `Operations:` list resolves only to operations openapi.yaml marks `deprecated: true` — the behaviour it governs is being retired\",\n },\n {\n code: \"spine.op-undefined\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a landscape edge into this service calls an operation its openapi.yaml does not define — a broken contract between services\",\n },\n {\n code: \"spine.op-link-missing\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: 'a landscape \"Calls\" edge into this service with no `metadata { op }`',\n },\n {\n code: \"spine.op-deprecated\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a landscape edge into this service calls an operation openapi.yaml marks `deprecated: true` — the consumer should be migrating off it\",\n },\n {\n code: \"covers.unknown\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a `Covers:` entry in arch.spec.md that resolves to no element, edge, alert or SLI — a typo silently costs the coverage it was written for\",\n },\n {\n code: \"health.invalid\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"health.yaml exists but does not parse — its alert/SLI ids are unreadable, so `Covers: alert:/sli:` entries and health coverage go unchecked until it reads (a missing health.yaml stays silent)\",\n },\n {\n code: \"health.uncovered\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"an alert or SLI declared in health.yaml that no arch.spec.md requirement covers — expected until the arch spec is written; each one is a signal nothing tests\",\n },\n {\n code: \"health.dependency-unmodelled\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a health.yaml `dependencies:` id that nothing in this service's OWN model.likec4 answers to (element id, `metadata { service }` binding, or title) — the model carries everything the service touches, private stores included as nested containers, and the on-call file must name the same world\",\n },\n {\n code: \"frontmatter.malformed\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"the frontmatter block does not parse as YAML — owner, status and sources are unreadable, and the field checks are suspended until the header is fixed\",\n },\n {\n code: \"frontmatter.field-mismatch\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"spec.md declares a different `service:` than the directory it lives under\",\n },\n {\n code: \"frontmatter.status-unknown\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a status outside `draft` / `verified` — a typo here reads as unverified forever\",\n },\n { code: \"frontmatter.field-missing\", severity: \"warn\", via: VIA_SERVICE, what: \"no `owner`, `status` or `service`\" },\n { code: \"sources.absent\", severity: \"warn\", via: VIA_SERVICE, what: \"spec.md names no `sources`\" },\n {\n code: \"sources.path-missing\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a listed source does not exist, or is a glob pattern — no longer supported (checked when loam runs inside the service's repo)\",\n },\n {\n code: \"sources.unvouched\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"`sources` with no digest — nobody has vouched for the document yet. Expected on a fresh baseline; only a human closes it\",\n },\n {\n code: \"sources.unwalked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"`sources` leaves whole top-level paths of the service repo untouched — the finding names them (checked when loam runs inside the service's repo, against the files git tracks). It grades the WALK, not the writing: the only completeness signal loam can compute, because it is the only one that compares the document against the repository rather than against itself\",\n },\n // sources.stale, sources.current and content.stale are deliberately absent:\n // all three compare against digests `loam vouch` stamps, and a fresh baseline\n // has no stamp to compare — the table lists what a baseline can actually meet.\n // The gherkin.* staleness chain is absent the same way: it fires only once\n // `loam gherkin` has generated a suite under <gherkinDir>/loam/ in the\n // service's own repo, and a fresh baseline has not generated one.\n {\n code: \"landscape.service-unmodelled\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"nothing in architecture/landscape.likec4 resolves to services/<id>/ — the fleet map is incomplete until an element exists or an existing one is bound with `metadata { service '<id>' }`\",\n },\n {\n code: \"landscape.missing\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"architecture/landscape.likec4 does not exist at all — an error as soon as services/ holds one service (a warning only in an empty docs repo): with no fleet map, every cross-service check is blind rather than passing\",\n },\n {\n code: \"landscape.invalid\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"architecture/landscape.likec4 exists but does not parse — fix it before anything else; the whole cross-service layer is unchecked until it reads\",\n },\n {\n code: \"landscape.binding-unknown\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"an element's `metadata { service }` names a directory that does not exist — a binding is a claim about this repo, and this one is false\",\n },\n {\n code: \"landscape.binding-duplicate\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"two landscape elements resolve to the same services/<id>/ — every element→service join picks one of them arbitrarily, so the other's edges are filed under a service that does not own them\",\n },\n // Fires on a fresh baseline by construction: the adopt protocol's step 3\n // writes the model and `subsystem sync` writes the project file, so a\n // service is owed the second command from the moment the first file lands.\n {\n code: \"service.likec4-config-missing\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"model.likec4 exists and services/<id>/likec4.config.json does not — the renderer's per-service project file, which `loam subsystem sync` writes; without it the model is a box on the fleet map with nothing inside it\",\n },\n];\n"]}
|
|
1
|
+
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../../src/core/brief/checks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The checks half of the adoption brief: every check `loam validate` will run\n * against a baseline, each naming the invocation that surfaces it. Its\n * counterpart — the UNCHECKED list, the checks that do not exist — lives in\n * unchecked.ts: honesty no validator will ever supply, and an agent that does\n * not know where the checking stops will assume it never does.\n */\n\n/* ------------------------------------------------------------------ */\n/* The checks */\n/* ------------------------------------------------------------------ */\n\n/**\n * The two invocations a baseline meets. `<id>` is a placeholder for the service\n * id — the JSON contract keeps it symbolic, the text renderer substitutes it.\n */\nconst VIA_SERVICE = \"loam validate --service <id>\";\nexport const VIA_ALL = \"loam validate --all\";\n\nexport interface BriefCheck {\n code: string;\n severity: \"error\" | \"warn\";\n /**\n * The invocation that surfaces this check. Most run under `--service`, but\n * the fleet cross-check does not — attributing it there taught agents to\n * expect a finding that run never reports.\n */\n via: typeof VIA_SERVICE | typeof VIA_ALL;\n what: string;\n}\n\n/** What `loam validate` will run against the result — each check names the invocation that surfaces it. */\nexport const VALIDATE_CHECKS: BriefCheck[] = [\n { code: \"service.no-model\", severity: \"error\", via: VIA_SERVICE, what: \"there is no model.likec4 — every other check stops here\" },\n {\n code: \"c4.invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"model.likec4 does not parse as LikeC4 — for a model that extends the fleet map, parsed together with `architecture/landscape.likec4`, and every error is this model's, including one the parser blames on the map (the map parses clean on its own)\",\n },\n // The two shapes' own grades. A model that extends the map cannot diverge from\n // it — there is one declaration — and a model that stands alone cannot own an\n // element outside itself, because nothing else is in its project. So exactly\n // one of these two can ever fire for a given model, and which one is decided\n // by the file's own grammar (SCHEMA.md, \"Two shapes of a service model\").\n {\n code: \"c4.declaration-diverged\",\n severity: \"warn\",\n via: VIA_SERVICE,\n // \"the fleet map (architecture/)\", never one file: the map is a project, an\n // element may be declared in any document of it, and the finding's own\n // message and `loam explain` both refuse to name a file for that reason.\n // Naming `architecture/landscape.likec4` here sent a reader of a fleet whose\n // map is spread over several documents to a file that may not declare the\n // element at all (verification 2026-09-04).\n what: \"a model that declares its own `specification` (the standalone shape) declares an element the fleet map (`architecture/`) also declares, and the two copies disagree about `kind`, `title`, the tag SET or the `metadata { service }` binding — two documents are two authorities on one element. `description` is deliberately not compared. Copy the map's declaration verbatim, or migrate the model to extend the map\",\n },\n {\n code: \"c4.element-unowned\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a model that extends the fleet map declares an element OUTSIDE the element that resolves to this service — a system this service reaches belongs in `architecture/landscape.likec4`, declared once, and another service's internals belong in that service's model\",\n },\n {\n code: \"requirements.missing-scenarios\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a requirement in spec.md or arch.spec.md has no `#### Scenario:`\",\n },\n {\n code: \"spec.duplicate-requirement\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one `### Requirement:` name defined twice in one living spec.md or arch.spec.md — a later merge edits only the first, the rest live on as stale copies\",\n },\n {\n code: \"spec.requirement-id-invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"an optional `Requirement-ID:` violates `[A-Za-z][A-Za-z0-9._-]{0,127}` — stable identities must be portable and unambiguous\",\n },\n {\n code: \"spec.requirement-id-repeated\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one requirement declares `Requirement-ID:` more than once — keep exactly one identity line\",\n },\n {\n code: \"spec.requirement-id-duplicate\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"one stable Requirement-ID identifies multiple requirements in one spec.md or arch.spec.md\",\n },\n // The keep-last pair: a second `Operations:` / `Covers:` line in one\n // requirement body REPLACES the first — assignment, not append — so the\n // author's two-line list silently loses its first line. The semantics stay;\n // the loss no longer happens in silence.\n {\n code: \"spec.repeated-operations\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a second `Operations:` line in one requirement body — the last replaces the earlier ones, whose list is silently dropped; merge into one comma-separated line\",\n },\n {\n code: \"spec.repeated-covers\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a second `Covers:` line in one requirement body — same keep-last rule, same silent loss; merge into one comma-separated line\",\n },\n // The graded absences: the brief marks spec.md and openapi.yaml required, and\n // validate agrees they belong — as warns, because partial adoption is a\n // supported state and these are its honest progress meter, not a gate.\n {\n code: \"service.no-spec\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"spec.md does not exist yet — legal mid-adoption, but requirement coverage and API governance are unchecked until it does\",\n },\n {\n // `warn` is the floor, not the whole story, and the difference matters to a\n // baseline: the absence is graded an ERROR once something already written\n // down joins into the file — a living requirement's `Operations:` line, or\n // an op-linked landscape edge (`landscape.expects`, landscape.ts). For a\n // service the fleet already calls, `required: true` on this target and this\n // row are the same fact stated twice.\n code: \"service.no-openapi\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"openapi.yaml does not exist yet — quiet only when the landscape proves no other service calls an operation on this one, and an ERROR rather than a warning once a living `Operations:` line or a landscape edge already names an operation it would have defined\",\n },\n {\n code: \"openapi.invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"openapi.yaml exists but does not parse — an unreadable contract proves nothing, so the `api.*` checks and the spine's op resolution are suspended until it reads\",\n },\n {\n code: \"api.ungoverned\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"an operationId in openapi.yaml that no requirement's `Operations:` line names\",\n },\n {\n code: \"api.ops-unlinked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"openapi.yaml defines operations and spec.md has requirements, but no `Operations:` line joins them — every cross-axis check is vacuously green\",\n },\n // The `Operations:` spine, checked against the LIVING contract. It used to\n // fire only inside a feature delta, so a baseline could ship a requirement\n // governing an operation nobody defines and stay green until the first\n // feature touched it.\n {\n code: \"spec-api.op-undefined\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a living requirement's `Operations:` line names an operationId this service's openapi.yaml does not define — the two axes disagree about what the service exposes\",\n },\n {\n code: \"spec.no-requirements\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"spec.md exists but holds no `### Requirement:` block at all — every requirement-driven check below it is vacuous rather than passing\",\n },\n // The deprecation pair fires on a fresh baseline too — a legacy service is\n // exactly where ops marked `deprecated: true` coexist with their\n // replacements, and documenting that lifecycle honestly is part of the\n // adoption. Mark the flag where the code retires an op; loam reads it, and\n // has no removal semantics beyond it.\n {\n code: \"api.requirement-deprecated\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a requirement whose `Operations:` list resolves only to operations openapi.yaml marks `deprecated: true` — the behaviour it governs is being retired\",\n },\n {\n code: \"spine.op-undefined\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a landscape edge into this service calls an operation its openapi.yaml does not define — a broken contract between services\",\n },\n {\n code: \"spine.op-link-missing\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: 'a landscape \"Calls\" edge into this service with no `metadata { op }`',\n },\n {\n code: \"spine.op-deprecated\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a landscape edge into this service calls an operation openapi.yaml marks `deprecated: true` — the consumer should be migrating off it\",\n },\n {\n code: \"covers.unknown\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a `Covers:` entry in arch.spec.md that resolves to no element, edge, alert or SLI — a typo silently costs the coverage it was written for\",\n },\n {\n code: \"health.invalid\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"health.yaml exists but does not parse — its alert/SLI ids are unreadable, so `Covers: alert:/sli:` entries and health coverage go unchecked until it reads (a missing health.yaml stays silent)\",\n },\n {\n code: \"health.uncovered\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"an alert or SLI declared in health.yaml that no arch.spec.md requirement covers — expected until the arch spec is written; each one is a signal nothing tests\",\n },\n {\n code: \"health.dependency-unmodelled\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a health.yaml `dependencies:` id that nothing in this service's OWN model.likec4 answers to (element id, `metadata { service }` binding, or title) — the model carries everything the service touches, private stores included as nested containers, and the on-call file must name the same world\",\n },\n {\n code: \"frontmatter.malformed\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"the frontmatter block does not parse as YAML — owner, status and sources are unreadable, and the field checks are suspended until the header is fixed\",\n },\n {\n code: \"frontmatter.field-mismatch\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"spec.md declares a different `service:` than the directory it lives under\",\n },\n {\n code: \"frontmatter.status-unknown\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a status outside `draft` / `verified` — a typo here reads as unverified forever\",\n },\n { code: \"frontmatter.field-missing\", severity: \"warn\", via: VIA_SERVICE, what: \"no `owner`, `status` or `service`\" },\n { code: \"sources.absent\", severity: \"warn\", via: VIA_SERVICE, what: \"spec.md names no `sources`\" },\n {\n code: \"sources.path-missing\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a listed source does not exist, or is a glob pattern — no longer supported (checked when loam runs inside the service's repo)\",\n },\n {\n code: \"sources.unvouched\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"`sources` with no digest — nobody has vouched for the document yet. Expected on a fresh baseline; only a human closes it\",\n },\n {\n code: \"sources.unwalked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"`sources` leaves whole top-level paths of the service repo untouched — the finding names them (checked when loam runs inside the service's repo, against the files git tracks). It grades the WALK, not the writing: the only completeness signal loam can compute, because it is the only one that compares the document against the repository rather than against itself\",\n },\n // sources.stale, sources.current and content.stale are deliberately absent:\n // all three compare against digests `loam vouch` stamps, and a fresh baseline\n // has no stamp to compare — the table lists what a baseline can actually meet.\n // The gherkin.* staleness chain is absent the same way: it fires only once\n // `loam gherkin` has generated a suite under <gherkinDir>/loam/ in the\n // service's own repo, and a fresh baseline has not generated one.\n //\n // The service's OWN use cases: a `dynamic view` in any `.likec4` beside\n // model.likec4 (or inside it) that opts in with `#req-`. The same codes the\n // fleet target grades over architecture/usecases/, reused on the service\n // target because the flow's hops name this service's containers, which only\n // its own project can resolve. `usecase.step-unlinked` was at first left\n // out, on the reasoning that a hop between two containers of one service\n // owes no operationId — true, and exactly the scope of the guard that keeps\n // it quiet there (a service owes none to itself); but a service's model\n // routinely declares a stand-in for a sibling service it calls, and a hop\n // from a service-local flow into that stand-in with no `metadata { op }`\n // warns exactly as it does on the fleet map. The row is listed so the\n // agent's own baseline run never reports a code the brief left unexplained.\n {\n code: \"usecase.step-unbacked\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a hop of a `#req-`-tagged dynamic view in this service's project that no relationship in model.likec4 backs — LikeC4 reports nothing for it, so only this does\",\n },\n {\n code: \"usecase.step-contested\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"two or more relationships back one hop of a service-local flow and name different operations — loam names the candidates rather than picking one\",\n },\n {\n code: \"usecase.step-unlinked\",\n severity: \"warn\",\n via: VIA_SERVICE,\n what: \"a hop of a service-local flow into ANOTHER service's element — a stand-in this model.likec4 declares for a sibling, bound or titled as that service — backed by an edge carrying no `metadata { op }` and no `publishes`/`consumes`; never on a hop whose caller and provider resolve to the same service, because a service owes no operationId to itself\",\n },\n {\n code: \"usecase.requirement-unresolved\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a `#req-` tag on a service-local flow naming no `Requirement-ID` of this service's spec.md or arch.spec.md — the id is scoped by the directory, never by a capability\",\n },\n {\n code: \"usecase.capability-unresolved\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a `#cap-` tag on a flow inside this service's own project — a capability is claimed on the fleet map, never inside one service; drop the tag and keep the `#req-` one\",\n },\n {\n code: \"usecase.flow-invalid\",\n severity: \"error\",\n via: VIA_SERVICE,\n what: \"a `.likec4` beside model.likec4 that does not parse as part of the service's LikeC4 project — no flow in it was graded; model.likec4 itself is still graded alone\",\n },\n {\n code: \"landscape.service-unmodelled\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"nothing in architecture/landscape.likec4 resolves to services/<id>/ — the fleet map is incomplete until an element exists or an existing one is bound with `metadata { service '<id>' }`. On the FLEET target for any service; on the SERVICE target too when that service's model.likec4 EXTENDS the map, where an unbound directory leaves the model with nothing to be inside and nothing in it can be graded\",\n },\n {\n code: \"landscape.missing\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"architecture/landscape.likec4 does not exist at all — an error as soon as services/ holds one service (a warning only in an empty docs repo): with no fleet map, every cross-service check is blind rather than passing\",\n },\n {\n code: \"landscape.invalid\",\n severity: \"error\",\n via: VIA_ALL,\n // The fleet map is the whole `architecture/` PROJECT, so this code fires for\n // a palette or a use case beside the landscape just as readily as for the\n // landscape itself — and the message names the document that broke. Saying\n // \"landscape.likec4\" here sent an author to a file with no errors in it.\n what: \"the `architecture/` project does not parse — the landscape, or any `.likec4` beside it; the message names the document that broke. Fix it before anything else: the whole cross-service layer is unchecked until the map reads\",\n },\n {\n code: \"landscape.binding-unknown\",\n severity: \"error\",\n via: VIA_ALL,\n what: \"an element's `metadata { service }` names a directory that does not exist — a binding is a claim about this repo, and this one is false\",\n },\n {\n code: \"landscape.binding-duplicate\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"two landscape elements resolve to the same services/<id>/ — every element→service join picks one of them arbitrarily, so the other's edges are filed under a service that does not own them\",\n },\n // Evidence-gated: the model's own cross-boundary calls are the proof that\n // the map owes an edge, so a service whose model declares none stays silent\n // — that state is the adopt brief's to name (`landscape.touched`), never a\n // fleet finding that cannot tell a hermit from a batch job. And BINARY: one\n // edge closes it, because loam cannot tell a call the map forgot from one the\n // fleet deliberately does not draw at its own altitude, and a set difference\n // reported as an omission is an invented edge with a code beside it.\n { code: \"landscape.service-isolated\", severity: \"warn\", via: VIA_ALL, what: \"an element resolves to services/<id>/ and no edge anywhere in architecture/ touches it, while model.likec4 declares a call across its boundary — the service is drawn and invisible to every cross-service check; silent when the model declares no such call, and silent once ONE edge is drawn: the check is touched/untouched, not a set difference over the attested calls\" },\n // The four renderer-wiring grades, all repaired by one `loam subsystem sync`.\n // None of them fires on a baseline written from this brief: the model it asks\n // for extends the map, so it belongs to the root project and is owed no file\n // and no exclusion. They are listed because an agent adopting into an OLDER\n // repository — one whose root `likec4.config.json` still excludes\n // `services/**`, or that carries a hand-written project file — meets them on\n // the first `--all` run and must not read one as work on the model.\n {\n code: \"service.likec4-config-missing\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"a model that stands alone (declares its own `specification`) renders only as a project of its own, and services/<id>/likec4.config.json is not there — `loam subsystem sync` writes it; without it the model is a box on the fleet map with nothing renderable inside it. Never raised for a model that extends the map\",\n },\n {\n code: \"service.likec4-config-stray\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"the reverse: model.likec4 extends the fleet map and a likec4.config.json sits beside it — the renderer registers that file as a project of its own, holding nothing, or holding the model alone where it cannot parse. Delete it; loam never writes one beside an extending model\",\n },\n {\n code: \"service.model-excluded\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"model.likec4 extends the fleet map and the root likec4.config.json's `exclude` hides it, so the renderer never loads it. Two entry shapes reach it: one covering the DIRECTORY, which `loam subsystem sync` rewrites (that command maintains the `services/` entries), and one shaped like a FILE (`services/**/model.likec4`), which sync cannot repair — the message says which one you have, and the file shape has to be removed or narrowed by hand\",\n },\n {\n code: \"service.model-unexcluded\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"the mirror, and the damaging one: a model that stands alone whose directory the root `exclude` does NOT cover — the renderer merges it into the map, reports every kind and element it declares as a duplicate, and blanks the whole root project, not just this service. Same repair\",\n },\n // The same list, one level up: the entry covers the MAP. It is not a\n // per-service wiring grade and no `sync` repairs it — sync recomputes the\n // `services/` entries only — so it is listed separately from the four above.\n {\n code: \"landscape.excluded\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"the root likec4.config.json's `exclude` covers architecture/landscape.likec4 itself — the renderer loads no fleet map at all, and every extending model in the root project resolves against nothing, while loam keeps grading the map it reads from disk. `c4.fleet-project-invalid` is not graded while the entry stands, because that check reads the renderer's project and would report this one line once per model. The finding quotes the entry verbatim; remove or narrow it by hand, because `loam subsystem sync` maintains only the `services/` entries in that list\",\n },\n {\n code: \"c4.fleet-project-invalid\",\n severity: \"warn\",\n via: VIA_ALL,\n what: \"every document reads clean where loam grades it, and the ONE project the renderer builds out of them — the map, every extending model, and every `.likec4` beside one — does not parse. A tag or an element declared in two of those documents is declared twice there; declare it once, in the map or in the single service that owns it. Not graded at all while `landscape.excluded` holds: with the map out of that project every `extend` is unresolvable, so the entry is the finding\",\n },\n];\n"]}
|
|
@@ -1,194 +1,277 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The fleet-map half of the adoption brief: what
|
|
2
|
+
* The fleet-map half of the adoption brief, READ side: what the fleet map
|
|
3
3
|
* already says about one service — its elements, its inbound and outbound
|
|
4
|
-
* edges, the operations the fleet already calls on it — and
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* edges, the operations the fleet already calls on it — and which of the four
|
|
5
|
+
* states the map is in for it. The write the service still owes the file — the
|
|
6
|
+
* block, when nothing in it resolves to the service yet, or the EDGES, when an
|
|
7
|
+
* element does and no edge touches it — is spelled in `./map/owed.ts`
|
|
8
|
+
* (`landscapeArtifact`, `instructionFor`), which this module hands its findings
|
|
9
|
+
* to. Both halves exist to stop an agent inventing a parallel model: the fleet
|
|
10
|
+
* has already drawn some of these boxes and edges, and a baseline has to attach.
|
|
11
|
+
*
|
|
12
|
+
* "The fleet map" is the `architecture/` PROJECT — the landscape plus every
|
|
13
|
+
* other `.likec4` under it, minus the generated views file — and not the
|
|
14
|
+
* landscape FILE. The two are the same document set `validate --all` grades
|
|
15
|
+
* `landscape.service-isolated` over, and reading one file here let the two
|
|
16
|
+
* surfaces disagree about the same tree.
|
|
9
17
|
*/
|
|
10
18
|
import { existsSync } from "node:fs";
|
|
11
19
|
import { loadFile } from "../c4/likec4.js";
|
|
12
|
-
import {
|
|
20
|
+
import { loadArchitecture } from "../c4/project/architecture.js";
|
|
21
|
+
import { attestedCalls } from "../c4/resolve/attested.js";
|
|
22
|
+
import { serviceResolver } from "../c4/resolve/service.js";
|
|
23
|
+
import { ServiceModels } from "../c4/prefetch/fleet.js";
|
|
24
|
+
import { OBLIGATION_TAG_PREFIX } from "../obligations/obligations.js";
|
|
13
25
|
import { landscapePath } from "../repo/paths.js";
|
|
14
26
|
import { enumeratedServiceIds, serviceTreePathOf } from "../repo/service-target.js";
|
|
27
|
+
import { EXTERNAL_TAG, GRADED_TAGS } from "../vocabulary/maturity.js";
|
|
28
|
+
import { brokenDocuments, instructionFor } from "./map/owed.js";
|
|
29
|
+
import { properAncestorIds } from "../kernel/ids/fqn/ancestors.js";
|
|
15
30
|
/**
|
|
16
|
-
* The
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* the
|
|
20
|
-
* and one that describes a block in prose gets improvised on.
|
|
31
|
+
* The service ids the resolver may fall back on when an element carries no
|
|
32
|
+
* `metadata { service }` binding — the same positive evidence `validate` and
|
|
33
|
+
* `list` hand it. A docs repo with no `services/` yet (`adopt` runs there)
|
|
34
|
+
* leaves the resolver with bindings alone, which is what it had before.
|
|
21
35
|
*/
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
if (parts.length === 0)
|
|
25
|
-
return "service";
|
|
26
|
-
const head = parts[0].toLowerCase();
|
|
27
|
-
const tail = parts.slice(1).map((p) => p[0].toUpperCase() + p.slice(1).toLowerCase());
|
|
28
|
-
const id = head + tail.join("");
|
|
29
|
-
// LikeC4 identifiers cannot open with a digit; a service id can.
|
|
30
|
-
return /^[0-9]/.test(id) ? `svc${id[0].toUpperCase()}${id.slice(1)}` : id;
|
|
36
|
+
async function knownServices(docsDir) {
|
|
37
|
+
return new Set(await enumeratedServiceIds(docsDir));
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
33
|
-
* The
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
40
|
+
* The tags loam reads on an ELEMENT. `GRADED_TAGS` is the vocabulary's own
|
|
41
|
+
* list of the tags that change what loam concludes — spelled once there, so
|
|
42
|
+
* the day a third tag is graded this set follows and the instruction below
|
|
43
|
+
* does not tell an agent to clear it as unread. The obligation prefix is the
|
|
44
|
+
* one tag PREFIX loam reads on an element; `#cap-`/`#req-` are view tags and
|
|
45
|
+
* never sit on one, so they are not in this list.
|
|
38
46
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const calls = expects.length > 0
|
|
42
|
-
? expects.map((op) => `// <caller> -> ${id} 'Calls ${op}' { metadata { op '${op}' } }`)
|
|
43
|
-
: [`// <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`];
|
|
44
|
-
const model = [
|
|
45
|
-
"model {",
|
|
46
|
-
...(present ? [" // ... the fleet's other services stay exactly as they are ...", ""] : []),
|
|
47
|
-
` ${id} = softwareSystem '${service}' {`,
|
|
48
|
-
" description '<one line: what this service is responsible for>'",
|
|
49
|
-
` metadata { service '${service}' }`,
|
|
50
|
-
" }",
|
|
51
|
-
"",
|
|
52
|
-
" // every call in or out of it, one edge each:",
|
|
53
|
-
...calls.map((line) => ` ${line}`),
|
|
54
|
-
"}",
|
|
55
|
-
];
|
|
56
|
-
// When the file does not exist the example is the WHOLE file, so it carries
|
|
57
|
-
// the `specification` block a bare `model` would be rejected without: the
|
|
58
|
-
// brief must never hand over a document `loam validate` refuses. When the file
|
|
59
|
-
// does exist the example is a fragment to splice, and repeating a
|
|
60
|
-
// `specification` block there would invite a second one.
|
|
61
|
-
return present
|
|
62
|
-
? model.join("\n")
|
|
63
|
-
: ["specification {", " element softwareSystem", "}", "", ...model].join("\n");
|
|
64
|
-
}
|
|
65
|
-
/** The fleet map's own brief — assembled per service, because the block it owes names the service. */
|
|
66
|
-
export function landscapeArtifact(service, expects, present, servicePath) {
|
|
67
|
-
return {
|
|
68
|
-
artifact: "landscape.likec4",
|
|
69
|
-
required: true,
|
|
70
|
-
purpose: "the fleet map — until an element here resolves to this directory, the service is documented and invisible to every cross-service check",
|
|
71
|
-
shape: [
|
|
72
|
-
"This file is the WHOLE FLEET's, not this service's. Add to it; never rewrite it. Everything already in `model { ... }` belongs to the other services, and replacing the file destroys their map along with their edges.",
|
|
73
|
-
`Add one top-level element for the service, bound to its directory: \`metadata { service '${service}' }\`. Until an element resolves to \`${servicePath}/\`, \`loam validate --all\` reports \`landscape.service-unmodelled\` (error).`,
|
|
74
|
-
"Bind rather than rename: a binding whose id names no directory is `landscape.binding-unknown` (error), and two elements binding the SAME directory is `landscape.binding-duplicate` (warn) — every element→service join then picks one of them arbitrarily.",
|
|
75
|
-
"Draw every cross-service call as an edge carrying the operation it uses: `a -> b 'Calls createSplit' { metadata { op 'createSplit' } }`. The `op` must be an operationId the TARGET's openapi.yaml defines, or `spine.op-undefined` (error) — a broken contract between services.",
|
|
76
|
-
"If the file does not exist yet, create it with a `specification { ... }` block declaring the kinds you use, then the `model { ... }` block. A landscape that does not parse is `landscape.invalid` (error) and blinds every cross-service check at once.",
|
|
77
|
-
],
|
|
78
|
-
example: landscapeBlock(service, expects, present),
|
|
79
|
-
};
|
|
80
|
-
}
|
|
47
|
+
const LOAM_TAGS = new Set(GRADED_TAGS);
|
|
48
|
+
const LOAM_TAG_PREFIXES = [OBLIGATION_TAG_PREFIX];
|
|
81
49
|
/**
|
|
82
|
-
* The
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* had before.
|
|
50
|
+
* The element's tags loam does NOT read — named in the instruction and never
|
|
51
|
+
* interpreted. A placeholder convention ("drawn ahead of adoption") is the
|
|
52
|
+
* fleet's own, `loam seed` writes none, and a view written `exclude
|
|
53
|
+
* element.tag = #<that>` keeps an adopted service hidden until it is cleared.
|
|
87
54
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
55
|
+
function foreignTagsOf(elements) {
|
|
56
|
+
const unread = (t) => !LOAM_TAGS.has(t) && !LOAM_TAG_PREFIXES.some((p) => t.startsWith(p));
|
|
57
|
+
return [...new Set(elements.flatMap((e) => e.tags))].filter(unread);
|
|
90
58
|
}
|
|
91
59
|
/**
|
|
92
|
-
*
|
|
60
|
+
* The attested calls, or nothing off a model that could not be read: `c4.invalid`
|
|
61
|
+
* is validate's finding, and an edge list read off half a document is invention.
|
|
93
62
|
*
|
|
94
|
-
*
|
|
95
|
-
* the
|
|
96
|
-
*
|
|
97
|
-
*
|
|
63
|
+
* Through the ONE reader of `model.likec4` (`core/c4/service-model/load.ts`),
|
|
64
|
+
* because the two shapes are read differently and this brief is handed to an
|
|
65
|
+
* agent that will act on it: a model that EXTENDS the fleet map, opened as a
|
|
66
|
+
* lone file, comes back as a pile of parse errors — so the brief would tell
|
|
67
|
+
* every migrated service that its model attests nothing and quietly drop the
|
|
68
|
+
* edges the map most likely owes. `mapUnreadable` is the same empty answer for
|
|
69
|
+
* the same reason: an extending model whose map does not parse was never read.
|
|
70
|
+
*/
|
|
71
|
+
async function attestedFrom(docsDir, paths, service, known) {
|
|
72
|
+
// The memo directly rather than through `core/fleet-context.ts`, and the
|
|
73
|
+
// reason is the package graph: this module already depends on `core/c4/`, and
|
|
74
|
+
// reaching for the read index would add a `brief -> core-root` edge for one
|
|
75
|
+
// read in a command that grades a single service. The three things the memo
|
|
76
|
+
// cannot know for itself are supplied here — the enumeration this function
|
|
77
|
+
// was already handed, the per-file loader, and a counter nobody is keeping.
|
|
78
|
+
const models = new ServiceModels({
|
|
79
|
+
onProjectLoad: () => {
|
|
80
|
+
// `adopt` reports no read counters, so there is nothing to increment. The
|
|
81
|
+
// hook is the memo's way of letting a caller own its own statistics, and
|
|
82
|
+
// an empty one is that answer, not a forgotten line.
|
|
83
|
+
},
|
|
84
|
+
known: async () => known,
|
|
85
|
+
standalone: (path) => loadFile(path),
|
|
86
|
+
});
|
|
87
|
+
const model = await models.model(docsDir, paths);
|
|
88
|
+
if (model.mapUnreadable || model.doc.errors.length > 0)
|
|
89
|
+
return [];
|
|
90
|
+
return attestedCalls(model.doc, service, known);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* What the living fleet map already says about this service — the half of the
|
|
94
|
+
* brief that stops an agent inventing a parallel model: the fleet has already
|
|
95
|
+
* drawn some of these boxes and edges, and the baseline has to attach to them.
|
|
96
|
+
* An `architecture/` project that does not parse yields `modelled: null` —
|
|
97
|
+
* "nothing models it" would be a claim about documents nobody could read; the
|
|
98
|
+
* landscape FILE parsing on its own does not rescue it, because the map the
|
|
99
|
+
* renderer and `validate --all` see is the whole project. `paths` is the
|
|
100
|
+
* service's own artifact set, located by the caller through the enumeration
|
|
101
|
+
* (never joined at `services/<id>/`): the model is opened in exactly one state —
|
|
102
|
+
* an element with no edge — to name the calls the map most likely owes. The
|
|
103
|
+
* WHOLE record travels rather than the model path alone, because the model is
|
|
104
|
+
* now read through its own shape and an extending one is read as a project of
|
|
105
|
+
* the service's directory plus the map. It is required rather than optional
|
|
106
|
+
* because the edgeless instruction states what that file attests, and a caller
|
|
107
|
+
* that omitted it would get a brief asserting a fact about a document loam never
|
|
108
|
+
* opened.
|
|
98
109
|
*/
|
|
99
|
-
export async function landscapeContext(docsDir, service) {
|
|
110
|
+
export async function landscapeContext(docsDir, service, paths) {
|
|
100
111
|
const path = landscapePath(docsDir);
|
|
101
112
|
// The service's own directory, spelled from the enumeration rather than joined
|
|
102
113
|
// at the root: this text is handed to an AGENT that will go and edit files, and
|
|
103
114
|
// a filed service named at `services/<id>/` sends it to create a second
|
|
104
115
|
// directory beside the one that already exists.
|
|
105
116
|
const servicePath = await serviceTreePathOf(docsDir, service);
|
|
117
|
+
const bare = { service, expects: [], servicePath, attested: [], foreignTags: [] };
|
|
106
118
|
const empty = {
|
|
107
119
|
present: existsSync(path),
|
|
108
120
|
parses: false,
|
|
121
|
+
callersKnown: false,
|
|
109
122
|
modelled: null,
|
|
110
123
|
elements: [],
|
|
111
124
|
inbound: [],
|
|
112
125
|
outbound: [],
|
|
113
126
|
expects: [],
|
|
127
|
+
touched: null,
|
|
128
|
+
attested: [],
|
|
114
129
|
instruction: null,
|
|
115
130
|
};
|
|
116
131
|
if (!empty.present)
|
|
117
|
-
return { ...empty, modelled: false, instruction: instructionFor(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
return { ...empty, modelled: false, instruction: instructionFor({ ...bare, state: "absent" }) };
|
|
133
|
+
// The `architecture/` PROJECT, through the one loader every other reader
|
|
134
|
+
// shares — never `loadFile(path)`. A fleet spreads its model over more than
|
|
135
|
+
// the landscape (a second `model { }` block in a sibling, a use case, a
|
|
136
|
+
// palette), and `landscape.service-isolated` grades `touched` over exactly
|
|
137
|
+
// this set: reading the landscape alone made `adopt` report `touched: false`,
|
|
138
|
+
// the attested calls and an edit target for a service the fleet run on the
|
|
139
|
+
// same tree called matched, with nothing to tell an agent which was right.
|
|
140
|
+
const known = await knownServices(docsDir);
|
|
141
|
+
const land = await loadArchitecture(docsDir);
|
|
142
|
+
if (land.errors.length > 0) {
|
|
143
|
+
// Since W4 this arm answers for the whole `architecture/` PROJECT, so the
|
|
144
|
+
// document that broke is very often a SIBLING beside a landscape that parses
|
|
145
|
+
// perfectly — and two things followed from not saying so:
|
|
146
|
+
// - the instruction told an agent to go and fix parse errors in
|
|
147
|
+
// `architecture/landscape.likec4`, which has none, contradicting
|
|
148
|
+
// `validate --all` run one second later on the same tree;
|
|
149
|
+
// - the API question — who CALLS this service — was answered "unknown"
|
|
150
|
+
// off a landscape file loam can read, so a broken palette flipped
|
|
151
|
+
// `openapi.yaml` to required for a browser UI the map proves nobody
|
|
152
|
+
// calls, while `list` and `context` (which read the FILE, see
|
|
153
|
+
// core/pack/living.ts) answered the opposite about the same tree.
|
|
154
|
+
// Nothing else is rescued: `modelled`, `elements` and `touched` stay
|
|
155
|
+
// unknown, because those are claims about the project the renderer loads.
|
|
156
|
+
const expects = await expectsFromFile(path, service, known);
|
|
157
|
+
return {
|
|
158
|
+
...empty,
|
|
159
|
+
callersKnown: expects !== null,
|
|
160
|
+
expects: expects ?? [],
|
|
161
|
+
instruction: instructionFor({
|
|
162
|
+
...bare,
|
|
163
|
+
expects: expects ?? [],
|
|
164
|
+
state: "unparseable",
|
|
165
|
+
broken: brokenDocuments(docsDir, land),
|
|
166
|
+
}),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// One resolver for the whole scan, built with the real service ids, and
|
|
170
|
+
// built BEFORE the element filter: "an element resolves to this service" is
|
|
171
|
+
// the resolver's answer — a nearest-ancestor binding wins over a descendant's
|
|
172
|
+
// title — and it is the answer `validate --all` grades `service-unmodelled`
|
|
173
|
+
// on and `loam context` prints `modelled` from. This used to ask each
|
|
174
|
+
// element for its OWN binding-or-title instead, so a container titled
|
|
175
|
+
// 'payment-service' inside an element bound to order-service read as
|
|
176
|
+
// payment-service's box, and the edgeless arm told an agent "do not add a
|
|
177
|
+
// second element" for a service the fleet run reported unmodelled (error).
|
|
178
|
+
// The same resolver serves the edges: one drawn into a modelled container
|
|
179
|
+
// (`paymentService.api`) is an edge into the service, and `expects` — which
|
|
180
|
+
// decides whether this service owes an OpenAPI contract at all — has to see
|
|
181
|
+
// it. Building it once also stops the per-edge rebuild the old
|
|
182
|
+
// `serviceOf(land.elements, id)` call did.
|
|
183
|
+
const svcOf = serviceResolver(land.elements, known);
|
|
184
|
+
const mine = (id) => svcOf(id) === service;
|
|
185
|
+
// The service-LEVEL elements only: one that resolves to the service while
|
|
186
|
+
// an ancestor does too is a container of it, and listing it would show a
|
|
187
|
+
// second box the map does not have.
|
|
188
|
+
const elements = land.elements
|
|
189
|
+
.filter((e) => mine(e.id) && !properAncestorIds(e.id).some(mine))
|
|
190
|
+
.map((e) => ({
|
|
123
191
|
id: e.id,
|
|
124
192
|
title: e.title,
|
|
125
193
|
kind: e.kind,
|
|
126
194
|
tags: e.tags,
|
|
127
195
|
bound: e.service !== undefined,
|
|
128
196
|
}));
|
|
129
|
-
// One resolver for the whole scan, built with the real service ids: an edge
|
|
130
|
-
// drawn into a modelled container (`paymentService.api`) is an edge into the
|
|
131
|
-
// service, and `expects` — which now decides whether this service owes an
|
|
132
|
-
// OpenAPI contract at all — has to see it. Building it once also stops the
|
|
133
|
-
// per-edge rebuild the old `serviceOf(land.elements, id)` call did.
|
|
134
|
-
const svcOf = serviceResolver(land.elements, await knownServices(docsDir));
|
|
135
197
|
const inbound = [];
|
|
136
198
|
const outbound = [];
|
|
199
|
+
// An edge whose BOTH endpoints resolve here is the service's own internal
|
|
200
|
+
// wiring: filed as inbound it made the service its own caller (verification
|
|
201
|
+
// 2026-09-04). It still TOUCHES the service, so `touched` below reads the
|
|
202
|
+
// relationships rather than these lists — the isolation check counts either
|
|
203
|
+
// endpoint, and the two predicates must not disagree.
|
|
204
|
+
const self = (r) => mine(r.source) && mine(r.target);
|
|
137
205
|
for (const r of land.relationships) {
|
|
206
|
+
if (self(r))
|
|
207
|
+
continue;
|
|
138
208
|
const edge = { op: r.op ?? null, title: r.title ?? null };
|
|
139
|
-
if (
|
|
209
|
+
if (mine(r.target))
|
|
140
210
|
inbound.push({ from: svcOf(r.source), ...edge });
|
|
141
|
-
else if (
|
|
211
|
+
else if (mine(r.source))
|
|
142
212
|
outbound.push({ to: svcOf(r.target), ...edge });
|
|
143
213
|
}
|
|
144
214
|
const expects = [...new Set(inbound.map((e) => e.op).filter((op) => op !== null))];
|
|
215
|
+
const modelled = land.elements.some((e) => mine(e.id));
|
|
216
|
+
const touched = modelled ? land.relationships.some((r) => mine(r.source) || mine(r.target)) : null;
|
|
217
|
+
const attested = touched === false && existsSync(paths.model) ? await attestedFrom(docsDir, paths, service, known) : [];
|
|
218
|
+
const req = {
|
|
219
|
+
service,
|
|
220
|
+
expects,
|
|
221
|
+
servicePath,
|
|
222
|
+
attested,
|
|
223
|
+
foreignTags: foreignTagsOf(elements),
|
|
224
|
+
elementId: elements[0]?.id,
|
|
225
|
+
// The one graded tag that changes what the brief may PROMISE: an `#external`
|
|
226
|
+
// element is never `landscape.service-isolated`'s subject. Off the same
|
|
227
|
+
// element `elementId` names, never the union.
|
|
228
|
+
external: elements[0]?.tags.includes(EXTERNAL_TAG) ?? false,
|
|
229
|
+
};
|
|
230
|
+
const instruction = !modelled
|
|
231
|
+
? instructionFor({ ...req, state: "unmodelled" })
|
|
232
|
+
: touched === false
|
|
233
|
+
? instructionFor({ ...req, state: "edgeless" })
|
|
234
|
+
: null;
|
|
145
235
|
return {
|
|
146
236
|
present: true,
|
|
147
237
|
parses: true,
|
|
148
|
-
|
|
238
|
+
callersKnown: true,
|
|
239
|
+
modelled,
|
|
149
240
|
elements,
|
|
150
241
|
inbound,
|
|
151
242
|
outbound,
|
|
152
243
|
expects,
|
|
153
|
-
|
|
244
|
+
touched,
|
|
245
|
+
attested,
|
|
246
|
+
instruction,
|
|
154
247
|
};
|
|
155
248
|
}
|
|
156
249
|
/**
|
|
157
|
-
* The fleet
|
|
250
|
+
* The operations the fleet already calls on this service, read off the landscape
|
|
251
|
+
* FILE alone — `null` when that file cannot be read either.
|
|
158
252
|
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* this service"
|
|
253
|
+
* The fallback for an `architecture/` project that does not parse, and the
|
|
254
|
+
* narrowest one that can be justified: it answers ONE question, the one `loam
|
|
255
|
+
* list` and `loam context` answer off the same bytes (`landscapeEvidence`,
|
|
256
|
+
* core/vocabulary/maturity.ts). "Nothing calls this service" and "loam could not
|
|
257
|
+
* tell" are different answers, and only the first may switch an API contract
|
|
258
|
+
* off, which is why the empty list and the unreadable file are told apart here
|
|
259
|
+
* rather than collapsed to `[]`.
|
|
163
260
|
*/
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const owed = expects.length > 0
|
|
176
|
-
? ` The fleet already calls ${expects.map((o) => `'${o}'`).join(", ")} on this service, so those edges exist; your element is what they will resolve to.`
|
|
177
|
-
: "";
|
|
178
|
-
if (state === "absent") {
|
|
179
|
-
return (`architecture/landscape.likec4 does not exist yet. Create it with a \`specification { ... }\` block ` +
|
|
180
|
-
`declaring the kinds you use and a \`model { ... }\` block containing ${bind} ` +
|
|
181
|
-
`(conventionally \`${id} = softwareSystem '${service}'\`), plus one edge per cross-service call, each ` +
|
|
182
|
-
`carrying \`metadata { op '<operationId>' }\`. ${consequence}${owed}`);
|
|
261
|
+
async function expectsFromFile(path, service, known) {
|
|
262
|
+
try {
|
|
263
|
+
const doc = await loadFile(path);
|
|
264
|
+
if (doc.errors.length > 0)
|
|
265
|
+
return null;
|
|
266
|
+
const svcOf = serviceResolver(doc.elements, known);
|
|
267
|
+
const ops = doc.relationships
|
|
268
|
+
.filter((r) => svcOf(r.target) === service)
|
|
269
|
+
.map((r) => r.op)
|
|
270
|
+
.filter((op) => op !== undefined);
|
|
271
|
+
return [...new Set(ops)];
|
|
183
272
|
}
|
|
184
|
-
|
|
185
|
-
return
|
|
186
|
-
`about what it already models. Fix the parse errors first — they blind every cross-service check at once — ` +
|
|
187
|
-
`then make sure it holds ${bind}. ${consequence}`);
|
|
273
|
+
catch {
|
|
274
|
+
return null;
|
|
188
275
|
}
|
|
189
|
-
return (`Nothing in architecture/landscape.likec4 resolves to ${servicePath}/. ADD to that file — do not rewrite ` +
|
|
190
|
-
`it, every other service's map is in there — ${bind}, conventionally ` +
|
|
191
|
-
`\`${id} = softwareSystem '${service}'\`, inside the existing \`model { ... }\` block, and draw each ` +
|
|
192
|
-
`cross-service call as an edge carrying \`metadata { op '<operationId>' }\`. ${consequence}${owed}`);
|
|
193
276
|
}
|
|
194
277
|
//# sourceMappingURL=landscape.js.map
|