@ybotok/loam 0.1.0-beta.2 → 0.1.0-beta.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"brief.js","sourceRoot":"","sources":["../../src/core/brief.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The adoption brief: what an agent needs in order to write one service's\n * baseline documentation, and what will be checked when it does.\n *\n * `adopt` was specified for years as \"read the code, emit the C4\". It is not\n * being built, because it cannot be: nothing deterministic reads a legacy\n * service and says what its architecture MEANS, and a guessed model is worse\n * than no model — everyone downstream has to re-derive it to know whether to\n * believe it.\n *\n * So loam takes the half of the job that IS deterministic. It states the work\n * precisely (which files, in which grammar, bound to which existing elements),\n * it says exactly which checks the result will face, and then it says which\n * checks do not exist — because everything in that last list is honesty no\n * validator will ever supply, and an agent that does not know where the checking\n * stops will assume it never does.\n *\n * Nothing here reads code. `sources` in the frontmatter is the only line that\n * ties the result to a repository, which is why the brief argues for it instead\n * of merely listing it.\n */\nimport { existsSync, statSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { elementService, loadFile, serviceResolver, type Elem } from \"./likec4.js\";\nimport { landscapePath, listServices, servicePaths } from \"./repo.js\";\n\n/* ------------------------------------------------------------------ */\n/* The artifacts */\n/* ------------------------------------------------------------------ */\n\nexport interface BriefTarget {\n /** File name as it appears under `services/<svc>/`. */\n artifact: string;\n /** Repo-relative path to write. */\n path: string;\n exists: boolean;\n /**\n * `create` a missing artifact; `diff` an existing one — never overwrite it.\n *\n * `edit` is the third case and belongs to exactly one target: the fleet map.\n * `architecture/landscape.likec4` is a SHARED document that already holds\n * every other service, so \"diff it and report what disagrees\" is the wrong\n * instruction — the file is not this service's baseline, it is the fleet's,\n * and what this service owes it is an addition inside the existing\n * `model { … }` block. Spelling that as `diff` taught agents to leave the\n * fleet map alone; spelling it as `create` would have taught them to\n * overwrite ten other services.\n */\n action: \"create\" | \"diff\" | \"edit\";\n /**\n * False for artifacts a baseline can legitimately ship without. True means\n * required for a COMPLETE baseline — validate grades a missing spec.md or\n * openapi.yaml as a warn (`service.no-spec` / `service.no-openapi`), not an\n * error: partial adoption is a supported state, and the warns are the\n * progress meter.\n *\n * Constant per artifact except one: `openapi.yaml` is required only where the\n * fleet map shows an API is expected (see `apiExpected`), because the brief is\n * read as an instruction and \"MISSING openapi.yaml\" told an agent to write a\n * contract for a service that serves no requests.\n */\n required: boolean;\n /** What the artifact is for, in one line. */\n purpose: string;\n /** The grammar. Every rule here is one a later check depends on. */\n shape: string[];\n /** A minimal valid instance, where showing one is shorter than describing it. */\n example?: string;\n}\n\n/** Where an artifact lives — `repo.ts` spells the filenames, this only points at them. */\ntype PathKey = \"model\" | \"spec\" | \"archSpec\" | \"openapi\" | \"adrsDir\" | \"runbook\" | \"health\";\n\n/**\n * The LikeC4 identifier the fleet map's element for a service conventionally\n * carries: the directory name, camel-cased. It is a suggestion and nothing\n * more — `metadata { service '<id>' }` is what actually joins the element to\n * the directory — but a brief that hands over a concrete block gets followed,\n * and one that describes a block in prose gets improvised on.\n */\nfunction elementIdFor(service: string): string {\n const parts = service.split(/[^A-Za-z0-9]+/).filter((p) => p !== \"\");\n if (parts.length === 0) return \"service\";\n const head = parts[0]!.toLowerCase();\n const tail = parts.slice(1).map((p) => p[0]!.toUpperCase() + p.slice(1).toLowerCase());\n const id = head + tail.join(\"\");\n // LikeC4 identifiers cannot open with a digit; a service id can.\n return /^[0-9]/.test(id) ? `svc${id[0]!.toUpperCase()}${id.slice(1)}` : id;\n}\n\n/**\n * The block this service still owes `architecture/landscape.likec4`.\n *\n * `expects` is what the fleet ALREADY calls on the service — those edges exist\n * in the map today, so the example draws the outbound side as a comment rather\n * than telling an agent to duplicate an edge that is already there.\n */\nfunction landscapeBlock(service: string, expects: string[], present: boolean): string {\n const id = elementIdFor(service);\n const calls =\n expects.length > 0\n ? expects.map((op) => `// <caller> -> ${id} 'Calls ${op}' { metadata { op '${op}' } }`)\n : [`// <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`];\n const model = [\n \"model {\",\n ...(present ? [\" // ... the fleet's other services stay exactly as they are ...\", \"\"] : []),\n ` ${id} = softwareSystem '${service}' {`,\n \" description '<one line: what this service is responsible for>'\",\n ` metadata { service '${service}' }`,\n \" }\",\n \"\",\n \" // every call in or out of it, one edge each:\",\n ...calls.map((line) => ` ${line}`),\n \"}\",\n ];\n // When the file does not exist the example is the WHOLE file, so it carries\n // the `specification` block a bare `model` would be rejected without: the\n // brief must never hand over a document `loam validate` refuses. When the file\n // does exist the example is a fragment to splice, and repeating a\n // `specification` block there would invite a second one.\n return present\n ? model.join(\"\\n\")\n : [\"specification {\", \" element softwareSystem\", \"}\", \"\", ...model].join(\"\\n\");\n}\n\n/** The fleet map's own brief — assembled per service, because the block it owes names the service. */\nfunction landscapeArtifact(\n service: string,\n expects: string[],\n present: boolean,\n): Omit<BriefTarget, \"path\" | \"exists\" | \"action\"> {\n return {\n artifact: \"landscape.likec4\",\n required: true,\n purpose:\n \"the fleet map — until an element here resolves to this directory, the service is documented and invisible to every cross-service check\",\n shape: [\n \"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.\",\n `Add one top-level element for the service, bound to its directory: \\`metadata { service '${service}' }\\`. Until an element resolves to \\`services/${service}/\\`, \\`loam validate --all\\` reports \\`landscape.service-unmodelled\\` (error).`,\n \"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.\",\n \"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.\",\n \"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.\",\n ],\n example: landscapeBlock(service, expects, present),\n };\n}\n\n/** The artifacts, in the order a baseline is best written in. */\nconst ARTIFACTS: Array<Omit<BriefTarget, \"path\" | \"exists\" | \"action\"> & { key: PathKey }> = [\n {\n artifact: \"model.likec4\",\n key: \"model\",\n required: true,\n purpose: \"the service's C4 — what it is, what is inside it, what it talks to\",\n // Every rule below is one a later check depends on, and the mapping is\n // pinned in test/agent-contract.test.ts. Rules nobody checks — the `views`\n // block, \"exactly one top-level element\" — used to sit here reading exactly\n // like the enforced ones, which is how a brief teaches an agent that\n // `loam validate` passing means more than it does. They now live in\n // UNCHECKED, where their status is the point.\n shape: [\n \"A `specification { ... }` block declaring every element kind you use (`element softwareSystem`, `element container`). LikeC4 rejects an undeclared kind, and `loam validate --service <id>` reports it as `c4.invalid` with line numbers.\",\n \"A `model { ... }` block holding the service's element, with containers and components nested inside it. The whole file has to parse — `loam validate` runs LikeC4 in-process and reports `c4.invalid` with line numbers.\",\n \"The service element binds to this directory: `metadata { service '<id>' }`. Without a binding the element's TITLE has to equal the directory name — and then renaming the box silently unlinks every check that joined the two. The same binding is what makes the FLEET map resolve an element to `services/<id>/`; until one does, `loam validate --all` reports `landscape.service-unmodelled`.\",\n \"A call to another service is an edge that names the operation it uses: `a -> b 'Calls createSplit' { metadata { op 'createSplit' } }`. Cross-service edges belong in `architecture/landscape.likec4` (a target of its own, below) — this file cannot even resolve the other service's element — and there the `op` must be an operationId the TARGET's openapi.yaml defines, or `spine.op-undefined` fires.\",\n ],\n example: `specification {\n element softwareSystem\n element container\n}\n\nmodel {\n paymentService = softwareSystem 'payment-service' {\n description 'Owns payment authorization and capture'\n metadata { service 'payment-service' }\n\n api = container 'HTTP API'\n ledger = container 'Ledger store'\n }\n}\n\n// Views are LikeC4's, not loam's: loam parses this file and computes no view,\n// so nothing below is read by any check. Keep it for 'npx likec4 start', which\n// does render it. Scoped to one service 'include *' is cheap; the same line in\n// architecture/landscape.likec4 walks every call in the fleet and takes minutes.\nviews {\n view of paymentService {\n include *\n }\n}\n`,\n },\n {\n artifact: \"spec.md\",\n key: \"spec\",\n required: true,\n purpose: \"the living requirements — what this service is required to do, as it stands today\",\n shape: [\n \"Frontmatter (below), then a `## Requirements` heading.\",\n \"`### Requirement: <name>` — ONE observable behaviour, written with SHALL, testable without reading the code. Add `Requirement-ID: <id>` in its body for stable identity across heading renames; legacy requirements without one continue to match by exact name.\",\n \"At least one `#### Scenario: <name>` per requirement, with Given/When/Then bullets. This is gated (`requirements.missing-scenarios`) — scenarios are the acceptance criteria and the source for tests.\",\n \"`Operations: <operationId>[, <operationId>]` in the requirement body names the API operations it governs. Every one must exist in this service's openapi.yaml — a name that does not is `spec-api.op-undefined` (error), reported against the LIVING spec, not only inside a feature delta.\",\n \"Document what the service DOES today, not what it should do. This is the baseline; changes to it belong in a feature delta.\",\n ],\n example: `---\nservice: payment-service\nstatus: draft\nowner: payments-team\nsources:\n - src/main/java/com/shop/payment/\n---\n\n# payment-service\n\n## Requirements\n\n### Requirement: Authorize a payment\nThe service SHALL authorize a payment against the issuer before it is captured.\n\nOperations: authorizePayment\n\n#### Scenario: Successful authorization\n- **Given** a valid card and an amount within its limit\n- **When** authorization is requested\n- **Then** the payment is authorized and an authorization id is returned\n`,\n },\n {\n artifact: \"arch.spec.md\",\n key: \"archSpec\",\n required: false,\n purpose:\n \"the living architecture requirements — the obligations the business spec never carries: outbox, retries, idempotency, metrics, alerts\",\n shape: [\n \"Same grammar and frontmatter as spec.md: `## Requirements`, `### Requirement:` + `#### Scenario:` with Given/When/Then. The same checks read it.\",\n \"A `Covers:` line per requirement names what its scenarios exercise: a C4 element id (`paymentService.db`), an edge (`paymentService -> kafka`), or a health signal from health.yaml (`alert:<id>` / `sli:<id>`). Every entry must resolve, or `covers.unknown` (warn) flags the typo.\",\n \"Optional, but expected for a service with real architecture: every alert/SLI health.yaml declares wants a covering requirement here (`health.uncovered`, warn) — a signal nothing tests is dashboard decoration.\",\n \"Write the obligations no business requirement states — the transactional outbox, what a caller's retry may assume, what pages whom. An arch scenario becomes an integration/ops test, not an acceptance test.\",\n ],\n example: `---\nservice: payment-service\nstatus: draft\nowner: payments-team\nsources:\n - src/main/java/com/shop/payment/\n---\n\n# payment-service — architecture\n\n## Requirements\n\n### Requirement: Events leave through the transactional outbox\nThe service SHALL write a domain event and its state change in one transaction,\npublished by an outbox relay — never a dual write.\n\nCovers: paymentService.db, paymentService -> kafka\n\n#### Scenario: Broker down at commit time\n- **Given** an event in the outbox\n- **When** the broker is unavailable\n- **Then** the state stays committed and the event is published once it returns\n`,\n },\n {\n artifact: \"openapi.yaml\",\n key: \"openapi\",\n // Overridden per service in `serviceBrief`: an API is required only where\n // the fleet expects one. See `apiExpected`.\n required: true,\n purpose: \"the API contract — the operations this service exposes\",\n shape: [\n \"OpenAPI 3.x with a `paths` map.\",\n \"EVERY operation carries an `operationId`. That token is the spine: the C4 edge's `metadata { op }`, the requirement's `Operations:` line and this `operationId` are one name spelled three times, and they must be identical.\",\n \"loam reads operationIds and nothing else in this file. The schemas are for humans, codegen and review — they are not checked, so getting them wrong is invisible here and expensive later.\",\n \"Document the endpoints that exist. An operation the fleet already calls (see `landscape.expects`) must be in here, or the contract breaks the moment this lands.\",\n // The instruction the brief was missing, and the one that matters most:\n // it used to mark this artifact required unconditionally, in capitals,\n // which for a UI, a worker or a cron told the agent to invent an API into\n // the source of truth. `required` is derived now; this says so in prose\n // as well, because an agent reading the shape rules must not re-derive\n // \"MISSING\" as \"write something\".\n \"If this service exposes no HTTP API, there is no file to write. Do not invent one — an empty or imagined contract is a claim the whole fleet then joins against. `required` above is derived from the fleet map: it is false exactly when the landscape parses and no edge calls an operation on this service, which is the same evidence that keeps `service.no-openapi` quiet.\",\n ],\n },\n {\n artifact: \"adrs/\",\n key: \"adrsDir\",\n required: false,\n purpose: \"the decisions behind the shape of the service\",\n shape: [\n \"One file per decision: `adrs/NNNN-<slug>.md`, MADR-style (context · decision · consequences).\",\n \"Only decisions whose consequences are still visible in the code. A reconstructed rationale for something nobody decided is fiction, and it is the kind that survives review because it sounds reasonable.\",\n \"If a decision is evident but its reasoning is not, write the decision and say the reasoning is unknown. That is a fact; a plausible motive is not.\",\n ],\n },\n {\n artifact: \"runbook.md\",\n key: \"runbook\",\n required: false,\n purpose: \"how it is run — deploys, dependencies, what to do when it pages\",\n shape: [\n \"Frontmatter, then how it is deployed and configured, what it depends on, and what to do about the failures that actually happen.\",\n \"Written from what is in the repo (pipelines, manifests, alert definitions). Whoever carries the pager verifies it; leave `status: draft` until they have.\",\n ],\n },\n {\n artifact: \"health.yaml\",\n key: \"health\",\n required: false,\n purpose: \"SLIs, SLOs, checks and the dependencies that are critical\",\n shape: [\n \"SLIs and SLOs as they have been AGREED, plus health checks and critical dependencies.\",\n \"If no SLO has ever been agreed, say so and leave it out. An invented 99.9% becomes a number other teams plan against.\",\n ],\n },\n];\n\n/* ------------------------------------------------------------------ */\n/* The frontmatter */\n/* ------------------------------------------------------------------ */\n\nexport interface FrontmatterBrief {\n /** Field -> what to write in it. */\n fields: Record<string, string>;\n /** Fields to leave alone entirely. */\n never: string[];\n /** Why `sources` carries the weight. */\n why: string;\n}\n\nconst FRONTMATTER_BRIEF: FrontmatterBrief = {\n fields: {\n service: \"the directory this file lives under, exactly. A mismatch is an error (`frontmatter.field-mismatch`).\",\n status:\n \"`draft`, always. `verified` is a human's word, stamped by `loam vouch --service <id>` run inside the service's own repo — never written by hand.\",\n owner: \"the team or person who answers for this service.\",\n sources:\n \"the paths in THIS SERVICE'S repository you actually read to write the document — files and directories only, a directory covering everything beneath it; glob patterns are refused. Not the paths a reader would expect to have been read.\",\n },\n never: [\"last_verified\", \"sources_digest\", \"content_digest\", \"sources_files\"],\n why:\n \"`sources` is the only mechanical tie between this document and the code. Everything else loam checks is internal consistency, and a corpus can agree with itself perfectly while describing nothing that exists. `loam validate`, run inside the service's repo, checks every listed path is still there; `loam vouch` hashes their content so a later `validate` can say the code has moved since anyone read it. Both are worth exactly as much as the list is honest.\",\n};\n\n/* ------------------------------------------------------------------ */\n/* The checks — and the ones that do not exist */\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`, above). For a service\n // the fleet already calls, `required: true` on this target and this row are\n // 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: \"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 // 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];\n\n/**\n * What no check will ever tell you. Read it as the list of ways to be wrong\n * quietly.\n *\n * The first two entries are here because they used to be `shape` rules on\n * model.likec4, phrased exactly like the enforced ones. A brief that states an\n * unenforced rule beside four enforced ones is not merely useless — it is the\n * way an agent learns that a green `loam validate` means more than it does.\n * Anything nothing checks belongs on this list, where its status IS the point.\n */\nexport const UNCHECKED: string[] = [\n // Not merely unchecked — unread. This entry used to end \"write one\", on the\n // theory that a view is what makes a model legible. It is, to LikeC4's\n // renderer; loam computes none. Saying otherwise taught agents that a views\n // block was owed to loam, and an `include *` over the FLEET map is the one\n // shape that costs minutes rather than milliseconds.\n \"Whether model.likec4 declares a `views { ... }` block, or any view at all — and nothing in loam ever will. loam reads elements and relationships out of the PARSED model and renders nothing, so it computes no view and a model without one is missing nothing loam wants. Views belong to LikeC4's own renderer: write them if you want diagrams, and read them with `npx likec4 start`. Scope them when you do — computing a view is superlinear in the number of edges, and an `include *` over `architecture/landscape.likec4` is the expensive one, because that file holds every call in the fleet.\",\n \"Whether the model has exactly ONE top-level element for the service, with its containers nested inside. Five top-level boxes for one service parse, validate and bind exactly as well as one — and then the fleet map, which joins elements to directories, has five candidates for the same service and no way to say which is wrong.\",\n \"Whether the model is the architecture the code actually has. loam parses model.likec4; it never reads a line of the service.\",\n \"Whether a requirement is TRUE of the service. `loam validate` checks that a requirement has a scenario, never that either one describes real behaviour.\",\n \"Whether the scenarios are acceptance criteria somebody could test, or the requirement restated in Given/When/Then.\",\n \"Whether an operationId is the one the code serves, and whether the request and response schemas resemble what the endpoint really accepts and returns. Only the operationId is read.\",\n \"Whether the runbook's steps work, or the health.yaml SLOs are numbers anyone agreed to.\",\n \"Whether an arch scenario really exercises what its `Covers:` line names. The entries are resolved against the model and health.yaml — never against code, tests, dashboards or alert rules.\",\n \"Whether an ADR records a decision that was made, or one reconstructed afterwards to justify the code.\",\n \"COMPLETENESS. Forty behaviours documented as one requirement passes every check loam has. So does a service with one endpoint documented out of thirty.\",\n \"Whether `sources` names the files you read. loam checks those paths exist — not that they are the ones the document came from.\",\n];\n\n/* ------------------------------------------------------------------ */\n/* What the fleet already says about this service */\n/* ------------------------------------------------------------------ */\n\nexport interface LandscapeElement {\n id: string;\n title: string;\n kind: string;\n tags: string[];\n /** True when the element binds explicitly with `metadata { service }`. */\n bound: boolean;\n}\n\nexport interface LandscapeEdge {\n from?: string;\n to?: string;\n op: string | null;\n title: string | null;\n}\n\nexport interface LandscapeContext {\n present: boolean;\n /** False when the landscape exists but does not parse. */\n parses: boolean;\n /** Whether any element resolves to this service — null when nothing could be read. */\n modelled: boolean | null;\n elements: LandscapeElement[];\n inbound: LandscapeEdge[];\n outbound: LandscapeEdge[];\n /** Operations other services already call on this one: the contract owes them. */\n expects: string[];\n /**\n * The write this service still owes the fleet map, in prose — null once an\n * element resolves to it.\n *\n * It duplicates the `landscape.likec4` target's shape rules on purpose: an\n * agent driving off `--json` reads `landscape` to learn what the fleet\n * already says, and every `targets[]` consumer walks the artifact list. Only\n * one of those two readers used to exist, so `adopt` briefed eight files and\n * never once said the ninth thing — that a documented service nobody drew is\n * invisible to the whole cross-service layer.\n */\n instruction: string | null;\n}\n\nexport interface Brief {\n service: string;\n /** Absolute docs repo root — the anchor the repo-relative paths hang off. */\n docsDir: string;\n /** Repo-relative path of services/<id>/. */\n path: string;\n targets: BriefTarget[];\n landscape: LandscapeContext;\n frontmatter: FrontmatterBrief;\n checks: BriefCheck[];\n unchecked: string[];\n /** The one instruction that outranks the rest. */\n rule: string;\n}\n\nconst NEVER_OVERWRITE =\n \"Do not overwrite an artifact that already exists. Read it, diff your findings against it, and report what disagrees — a document somebody wrote is evidence, and replacing it destroys the only copy of what they knew.\";\n\n/** Assemble the brief for one service. Reads the docs repo; writes nothing. */\nexport async function serviceBrief(docsDir: string, service: string): Promise<Brief> {\n const paths = servicePaths(docsDir, service);\n const rel = (abs: string): string => relative(docsDir, abs).split(/[\\\\/]/).join(\"/\");\n\n // Read before the artifact loop, not after it: what the fleet already says\n // about this service decides whether it owes an API contract at all.\n const landscape = await landscapeContext(docsDir, service);\n const owesApi = apiExpected(landscape);\n\n const targets: BriefTarget[] = [];\n for (const { key, ...artifact } of ARTIFACTS) {\n // `adrs/` is a directory, and an empty one is not an existing artifact —\n // reporting it as present would tell the agent to diff against nothing.\n const dir = key === \"adrsDir\";\n const exists = dir ? await hasMarkdown(paths[key]) : existsSync(paths[key]);\n targets.push({\n ...artifact,\n ...(key === \"openapi\" ? { required: owesApi } : {}),\n path: dir ? `${rel(paths[key])}/` : rel(paths[key]),\n exists,\n action: exists ? \"diff\" : \"create\",\n });\n }\n\n // The eighth target is the fleet map, and it is conditional: a service the\n // landscape ALREADY resolves an element to owes it nothing, and briefing an\n // edit there would invite an agent to draw a second box for a service that\n // has one. Everything else about the target is unconditional — the file is\n // shared, so `action` is `edit` whenever it exists and `create` only for the\n // very first service of a brand-new docs repo.\n if (landscape.modelled !== true) {\n targets.push({\n ...landscapeArtifact(service, landscape.expects, landscape.present),\n path: rel(landscapePath(docsDir)),\n exists: landscape.present,\n action: landscape.present ? \"edit\" : \"create\",\n });\n }\n\n return {\n service,\n docsDir,\n path: rel(paths.dir),\n targets,\n landscape,\n frontmatter: FRONTMATTER_BRIEF,\n checks: VALIDATE_CHECKS,\n unchecked: UNCHECKED,\n rule: NEVER_OVERWRITE,\n };\n}\n\nasync function hasMarkdown(dir: string): Promise<boolean> {\n if (!existsSync(dir)) return false;\n const entries = await readdir(dir, { withFileTypes: true });\n // `statSync` for the symlink case: a Dirent never follows one, so `isFile()`\n // is false for a symlinked ADR and the directory reads as empty (repo.ts's\n // `entryIs` is the same rule for `services/` and `features/`).\n return entries.some((e) => {\n if (!e.name.endsWith(\".md\")) return false;\n if (e.isFile()) return true;\n if (!e.isSymbolicLink()) return false;\n try {\n return statSync(join(dir, e.name)).isFile();\n } catch {\n return false;\n }\n });\n}\n\n/**\n * Does the fleet expect an API of this service?\n *\n * The brief marked `openapi.yaml` required unconditionally, and printed the\n * absence as `MISSING` in capitals — while `loam validate` and `loam list`\n * derive the opposite from the same landscape (`service.no-openapi` stays quiet\n * when nothing calls the service, and `list`'s maturity ladder stops demanding\n * a contract). For a UI, a worker or a cron the brief was therefore telling an\n * agent to INVENT an API contract into the source of truth — the one class of\n * guess this whole module exists to refuse, and the one the brief itself names\n * 219 lines further down.\n *\n * Positive evidence only, exactly as `list` reads it: a landscape that is\n * absent or does not parse proves nothing about who calls this service, so the\n * contract is still owed. `expects` is the ops on inbound edges, which is\n * `list`'s `called` set seen from one service.\n */\nfunction apiExpected(landscape: LandscapeContext): boolean {\n return !landscape.parses || landscape.expects.length > 0;\n}\n\n/**\n * The service ids the resolver may fall back on when an element carries no\n * `metadata { service }` binding — the same positive evidence `validate` and\n * `list` hand it. A docs repo that cannot be enumerated (no `services/` yet:\n * `adopt` runs there) leaves the resolver with bindings alone, which is what it\n * had before.\n */\nasync function knownServices(docsDir: string): Promise<ReadonlySet<string>> {\n try {\n return new Set((await listServices(docsDir)).map((s) => s.id));\n } catch {\n return new Set();\n }\n}\n\n/**\n * What the living landscape already says about this service.\n *\n * This is the half of the brief that stops an agent inventing a parallel model:\n * the fleet has already drawn some of these boxes and edges, and the baseline has\n * to attach to them. A landscape that does not parse yields `modelled: null` —\n * \"nothing models it\" would be a claim about a document nobody could read.\n */\nasync function landscapeContext(docsDir: string, service: string): Promise<LandscapeContext> {\n const path = landscapePath(docsDir);\n const empty: LandscapeContext = {\n present: existsSync(path),\n parses: false,\n modelled: null,\n elements: [],\n inbound: [],\n outbound: [],\n expects: [],\n instruction: null,\n };\n if (!empty.present)\n return { ...empty, modelled: false, instruction: instructionFor(service, \"absent\", []) };\n\n const land = await loadFile(path);\n if (land.errors.length > 0)\n return { ...empty, instruction: instructionFor(service, \"unparseable\", []) };\n\n const mine = (e: Elem): boolean => elementService(e) === service;\n const elements: LandscapeElement[] = land.elements.filter(mine).map((e) => ({\n id: e.id,\n title: e.title,\n kind: e.kind,\n tags: e.tags,\n bound: e.service !== undefined,\n }));\n\n // One resolver for the whole scan, built with the real service ids: an edge\n // drawn into a modelled container (`paymentService.api`) is an edge into the\n // service, and `expects` — which now decides whether this service owes an\n // OpenAPI contract at all — has to see it. Building it once also stops the\n // per-edge rebuild the old `serviceOf(land.elements, id)` call did.\n const svcOf = serviceResolver(land.elements, await knownServices(docsDir));\n const inbound: LandscapeEdge[] = [];\n const outbound: LandscapeEdge[] = [];\n for (const r of land.relationships) {\n const edge = { op: r.op ?? null, title: r.title ?? null };\n if (svcOf(r.target) === service) inbound.push({ from: svcOf(r.source), ...edge });\n else if (svcOf(r.source) === service) outbound.push({ to: svcOf(r.target), ...edge });\n }\n\n const expects = [...new Set(inbound.map((e) => e.op).filter((op): op is string => op !== null))];\n return {\n present: true,\n parses: true,\n modelled: elements.length > 0,\n elements,\n inbound,\n outbound,\n expects,\n instruction:\n elements.length > 0 ? null : instructionFor(service, \"unmodelled\", expects),\n };\n}\n\n/**\n * The fleet-map instruction, in the three states a landscape can be in.\n *\n * It says the file, the block, and the consequence, in that order. The\n * consequence is the part agents acted on: \"add an element\" reads as tidiness,\n * \"until you do, `loam validate --all` fails and no cross-service check can see\n * this service\" reads as work.\n */\nfunction instructionFor(\n service: string,\n state: \"absent\" | \"unparseable\" | \"unmodelled\",\n expects: string[],\n): string {\n const id = elementIdFor(service);\n const bind = `an element bound with \\`metadata { service '${service}' }\\``;\n const consequence =\n `Until one resolves, \\`loam validate --all\\` reports \\`landscape.service-unmodelled\\` (error) for ` +\n `services/${service}/: the service is documented and invisible — no edge into it can be checked ` +\n `against its openapi.yaml, and no feature can draw a call to it.`;\n // `expects` is reachable only through elements that already resolve to this\n // service, so it is empty in every state that produces an instruction. It is\n // threaded through anyway rather than dropped: the caller computes it once,\n // and the day an unmodelled service can inherit an edge (a container-level\n // binding, say) the sentence is already here instead of being noticed missing.\n const owed =\n expects.length > 0\n ? ` The fleet already calls ${expects.map((o) => `'${o}'`).join(\", \")} on this service, so those edges exist; your element is what they will resolve to.`\n : \"\";\n\n if (state === \"absent\") {\n return (\n `architecture/landscape.likec4 does not exist yet. Create it with a \\`specification { ... }\\` block ` +\n `declaring the kinds you use and a \\`model { ... }\\` block containing ${bind} ` +\n `(conventionally \\`${id} = softwareSystem '${service}'\\`), plus one edge per cross-service call, each ` +\n `carrying \\`metadata { op '<operationId>' }\\`. ${consequence}${owed}`\n );\n }\n if (state === \"unparseable\") {\n return (\n `architecture/landscape.likec4 exists but does not parse (\\`landscape.invalid\\`), so nothing can be said ` +\n `about what it already models. Fix the parse errors first — they blind every cross-service check at once — ` +\n `then make sure it holds ${bind}. ${consequence}`\n );\n }\n return (\n `Nothing in architecture/landscape.likec4 resolves to services/${service}/. ADD to that file — do not rewrite ` +\n `it, every other service's map is in there — ${bind}, conventionally ` +\n `\\`${id} = softwareSystem '${service}'\\`, inside the existing \\`model { ... }\\` block, and draw each ` +\n `cross-service call as an edge carrying \\`metadata { op '<operationId>' }\\`. ${consequence}${owed}`\n );\n}\n"]}
1
+ {"version":3,"file":"brief.js","sourceRoot":"","sources":["../../src/core/brief.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The adoption brief: what an agent needs in order to write one service's\n * baseline documentation, and what will be checked when it does.\n *\n * `adopt` was specified for years as \"read the code, emit the C4\". It is not\n * being built, because it cannot be: nothing deterministic reads a legacy\n * service and says what its architecture MEANS, and a guessed model is worse\n * than no model — everyone downstream has to re-derive it to know whether to\n * believe it.\n *\n * So loam takes the half of the job that IS deterministic. It states the work\n * precisely (which files, in which grammar, bound to which existing elements),\n * it says exactly which checks the result will face, and then it says which\n * checks do not exist — because everything in that last list is honesty no\n * validator will ever supply, and an agent that does not know where the checking\n * stops will assume it never does.\n *\n * Nothing here reads code. `sources` in the frontmatter is the only line that\n * ties the result to a repository, which is why the brief argues for it instead\n * of merely listing it.\n */\nimport { existsSync, statSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { elementService, loadFile, serviceResolver, type Elem } from \"./likec4.js\";\nimport { landscapePath, listServices, servicePaths } from \"./repo.js\";\n\n/* ------------------------------------------------------------------ */\n/* The artifacts */\n/* ------------------------------------------------------------------ */\n\nexport interface BriefTarget {\n /** File name as it appears under `services/<svc>/`. */\n artifact: string;\n /** Repo-relative path to write. */\n path: string;\n exists: boolean;\n /**\n * `create` a missing artifact; `diff` an existing one — never overwrite it.\n *\n * `edit` is the third case and belongs to exactly one target: the fleet map.\n * `architecture/landscape.likec4` is a SHARED document that already holds\n * every other service, so \"diff it and report what disagrees\" is the wrong\n * instruction — the file is not this service's baseline, it is the fleet's,\n * and what this service owes it is an addition inside the existing\n * `model { … }` block. Spelling that as `diff` taught agents to leave the\n * fleet map alone; spelling it as `create` would have taught them to\n * overwrite ten other services.\n */\n action: \"create\" | \"diff\" | \"edit\";\n /**\n * False for artifacts a baseline can legitimately ship without. True means\n * required for a COMPLETE baseline — validate grades a missing spec.md or\n * openapi.yaml as a warn (`service.no-spec` / `service.no-openapi`), not an\n * error: partial adoption is a supported state, and the warns are the\n * progress meter.\n *\n * Constant per artifact except one: `openapi.yaml` is required only where the\n * fleet map shows an API is expected (see `apiExpected`), because the brief is\n * read as an instruction and \"MISSING openapi.yaml\" told an agent to write a\n * contract for a service that serves no requests.\n */\n required: boolean;\n /** What the artifact is for, in one line. */\n purpose: string;\n /** The grammar. Every rule here is one a later check depends on. */\n shape: string[];\n /** A minimal valid instance, where showing one is shorter than describing it. */\n example?: string;\n}\n\n/** Where an artifact lives — `repo.ts` spells the filenames, this only points at them. */\ntype PathKey = \"model\" | \"spec\" | \"archSpec\" | \"openapi\" | \"asyncapi\" | \"adrsDir\" | \"runbook\" | \"health\";\n\n/**\n * The LikeC4 identifier the fleet map's element for a service conventionally\n * carries: the directory name, camel-cased. It is a suggestion and nothing\n * more — `metadata { service '<id>' }` is what actually joins the element to\n * the directory — but a brief that hands over a concrete block gets followed,\n * and one that describes a block in prose gets improvised on.\n */\nfunction elementIdFor(service: string): string {\n const parts = service.split(/[^A-Za-z0-9]+/).filter((p) => p !== \"\");\n if (parts.length === 0) return \"service\";\n const head = parts[0]!.toLowerCase();\n const tail = parts.slice(1).map((p) => p[0]!.toUpperCase() + p.slice(1).toLowerCase());\n const id = head + tail.join(\"\");\n // LikeC4 identifiers cannot open with a digit; a service id can.\n return /^[0-9]/.test(id) ? `svc${id[0]!.toUpperCase()}${id.slice(1)}` : id;\n}\n\n/**\n * The block this service still owes `architecture/landscape.likec4`.\n *\n * `expects` is what the fleet ALREADY calls on the service — those edges exist\n * in the map today, so the example draws the outbound side as a comment rather\n * than telling an agent to duplicate an edge that is already there.\n */\nfunction landscapeBlock(service: string, expects: string[], present: boolean): string {\n const id = elementIdFor(service);\n const calls =\n expects.length > 0\n ? expects.map((op) => `// <caller> -> ${id} 'Calls ${op}' { metadata { op '${op}' } }`)\n : [`// <caller> -> ${id} 'Calls <operationId>' { metadata { op '<operationId>' } }`];\n const model = [\n \"model {\",\n ...(present ? [\" // ... the fleet's other services stay exactly as they are ...\", \"\"] : []),\n ` ${id} = softwareSystem '${service}' {`,\n \" description '<one line: what this service is responsible for>'\",\n ` metadata { service '${service}' }`,\n \" }\",\n \"\",\n \" // every call in or out of it, one edge each:\",\n ...calls.map((line) => ` ${line}`),\n \"}\",\n ];\n // When the file does not exist the example is the WHOLE file, so it carries\n // the `specification` block a bare `model` would be rejected without: the\n // brief must never hand over a document `loam validate` refuses. When the file\n // does exist the example is a fragment to splice, and repeating a\n // `specification` block there would invite a second one.\n return present\n ? model.join(\"\\n\")\n : [\"specification {\", \" element softwareSystem\", \"}\", \"\", ...model].join(\"\\n\");\n}\n\n/** The fleet map's own brief — assembled per service, because the block it owes names the service. */\nfunction landscapeArtifact(\n service: string,\n expects: string[],\n present: boolean,\n): Omit<BriefTarget, \"path\" | \"exists\" | \"action\"> {\n return {\n artifact: \"landscape.likec4\",\n required: true,\n purpose:\n \"the fleet map — until an element here resolves to this directory, the service is documented and invisible to every cross-service check\",\n shape: [\n \"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.\",\n `Add one top-level element for the service, bound to its directory: \\`metadata { service '${service}' }\\`. Until an element resolves to \\`services/${service}/\\`, \\`loam validate --all\\` reports \\`landscape.service-unmodelled\\` (error).`,\n \"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.\",\n \"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.\",\n \"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.\",\n ],\n example: landscapeBlock(service, expects, present),\n };\n}\n\n/** The artifacts, in the order a baseline is best written in. */\nconst ARTIFACTS: Array<Omit<BriefTarget, \"path\" | \"exists\" | \"action\"> & { key: PathKey }> = [\n {\n artifact: \"model.likec4\",\n key: \"model\",\n required: true,\n purpose: \"the service's C4 — what it is, what is inside it, what it talks to\",\n // Every rule below is one a later check depends on, and the mapping is\n // pinned in test/agent-contract.test.ts. Rules nobody checks — the `views`\n // block, \"exactly one top-level element\" — used to sit here reading exactly\n // like the enforced ones, which is how a brief teaches an agent that\n // `loam validate` passing means more than it does. They now live in\n // UNCHECKED, where their status is the point.\n shape: [\n \"A `specification { ... }` block declaring every element kind you use (`element softwareSystem`, `element container`). LikeC4 rejects an undeclared kind, and `loam validate --service <id>` reports it as `c4.invalid` with line numbers.\",\n \"A `model { ... }` block holding the service's element, with containers and components nested inside it. The whole file has to parse — `loam validate` runs LikeC4 in-process and reports `c4.invalid` with line numbers.\",\n \"The service element binds to this directory: `metadata { service '<id>' }`. Without a binding the element's TITLE has to equal the directory name — and then renaming the box silently unlinks every check that joined the two. The same binding is what makes the FLEET map resolve an element to `services/<id>/`; until one does, `loam validate --all` reports `landscape.service-unmodelled`.\",\n \"A call to another service is an edge that names the operation it uses: `a -> b 'Calls createSplit' { metadata { op 'createSplit' } }`. Cross-service edges belong in `architecture/landscape.likec4` (a target of its own, below) — this file cannot even resolve the other service's element — and there the `op` must be an operationId the TARGET's openapi.yaml defines, or `spine.op-undefined` fires.\",\n ],\n example: `specification {\n element softwareSystem\n element container\n}\n\nmodel {\n paymentService = softwareSystem 'payment-service' {\n description 'Owns payment authorization and capture'\n metadata { service 'payment-service' }\n\n api = container 'HTTP API'\n ledger = container 'Ledger store'\n }\n}\n\n// Views are LikeC4's, not loam's: loam parses this file and computes no view,\n// so nothing below is read by any check. Keep it for the renderer, which does\n// draw it — 'npx likec4 start services/<id>' from the docs repo, pointed at THIS\n// directory. Not the repo root: every service model declares its own\n// 'specification' block, so the root is scoped to architecture/ (likec4.config.json)\n// and one model at a time is how these files are meant to be read.\n// Scoped to one service 'include *' is cheap; the same line in\n// architecture/landscape.likec4 walks every call in the fleet and takes minutes.\nviews {\n view of paymentService {\n include *\n }\n}\n`,\n },\n {\n artifact: \"spec.md\",\n key: \"spec\",\n required: true,\n purpose: \"the living requirements — what this service is required to do, as it stands today\",\n shape: [\n \"Frontmatter (below), then a `## Requirements` heading.\",\n \"`### Requirement: <name>` — ONE observable behaviour, written with SHALL, testable without reading the code. Add `Requirement-ID: <id>` in its body for stable identity across heading renames; legacy requirements without one continue to match by exact name.\",\n \"At least one `#### Scenario: <name>` per requirement, with Given/When/Then bullets. This is gated (`requirements.missing-scenarios`) — scenarios are the acceptance criteria and the source for tests.\",\n \"`Operations: <operationId>[, <operationId>]` in the requirement body names the API operations it governs. Every one must exist in this service's openapi.yaml — a name that does not is `spec-api.op-undefined` (error), reported against the LIVING spec, not only inside a feature delta.\",\n \"Document what the service DOES today, not what it should do. This is the baseline; changes to it belong in a feature delta.\",\n ],\n example: `---\nservice: payment-service\nstatus: draft\nowner: payments-team\nsources:\n - src/main/java/com/shop/payment/\n---\n\n# payment-service\n\n## Requirements\n\n### Requirement: Authorize a payment\nThe service SHALL authorize a payment against the issuer before it is captured.\n\nOperations: authorizePayment\n\n#### Scenario: Successful authorization\n- **Given** a valid card and an amount within its limit\n- **When** authorization is requested\n- **Then** the payment is authorized and an authorization id is returned\n`,\n },\n {\n artifact: \"arch.spec.md\",\n key: \"archSpec\",\n required: false,\n purpose:\n \"the living architecture requirements — the obligations the business spec never carries: outbox, retries, idempotency, metrics, alerts\",\n shape: [\n \"Same grammar and frontmatter as spec.md: `## Requirements`, `### Requirement:` + `#### Scenario:` with Given/When/Then. The same checks read it.\",\n \"A `Covers:` line per requirement names what its scenarios exercise: a C4 element id (`paymentService.db`), an edge (`paymentService -> kafka`), or a health signal from health.yaml (`alert:<id>` / `sli:<id>`). Every entry must resolve, or `covers.unknown` (warn) flags the typo.\",\n \"Optional, but expected for a service with real architecture: every alert/SLI health.yaml declares wants a covering requirement here (`health.uncovered`, warn) — a signal nothing tests is dashboard decoration.\",\n \"Write the obligations no business requirement states — the transactional outbox, what a caller's retry may assume, what pages whom. An arch scenario becomes an integration/ops test, not an acceptance test.\",\n ],\n example: `---\nservice: payment-service\nstatus: draft\nowner: payments-team\nsources:\n - src/main/java/com/shop/payment/\n---\n\n# payment-service — architecture\n\n## Requirements\n\n### Requirement: Events leave through the transactional outbox\nThe service SHALL write a domain event and its state change in one transaction,\npublished by an outbox relay — never a dual write.\n\nCovers: paymentService.db, paymentService -> kafka\n\n#### Scenario: Broker down at commit time\n- **Given** an event in the outbox\n- **When** the broker is unavailable\n- **Then** the state stays committed and the event is published once it returns\n`,\n },\n {\n artifact: \"openapi.yaml\",\n key: \"openapi\",\n // Overridden per service in `serviceBrief`: an API is required only where\n // the fleet expects one. See `apiExpected`.\n required: true,\n purpose: \"the API contract — the operations this service exposes\",\n shape: [\n \"OpenAPI 3.x with a `paths` map.\",\n \"EVERY operation carries an `operationId`. That token is the spine: the C4 edge's `metadata { op }`, the requirement's `Operations:` line and this `operationId` are one name spelled three times, and they must be identical.\",\n \"loam reads operationIds and nothing else in this file. The schemas are for humans, codegen and review — they are not checked, so getting them wrong is invisible here and expensive later.\",\n \"Document the endpoints that exist. An operation the fleet already calls (see `landscape.expects`) must be in here, or the contract breaks the moment this lands.\",\n // The instruction the brief was missing, and the one that matters most:\n // it used to mark this artifact required unconditionally, in capitals,\n // which for a UI, a worker or a cron told the agent to invent an API into\n // the source of truth. `required` is derived now; this says so in prose\n // as well, because an agent reading the shape rules must not re-derive\n // \"MISSING\" as \"write something\".\n \"If this service exposes no HTTP API, there is no file to write. Do not invent one — an empty or imagined contract is a claim the whole fleet then joins against. `required` above is derived from the fleet map: it is false exactly when the landscape parses and no edge calls an operation on this service, which is the same evidence that keeps `service.no-openapi` quiet.\",\n ],\n },\n {\n artifact: \"asyncapi.yaml\",\n key: \"asyncapi\",\n // Never required. Unlike the HTTP contract, whose necessity the fleet map\n // can PROVE (an op-linked edge points here), most services in a legacy fleet\n // touch no topic at all — and a required artifact nobody owes is how a\n // whole adoption wave learns to ignore the brief.\n required: false,\n purpose: \"the async contract — the messages this service puts on and takes off the bus\",\n shape: [\n \"AsyncAPI **3.0** with `channels`, `operations` and `components.messages`. Only 3.0 is read: its operations are named top-level objects carrying `action: send|receive`, which is what makes a message's direction legible. A 2.x document declares no `operations` and reads as a contract with no messages.\",\n \"EVERY message carries a `name` (or is declared under the key you intend to reference). That token is the spine: the C4 edge's `metadata { publishes }` / `metadata { consumes }`, the requirement's `Publishes:` / `Consumes:` line and this name are one name spelled three times.\",\n \"Namespace the name by the domain that owns it — `payment.PaymentAuthorized`. One message has exactly one producer; two services declaring they send one name is `asyncapi.message-contested`, and then which contract a consumer reads is a coin flip.\",\n \"loam reads message names and the `action` of the operation carrying them. **It never looks inside `payload`.** Write the payload as JSON Schema today; if the fleet later adopts Avro, that is a `schemaFormat` line here and nothing in loam changes.\",\n \"Keep payloads in `components.schemas` in THIS file rather than `$ref`-ing an external `.avsc`. External references are out of scope for the merge, exactly as on the OpenAPI axis, so a schema in another file will not travel with the message that needs it.\",\n \"If this service publishes and consumes nothing, there is no file to write. Do not invent one.\",\n ],\n },\n {\n artifact: \"adrs/\",\n key: \"adrsDir\",\n required: false,\n purpose: \"the decisions behind the shape of the service\",\n shape: [\n \"One file per decision: `adrs/NNNN-<slug>.md`, MADR-style (context · decision · consequences).\",\n \"Only decisions whose consequences are still visible in the code. A reconstructed rationale for something nobody decided is fiction, and it is the kind that survives review because it sounds reasonable.\",\n \"If a decision is evident but its reasoning is not, write the decision and say the reasoning is unknown. That is a fact; a plausible motive is not.\",\n ],\n },\n {\n artifact: \"runbook.md\",\n key: \"runbook\",\n required: false,\n purpose: \"how it is run — deploys, dependencies, what to do when it pages\",\n shape: [\n \"Frontmatter, then how it is deployed and configured, what it depends on, and what to do about the failures that actually happen.\",\n \"Written from what is in the repo (pipelines, manifests, alert definitions). Whoever carries the pager verifies it; leave `status: draft` until they have.\",\n ],\n },\n {\n artifact: \"health.yaml\",\n key: \"health\",\n required: false,\n purpose: \"SLIs, SLOs, checks and the dependencies that are critical\",\n shape: [\n \"SLIs and SLOs as they have been AGREED, plus health checks and critical dependencies.\",\n \"If no SLO has ever been agreed, say so and leave it out. An invented 99.9% becomes a number other teams plan against.\",\n ],\n },\n];\n\n/* ------------------------------------------------------------------ */\n/* The frontmatter */\n/* ------------------------------------------------------------------ */\n\nexport interface FrontmatterBrief {\n /** Field -> what to write in it. */\n fields: Record<string, string>;\n /** Fields to leave alone entirely. */\n never: string[];\n /** Why `sources` carries the weight. */\n why: string;\n}\n\nconst FRONTMATTER_BRIEF: FrontmatterBrief = {\n fields: {\n service: \"the directory this file lives under, exactly. A mismatch is an error (`frontmatter.field-mismatch`).\",\n status:\n \"`draft`, always. `verified` is a human's word, stamped by `loam vouch --service <id>` run inside the service's own repo — never written by hand.\",\n owner: \"the team or person who answers for this service.\",\n sources:\n \"the paths in THIS SERVICE'S repository you actually read to write the document — files and directories only, a directory covering everything beneath it; glob patterns are refused. Not the paths a reader would expect to have been read.\",\n },\n never: [\"last_verified\", \"sources_digest\", \"content_digest\", \"sources_files\"],\n why:\n \"`sources` is the only mechanical tie between this document and the code. Everything else loam checks is internal consistency, and a corpus can agree with itself perfectly while describing nothing that exists. `loam validate`, run inside the service's repo, checks every listed path is still there; `loam vouch` hashes their content so a later `validate` can say the code has moved since anyone read it. Both are worth exactly as much as the list is honest.\",\n};\n\n/* ------------------------------------------------------------------ */\n/* The checks — and the ones that do not exist */\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`, above). For a service\n // the fleet already calls, `required: true` on this target and this row are\n // 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: \"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 // 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];\n\n/**\n * What no check will ever tell you. Read it as the list of ways to be wrong\n * quietly.\n *\n * The first two entries are here because they used to be `shape` rules on\n * model.likec4, phrased exactly like the enforced ones. A brief that states an\n * unenforced rule beside four enforced ones is not merely useless — it is the\n * way an agent learns that a green `loam validate` means more than it does.\n * Anything nothing checks belongs on this list, where its status IS the point.\n */\nexport const UNCHECKED: string[] = [\n // Not merely unchecked — unread. This entry used to end \"write one\", on the\n // theory that a view is what makes a model legible. It is, to LikeC4's\n // renderer; loam computes none. Saying otherwise taught agents that a views\n // block was owed to loam, and an `include *` over the FLEET map is the one\n // shape that costs minutes rather than milliseconds.\n \"Whether model.likec4 declares a `views { ... }` block, or any view at all — and nothing in loam ever will. loam reads elements and relationships out of the PARSED model and renders nothing, so it computes no view and a model without one is missing nothing loam wants. Views belong to LikeC4's own renderer: write them if you want diagrams, and read them with `npx likec4 start <dir>` pointed at ONE directory — `services/<id>` for a service model, the docs repo root for the fleet map (`likec4.config.json` scopes that root project to `architecture/`). The renderer merges every `.likec4` file it is given into one model, and loam parses each of them alone, so each declares its own `specification` block: point it at a directory holding two of them and every declaration reads as a duplicate. Scope your views too — computing a view is superlinear in the number of edges, and an `include *` over `architecture/landscape.likec4` is the expensive one, because that file holds every call in the fleet.\",\n \"Whether the model has exactly ONE top-level element for the service, with its containers nested inside. Five top-level boxes for one service parse, validate and bind exactly as well as one — and then the fleet map, which joins elements to directories, has five candidates for the same service and no way to say which is wrong.\",\n \"Whether the model is the architecture the code actually has. loam parses model.likec4; it never reads a line of the service.\",\n \"Whether a requirement is TRUE of the service. `loam validate` checks that a requirement has a scenario, never that either one describes real behaviour.\",\n \"Whether the scenarios are acceptance criteria somebody could test, or the requirement restated in Given/When/Then.\",\n \"Whether an operationId is the one the code serves, and whether the request and response schemas resemble what the endpoint really accepts and returns. Only the operationId is read.\",\n \"Whether the runbook's steps work, or the health.yaml SLOs are numbers anyone agreed to.\",\n \"Whether an arch scenario really exercises what its `Covers:` line names. The entries are resolved against the model and health.yaml — never against code, tests, dashboards or alert rules.\",\n \"Whether an ADR records a decision that was made, or one reconstructed afterwards to justify the code.\",\n \"COMPLETENESS. Forty behaviours documented as one requirement passes every check loam has. So does a service with one endpoint documented out of thirty.\",\n \"Whether `sources` names the files you read. loam checks those paths exist — not that they are the ones the document came from.\",\n];\n\n/* ------------------------------------------------------------------ */\n/* What the fleet already says about this service */\n/* ------------------------------------------------------------------ */\n\nexport interface LandscapeElement {\n id: string;\n title: string;\n kind: string;\n tags: string[];\n /** True when the element binds explicitly with `metadata { service }`. */\n bound: boolean;\n}\n\nexport interface LandscapeEdge {\n from?: string;\n to?: string;\n op: string | null;\n title: string | null;\n}\n\nexport interface LandscapeContext {\n present: boolean;\n /** False when the landscape exists but does not parse. */\n parses: boolean;\n /** Whether any element resolves to this service — null when nothing could be read. */\n modelled: boolean | null;\n elements: LandscapeElement[];\n inbound: LandscapeEdge[];\n outbound: LandscapeEdge[];\n /** Operations other services already call on this one: the contract owes them. */\n expects: string[];\n /**\n * The write this service still owes the fleet map, in prose — null once an\n * element resolves to it.\n *\n * It duplicates the `landscape.likec4` target's shape rules on purpose: an\n * agent driving off `--json` reads `landscape` to learn what the fleet\n * already says, and every `targets[]` consumer walks the artifact list. Only\n * one of those two readers used to exist, so `adopt` briefed eight files and\n * never once said the ninth thing — that a documented service nobody drew is\n * invisible to the whole cross-service layer.\n */\n instruction: string | null;\n}\n\nexport interface Brief {\n service: string;\n /** Absolute docs repo root — the anchor the repo-relative paths hang off. */\n docsDir: string;\n /** Repo-relative path of services/<id>/. */\n path: string;\n targets: BriefTarget[];\n landscape: LandscapeContext;\n frontmatter: FrontmatterBrief;\n checks: BriefCheck[];\n unchecked: string[];\n /** The one instruction that outranks the rest. */\n rule: string;\n}\n\nconst NEVER_OVERWRITE =\n \"Do not overwrite an artifact that already exists. Read it, diff your findings against it, and report what disagrees — a document somebody wrote is evidence, and replacing it destroys the only copy of what they knew.\";\n\n/** Assemble the brief for one service. Reads the docs repo; writes nothing. */\nexport async function serviceBrief(docsDir: string, service: string): Promise<Brief> {\n const paths = servicePaths(docsDir, service);\n const rel = (abs: string): string => relative(docsDir, abs).split(/[\\\\/]/).join(\"/\");\n\n // Read before the artifact loop, not after it: what the fleet already says\n // about this service decides whether it owes an API contract at all.\n const landscape = await landscapeContext(docsDir, service);\n const owesApi = apiExpected(landscape);\n\n const targets: BriefTarget[] = [];\n for (const { key, ...artifact } of ARTIFACTS) {\n // `adrs/` is a directory, and an empty one is not an existing artifact —\n // reporting it as present would tell the agent to diff against nothing.\n const dir = key === \"adrsDir\";\n const exists = dir ? await hasMarkdown(paths[key]) : existsSync(paths[key]);\n targets.push({\n ...artifact,\n ...(key === \"openapi\" ? { required: owesApi } : {}),\n path: dir ? `${rel(paths[key])}/` : rel(paths[key]),\n exists,\n action: exists ? \"diff\" : \"create\",\n });\n }\n\n // The eighth target is the fleet map, and it is conditional: a service the\n // landscape ALREADY resolves an element to owes it nothing, and briefing an\n // edit there would invite an agent to draw a second box for a service that\n // has one. Everything else about the target is unconditional — the file is\n // shared, so `action` is `edit` whenever it exists and `create` only for the\n // very first service of a brand-new docs repo.\n if (landscape.modelled !== true) {\n targets.push({\n ...landscapeArtifact(service, landscape.expects, landscape.present),\n path: rel(landscapePath(docsDir)),\n exists: landscape.present,\n action: landscape.present ? \"edit\" : \"create\",\n });\n }\n\n return {\n service,\n docsDir,\n path: rel(paths.dir),\n targets,\n landscape,\n frontmatter: FRONTMATTER_BRIEF,\n checks: VALIDATE_CHECKS,\n unchecked: UNCHECKED,\n rule: NEVER_OVERWRITE,\n };\n}\n\nasync function hasMarkdown(dir: string): Promise<boolean> {\n if (!existsSync(dir)) return false;\n const entries = await readdir(dir, { withFileTypes: true });\n // `statSync` for the symlink case: a Dirent never follows one, so `isFile()`\n // is false for a symlinked ADR and the directory reads as empty (repo.ts's\n // `entryIs` is the same rule for `services/` and `features/`).\n return entries.some((e) => {\n if (!e.name.endsWith(\".md\")) return false;\n if (e.isFile()) return true;\n if (!e.isSymbolicLink()) return false;\n try {\n return statSync(join(dir, e.name)).isFile();\n } catch {\n return false;\n }\n });\n}\n\n/**\n * Does the fleet expect an API of this service?\n *\n * The brief marked `openapi.yaml` required unconditionally, and printed the\n * absence as `MISSING` in capitals — while `loam validate` and `loam list`\n * derive the opposite from the same landscape (`service.no-openapi` stays quiet\n * when nothing calls the service, and `list`'s maturity ladder stops demanding\n * a contract). For a UI, a worker or a cron the brief was therefore telling an\n * agent to INVENT an API contract into the source of truth — the one class of\n * guess this whole module exists to refuse, and the one the brief itself names\n * 219 lines further down.\n *\n * Positive evidence only, exactly as `list` reads it: a landscape that is\n * absent or does not parse proves nothing about who calls this service, so the\n * contract is still owed. `expects` is the ops on inbound edges, which is\n * `list`'s `called` set seen from one service.\n */\nfunction apiExpected(landscape: LandscapeContext): boolean {\n return !landscape.parses || landscape.expects.length > 0;\n}\n\n/**\n * The service ids the resolver may fall back on when an element carries no\n * `metadata { service }` binding — the same positive evidence `validate` and\n * `list` hand it. A docs repo that cannot be enumerated (no `services/` yet:\n * `adopt` runs there) leaves the resolver with bindings alone, which is what it\n * had before.\n */\nasync function knownServices(docsDir: string): Promise<ReadonlySet<string>> {\n try {\n return new Set((await listServices(docsDir)).map((s) => s.id));\n } catch {\n return new Set();\n }\n}\n\n/**\n * What the living landscape already says about this service.\n *\n * This is the half of the brief that stops an agent inventing a parallel model:\n * the fleet has already drawn some of these boxes and edges, and the baseline has\n * to attach to them. A landscape that does not parse yields `modelled: null` —\n * \"nothing models it\" would be a claim about a document nobody could read.\n */\nasync function landscapeContext(docsDir: string, service: string): Promise<LandscapeContext> {\n const path = landscapePath(docsDir);\n const empty: LandscapeContext = {\n present: existsSync(path),\n parses: false,\n modelled: null,\n elements: [],\n inbound: [],\n outbound: [],\n expects: [],\n instruction: null,\n };\n if (!empty.present)\n return { ...empty, modelled: false, instruction: instructionFor(service, \"absent\", []) };\n\n const land = await loadFile(path);\n if (land.errors.length > 0)\n return { ...empty, instruction: instructionFor(service, \"unparseable\", []) };\n\n const mine = (e: Elem): boolean => elementService(e) === service;\n const elements: LandscapeElement[] = land.elements.filter(mine).map((e) => ({\n id: e.id,\n title: e.title,\n kind: e.kind,\n tags: e.tags,\n bound: e.service !== undefined,\n }));\n\n // One resolver for the whole scan, built with the real service ids: an edge\n // drawn into a modelled container (`paymentService.api`) is an edge into the\n // service, and `expects` — which now decides whether this service owes an\n // OpenAPI contract at all — has to see it. Building it once also stops the\n // per-edge rebuild the old `serviceOf(land.elements, id)` call did.\n const svcOf = serviceResolver(land.elements, await knownServices(docsDir));\n const inbound: LandscapeEdge[] = [];\n const outbound: LandscapeEdge[] = [];\n for (const r of land.relationships) {\n const edge = { op: r.op ?? null, title: r.title ?? null };\n if (svcOf(r.target) === service) inbound.push({ from: svcOf(r.source), ...edge });\n else if (svcOf(r.source) === service) outbound.push({ to: svcOf(r.target), ...edge });\n }\n\n const expects = [...new Set(inbound.map((e) => e.op).filter((op): op is string => op !== null))];\n return {\n present: true,\n parses: true,\n modelled: elements.length > 0,\n elements,\n inbound,\n outbound,\n expects,\n instruction:\n elements.length > 0 ? null : instructionFor(service, \"unmodelled\", expects),\n };\n}\n\n/**\n * The fleet-map instruction, in the three states a landscape can be in.\n *\n * It says the file, the block, and the consequence, in that order. The\n * consequence is the part agents acted on: \"add an element\" reads as tidiness,\n * \"until you do, `loam validate --all` fails and no cross-service check can see\n * this service\" reads as work.\n */\nfunction instructionFor(\n service: string,\n state: \"absent\" | \"unparseable\" | \"unmodelled\",\n expects: string[],\n): string {\n const id = elementIdFor(service);\n const bind = `an element bound with \\`metadata { service '${service}' }\\``;\n const consequence =\n `Until one resolves, \\`loam validate --all\\` reports \\`landscape.service-unmodelled\\` (error) for ` +\n `services/${service}/: the service is documented and invisible — no edge into it can be checked ` +\n `against its openapi.yaml, and no feature can draw a call to it.`;\n // `expects` is reachable only through elements that already resolve to this\n // service, so it is empty in every state that produces an instruction. It is\n // threaded through anyway rather than dropped: the caller computes it once,\n // and the day an unmodelled service can inherit an edge (a container-level\n // binding, say) the sentence is already here instead of being noticed missing.\n const owed =\n expects.length > 0\n ? ` The fleet already calls ${expects.map((o) => `'${o}'`).join(\", \")} on this service, so those edges exist; your element is what they will resolve to.`\n : \"\";\n\n if (state === \"absent\") {\n return (\n `architecture/landscape.likec4 does not exist yet. Create it with a \\`specification { ... }\\` block ` +\n `declaring the kinds you use and a \\`model { ... }\\` block containing ${bind} ` +\n `(conventionally \\`${id} = softwareSystem '${service}'\\`), plus one edge per cross-service call, each ` +\n `carrying \\`metadata { op '<operationId>' }\\`. ${consequence}${owed}`\n );\n }\n if (state === \"unparseable\") {\n return (\n `architecture/landscape.likec4 exists but does not parse (\\`landscape.invalid\\`), so nothing can be said ` +\n `about what it already models. Fix the parse errors first — they blind every cross-service check at once — ` +\n `then make sure it holds ${bind}. ${consequence}`\n );\n }\n return (\n `Nothing in architecture/landscape.likec4 resolves to services/${service}/. ADD to that file — do not rewrite ` +\n `it, every other service's map is in there — ${bind}, conventionally ` +\n `\\`${id} = softwareSystem '${service}'\\`, inside the existing \\`model { ... }\\` block, and draw each ` +\n `cross-service call as an edge carrying \\`metadata { op '<operationId>' }\\`. ${consequence}${owed}`\n );\n}\n"]}
package/dist/core/docs.js CHANGED
@@ -20,6 +20,16 @@ import { AGENTS_FILENAME } from "./repo.js";
20
20
  * `docsDir` that has none. So it is laid down even when nothing is in it yet.
21
21
  */
22
22
  export const DOCS_SUBDIRS = ["architecture", "services", "features"];
23
+ /**
24
+ * The scaffolded directories that start out empty, and the file that keeps each
25
+ * of them in version control. See {@link GITKEEP}. Spelled as pairs so the
26
+ * marker's name is written once — `listServices` and `listFeatures` enumerate
27
+ * subdirectories only, so a file here is invisible to both.
28
+ */
29
+ const EMPTY_SUBDIRS = [
30
+ ["services", ".gitkeep"],
31
+ ["features", ".gitkeep"],
32
+ ];
23
33
  /**
24
34
  * The fleet map, empty but valid.
25
35
  *
@@ -64,8 +74,12 @@ const LANDSCAPE_STUB = `// The fleet map: every service in services/ appears her
64
74
  //
65
75
  // There is no \`views\` block, on purpose: loam reads the model and never a
66
76
  // view, so it would draw nothing. Add views here if you want diagrams, and
67
- // render them with LikeC4's own tooling (\`npx likec4 start\`) — but scope them,
68
- // because computing a view is superlinear in the number of edges and an
77
+ // render them with LikeC4's own tooling — \`npx likec4 start\` from the docs
78
+ // repo root, which likec4.config.json scopes to this directory. (A service
79
+ // model or a feature delta renders from its OWN directory, e.g.
80
+ // \`npx likec4 start services/<id>\`: each declares its own \`specification\`
81
+ // block, so the renderer can only be given one of them at a time.) Scope your
82
+ // views, because computing one is superlinear in the number of edges and an
69
83
  // \`include *\` over a whole fleet takes minutes.
70
84
 
71
85
  specification {
@@ -78,6 +92,49 @@ specification {
78
92
  model {
79
93
  }
80
94
  `;
95
+ /**
96
+ * The LikeC4 project file, and the one thing in the docs repo loam writes for a
97
+ * tool other than itself.
98
+ *
99
+ * LikeC4's workspace loader merges EVERY `.likec4` file under a directory tree
100
+ * into one model. loam's layout is the opposite by design: the landscape,
101
+ * each `services/<id>/model.likec4` and each `features/<FEAT>/delta.likec4` is
102
+ * parsed in isolation (`loadSource`, one file, one throwaway workspace), so
103
+ * every one of them legitimately declares its own `specification { … }` block
104
+ * and re-declares the elements it needs to talk about. Point the real renderer
105
+ * at the repository root and those declarations collide: on loam's own
106
+ * `examples/docs` — four files — `npx likec4 start` reported 16 errors, sixteen
107
+ * duplicate kinds and names, while `loam validate --all` reported none. Both
108
+ * were right; only one of them was reading the tree as a workspace.
109
+ *
110
+ * So the root is declared as one LikeC4 project holding the landscape, and the
111
+ * two directories whose files are meant to be read alone are excluded from it.
112
+ * That makes `npx likec4 start` in the docs repo — the command loam's own docs
113
+ * have always recommended — render the fleet map instead of failing. A service
114
+ * model or a feature delta is still rendered by pointing the renderer at its own
115
+ * directory, which is what parsing them in isolation meant all along.
116
+ *
117
+ * `**\/node_modules/**` is repeated because naming `exclude` replaces LikeC4's
118
+ * default rather than adding to it. loam reads none of this file: it is written
119
+ * once, never re-read, and a team that wants a different project layout owns it
120
+ * like every other scaffolded file.
121
+ */
122
+ export const LIKEC4_PROJECT_FILENAME = "likec4.config.json";
123
+ export const LIKEC4_PROJECT_CONFIG = `${JSON.stringify({
124
+ name: "fleet",
125
+ title: "Fleet landscape",
126
+ exclude: ["**/node_modules/**", "services/**", "features/**"],
127
+ }, null, 2)}\n`;
128
+ /**
129
+ * The placeholder that makes an empty scaffolded directory survive a clone.
130
+ *
131
+ * git tracks files, not directories, so `services/` and `features/` — created
132
+ * empty by `loam init --create` — were absent for everyone after the first push:
133
+ * the person who ran init had a green repo, and the second person to clone it
134
+ * got `doctor.services-missing`, a BLOCKER, on a repository nobody had touched.
135
+ * A repo that cannot survive being cloned is not a shared docs repo.
136
+ */
137
+ const GITKEEP = "";
81
138
  /**
82
139
  * The docs repo's own loam.json. It makes the docs repo self-describing: a
83
140
  * command run from inside it (or from any directory under it) finds this file
@@ -131,6 +188,10 @@ export function docsRepoFiles(opts = {}) {
131
188
  [AGENTS_FILENAME, AGENTS_MD],
132
189
  [join("architecture", "landscape.likec4"), `${preamble}${LANDSCAPE_STUB}`],
133
190
  ["loam.json", DOCS_SELF_CONFIG],
191
+ [LIKEC4_PROJECT_FILENAME, LIKEC4_PROJECT_CONFIG],
192
+ // The two directories git would otherwise drop on the way to the next
193
+ // clone. `architecture/` needs none — the landscape above is in it.
194
+ ...EMPTY_SUBDIRS.map(([dir, keep]) => [join(dir, keep), GITKEEP]),
134
195
  ];
135
196
  }
136
197
  /** Everything `scaffoldDocs(docsDir)` would create, in the order it creates it. */
@@ -1 +1 @@
1
- {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/core/docs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Scaffolding for the shared docs repo.\n *\n * There is deliberately no manifest. `init` used to write a `loam.docs.json`\n * listing the repo's services; nothing ever read it — `repo.ts` enumerates from\n * the filesystem, because files are the source of truth — and nothing ever\n * updated it, so it named an empty fleet forever. A second list of services is\n * exactly the drift `loam validate` now cross-checks the landscape for; the\n * cheapest way to keep it honest is not to have it.\n */\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { AGENTS_MD } from \"./agent.js\";\nimport { AGENTS_FILENAME } from \"./repo.js\";\n\n/**\n * Top-level layout of the shared docs repo, and part of its identity rather\n * than a convenience: `services/` is what makes a directory a docs repo —\n * `init` will not join one without it, and every enumerating command refuses a\n * `docsDir` that has none. So it is laid down even when nothing is in it yet.\n */\nexport const DOCS_SUBDIRS = [\"architecture\", \"services\", \"features\"] as const;\n\n/**\n * The fleet map, empty but valid.\n *\n * Nobody writes this file automatically — `adopt` deliberately does not touch\n * it, because who calls whom is a human judgement and a generated landscape\n * would be a guess presented as the map. But \"nobody writes it\" used to mean\n * \"it is simply absent\", and an absent landscape is the one artifact whose\n * absence silences the fleet-wide checks entirely: every cross-service breach\n * `validate` exists to find is invisible on a repo with no landscape.\n *\n * So the scaffold lays down the empty map instead: the four element kinds a\n * fleet actually uses are declared, the model is empty, and the comments say\n * what to add and why. The first `loam adopt` then has somewhere to be drawn.\n *\n * No `views` block, deliberately. loam reads elements and relationships and\n * never a view, so the scaffolded `view index { include * }` was a diagram loam\n * planted on every repo and then never drew — and views are the expensive half\n * of LikeC4: computing them is superlinear in edge count (120 services / 300\n * calls does not finish in three minutes; parsing the same file takes 11 ms).\n * loam stopped computing views, so the block is free for loam itself now, but\n * scaffolding one still hands the user a fleet-sized bill for the first tool\n * that does compute it. Teams who want diagrams declare their own.\n */\nconst LANDSCAPE_STUB = `// The fleet map: every service in services/ appears here, and every call\n// between two of them is an edge. This file is written by hand — loam never\n// guesses it, because \"who calls whom\" is the one fact no generator can read\n// off a repository.\n//\n// After \\`loam adopt --service <id>\\`, add the service here:\n//\n// paymentService = softwareSystem 'payment-service' {\n// description 'Owns payment authorization/capture'\n// metadata { service 'payment-service' } // binds the box to services/<id>/\n// }\n//\n// and give each call the operationId it uses, so requirements, C4 and OpenAPI\n// can be cross-checked:\n//\n// checkoutWeb -> paymentService 'Authorizes' {\n// metadata { op 'authorizePayment' }\n// }\n//\n// There is no \\`views\\` block, on purpose: loam reads the model and never a\n// view, so it would draw nothing. Add views here if you want diagrams, and\n// render them with LikeC4's own tooling (\\`npx likec4 start\\`) — but scope them,\n// because computing a view is superlinear in the number of edges and an\n// \\`include *\\` over a whole fleet takes minutes.\n\nspecification {\n element person\n element softwareSystem\n element container\n element database\n}\n\nmodel {\n}\n`;\n\n/**\n * The docs repo's own loam.json. It makes the docs repo self-describing: a\n * command run from inside it (or from any directory under it) finds this file\n * first and resolves the fleet to the repo it is standing in, instead of\n * walking out to whatever service repo happens to be above.\n *\n * `\".\"` and not an absolute path for the same reason every other docsDir is\n * stored as written: this file is committed and cloned to machines whose\n * directory layout nobody here can predict.\n */\nconst DOCS_SELF_CONFIG = `${JSON.stringify({ docsDir: \".\" }, null, 2)}\\n`;\n\nexport interface ScaffoldResult {\n root: string;\n created: string[];\n}\n\n/** Idempotently create the docs-repo skeleton. Existing files/dirs are left untouched. */\nexport async function scaffoldDocs(docsDir: string): Promise<ScaffoldResult> {\n const root = resolve(docsDir);\n const created: string[] = [];\n\n await mkdir(root, { recursive: true });\n\n for (const dir of DOCS_SUBDIRS) {\n const p = join(root, dir);\n if (!existsSync(p)) {\n await mkdir(p, { recursive: true });\n created.push(p);\n }\n }\n\n // Never overwritten, all three of them — a team's own house rules, their own\n // map and their own config outrank the template. The order here IS the order\n // `init` probes for what it will skip; keep the two lists in step.\n for (const [rel, content] of docsRepoFiles()) {\n const path = join(root, rel);\n if (!existsSync(path)) {\n await writeFile(path, content, \"utf8\");\n created.push(path);\n }\n }\n\n return { root, created };\n}\n\nexport interface DocsRepoFileOptions {\n /**\n * Comment lines prepended to the landscape stub, for a scaffold with a reason\n * of its own to hand over an empty map — `migrate-openspec` stages a docs repo\n * out of a corpus that HAS no topology, and says so where the migrator reads.\n *\n * A parameter and not a second template on purpose. The rest of the stub —\n * how to declare a service, how to bind an edge to an operationId, why there\n * is no `views` block — is the same advice however the repo came to exist, and\n * the migration used to carry its own shorter copy of it: two files that were\n * both correct on the day they were written and could only diverge after.\n */\n landscapePreamble?: string;\n}\n\n/**\n * The files every docs repo starts with, relative to its root, in creation\n * order. `init` scaffolds them directly; `migrate-openspec` stages the same\n * bytes into its target, because the whole point of migrating into a docs repo\n * is that it be the docs repo `loam init` makes.\n *\n * Exported as paths only (via `plannedDocsFiles`) where `init` reports what it\n * will skip — a duplicate list is exactly the drift that made `created +\n * skipped` disagree with reality.\n */\nexport function docsRepoFiles(opts: DocsRepoFileOptions = {}): Array<[string, string]> {\n const preamble = opts.landscapePreamble === undefined ? \"\" : `${opts.landscapePreamble.trimEnd()}\\n//\\n`;\n return [\n // The process contract lives with the docs it describes, so an agent handed\n // only the docs repo still knows the cycle.\n [AGENTS_FILENAME, AGENTS_MD],\n [join(\"architecture\", \"landscape.likec4\"), `${preamble}${LANDSCAPE_STUB}`],\n [\"loam.json\", DOCS_SELF_CONFIG],\n ];\n}\n\n/** Everything `scaffoldDocs(docsDir)` would create, in the order it creates it. */\nexport function plannedDocsFiles(docsDir: string): string[] {\n const root = resolve(docsDir);\n return [\n ...DOCS_SUBDIRS.map((d) => join(root, d)),\n ...docsRepoFiles().map(([rel]) => join(root, rel)),\n ];\n}\n"]}
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/core/docs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Scaffolding for the shared docs repo.\n *\n * There is deliberately no manifest. `init` used to write a `loam.docs.json`\n * listing the repo's services; nothing ever read it — `repo.ts` enumerates from\n * the filesystem, because files are the source of truth — and nothing ever\n * updated it, so it named an empty fleet forever. A second list of services is\n * exactly the drift `loam validate` now cross-checks the landscape for; the\n * cheapest way to keep it honest is not to have it.\n */\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { AGENTS_MD } from \"./agent.js\";\nimport { AGENTS_FILENAME } from \"./repo.js\";\n\n/**\n * Top-level layout of the shared docs repo, and part of its identity rather\n * than a convenience: `services/` is what makes a directory a docs repo —\n * `init` will not join one without it, and every enumerating command refuses a\n * `docsDir` that has none. So it is laid down even when nothing is in it yet.\n */\nexport const DOCS_SUBDIRS = [\"architecture\", \"services\", \"features\"] as const;\n\n/**\n * The scaffolded directories that start out empty, and the file that keeps each\n * of them in version control. See {@link GITKEEP}. Spelled as pairs so the\n * marker's name is written once — `listServices` and `listFeatures` enumerate\n * subdirectories only, so a file here is invisible to both.\n */\nconst EMPTY_SUBDIRS = [\n [\"services\", \".gitkeep\"],\n [\"features\", \".gitkeep\"],\n] as const;\n\n/**\n * The fleet map, empty but valid.\n *\n * Nobody writes this file automatically — `adopt` deliberately does not touch\n * it, because who calls whom is a human judgement and a generated landscape\n * would be a guess presented as the map. But \"nobody writes it\" used to mean\n * \"it is simply absent\", and an absent landscape is the one artifact whose\n * absence silences the fleet-wide checks entirely: every cross-service breach\n * `validate` exists to find is invisible on a repo with no landscape.\n *\n * So the scaffold lays down the empty map instead: the four element kinds a\n * fleet actually uses are declared, the model is empty, and the comments say\n * what to add and why. The first `loam adopt` then has somewhere to be drawn.\n *\n * No `views` block, deliberately. loam reads elements and relationships and\n * never a view, so the scaffolded `view index { include * }` was a diagram loam\n * planted on every repo and then never drew — and views are the expensive half\n * of LikeC4: computing them is superlinear in edge count (120 services / 300\n * calls does not finish in three minutes; parsing the same file takes 11 ms).\n * loam stopped computing views, so the block is free for loam itself now, but\n * scaffolding one still hands the user a fleet-sized bill for the first tool\n * that does compute it. Teams who want diagrams declare their own.\n */\nconst LANDSCAPE_STUB = `// The fleet map: every service in services/ appears here, and every call\n// between two of them is an edge. This file is written by hand — loam never\n// guesses it, because \"who calls whom\" is the one fact no generator can read\n// off a repository.\n//\n// After \\`loam adopt --service <id>\\`, add the service here:\n//\n// paymentService = softwareSystem 'payment-service' {\n// description 'Owns payment authorization/capture'\n// metadata { service 'payment-service' } // binds the box to services/<id>/\n// }\n//\n// and give each call the operationId it uses, so requirements, C4 and OpenAPI\n// can be cross-checked:\n//\n// checkoutWeb -> paymentService 'Authorizes' {\n// metadata { op 'authorizePayment' }\n// }\n//\n// There is no \\`views\\` block, on purpose: loam reads the model and never a\n// view, so it would draw nothing. Add views here if you want diagrams, and\n// render them with LikeC4's own tooling — \\`npx likec4 start\\` from the docs\n// repo root, which likec4.config.json scopes to this directory. (A service\n// model or a feature delta renders from its OWN directory, e.g.\n// \\`npx likec4 start services/<id>\\`: each declares its own \\`specification\\`\n// block, so the renderer can only be given one of them at a time.) Scope your\n// views, because computing one is superlinear in the number of edges and an\n// \\`include *\\` over a whole fleet takes minutes.\n\nspecification {\n element person\n element softwareSystem\n element container\n element database\n}\n\nmodel {\n}\n`;\n\n/**\n * The LikeC4 project file, and the one thing in the docs repo loam writes for a\n * tool other than itself.\n *\n * LikeC4's workspace loader merges EVERY `.likec4` file under a directory tree\n * into one model. loam's layout is the opposite by design: the landscape,\n * each `services/<id>/model.likec4` and each `features/<FEAT>/delta.likec4` is\n * parsed in isolation (`loadSource`, one file, one throwaway workspace), so\n * every one of them legitimately declares its own `specification { … }` block\n * and re-declares the elements it needs to talk about. Point the real renderer\n * at the repository root and those declarations collide: on loam's own\n * `examples/docs` — four files — `npx likec4 start` reported 16 errors, sixteen\n * duplicate kinds and names, while `loam validate --all` reported none. Both\n * were right; only one of them was reading the tree as a workspace.\n *\n * So the root is declared as one LikeC4 project holding the landscape, and the\n * two directories whose files are meant to be read alone are excluded from it.\n * That makes `npx likec4 start` in the docs repo — the command loam's own docs\n * have always recommended — render the fleet map instead of failing. A service\n * model or a feature delta is still rendered by pointing the renderer at its own\n * directory, which is what parsing them in isolation meant all along.\n *\n * `**\\/node_modules/**` is repeated because naming `exclude` replaces LikeC4's\n * default rather than adding to it. loam reads none of this file: it is written\n * once, never re-read, and a team that wants a different project layout owns it\n * like every other scaffolded file.\n */\nexport const LIKEC4_PROJECT_FILENAME = \"likec4.config.json\";\n\nexport const LIKEC4_PROJECT_CONFIG = `${JSON.stringify(\n {\n name: \"fleet\",\n title: \"Fleet landscape\",\n exclude: [\"**/node_modules/**\", \"services/**\", \"features/**\"],\n },\n null,\n 2,\n)}\\n`;\n\n/**\n * The placeholder that makes an empty scaffolded directory survive a clone.\n *\n * git tracks files, not directories, so `services/` and `features/` — created\n * empty by `loam init --create` — were absent for everyone after the first push:\n * the person who ran init had a green repo, and the second person to clone it\n * got `doctor.services-missing`, a BLOCKER, on a repository nobody had touched.\n * A repo that cannot survive being cloned is not a shared docs repo.\n */\nconst GITKEEP = \"\";\n\n/**\n * The docs repo's own loam.json. It makes the docs repo self-describing: a\n * command run from inside it (or from any directory under it) finds this file\n * first and resolves the fleet to the repo it is standing in, instead of\n * walking out to whatever service repo happens to be above.\n *\n * `\".\"` and not an absolute path for the same reason every other docsDir is\n * stored as written: this file is committed and cloned to machines whose\n * directory layout nobody here can predict.\n */\nconst DOCS_SELF_CONFIG = `${JSON.stringify({ docsDir: \".\" }, null, 2)}\\n`;\n\nexport interface ScaffoldResult {\n root: string;\n created: string[];\n}\n\n/** Idempotently create the docs-repo skeleton. Existing files/dirs are left untouched. */\nexport async function scaffoldDocs(docsDir: string): Promise<ScaffoldResult> {\n const root = resolve(docsDir);\n const created: string[] = [];\n\n await mkdir(root, { recursive: true });\n\n for (const dir of DOCS_SUBDIRS) {\n const p = join(root, dir);\n if (!existsSync(p)) {\n await mkdir(p, { recursive: true });\n created.push(p);\n }\n }\n\n // Never overwritten, all three of them — a team's own house rules, their own\n // map and their own config outrank the template. The order here IS the order\n // `init` probes for what it will skip; keep the two lists in step.\n for (const [rel, content] of docsRepoFiles()) {\n const path = join(root, rel);\n if (!existsSync(path)) {\n await writeFile(path, content, \"utf8\");\n created.push(path);\n }\n }\n\n return { root, created };\n}\n\nexport interface DocsRepoFileOptions {\n /**\n * Comment lines prepended to the landscape stub, for a scaffold with a reason\n * of its own to hand over an empty map — `migrate-openspec` stages a docs repo\n * out of a corpus that HAS no topology, and says so where the migrator reads.\n *\n * A parameter and not a second template on purpose. The rest of the stub —\n * how to declare a service, how to bind an edge to an operationId, why there\n * is no `views` block — is the same advice however the repo came to exist, and\n * the migration used to carry its own shorter copy of it: two files that were\n * both correct on the day they were written and could only diverge after.\n */\n landscapePreamble?: string;\n}\n\n/**\n * The files every docs repo starts with, relative to its root, in creation\n * order. `init` scaffolds them directly; `migrate-openspec` stages the same\n * bytes into its target, because the whole point of migrating into a docs repo\n * is that it be the docs repo `loam init` makes.\n *\n * Exported as paths only (via `plannedDocsFiles`) where `init` reports what it\n * will skip — a duplicate list is exactly the drift that made `created +\n * skipped` disagree with reality.\n */\nexport function docsRepoFiles(opts: DocsRepoFileOptions = {}): Array<[string, string]> {\n const preamble = opts.landscapePreamble === undefined ? \"\" : `${opts.landscapePreamble.trimEnd()}\\n//\\n`;\n return [\n // The process contract lives with the docs it describes, so an agent handed\n // only the docs repo still knows the cycle.\n [AGENTS_FILENAME, AGENTS_MD],\n [join(\"architecture\", \"landscape.likec4\"), `${preamble}${LANDSCAPE_STUB}`],\n [\"loam.json\", DOCS_SELF_CONFIG],\n [LIKEC4_PROJECT_FILENAME, LIKEC4_PROJECT_CONFIG],\n // The two directories git would otherwise drop on the way to the next\n // clone. `architecture/` needs none — the landscape above is in it.\n ...EMPTY_SUBDIRS.map(([dir, keep]) => [join(dir, keep), GITKEEP] as [string, string]),\n ];\n}\n\n/** Everything `scaffoldDocs(docsDir)` would create, in the order it creates it. */\nexport function plannedDocsFiles(docsDir: string): string[] {\n const root = resolve(docsDir);\n return [\n ...DOCS_SUBDIRS.map((d) => join(root, d)),\n ...docsRepoFiles().map(([rel]) => join(root, rel)),\n ];\n}\n"]}
@@ -6,6 +6,7 @@ import { CONFIG_FILENAME, configPath, parseConfig } from "./config.js";
6
6
  import { LOAM_VERSION } from "./version.js";
7
7
  import { agentsPath, docsRepoState, landscapePath, listFeatures, listServices } from "./repo.js";
8
8
  import { loadFile } from "./likec4.js";
9
+ import { LIKEC4_PROJECT_CONFIG, LIKEC4_PROJECT_FILENAME } from "./docs.js";
9
10
  import { conflictMarkerLines } from "./fleet-context.js";
10
11
  import { AGENT_TOOLS, DELIVERIES, plannedCommandFiles } from "./agent.js";
11
12
  import { agentsStaleFinding, agentsStampLine, agentsStampVersion, versionTrails, } from "./agents-stamp.js";
@@ -459,6 +460,30 @@ export async function diagnose(cwd = process.cwd()) {
459
460
  else if (exists && landscapeFile !== null && readable) {
460
461
  await inspectLandscape(landscapeFile, findings);
461
462
  }
463
+ // The renderer's view of the repo, which is not loam's. loam parses every
464
+ // `.likec4` file ALONE, so each one declares its own `specification` block and
465
+ // re-declares whatever elements it names; LikeC4's own workspace loader merges
466
+ // the whole tree into one model, and those declarations then collide. The
467
+ // result was two tools disagreeing in the most confusing possible direction:
468
+ // `loam validate --all` reported zero errors on a tree where `npx likec4
469
+ // start` — the command loam's own brief recommends — refused to load at all.
470
+ // `loam init --create` writes the project file that scopes the root project to
471
+ // the landscape; a docs repo created before it exists has to be told, because
472
+ // nothing else will ever mention it. A warning, not a blocker: no loam check
473
+ // reads this file, and a repo without it is only unrenderable.
474
+ if (docsDir !== null && exists && readable && !existsSync(join(docsDir, LIKEC4_PROJECT_FILENAME))) {
475
+ findings.push({
476
+ severity: "warning",
477
+ code: "doctor.likec4-config-missing",
478
+ message: `${docsDir}/${LIKEC4_PROJECT_FILENAME} is missing, so this tree is not a loadable LikeC4 workspace — `
479
+ + "every service model and feature delta declares its own `specification` block, and pointing a "
480
+ + "renderer at the repo root merges them into one model and reports every declaration as a duplicate.",
481
+ fix: `Write ${docsDir}/${LIKEC4_PROJECT_FILENAME} with:\n${LIKEC4_PROJECT_CONFIG.trimEnd()}\n`
482
+ + "That scopes the root project to architecture/, so `npx likec4 start` in the docs repo renders "
483
+ + "the fleet map. A service model or feature delta is rendered by pointing the renderer at its own "
484
+ + "directory (`npx likec4 start services/<id>`) — being readable alone is what those files are for.",
485
+ });
486
+ }
462
487
  // What a killed writer left behind. Read from `scanWritePathResidue`, whose
463
488
  // spellings of `.loam-lock` / `.loam-commit` / the temp-file pattern are
464
489
  // staging's own — doctor grades, it does not re-spell. This is the surface
@@ -498,7 +523,19 @@ export async function diagnose(cwd = process.cwd()) {
498
523
  : services.some((service) => service.id === configuredService)
499
524
  ? { configured: configuredService, status: "matched" }
500
525
  : { configured: configuredService, status: "unknown" };
501
- if (currentService.status === "unbound") {
526
+ // Standing INSIDE the docs repo, an absent service binding is the correct
527
+ // state, not a gap: the docs repo is the fleet, it is not any one service, and
528
+ // the fix this finding prints — `loam init --service <id>` — would bind the
529
+ // shared repo to one of the services it holds, which is not a thing loam has
530
+ // a meaning for. doctor already knew where it was standing (it prints the
531
+ // fleet count two lines up) and reported the warning anyway, so the first
532
+ // preflight anyone ran in a freshly created docs repo came back yellow with
533
+ // advice that made it worse. The docs repo's own loam.json is the one that
534
+ // resolves `docsDir` to the directory holding it — that identity, not a
535
+ // filename, is the test.
536
+ const inDocsRepo = configRoot !== undefined && docsDir !== null
537
+ && resolve(configRoot) === resolve(docsDir);
538
+ if (currentService.status === "unbound" && !inDocsRepo) {
502
539
  findings.push({
503
540
  severity: "warning",
504
541
  code: "doctor.service-unbound",