@ybotok/loam 0.2.0-alpha.2 → 0.2.0-alpha.5
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 +124 -0
- package/MIGRATING-from-OpenSpec.md +4 -1
- package/README.md +11 -10
- package/ROADMAP.md +103 -17
- package/SCHEMA.md +86 -7
- package/dist/commands/archive/run.js +46 -2
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/seed/plan.js +1 -1
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/subsystem/sync.js +111 -20
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +20 -1
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +23 -11
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +6 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/views/ids.js +6 -1
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/projects.js +90 -0
- package/dist/commands/validate/fleet/views/projects.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +15 -39
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +3 -1
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +18 -2
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md.js +4 -0
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +13 -3
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +1 -0
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/brief/checks.js +9 -0
- package/dist/core/brief/checks.js.map +1 -1
- package/dist/core/brief/targets.js +7 -4
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +1 -1
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/docs.js +11 -7
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/doctor.js +17 -6
- package/dist/core/doctor/doctor.js.map +1 -1
- package/dist/core/explain/families.js +1 -1
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/repo/paths.js +39 -1
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/tree/render/body.js +219 -0
- package/dist/core/repo/tree/render/body.js.map +1 -0
- package/dist/core/repo/tree/render/projects.js +124 -0
- package/dist/core/repo/tree/render/projects.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +26 -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/{views.js → render/views.js} +38 -34
- package/dist/core/repo/tree/render/views.js.map +1 -0
- package/examples/README.md +6 -1
- package/examples/docs/architecture/subsystems.likec4 +4 -0
- package/examples/docs/services/checkout-web/likec4.config.json +4 -0
- package/examples/docs/services/order-service/likec4.config.json +4 -0
- package/examples/docs/services/payment-service/likec4.config.json +4 -0
- package/examples/docs/services/payment-service/model.likec4 +4 -2
- package/examples/docs/services/platform/identity-service/likec4.config.json +4 -0
- package/examples/docs/services/platform/notification-service/likec4.config.json +4 -0
- package/package.json +1 -1
- package/dist/core/repo/tree/views.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,130 @@ case for a change — the alternative that was rejected, the defect it came from
|
|
|
10
10
|
generalises — lives where it is maintained: [SCHEMA.md](SCHEMA.md) for the rule,
|
|
11
11
|
[ROADMAP.md](ROADMAP.md) for the priority and its exit criteria, and the commit that landed it._
|
|
12
12
|
|
|
13
|
+
## [0.2.0-alpha.5] - 2026-09-03
|
|
14
|
+
|
|
15
|
+
An adopted service's model renders from the docs root. Still a prerelease on the `alpha` line:
|
|
16
|
+
`npm i @ybotok/loam@alpha` installs it. Nothing below is breaking. What a repo with adopted services
|
|
17
|
+
NOTICES: one `service.likec4-config-missing` warning per service on its first `validate --all`
|
|
18
|
+
until one `loam subsystem sync`, and from that sync on `--project` for `likec4 validate` at the
|
|
19
|
+
docs root.
|
|
20
|
+
|
|
21
|
+
### Added — an adopted service's model renders from the docs root
|
|
22
|
+
|
|
23
|
+
The root `likec4.config.json` excludes `services/**`, and has to; what that cost was never stated:
|
|
24
|
+
an adopted service's `model.likec4` — the artifact `loam adopt` spends the most effort on — was a
|
|
25
|
+
box on the fleet map with nothing renderable inside it in the renderer opened at the docs root, and
|
|
26
|
+
the only documented way to see its containers was one `npx likec4 start services/<id>` per service.
|
|
27
|
+
One problem report; one file per service closes it.
|
|
28
|
+
|
|
29
|
+
- `loam subsystem sync` now writes `services/<…>/<svc>/likec4.config.json` beside every service
|
|
30
|
+
`model.likec4` that has none, whenever the root `likec4.config.json` exists — **create-only**: a
|
|
31
|
+
file that exists is yours and is never compared, rewritten or removed. Measured at the pinned
|
|
32
|
+
likec4, a project file inside a service directory registers the model as a project of its own
|
|
33
|
+
regardless of the root's exclusion, so `npx likec4 start` at the docs root shows `fleet` plus one
|
|
34
|
+
project per service. Without the root file nothing is written and the text says `loam doctor`
|
|
35
|
+
prints it. `subsystem sync --json` carries an additive `projects` key, `{ root, created, current }`
|
|
36
|
+
— whether the root file exists, the repo-relative paths written this run (sorted by id), and how
|
|
37
|
+
many model-bearing services already had one. `action` still describes
|
|
38
|
+
`architecture/subsystems.likec4` alone.
|
|
39
|
+
- `loam validate --all` gains one warning per service, `service.likec4-config-missing`, on the
|
|
40
|
+
`landscape` target with the service's tree path as its `scope`: `model.likec4` exists and the
|
|
41
|
+
project file does not. Never an error, never gating, never in `--service`, `status` or `doctor`;
|
|
42
|
+
the fix is `loam subsystem sync`, never a hand-written file. `doctor.likec4-config-missing`'s fix
|
|
43
|
+
text now hands over to `sync` once the root file is written.
|
|
44
|
+
- The project name: the id with every `.` folded to `-` (LikeC4's name grammar refuses the byte),
|
|
45
|
+
prefixed `service-` when that would equal the root project's `fleet` or LikeC4's reserved
|
|
46
|
+
`default`; `title` is the id verbatim, and the title is what the renderer's picker shows. Not
|
|
47
|
+
injective — an id spelled billing.core beside one spelled billing-core, or `service-fleet` beside
|
|
48
|
+
`fleet` — and LikeC4 suffixes the later one `-1`; disclosed, never graded.
|
|
49
|
+
|
|
50
|
+
What a repo NOTICES on upgrade: one warning per adopted service on the first `validate --all` until
|
|
51
|
+
one `loam subsystem sync` — under `--strict`, CI is red until then; after that `sync`, N untracked
|
|
52
|
+
files (`sync` does not `git add` them) and `move-uncommitted` on the next `loam subsystem move` of
|
|
53
|
+
such a service until they are committed; from the first `sync` on, `likec4 validate` at the docs
|
|
54
|
+
root needs `--project <name>` (measured: with more than one project it refuses bare, while
|
|
55
|
+
`likec4 build` and `likec4 export` take every project and need no flag); and the renderer's project
|
|
56
|
+
list grows by one per adopted service, beside `fleet`. No command, flag, exit code or envelope key
|
|
57
|
+
changes; `projects` is additive.
|
|
58
|
+
|
|
59
|
+
## [0.2.0-alpha.4] - 2026-09-03
|
|
60
|
+
|
|
61
|
+
The view title becomes the subsystem's path. `0.2.0-alpha.3` was tagged and **never published** — it
|
|
62
|
+
was withdrawn before its publish step for a reason unrelated to the code — so this is the release
|
|
63
|
+
that carries both it and the entry below, and `npm i @ybotok/loam@alpha` moves from `0.2.0-alpha.2`
|
|
64
|
+
straight to this one. A repo upgrading from either reports one `subsystem.views-stale` until one
|
|
65
|
+
`loam subsystem sync`.
|
|
66
|
+
|
|
67
|
+
### Changed — a generated subsystem view is titled by its path
|
|
68
|
+
|
|
69
|
+
`0.2.0-alpha.3` gave each view the subsystem's own label. It now carries the whole path — every
|
|
70
|
+
marked ancestor's label and its own, joined with ` / `, so `services/platform/order-flow` reads
|
|
71
|
+
`Platform / Order flow`. Measured against the pinned likec4: the renderer reads that as a folder
|
|
72
|
+
path, so the view list mirrors the directory tree a human already navigates — one folder per
|
|
73
|
+
ancestor, the leaf's own label as the title — and two subsystems whose labels are equal are told
|
|
74
|
+
apart by their parents instead of appearing twice under one name. The box a view draws keeps the
|
|
75
|
+
subsystem's own label, unprefixed.
|
|
76
|
+
|
|
77
|
+
Because loam is now the one composing with `/`, it owns what a `/` inside an authored label would
|
|
78
|
+
do: measured, LikeC4 would show only the last segment and add a folder level the tree does not have,
|
|
79
|
+
so a slash in a marker's `title` becomes `∕` (U+2215) and the whole authored label stays visible as
|
|
80
|
+
one folder name. The alternative — a fixed `loam subsystems /` prefix — bought the same folder
|
|
81
|
+
without the churn and put loam's own name in a reader's UI; the tree's own words won.
|
|
82
|
+
|
|
83
|
+
Two costs, both real: the generated bytes move again, so a repo upgrading from `0.2.0-alpha.3`
|
|
84
|
+
reports one `subsystem.views-stale` until one `loam subsystem sync`; and renaming a PARENT's title
|
|
85
|
+
now rewrites the bytes of every view beneath it.
|
|
86
|
+
|
|
87
|
+
## [0.2.0-alpha.3] - 2026-09-03
|
|
88
|
+
|
|
89
|
+
The generated subsystem views, made readable — and the defect that hunt found underneath them.
|
|
90
|
+
Still a prerelease on the `alpha` line: `npm i @ybotok/loam@alpha` installs it. Nothing below is
|
|
91
|
+
breaking, and one entry is a change every repo NOTICES: the generated file's bytes move, so the
|
|
92
|
+
first `loam validate --all` after upgrading asks for one `loam subsystem sync`.
|
|
93
|
+
|
|
94
|
+
### Fixed — a `subsystem.views-stale` no command could clear
|
|
95
|
+
|
|
96
|
+
`loam validate --all` grades `architecture/subsystems.likec4` against the whole `architecture/`
|
|
97
|
+
project — the landscape merged with every `architecture/usecases/*.likec4` — while `loam subsystem
|
|
98
|
+
sync` and the write verbs rendered it from `landscape.likec4` alone. One use-case document declaring
|
|
99
|
+
an element bound to a filed service was therefore enough to make `sync` answer `current` while
|
|
100
|
+
`validate --all` exited 1 with `subsystem.views-stale`, whose message names `sync` as the repair.
|
|
101
|
+
Reproduced on `examples/docs`; the two now read the same documents. No code, flag or payload key
|
|
102
|
+
changes.
|
|
103
|
+
|
|
104
|
+
### Changed — the generated subsystem views carry a label and a boundary
|
|
105
|
+
|
|
106
|
+
`architecture/subsystems.likec4` exists only to be rendered, and in the renderer its views were the
|
|
107
|
+
least readable objects in a docs repo: no title, so LikeC4 showed the hex-escaped id
|
|
108
|
+
(`subsystem_platform__order_2dflow`), and leaf includes only, so a fleet drawn as ordinary
|
|
109
|
+
grouped C4 rendered flat. Every view now carries `title` (the marker's, or the directory name) and
|
|
110
|
+
`description` when the marker authors one, and draws a boundary: the model's own containing element
|
|
111
|
+
when one holds exactly the subsystem's members, a `group '<title>'` mirroring the directory subtree
|
|
112
|
+
when the landscape nests nothing, and — when the members are nested but no element owns exactly them
|
|
113
|
+
— no box plus one comment line, `// model: no boundary — …`, which is loam's whole answer to a tree
|
|
114
|
+
and a map that disagree. Nothing grades that comment: placement still carries no policy.
|
|
115
|
+
|
|
116
|
+
Marker text is normalized to one line and escaped before it is written (measured: a raw `'` in a
|
|
117
|
+
title is two parse errors and blanks the fleet's whole map in the renderer; `Ops' } view evil {` is
|
|
118
|
+
zero errors and injects a view). [SCHEMA.md](SCHEMA.md) states the rules.
|
|
119
|
+
|
|
120
|
+
What a repo notices, and what it costs:
|
|
121
|
+
|
|
122
|
+
- the first `loam validate --all` after upgrading reports exactly one `subsystem.views-stale` error
|
|
123
|
+
(exit 1) until one `loam subsystem sync` runs. `sync` does not `git add`, so the repair leaves one
|
|
124
|
+
unstaged one-file diff to commit;
|
|
125
|
+
- the boundary depends on which OTHER services sit under an element, so an unrelated adoption or
|
|
126
|
+
archive can flip a view from boxed to flat and restale the file. `loam archive` now withholds its
|
|
127
|
+
"living spec + landscape are now complete + current" line, prints one warning naming
|
|
128
|
+
`loam subsystem sync`, and adds `subsystemViewsStale: true` to its `--json` payload when it left
|
|
129
|
+
the file behind;
|
|
130
|
+
- `subsystem.views-stale` and `subsystem.view-id-collision` now carry `locations`: the generated
|
|
131
|
+
file (or `architecture/` when it does not exist yet) and the claimant document respectively. Both
|
|
132
|
+
used to fall back to the landscape, sending an editor to a file with nothing to fix in it.
|
|
133
|
+
|
|
134
|
+
No command, flag, exit code, envelope key or stable code string changes; `subsystem.views-stale` is
|
|
135
|
+
not in the `IssueCode` union and carries no `gates` key, so `loam archive` remains unaffected by it.
|
|
136
|
+
|
|
13
137
|
## [0.2.0-alpha.2] - 2026-09-02
|
|
14
138
|
|
|
15
139
|
The deployment axis, and the two defects the axis surfaced on its way in. Still a prerelease on the
|
|
@@ -312,7 +312,10 @@ services, loam frontmatter, Operations/Covers links and feature identity still r
|
|
|
312
312
|
specs intentionally remain `status: draft`.
|
|
313
313
|
- `Operations:` links to provider OpenAPI `operationId`s, and `Covers:` links where architecture
|
|
314
314
|
requirements are created.
|
|
315
|
-
- `architecture/landscape.likec4`, service `model.likec4`, and explicit fleet relationships
|
|
315
|
+
- `architecture/landscape.likec4`, service `model.likec4`, and explicit fleet relationships — then
|
|
316
|
+
`loam subsystem sync`, which writes each model's `likec4.config.json` (the staged repo already
|
|
317
|
+
carries the root one) so the models render beside the map; `validate --all` warns
|
|
318
|
+
`service.likec4-config-missing` for any model still without one.
|
|
316
319
|
- Service OpenAPI contracts and provider-before-consumer sequencing where inbound edges already name
|
|
317
320
|
operations.
|
|
318
321
|
- LikeC4 and OpenAPI deltas for the already mapped active feature ids (`FEAT-12`, `BUG-42`, and so
|
package/README.md
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|

|
|
6
6
|

|
|
7
7
|
[](https://github.com/ybotok/loam/actions/workflows/ci.yml)
|
|
8
|
-
[](https://www.npmjs.com/package/@ybotok/loam)
|
|
8
|
+
[](https://www.npmjs.com/package/@ybotok/loam)
|
|
9
9
|
|
|
10
|
-
> **Pre-release: `0.2.0-alpha.
|
|
10
|
+
> **Pre-release: `0.2.0-alpha.5`**, the fourth published release of the 0.2 line and the newest published
|
|
11
11
|
> version of [`@ybotok/loam`](https://www.npmjs.com/package/@ybotok/loam). It publishes under the
|
|
12
|
-
> `alpha` npm dist-tag: `npm i @ybotok/loam@alpha`, or the
|
|
13
|
-
> line. `latest` is moved onto a release by hand once it has
|
|
14
|
-
> `npm i @ybotok/loam`
|
|
12
|
+
> `alpha` npm dist-tag — which is what the badge above reads: `npm i @ybotok/loam@alpha`, or the
|
|
13
|
+
> exact version, always installs this line. `latest` is moved onto a release by hand once it has
|
|
14
|
+
> published, so a plain `npm i @ybotok/loam` resolves to an earlier release until it is moved. See
|
|
15
|
+
> [Status](#status).
|
|
15
16
|
|
|
16
17
|
`loam` keeps an authored C4 model, requirements, API and event contracts, permissions, domain
|
|
17
18
|
vocabulary and verification evidence in explicit agreement as a system changes. The same lifecycle
|
|
@@ -486,7 +487,7 @@ always preserved byte for byte, with the entry printed for manual merge.
|
|
|
486
487
|
maps, what is lost, what must be added.
|
|
487
488
|
- [ROADMAP.md](ROADMAP.md) — the evidence-backed improvement plan, priorities, non-goals and exit
|
|
488
489
|
criteria.
|
|
489
|
-
- [CHANGELOG.md](CHANGELOG.md) — released beta.1–alpha.
|
|
490
|
+
- [CHANGELOG.md](CHANGELOG.md) — released beta.1–alpha.5 plus the changes on `main` under
|
|
490
491
|
`[Unreleased]`.
|
|
491
492
|
- [docs/BENCHMARKS.md](https://github.com/ybotok/loam/blob/main/docs/BENCHMARKS.md) — what a
|
|
492
493
|
fleet-sized run costs, with the method, the machine and the committed runs behind every number.
|
|
@@ -520,14 +521,14 @@ component baselines, the `architecture/permissions.yaml` authorization vocabular
|
|
|
520
521
|
authored-capability axes with the `Realizes:` join, use cases graded as `dynamic view`s, the
|
|
521
522
|
`glossary/<term>.md` domain vocabulary checked through the links that cite it, and the
|
|
522
523
|
`architecture/obligations.yaml` architectural obligations checked through `#obl-` tags and
|
|
523
|
-
`Covers:`. Known limits, each with its owner in [ROADMAP.md](ROADMAP.md): the
|
|
524
|
-
|
|
525
|
-
|
|
524
|
+
`Covers:`. Known limits, each with its owner in [ROADMAP.md](ROADMAP.md): the scheduled CI
|
|
525
|
+
`stability` job has still to be observed green. Speculative `render`, health composition and UI
|
|
526
|
+
generation come later. Behind that status stand **159 test files** (counted 2026-09-02): the count is graded
|
|
526
527
|
against a live readdir by `test/docs-facts.test.ts`, so this sentence fails the suite the moment it
|
|
527
528
|
trails the tree.
|
|
528
529
|
|
|
529
530
|
The package is published as [`@ybotok/loam`](https://www.npmjs.com/package/@ybotok/loam), currently
|
|
530
|
-
`0.2.0-alpha.
|
|
531
|
+
`0.2.0-alpha.5`: the unscoped `loam` name is taken by an unrelated GDAL wrapper, so the package ships
|
|
531
532
|
under the maintainer's own npm user scope. Releases are tag-driven and maintainer-only
|
|
532
533
|
([CONTRIBUTING.md](CONTRIBUTING.md)); the version this line names is pinned to `package.json` by a
|
|
533
534
|
drift test, so it cannot silently trail a release.
|
package/ROADMAP.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Roadmap
|
|
2
2
|
|
|
3
|
-
_Assessed 2026-09-
|
|
3
|
+
_Assessed 2026-09-03._
|
|
4
4
|
|
|
5
5
|
This is loam's canonical improvement sequence. It records priorities and exit criteria, not delivery
|
|
6
6
|
dates. An item is complete only when its exit criteria are supported by repeatable evidence; moving
|
|
@@ -18,27 +18,30 @@ acceptance tests, and implementation evidence remain ordinary files in repositor
|
|
|
18
18
|
state inside a service. The CLI is small enough to audit, and every writer — not only archive — now
|
|
19
19
|
commits through a locked, journaled transaction that a crash cannot leave half-applied.
|
|
20
20
|
|
|
21
|
-
The tree contains **
|
|
21
|
+
The tree contains **429 TypeScript modules in 130 source packages**, with an acyclic package graph
|
|
22
22
|
checked by
|
|
23
23
|
[scripts/package-graph.mjs](https://github.com/ybotok/loam/blob/main/scripts/package-graph.mjs). The
|
|
24
24
|
CLI exposes **29 commands** from [src/cli.ts](https://github.com/ybotok/loam/blob/main/src/cli.ts),
|
|
25
|
-
and the suite stands at **
|
|
25
|
+
and the suite stands at **159 test files**. Those four counts are deliberately stated OUTSIDE the
|
|
26
26
|
dated snapshot below: each derives from the tree in one readdir, so
|
|
27
27
|
[test/docs-facts.test.ts](https://github.com/ybotok/loam/blob/main/test/docs-facts.test.ts) grades
|
|
28
28
|
them live and this paragraph cannot quietly trail the code the way its predecessor did.
|
|
29
29
|
|
|
30
|
-
_Measured 2026-09-
|
|
30
|
+
_Measured 2026-09-03 at `0.2.0-alpha.5`, and dated because neither number has a cheap derivation:_
|
|
31
31
|
lint, typecheck, `npm run arch:check` and `npm run meta:check` green; `npm test` passing
|
|
32
|
-
**3,
|
|
33
|
-
coverage gate passing with **92.
|
|
32
|
+
**3,434/3,434 tests**, with two `skipIf(asRoot)` cases the root gate container cannot run; the
|
|
33
|
+
coverage gate passing with **92.30% statements, 84.11% branches, 96.64% functions, and 94.24%
|
|
34
34
|
lines** against its thresholds of 91 / 82 / 95 / 93; and `npm run release:check` plus
|
|
35
35
|
`npm run test:package` green against the real tarball.
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
Most of that qualification is now closed by observation rather than by code. Measured 2026-09-03 on
|
|
38
|
+
pushed commits: `ci` at `1de7c97` green — `test` on Node 22.22.3 and 24, and the installed-package
|
|
39
|
+
`package` job on ubuntu (both Node lines) and windows — and the `release` workflow on tag
|
|
40
|
+
`v0.2.0-alpha.4` green end to end, `release-gate` (lint, typecheck, coverage, the packed candidate,
|
|
41
|
+
the smoke of that exact candidate, `release:check`) followed by `publish`, which put the tarball on
|
|
42
|
+
npm under the `alpha` dist-tag. What is left is one job: `stability` is SKIPPED on a push — it runs
|
|
43
|
+
on the weekly schedule and on dispatch — so it is still release evidence to collect, even though the
|
|
44
|
+
gate-stress proof passes locally. The runner class recorded on 2026-08-18 is
|
|
42
45
|
still the reading to apply to a red of that shape: a restricted filesystem sandbox that denies `tsx`
|
|
43
46
|
its local IPC socket fails only the
|
|
44
47
|
[test/cli-entry.test.ts](https://github.com/ybotok/loam/blob/main/test/cli-entry.test.ts) cases with
|
|
@@ -103,12 +106,15 @@ has landed (see [Recently landed](#recently-landed)) — the authored business a
|
|
|
103
106
|
axis, and as of 2026-09-02 the deployment axis, which is the first item this file ever wrote
|
|
104
107
|
directly into this section rather than promoting from [Later](#later--promote-only-from-evidence).
|
|
105
108
|
|
|
106
|
-
### Release evidence
|
|
109
|
+
### Release evidence — one job left
|
|
107
110
|
|
|
108
|
-
What is left is the one thing this file cannot close by writing code, and it stays
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
What is left is the one thing this file cannot close by writing code, and it stays where it is
|
|
112
|
+
rather than being promoted for the shape of it. The installed-package half closed on 2026-09-03:
|
|
113
|
+
the `package` job ran green on a pushed commit across three runner/Node combinations, and the whole
|
|
114
|
+
publication path ran green on tag `v0.2.0-alpha.4`. The CI `stability` job has still to be OBSERVED
|
|
115
|
+
green — it is skipped on a push and runs on the weekly schedule — so it is still release evidence to
|
|
116
|
+
collect, not an improvement to make. `workflow_dispatch` is the way to collect it deliberately
|
|
117
|
+
rather than waiting for Monday.
|
|
112
118
|
|
|
113
119
|
An item returns to this section from [Later](#later--promote-only-from-evidence) when fleet evidence
|
|
114
120
|
names its operator, repeated task, failure mode, frequency, current workaround and measurable
|
|
@@ -125,6 +131,72 @@ changes, exit criteria, and what each review surfaced — are in this file's his
|
|
|
125
131
|
working tree and not committed, and the range goes in with the commit rather than being guessed at
|
|
126
132
|
here.
|
|
127
133
|
|
|
134
|
+
#### The service model, renderable from the fleet root
|
|
135
|
+
|
|
136
|
+
**Landed 2026-09-03**, from one problem report. The root `likec4.config.json` that
|
|
137
|
+
`loam init --create` writes excludes `services/**` on purpose — every `model.likec4` declares its
|
|
138
|
+
own `specification` block, and a renderer merging the tree calls each one a duplicate — and the
|
|
139
|
+
cost of that exclusion had gone unstated for a release: the model `loam adopt` spends the most
|
|
140
|
+
effort on was a box with nothing inside it in the renderer opened at the docs root, and the only
|
|
141
|
+
documented path to its containers was one renderer invocation per service directory. The report's
|
|
142
|
+
hand-written workaround, one project file per service, is what this entry makes loam's.
|
|
143
|
+
|
|
144
|
+
**Measured** at the pinned likec4 before a line was written. A `services/<svc>/likec4.config.json`
|
|
145
|
+
is registered as a project of its own even though the root excludes the directory: the workspace
|
|
146
|
+
loads `fleet` plus one project per service with zero errors, the root project sees exactly the
|
|
147
|
+
files it saw before, and each service project carries an auto `index` view of the containers beside
|
|
148
|
+
the authored view. A nested project named `fleet` is silently registered as `fleet-1`. With more
|
|
149
|
+
than one project, `likec4 validate` at the root refuses with `Specify exact project` until given
|
|
150
|
+
`--project`. A nested project holding no `.likec4` file is silently absent from the project list. A
|
|
151
|
+
service model that does not parse breaks only its own project; siblings and `fleet` still compute.
|
|
152
|
+
And the project picker shows `title ?? id`, so the title is what a human reads.
|
|
153
|
+
|
|
154
|
+
**What landed.** `loam subsystem sync` writes the file — create-only, two keys, bytes a function of
|
|
155
|
+
the id alone — beside every service model that has none, once the root project file exists, and
|
|
156
|
+
reports them under an additive `projects` key. `loam validate --all` grades presence with one
|
|
157
|
+
warning per service, `service.likec4-config-missing`, whose fix is `sync`. No new command: the
|
|
158
|
+
[Later](#later--promote-only-from-evidence) Rendering row's evidence bar was met for the SCOPING
|
|
159
|
+
half of its trigger and answered by wiring — one file that tells the renderer where a project is —
|
|
160
|
+
not by a drawing verb; drawing stays LikeC4's, and the row says so now.
|
|
161
|
+
|
|
162
|
+
**Rejected.** A new command, because the report's problem was a missing file, not a missing verb,
|
|
163
|
+
and a verb would have put loam between a team and its renderer for good. The agent writing the file
|
|
164
|
+
from the adopt brief, because a file written by hand per service is the state the report found —
|
|
165
|
+
one of four adopted services had one — and loam already writes the root file and knows every
|
|
166
|
+
service directory. A generated-and-compared file with a `-stale` grade, because the bytes cannot go
|
|
167
|
+
stale and a compare would fight the `title` and styles LikeC4 lets a team add. Grading in `doctor`
|
|
168
|
+
alone, because the gap is per service and `doctor` reports on the repository, not on its services;
|
|
169
|
+
the docs repo's CI runs `validate --all`.
|
|
170
|
+
|
|
171
|
+
**Costs**, each where a user meets it. A repo with adopted services reports one warning per service
|
|
172
|
+
on its first `validate --all` after upgrading, until one `sync`; that `sync` leaves N untracked
|
|
173
|
+
files it does not `git add`; from the first service project on, `likec4 validate` at the docs root
|
|
174
|
+
needs `--project <name>` (`build` and `export` take every project without one — measured); the
|
|
175
|
+
renderer's project list grows by one per adopted service, and a fleet-map box is not a link into
|
|
176
|
+
that project (LikeC4 projects are separate models); and the project name is not injective (an id spelled billing.core beside one spelled
|
|
177
|
+
billing-core, `service-fleet` beside `fleet`), disclosed in SCHEMA rather than escaped away,
|
|
178
|
+
because the picker shows the id.
|
|
179
|
+
|
|
180
|
+
#### The generated subsystem views — a label, a boundary, and one reader of the map
|
|
181
|
+
|
|
182
|
+
**Landed 2026-09-03.** Three problem reports from a fleet running loam, one axis. The generated
|
|
183
|
+
`architecture/subsystems.likec4` exists only to be rendered and was the least readable object in a
|
|
184
|
+
docs repo: no `title`, so the renderer showed the hex-escaped view id, and leaf includes only, so a
|
|
185
|
+
fleet drawn as grouped C4 rendered flat. Each view now carries the marker's title (the directory
|
|
186
|
+
name when it has none) and description, and draws a boundary the LANDSCAPE decides: the model's own
|
|
187
|
+
containing element when one holds exactly the members, a `group` mirroring the directory subtree
|
|
188
|
+
when the map nests nothing, and one `// model: no boundary — …` comment when the two disagree.
|
|
189
|
+
That comment is the whole answer to the drift the third report asked about — disclosure in a
|
|
190
|
+
generated artifact, never a finding, because grading placement against the map is the policy the
|
|
191
|
+
tree is defined not to carry. The graded alternative is written down under
|
|
192
|
+
[Later](#later--promote-only-from-evidence) with the evidence bar it has to clear.
|
|
193
|
+
|
|
194
|
+
The hunt found what neither report did: `validate --all` graded that file against the whole
|
|
195
|
+
`architecture/` project while every writer rendered it from `landscape.likec4` alone, so a
|
|
196
|
+
use-case document binding one element could produce a `subsystem.views-stale` that
|
|
197
|
+
`loam subsystem sync` reported as `current` — reproduced on `examples/docs`, and fixed by pointing
|
|
198
|
+
both sides at the same read.
|
|
199
|
+
|
|
128
200
|
#### The deployment axis — topology joined to requirements
|
|
129
201
|
|
|
130
202
|
**Promoted 2026-09-02, and authored as a feature in loam's own docs repo** rather than as an item
|
|
@@ -345,10 +417,24 @@ item gets promoted — it went from here to `## Now` and from there to
|
|
|
345
417
|
invocation, scoping, or artifact reproducibility is the recurring problem, and it must stay
|
|
346
418
|
outside validation so view computation cannot slow or change the gate. This item is about
|
|
347
419
|
**drawing**, and LikeC4 keeps that job. Reading a `dynamic view`'s declared steps is not rendering
|
|
348
|
-
and is not this item — see docs/DESIGN.md rule 26.
|
|
420
|
+
and is not this item — see docs/DESIGN.md rule 26. The scoping half of that bar was met and
|
|
421
|
+
answered on 2026-09-03 by the per-service project file `loam subsystem sync` writes (see
|
|
422
|
+
[Recently landed](#recently-landed)) — wiring rather than a verb; drawing stays LikeC4's.
|
|
349
423
|
- **UI generation:** begin with a disposable projection over the stable JSON contract only after CLI
|
|
350
424
|
consumers demonstrate a repeated navigation problem. It must not introduce a second mutable state,
|
|
351
425
|
hidden workflow state, or a required service.
|
|
426
|
+
- **A graded subsystem/model correspondence:** a fleet that draws its landscape as grouped C4 states
|
|
427
|
+
its grouping twice — once as directories under `services/`, once as the nesting of the elements —
|
|
428
|
+
and `loam subsystem move` changes the first while the second stays put. Since 2026-09-02 the
|
|
429
|
+
generated views file DISCLOSES that (the `// model: no boundary — …` line; see
|
|
430
|
+
[SCHEMA.md](SCHEMA.md)), and nothing grades it, because grading placement against the map is the
|
|
431
|
+
policy the tree is defined not to carry. The shape a graded version would take is known and
|
|
432
|
+
measured: an author's opt-in `metadata { subsystem '<name>' }` on the grouping element, read
|
|
433
|
+
through the existing metadata reader, the NAME rather than the path (names are unique in one flat
|
|
434
|
+
namespace and survive a move, which a path binding would not), with the check structurally silent
|
|
435
|
+
for every fleet that never writes one. Promote only on evidence from a second fleet, after living
|
|
436
|
+
with the disclosure — the first report classified itself `inconclusive`, and one fleet is not
|
|
437
|
+
enough to qualify an invariant that says placement is never part of any identity.
|
|
352
438
|
- **Landscape scaling:** retain one landscape while conflicts are exceptional. If same-service
|
|
353
439
|
conflicts become routine — the current trigger in [SCHEMA.md](SCHEMA.md) is weekly rather than
|
|
354
440
|
monthly — evaluate service-owned model files plus a thin global cross-service map. Migration must
|
package/SCHEMA.md
CHANGED
|
@@ -72,6 +72,7 @@ docs/
|
|
|
72
72
|
glossary/<term>.md the system's domain vocabulary, one file per term; nesting allowed [authored, opt-in]
|
|
73
73
|
services/<svc>/ at the root (unfiled), or inside subsystem directories at any depth
|
|
74
74
|
model.likec4 boundary C4 (containers/components) [adopt]
|
|
75
|
+
likec4.config.json one LikeC4 project per service model, create-only [GENERATED: loam subsystem sync]
|
|
75
76
|
spec.md living requirements (Requirement/Scenario) [adopt]
|
|
76
77
|
arch.spec.md living ARCHITECTURE requirements (outbox, retries, alerts; Covers:) [authored]
|
|
77
78
|
openapi.yaml API contract [adopt / authored]
|
|
@@ -164,14 +165,84 @@ check uses; a member nothing models is omitted, which `landscape.service-unmodel
|
|
|
164
165
|
reports). The output is deterministic and line-oriented — subsystems sorted by path, members by id,
|
|
165
166
|
one `include` per line, no timestamps, no absolute paths — so it is byte-reproducible on any machine
|
|
166
167
|
and two concurrent moves into different groups merge in git without intervention.
|
|
168
|
+
|
|
169
|
+
Each view carries the **label a human reads**: `title`, and `description` when the marker authors
|
|
170
|
+
one. The title is the subsystem's **path** — every marked ancestor's label and its own, joined with
|
|
171
|
+
` / `, so `services/platform/order-flow` reads `Platform / Order flow` — and each label is the
|
|
172
|
+
marker's `title`, falling back to the directory name (an unlabelled view is shown by its id, which
|
|
173
|
+
is hex-escaped for injectivity and reads as `subsystem_platform__order_2dflow`). LikeC4
|
|
174
|
+
reads that path as folders: the view list mirrors the directory tree, showing one folder per
|
|
175
|
+
ancestor and the leaf's own label as the title. A `/` **inside** a label becomes `∕` (U+2215), so an
|
|
176
|
+
authored slash can neither hide half of what somebody wrote nor invent a folder level the tree does
|
|
177
|
+
not have. Both strings are somebody's prose written into syntax, so both are normalized to one line
|
|
178
|
+
and escaped. Titles are not unique — only ids are — and renaming a parent's title rewrites every
|
|
179
|
+
view beneath it.
|
|
180
|
+
|
|
181
|
+
The **boundary** each view draws is a fact about the landscape, never a setting:
|
|
182
|
+
|
|
183
|
+
1. an element that holds *exactly* the subsystem's members and nothing else is included beside the
|
|
184
|
+
leaves, so the renderer nests them in the box the author drew, under the author's title;
|
|
185
|
+
2. a landscape that nests nothing puts the services in a `group '<title>'` of loam's own, mirroring
|
|
186
|
+
the directory subtree, so a parent subsystem shows its children as boxes rather than one pile;
|
|
187
|
+
3. members that ARE nested with no element owning exactly them get no box and one comment line —
|
|
188
|
+
`// model: no boundary — <id> also holds services outside this subsystem.` — because a synthetic
|
|
189
|
+
box would look identical whether or not the tree and the map agree. That comment is the whole of
|
|
190
|
+
what loam says about the two disagreeing: nothing grades it, nothing fails on it, and it lands in
|
|
191
|
+
the diff of the commit that caused it. **The tree and any `group` nesting in the landscape stay
|
|
192
|
+
independent**; `loam subsystem move` never rewrites the map, and no finding compares them.
|
|
193
|
+
|
|
194
|
+
Because the boundary depends on which *other* services sit under an element, an unrelated adoption
|
|
195
|
+
or archive can flip a view from boxed to flat — a new reason to see `subsystem.views-stale`, and why
|
|
196
|
+
`loam archive` withholds its "complete + current" line and names `loam subsystem sync` when it left
|
|
197
|
+
the file behind.
|
|
198
|
+
|
|
167
199
|
`loam subsystem sync` is the one regenerator (it also *removes* the file when the tree has no
|
|
168
200
|
subsystems), every `loam subsystem` write verb regenerates it inside the same transaction, and
|
|
169
|
-
`validate --all` grades staleness by **
|
|
201
|
+
`validate --all` grades staleness by **content comparison** (line endings normalized; a CRLF
|
|
202
|
+
checkout changes no fact) — exactly one error,
|
|
170
203
|
`subsystem.views-stale`, on exactly that file; nothing in loam ever parses it: a views-only document
|
|
171
204
|
does not parse standalone (its `include` lines reference elements the landscape defines), and it
|
|
172
205
|
holds **static** views, whose contents loam never reads in any document. Only the LikeC4 renderer,
|
|
173
206
|
which merges the whole `architecture/` project, resolves its includes.
|
|
174
207
|
|
|
208
|
+
The tree's **second renderer file** is per service — `services/<…>/<svc>/likec4.config.json`,
|
|
209
|
+
which `loam subsystem sync` also writes, beside every `model.likec4` that has none. The root
|
|
210
|
+
project is scoped to `architecture/` and excludes `services/**`, and must be, since each model
|
|
211
|
+
declares its own `specification` block; what that exclusion cost went unstated for a release: an
|
|
212
|
+
adopted service was a box on the fleet map with nothing renderable inside it. Measured at the
|
|
213
|
+
pinned likec4, a project file INSIDE a service directory is registered as a project of its own
|
|
214
|
+
regardless of that exclusion, so one `npx likec4 start` at the docs root loads `fleet` plus one
|
|
215
|
+
project per service, each carrying LikeC4's own `index` view of the containers beside the authored
|
|
216
|
+
one, and a model that does not parse breaks only its own project. The file is **create-only** —
|
|
217
|
+
never compared, never rewritten, never removed — under the root config's ownership rule, not the
|
|
218
|
+
views file's: there is no `-stale` grade because nothing can make it stale. Its bytes are a
|
|
219
|
+
function of the id alone (two keys, `name` and `title`), so placement never changes them and a
|
|
220
|
+
`subsystem move` carries the file with no write of its own — it rides inside the rename the move's
|
|
221
|
+
transaction already journals (in a git-backed repo, commit the files `sync` wrote first: `move`
|
|
222
|
+
refuses over untracked paths, `move-uncommitted`); a compare would only fight the keys LikeC4 lets a
|
|
223
|
+
team add (`title`, styles). `name` is the id with every
|
|
224
|
+
`.` folded to `-` (LikeC4's name grammar refuses the byte; loam's id grammar admits it), prefixed
|
|
225
|
+
`service-` when the result would equal the root project's `fleet` (a nested project of that name
|
|
226
|
+
is silently registered as `fleet-1`) or LikeC4's reserved `default`; `title` is the id verbatim,
|
|
227
|
+
and the title is what the renderer's project picker shows. The name is **not injective**, and
|
|
228
|
+
that is disclosed rather than escaped away: an id spelled billing.core beside a service literally
|
|
229
|
+
named billing-core, or `service-fleet` beside `fleet`, share a name and LikeC4 suffixes the later
|
|
230
|
+
one `-1`; no loam
|
|
231
|
+
finding grades it, because the picker still reads the id. Order is a gate — the root file first
|
|
232
|
+
(`loam init --create` writes it, `loam doctor` prints it), then `sync`: without the root file
|
|
233
|
+
nothing is written and the text output says so. No model, no file: a spec-only service is owed
|
|
234
|
+
nothing and gets its file on the first `sync` after its model lands. No legal id, no file either: a
|
|
235
|
+
directory whose name breaks the service-id grammar (`service.id-invalid`) would get a `name` the
|
|
236
|
+
renderer silently drops, and a create-only file the renderer ignores would silence the grade for
|
|
237
|
+
good, so `sync` skips it and the rename that clears `service.id-invalid` earns the file. A file left behind by a
|
|
238
|
+
deleted model is the team's and harmless (measured: a nested project holding no `.likec4` file is
|
|
239
|
+
absent from the project list). `validate --all` grades presence only — one warning per service,
|
|
240
|
+
`service.likec4-config-missing`, on the fleet `landscape` target, never an error, never in
|
|
241
|
+
`--service`, `status` or `doctor`; the fix is `loam subsystem sync`, never a hand-written file. A
|
|
242
|
+
feature's `delta.likec4` is deliberately out of scope: the directory is transient and
|
|
243
|
+
`loam archive` moves it where a project file would still be registered, so every shipped feature
|
|
244
|
+
would keep a dead project in the list forever.
|
|
245
|
+
|
|
175
246
|
A move is **one journaled transaction over N directory renames plus the regenerated views file**
|
|
176
247
|
(`loam subsystem move <name>... --into <sub|.>`; `rename` is a one-rename move; whole subtrees move
|
|
177
248
|
as one rename). It refuses only when git reports uncommitted changes under a directory being moved
|
|
@@ -2263,9 +2334,17 @@ through the forge, not through loam features:
|
|
|
2263
2334
|
**parsed** model and never computes a view — it reads what a view **declares** (a `dynamic view`'s
|
|
2264
2335
|
tags and ordered steps, already materialized by the parse loam pays for) and never what a view
|
|
2265
2336
|
**shows**. It renders nothing, and view *computation* is what is superlinear in edges. (If you
|
|
2266
|
-
want diagrams, add a `views` block and render it with LikeC4's own tooling — `npx likec4 start`
|
|
2267
|
-
the docs
|
|
2268
|
-
|
|
2337
|
+
want diagrams, add a `views` block and render it with LikeC4's own tooling — `npx likec4 start`
|
|
2338
|
+
at the docs root, which renders the fleet map AND every adopted service's model, each as the
|
|
2339
|
+
project its own `services/<svc>/likec4.config.json` declares (written by `loam subsystem sync`;
|
|
2340
|
+
the Layout section has the rule), and `npx likec4 start features/<FEAT>` for a delta, which is
|
|
2341
|
+
transient and gets no project — and scope the views, because an `include *` over a fleet-sized
|
|
2342
|
+
landscape takes minutes. Two costs arrive with the first service project. Measured at the
|
|
2343
|
+
pinned likec4: with more than one project, `likec4 validate` at the docs root refuses without
|
|
2344
|
+
`--project <name>`, so a CI step that ran it bare changes (`likec4 build` and `likec4 export` take
|
|
2345
|
+
every project and need no flag). And the renderer's project list grows by one per adopted service
|
|
2346
|
+
beside `fleet` — LikeC4 projects are separate models, so a box on the fleet map is not a link into
|
|
2347
|
+
that service's project. `loam init`
|
|
2269
2348
|
scaffolds the landscape with no `views` block for that reason: loam should not plant a diagram it
|
|
2270
2349
|
never draws **— and never asks for one: a fleet that draws nothing owes no views block.** It also
|
|
2271
2350
|
scaffolds a `likec4.config.json` that scopes the root LikeC4 project to `architecture/`: loam
|
|
@@ -2524,9 +2603,9 @@ component-surface pins), `verify`,
|
|
|
2524
2603
|
transactional `archive` / `unarchive`, `vouch`, and the OpenSpec audit/migration on-ramp are
|
|
2525
2604
|
implemented, each with a `--json` contract.
|
|
2526
2605
|
|
|
2527
|
-
Known and unclosed, so nobody has to discover them: the
|
|
2528
|
-
|
|
2529
|
-
|
|
2606
|
+
Known and unclosed, so nobody has to discover them: the scheduled CI `stability` job has still to be
|
|
2607
|
+
observed green, even though behavior, coverage, the installed-package smoke and the local stress
|
|
2608
|
+
runner all pass. Both contract axes now merge a components-only delta: a feature
|
|
2530
2609
|
asyncapi.yaml declaring no channel/operation/message slot (`components.schemas`-only, say) merges
|
|
2531
2610
|
the component surfaces it declares, pinned by the root `x-loam-baselines` record, and they enter the
|
|
2532
2611
|
ref sweep like anything else the merge wrote — so a `$ref` whose target the delta itself defines
|
|
@@ -15,6 +15,10 @@ import { message, quietPruneEmptyParents, quietRm, rollbackError, rollbackStaged
|
|
|
15
15
|
import { clearCommitIntent, writeCommitIntent } from "../../core/staging/recovery/intent.js";
|
|
16
16
|
import { SNAPSHOT_DIR, SnapshotClobberError, snapshotDir, writeSnapshot, } from "../../core/staging/snapshot.js";
|
|
17
17
|
import { enumeratedServices } from "../../core/repo/service-target.js";
|
|
18
|
+
import { listFleetTree } from "../../core/repo/repo.js";
|
|
19
|
+
import { subsystemViewsPath } from "../../core/repo/paths.js";
|
|
20
|
+
import { viewsState } from "../../core/repo/tree/render/stale.js";
|
|
21
|
+
import { loadArchitecture } from "../../core/c4/project/architecture.js";
|
|
18
22
|
import { FleetContext } from "../../core/fleet-context.js";
|
|
19
23
|
import { gate } from "./plan/gate.js";
|
|
20
24
|
import {} from "./plan/refusal.js";
|
|
@@ -227,8 +231,19 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
227
231
|
: "";
|
|
228
232
|
throw new ArchiveFailure(failures.length > 0 ? "rollback-incomplete" : "merge-failed", wrapped.message + kept);
|
|
229
233
|
}
|
|
234
|
+
// This merge just changed the map, and the generated views file is a
|
|
235
|
+
// function of the tree AND the map: a delta whose element lands inside a
|
|
236
|
+
// group that exactly covered a subsystem takes that subsystem's boundary
|
|
237
|
+
// line away, and a delta binding an element to a filed service adds an
|
|
238
|
+
// include. Archive does not rewrite the file — `loam subsystem sync` owns it
|
|
239
|
+
// outright, and folding a second generated artifact into this transaction
|
|
240
|
+
// would mean archive computing the post-merge tree AND the post-merge map,
|
|
241
|
+
// which is how the two writers disagree — but it must not report a repo as
|
|
242
|
+
// current when the next `validate --all` will call it stale. Graded through
|
|
243
|
+
// validate's own function, so the two cannot answer differently.
|
|
244
|
+
const staleViews = await subsystemViewsStale(config.docsDir);
|
|
230
245
|
if (json) {
|
|
231
|
-
emitJson(payload(true));
|
|
246
|
+
emitJson({ ...payload(true), ...(staleViews ? { subsystemViewsStale: true } : {}) });
|
|
232
247
|
return;
|
|
233
248
|
}
|
|
234
249
|
console.log(`\n archived: features/${dirName} → features/archive/${dirName}`);
|
|
@@ -239,14 +254,43 @@ export async function archiveLocked(config, featureId, opts) {
|
|
|
239
254
|
// caller told it to merge past. Printing it over either is how a red fleet
|
|
240
255
|
// gets reported as a finished one.
|
|
241
256
|
const incomplete = planWarns.filter((w) => w.code === "service.no-model");
|
|
242
|
-
if (incomplete.length === 0 && overridden.length === 0) {
|
|
257
|
+
if (incomplete.length === 0 && overridden.length === 0 && !staleViews) {
|
|
243
258
|
console.log(" living spec + landscape are now complete + current.");
|
|
244
259
|
return;
|
|
245
260
|
}
|
|
261
|
+
if (staleViews) {
|
|
262
|
+
console.log(" ⚠ architecture/subsystems.likec4 no longer matches the tree and this map — run `loam subsystem sync`.");
|
|
263
|
+
}
|
|
246
264
|
for (const w of incomplete)
|
|
247
265
|
console.log(` ⚠ ${w.message}`);
|
|
248
266
|
if (overridden.length > 0) {
|
|
249
267
|
console.log(` ⚠ merged past ${overridden.length} gating issue(s) with --approve — the living docs carry them now; \`loam validate --all\` says what they cost.`);
|
|
250
268
|
}
|
|
251
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* Would the next `validate --all` report `subsystem.views-stale`?
|
|
272
|
+
*
|
|
273
|
+
* Asked through validate's OWN function rather than a second compare, so the
|
|
274
|
+
* warning archive prints and the error validate raises can never disagree —
|
|
275
|
+
* including about the gate validate holds: a map that does not parse is
|
|
276
|
+
* `landscape.invalid`'s business, and grading a generated file against a map
|
|
277
|
+
* nobody can read would cascade a second complaint behind the first. An
|
|
278
|
+
* unreadable map therefore answers "no claim", exactly as validate does.
|
|
279
|
+
*/
|
|
280
|
+
async function subsystemViewsStale(docsDir) {
|
|
281
|
+
try {
|
|
282
|
+
const doc = await loadArchitecture(docsDir);
|
|
283
|
+
if (doc.errors.length > 0)
|
|
284
|
+
return false;
|
|
285
|
+
const state = await viewsState(subsystemViewsPath(docsDir), await listFleetTree(docsDir), doc.elements);
|
|
286
|
+
return !state.agrees;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Tolerant for the reason the snapshot resolver above is: a docs repo with
|
|
290
|
+
// no `services/` at all still archives a landscape-only feature, and this
|
|
291
|
+
// question is an ADVISORY printed after a commit that already succeeded.
|
|
292
|
+
// Nothing here may turn a landed archive into a non-zero exit.
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
252
296
|
//# sourceMappingURL=run.js.map
|