@ybotok/loam 0.0.0 → 0.1.0-beta.2

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.
Files changed (126) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/COMPARISON.md +63 -0
  3. package/LICENSE +21 -0
  4. package/MIGRATING-from-OpenSpec.md +202 -0
  5. package/README.md +246 -9
  6. package/SCHEMA.md +425 -0
  7. package/SECURITY.md +9 -0
  8. package/dist/cli.js +142 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/commands/adopt.js +240 -0
  11. package/dist/commands/adopt.js.map +1 -0
  12. package/dist/commands/archive.js +1313 -0
  13. package/dist/commands/archive.js.map +1 -0
  14. package/dist/commands/delta.js +388 -0
  15. package/dist/commands/delta.js.map +1 -0
  16. package/dist/commands/dependencies.js +83 -0
  17. package/dist/commands/dependencies.js.map +1 -0
  18. package/dist/commands/docs-repo-gate.js +104 -0
  19. package/dist/commands/docs-repo-gate.js.map +1 -0
  20. package/dist/commands/doctor.js +85 -0
  21. package/dist/commands/doctor.js.map +1 -0
  22. package/dist/commands/explore.js +195 -0
  23. package/dist/commands/explore.js.map +1 -0
  24. package/dist/commands/format.js +17 -0
  25. package/dist/commands/format.js.map +1 -0
  26. package/dist/commands/gherkin.js +309 -0
  27. package/dist/commands/gherkin.js.map +1 -0
  28. package/dist/commands/init.js +250 -0
  29. package/dist/commands/init.js.map +1 -0
  30. package/dist/commands/instructions.js +70 -0
  31. package/dist/commands/instructions.js.map +1 -0
  32. package/dist/commands/list.js +291 -0
  33. package/dist/commands/list.js.map +1 -0
  34. package/dist/commands/migrate-openspec.js +950 -0
  35. package/dist/commands/migrate-openspec.js.map +1 -0
  36. package/dist/commands/new.js +413 -0
  37. package/dist/commands/new.js.map +1 -0
  38. package/dist/commands/rebase.js +356 -0
  39. package/dist/commands/rebase.js.map +1 -0
  40. package/dist/commands/show.js +320 -0
  41. package/dist/commands/show.js.map +1 -0
  42. package/dist/commands/status.js +254 -0
  43. package/dist/commands/status.js.map +1 -0
  44. package/dist/commands/unarchive.js +317 -0
  45. package/dist/commands/unarchive.js.map +1 -0
  46. package/dist/commands/validate.js +1591 -0
  47. package/dist/commands/validate.js.map +1 -0
  48. package/dist/commands/verify.js +775 -0
  49. package/dist/commands/verify.js.map +1 -0
  50. package/dist/commands/vouch.js +343 -0
  51. package/dist/commands/vouch.js.map +1 -0
  52. package/dist/core/agent.js +2065 -0
  53. package/dist/core/agent.js.map +1 -0
  54. package/dist/core/agents-stamp.js +68 -0
  55. package/dist/core/agents-stamp.js.map +1 -0
  56. package/dist/core/arch.js +130 -0
  57. package/dist/core/arch.js.map +1 -0
  58. package/dist/core/brief.js +733 -0
  59. package/dist/core/brief.js.map +1 -0
  60. package/dist/core/coherence.js +577 -0
  61. package/dist/core/coherence.js.map +1 -0
  62. package/dist/core/concurrency.js +51 -0
  63. package/dist/core/concurrency.js.map +1 -0
  64. package/dist/core/config.js +180 -0
  65. package/dist/core/config.js.map +1 -0
  66. package/dist/core/delta.js +476 -0
  67. package/dist/core/delta.js.map +1 -0
  68. package/dist/core/dependencies.js +356 -0
  69. package/dist/core/dependencies.js.map +1 -0
  70. package/dist/core/docs.js +144 -0
  71. package/dist/core/docs.js.map +1 -0
  72. package/dist/core/doctor.js +559 -0
  73. package/dist/core/doctor.js.map +1 -0
  74. package/dist/core/document-bytes.js +59 -0
  75. package/dist/core/document-bytes.js.map +1 -0
  76. package/dist/core/explore.js +225 -0
  77. package/dist/core/explore.js.map +1 -0
  78. package/dist/core/fleet-context.js +225 -0
  79. package/dist/core/fleet-context.js.map +1 -0
  80. package/dist/core/frontmatter.js +199 -0
  81. package/dist/core/frontmatter.js.map +1 -0
  82. package/dist/core/gherkin.js +463 -0
  83. package/dist/core/gherkin.js.map +1 -0
  84. package/dist/core/health.js +67 -0
  85. package/dist/core/health.js.map +1 -0
  86. package/dist/core/ids.js +122 -0
  87. package/dist/core/ids.js.map +1 -0
  88. package/dist/core/issue.js +9 -0
  89. package/dist/core/issue.js.map +1 -0
  90. package/dist/core/json.js +74 -0
  91. package/dist/core/json.js.map +1 -0
  92. package/dist/core/likec4.js +479 -0
  93. package/dist/core/likec4.js.map +1 -0
  94. package/dist/core/maturity.js +66 -0
  95. package/dist/core/maturity.js.map +1 -0
  96. package/dist/core/openapi-merge.js +552 -0
  97. package/dist/core/openapi-merge.js.map +1 -0
  98. package/dist/core/openapi.js +255 -0
  99. package/dist/core/openapi.js.map +1 -0
  100. package/dist/core/openspec-inventory.js +1440 -0
  101. package/dist/core/openspec-inventory.js.map +1 -0
  102. package/dist/core/path-safety.js +168 -0
  103. package/dist/core/path-safety.js.map +1 -0
  104. package/dist/core/provenance.js +720 -0
  105. package/dist/core/provenance.js.map +1 -0
  106. package/dist/core/records.js +33 -0
  107. package/dist/core/records.js.map +1 -0
  108. package/dist/core/repo.js +436 -0
  109. package/dist/core/repo.js.map +1 -0
  110. package/dist/core/report.js +47 -0
  111. package/dist/core/report.js.map +1 -0
  112. package/dist/core/results.js +199 -0
  113. package/dist/core/results.js.map +1 -0
  114. package/dist/core/spec.js +481 -0
  115. package/dist/core/spec.js.map +1 -0
  116. package/dist/core/staging.js +890 -0
  117. package/dist/core/staging.js.map +1 -0
  118. package/dist/core/status.js +1104 -0
  119. package/dist/core/status.js.map +1 -0
  120. package/dist/core/steps.js +72 -0
  121. package/dist/core/steps.js.map +1 -0
  122. package/dist/core/verify.js +807 -0
  123. package/dist/core/verify.js.map +1 -0
  124. package/dist/core/version.js +24 -0
  125. package/dist/core/version.js.map +1 -0
  126. package/package.json +63 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/core/verify.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The done-check: did the code that was built match the feature that was\n * designed?\n *\n * The original plan was to extract C4 from the built code and diff it against\n * the delta. There is no such extractor and there will not be one — nothing\n * deterministic reads a service and says what its architecture MEANS, and two\n * generated models of the same code disagree in wording every run, so the diff\n * would flap and be switched off inside a week.\n *\n * What survives of the idea is the part that was always deterministic: the\n * QUESTIONS. A feature's own artifacts say exactly what it promised — this\n * service will exist, it will expose this operation, that service will call it,\n * this scenario will have a test. loam derives that list mechanically from the\n * same files `validate` already reads, an agent answers each claim with\n * evidence, and `--record` writes the answers down beside the feature.\n *\n * A scenario claim is answered mechanically wherever it can be: `--results`\n * reads a cucumber JSON report and matches it by the digest tag `loam gherkin`\n * stamped, and only a green run confirms one (results.ts). Where it cannot —\n * a legacy service whose suite is months away, which is the fleet loam exists\n * for — `--record` may still take an agent's word for it, and the record marks\n * that answer ATTESTED rather than run: `verified` is reserved for a checklist\n * whose scenarios a run answered, and `verify.scenario-attested` says so on\n * every surface that reads the record.\n *\n * Two properties make the record worth keeping. The claim ids are a function of\n * the claim and of nothing else — so two runs are diffable, reordering the delta\n * renames nothing, and rewording a scenario DOES rename its claim, because an\n * answer about text nobody wrote must not carry over. And an answer set that\n * does not correspond to the current checklist is refused rather than merged:\n * an unchecked claim must never be able to masquerade as checked.\n *\n * loam does not judge the answers. It cannot: it never read the code. It only\n * guarantees that every question was asked and that every answer is on the\n * record next to the question it answers.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parse, stringify } from \"yaml\";\nimport { elementService, loadFile, serviceOf, type Elem } from \"./likec4.js\";\nimport { operationIds, operations } from \"./openapi.js\";\nimport { isRecord } from \"./records.js\";\nimport { featurePaths, featureSpecPaths, featureSpecServices, servicePaths } from \"./repo.js\";\nimport { parseRequirements } from \"./spec.js\";\n\n/**\n * What a claim is about. The order is the order the checklist comes back in,\n * and it reads as the story of the feature: the service exists, it exposes its\n * operations, the calls into it are wired, the behaviour is tested.\n */\nexport const CLAIM_KINDS = [\"service.exists\", \"api.exposes\", \"c4.calls\", \"scenario.tested\"] as const;\nexport type ClaimKind = (typeof CLAIM_KINDS)[number];\n\nexport interface Claim {\n /** `<kind>-<8 hex>` — stable for the life of the claim. See `claimId`. */\n id: string;\n kind: ClaimKind;\n /** The service whose code answers this. A caller's claim is filed under the caller. */\n subject: string;\n /** The claim in one line, answerable without reading the feature's files. */\n claim: string;\n /**\n * `scenario.tested` only: the first {@link DIGEST_LENGTH} hex of the\n * scenario's body hash — the exact digest `loam gherkin` stamps as\n * `@loam-digest-…`, which is what a cucumber report scenario carries and\n * what `--results` matches on. Absent on every other kind.\n */\n digest?: string;\n}\n\nexport interface Checklist {\n feature: string;\n claims: Claim[];\n /** A digest of the claim id SET — what says a record still answers this feature. */\n digest: string;\n}\n\n/**\n * How much of the sha256 goes into an id, and into a digest — the checklist's,\n * and a scenario claim's runner-matching one, which is deliberately the same\n * 16 hex `loam gherkin` stamps (its GHERKIN_DIGEST_LENGTH): the tag in a\n * cucumber report and the digest on a claim must be the same string.\n */\nconst ID_LENGTH = 8;\nconst DIGEST_LENGTH = 16;\n\n/** C4 kinds that model people. A person is never a service. (Mirrors validate.ts.) */\nconst ACTOR_KINDS = new Set([\"person\", \"actor\", \"user\"]);\n\n/**\n * Derive the checklist from a feature's own artifacts. No code is read here and\n * none should be: the whole design rests on the questions being computed from\n * documents that do not change between runs.\n */\nexport async function featureChecklist(\n docsDir: string,\n featureDir: string,\n featureId: string,\n): Promise<Checklist> {\n const seen = new Map<string, number>();\n const claim = (kind: ClaimKind, subject: string, parts: string[], text: string): Claim => ({\n id: claimId(featureId, kind, parts, seen),\n kind,\n subject,\n claim: text,\n });\n\n const exists: Claim[] = [];\n const exposes: Claim[] = [];\n const calls: Claim[] = [];\n const scenarios: Claim[] = [];\n\n // Architecture — what the delta promised the fleet would look like.\n const deltaPath = featurePaths(featureDir).delta;\n if (existsSync(deltaPath)) {\n const res = await loadFile(deltaPath);\n // A delta nobody can parse promises nothing checkable. `validate` already\n // reports it; inventing claims out of a broken document would only add noise.\n if (res.errors.length === 0) {\n const elements: Elem[] = res.elements;\n for (const e of elements) {\n if (!e.tags.includes(featureId)) continue;\n // A dotted id is a container INSIDE a service, and an actor is nobody's\n // repository. Neither is a service that has to exist.\n if (e.id.includes(\".\") || ACTOR_KINDS.has(e.kind.toLowerCase())) continue;\n const svc = elementService(e);\n exists.push(claim(\"service.exists\", svc, [svc], `service '${svc}' exists`));\n }\n for (const r of res.relationships) {\n // Untagged edges are context for the diagram; an edge with no operation\n // names nothing specific enough to look for in the code.\n if (!r.tags.includes(featureId) || r.op === undefined) continue;\n const from = serviceOf(elements, r.source);\n const to = serviceOf(elements, r.target);\n calls.push(\n claim(\"c4.calls\", from, [from, to, r.op], `${from} calls '${r.op}' on ${to}`),\n );\n }\n }\n }\n\n for (const svc of await featureSpecServices(featureDir)) {\n const paths = featureSpecPaths(featureDir, svc);\n\n // Contract — only what is NEW. A delta's openapi.yaml is a whole document,\n // not a patch, so authors restate the living API inside it; asking whether a\n // service still exposes what it already exposed is noise.\n const featOps = (await operations(paths.openapi))\n .filter((operation) => !operation.remove)\n .map((operation) => operation.id);\n if (featOps.length > 0) {\n const living = new Set(await operationIds(servicePaths(docsDir, svc).openapi));\n for (const op of featOps) {\n if (living.has(op)) continue;\n exposes.push(\n claim(\"api.exposes\", svc, [svc, op], `${svc} exposes operationId '${op}'`),\n );\n }\n }\n\n // Behaviour — the scenarios of the requirements this feature CHANGES. A BASE\n // requirement is the living state quoted inside the delta, and a REMOVED one\n // is being retired: neither is work anybody has to have done.\n if (existsSync(paths.spec)) {\n for (const r of parseRequirements(await readFile(paths.spec, \"utf8\"))) {\n if (r.kind === \"BASE\" || r.kind === \"REMOVED\") continue;\n for (const s of r.scenarios) {\n const body = scenarioBodyHash(svc, s.lines);\n scenarios.push({\n ...claim(\n \"scenario.tested\",\n svc,\n [svc, r.id ?? r.name, s.name, body.slice(0, ID_LENGTH)],\n `scenario '${s.name}' of requirement '${r.name}' (${svc}) is covered by a test`,\n ),\n digest: body.slice(0, DIGEST_LENGTH),\n });\n }\n }\n }\n\n // The architecture spec axis, same walk: an arch scenario is a promise like\n // any other — the outbox test nobody was going to write IS the point of the\n // axis. The source file rides in the id tuple AND in the body hash itself\n // (two axes are two namespaces: identically-worded scenarios must stay two\n // questions, and the axis-salted digest is what keeps a business-only test\n // run from answering the arch claim) and in the claim text, so the\n // answering agent knows it is being asked for an integration/ops test, not\n // an acceptance test.\n if (existsSync(paths.archSpec)) {\n for (const r of parseRequirements(await readFile(paths.archSpec, \"utf8\"))) {\n if (r.kind === \"BASE\" || r.kind === \"REMOVED\") continue;\n for (const s of r.scenarios) {\n const body = scenarioBodyHash(svc, s.lines, \"arch\");\n scenarios.push({\n ...claim(\n \"scenario.tested\",\n svc,\n [svc, \"arch.spec.md\", r.id ?? r.name, s.name, body.slice(0, ID_LENGTH)],\n `scenario '${s.name}' of arch requirement '${r.name}' (${svc}, arch.spec.md) is covered by a test`,\n ),\n digest: body.slice(0, DIGEST_LENGTH),\n });\n }\n }\n }\n }\n\n const claims = [...exists, ...exposes, ...calls, ...scenarios];\n return { feature: featureId, claims, digest: checklistDigest(claims) };\n}\n\n/**\n * A claim's identity: a hash of what it says, and nothing about how it was\n * produced.\n *\n * The feature id is part of it so an answers file for one feature can never\n * validate against another. Two claims that really are identical (the same\n * scenario name twice under one requirement) are distinguished by occurrence, in\n * document order — they are still two questions, and answering one must not\n * answer the other.\n *\n * The hash is short because the claim text sits next to it everywhere it is\n * shown; it identifies a question, it does not authenticate one.\n */\nfunction claimId(\n featureId: string,\n kind: ClaimKind,\n parts: string[],\n seen: Map<string, number>,\n): string {\n // NUL-joined so no claim's own text can spell another claim's tuple by\n // containing the separator: ['a b','c'] and ['a','b c'] stay two questions.\n const tuple = [featureId, kind, ...parts].join(\"\\u0000\");\n const n = (seen.get(tuple) ?? 0) + 1;\n seen.set(tuple, n);\n const canonical = n === 1 ? tuple : `${tuple}\\u0000#${n}`;\n return `${kind}-${createHash(\"sha256\").update(canonical).digest(\"hex\").slice(0, ID_LENGTH)}`;\n}\n\n/** The axis a scenario's words live on: `spec.md` or `arch.spec.md`. */\nexport type ScenarioAxis = \"business\" | \"arch\";\n\n/**\n * The full sha256 of a scenario's body — its lines joined and edge-trimmed,\n * exactly as `serializeRequirements` frames them. The BODY, not the title,\n * because rewriting the Given/When/Then under an unchanged heading is new text\n * nobody answered for, and the promise — rewording a scenario renames its\n * claim — has to hold for the words that actually specify the behaviour.\n *\n * ONE recipe with three consumers, deliberately in one place:\n * `scenario.tested` claim ids fold in its first {@link ID_LENGTH} hex\n * characters, a claim's `digest` and the `@loam-digest-<16hex>` tag `loam\n * gherkin` stamps both take its first {@link DIGEST_LENGTH} — so the claim,\n * the stamp and the report `--results` reads can never disagree about what a\n * scenario says.\n *\n * The identity is (SERVICE, AXIS, body), and both salts are there for the same\n * reason: a digest that spans two namespaces lets one green run answer a\n * question nobody ran a test for.\n *\n * - The AXIS salt keeps `spec.md` and `arch.spec.md` apart. Without it a green\n * BUSINESS run answered the arch claim too — an integration test nobody wrote\n * read as run.\n * - The SERVICE salt keeps two repositories apart. \"the service returns 404 for\n * an unknown id\" is worded the same way in nine services of a real fleet;\n * unsalted, all nine shared one digest, `loam gherkin` stamped one tag into\n * nine repositories, and one repository's green run confirmed the other\n * eight's claims — across suites that never ran each other's tests.\n *\n * The service salt is what makes that case CORRECT rather than merely refused:\n * `contestedDigests` could only decline to answer a shared digest, which left a\n * fleet with ordinary repeated wording holding claims nothing could ever\n * answer.\n *\n * Both salts renamed every claim id and every stamped tag on the day they\n * landed, and both readings are legible rather than silent: an existing\n * `verification.yaml` answers a checklist digest that is no longer derived and\n * reports STALE, and an existing generated `.feature` carries digests the\n * living spec no longer computes and reports `gherkin.stale` beside the\n * `gherkin.missing` for the new ones. One `loam gherkin` and one re-record\n * clear both.\n */\nexport function scenarioBodyHash(\n service: string,\n lines: string[],\n axis: ScenarioAxis = \"business\",\n): string {\n // NUL-separated like claimId's tuples, and for its reason: no body can spell\n // the salt in front of it, so the namespaces stay disjoint. Spelled as an\n // escape and never as a raw NUL in the literal — a raw one makes this file\n // read as `data` to `file(1)` and invisible to `grep`.\n const body = lines.join(\"\\n\").trim();\n const onAxis = axis === \"arch\" ? `arch.spec.md\\u0000${body}` : body;\n return createHash(\"sha256\").update(`${service}\\u0000${onAxis}`).digest(\"hex\");\n}\n\n/**\n * A digest of the claim id SET — sorted, so reordering the artifacts does not\n * make a record look stale. It changes when a claim is added, removed or\n * reworded, which is exactly when an answer set stops describing the feature.\n */\nfunction checklistDigest(claims: Claim[]): string {\n const ids = claims.map((c) => c.id).sort();\n return createHash(\"sha256\").update(ids.join(\"\\n\")).digest(\"hex\").slice(0, DIGEST_LENGTH);\n}\n\n/* ------------------------------------------------------------------ */\n/* The answers */\n/* ------------------------------------------------------------------ */\n\n/**\n * Two verdicts, deliberately. Anything an agent cannot show evidence for is\n * unconfirmed — a third value for \"not applicable\" would absorb every claim\n * nobody wanted to answer.\n */\nexport const VERDICTS = [\"confirmed\", \"unconfirmed\"] as const;\nexport type Verdict = (typeof VERDICTS)[number];\n\n/**\n * Who answered a claim: the `runner` (a cucumber report's digest-tagged\n * scenarios, matched mechanically by `--results`) or an `agent` (somebody's\n * word about the code, taken back by `--record`). On the record so a reviewer\n * can tell a green run from an assertion.\n */\nexport const ANSWERED_BY = [\"runner\", \"agent\"] as const;\nexport type AnsweredBy = (typeof ANSWERED_BY)[number];\n\nexport interface Answer {\n id: string;\n verdict: Verdict;\n /** Where it can be seen — `file:line`, or a report scenario for the runner. Required for `confirmed`. */\n evidence: string[];\n note?: string;\n answered_by: AnsweredBy;\n}\n\n/** Why an answer set was refused. Each names a different way it fails to answer. */\nexport type AnswerRefusal = \"answers-unreadable\" | \"answers-mismatch\" | \"answers-unevidenced\";\n\nexport type AnswerCheck =\n /** The answers, re-ordered to the checklist, one per claim. */\n | { ok: true; answers: Answer[] }\n | { ok: false; code: AnswerRefusal; message: string };\n\n/**\n * Accept an answer set only if it answers THIS checklist: every claim once, no\n * claim twice, nothing extra, and no \"confirmed\" that shows nothing.\n *\n * The refusals are the point of the command. If a stale or partial answer file\n * could be recorded, `verification.yaml` would say a claim was checked when\n * nobody ever asked it — and a record that can lie is worse than no record,\n * because it looks like evidence.\n *\n * `runnerOwned` is the composition rule under `--results`: ids the test runner\n * answers, which an answers file must therefore not touch. An entry naming one\n * refuses with its own diagnosis — the id IS on the feature's checklist, so\n * calling it unknown would send the caller hunting a staleness that is not\n * there.\n *\n * `context` is what turns the last line from a circle into an instruction. In a\n * fleet the commonest way to \"miss\" a claim is that it is not yours: the\n * checklist spans ten repositories and this one can only answer its own. Told\n * only to \"answer the claims it lists\", an agent in the payment-service repo\n * re-runs verify, sees the same checkout-web claims it cannot speak for, and\n * either loops or invents evidence. So when every unanswered claim belongs to\n * another service, say THAT, and name the command that records them.\n */\nexport function checkAnswers(\n claims: Claim[],\n raw: unknown,\n runnerOwned?: ReadonlySet<string>,\n context?: { feature: string; service?: string },\n): AnswerCheck {\n const refuse = (code: AnswerRefusal, message: string): AnswerCheck => ({ ok: false, code, message });\n\n const list = Array.isArray(raw)\n ? raw\n : isRecord(raw) && Array.isArray(raw[\"answers\"])\n ? (raw[\"answers\"] as unknown[])\n : null;\n if (list === null) {\n return refuse(\n \"answers-unreadable\",\n \"Expected a JSON array of answers, or an object with an `answers` array: \" +\n `[{ \"id\": \"...\", \"verdict\": \"confirmed\", \"evidence\": [\"file:line\"] }]`,\n );\n }\n\n const answers: Answer[] = [];\n for (const [i, entry] of list.entries()) {\n const where = `answer ${i + 1}`;\n if (!isRecord(entry)) return refuse(\"answers-unreadable\", `${where} is not an object`);\n const id = entry[\"id\"];\n if (typeof id !== \"string\" || id.length === 0) {\n return refuse(\"answers-unreadable\", `${where} has no claim id`);\n }\n const verdict = entry[\"verdict\"];\n if (typeof verdict !== \"string\" || !(VERDICTS as readonly string[]).includes(verdict)) {\n return refuse(\n \"answers-unreadable\",\n `${where} ('${id}') has verdict ${JSON.stringify(verdict)} — expected one of ${VERDICTS.join(\", \")}`,\n );\n }\n const note = typeof entry[\"note\"] === \"string\" ? entry[\"note\"].trim() : \"\";\n answers.push({\n id,\n verdict: verdict as Verdict,\n evidence: stringList(entry[\"evidence\"]),\n ...(note.length > 0 ? { note } : {}),\n answered_by: \"agent\",\n });\n }\n\n const byId = new Map<string, Answer>();\n const twice: string[] = [];\n for (const a of answers) {\n if (byId.has(a.id)) twice.push(a.id);\n else byId.set(a.id, a);\n }\n const known = new Set(claims.map((c) => c.id));\n const strayIds = [...new Set(answers.map((a) => a.id).filter((id) => !known.has(id)))];\n const runnerHit = strayIds.filter((id) => runnerOwned?.has(id) === true);\n const unknown = strayIds.filter((id) => runnerOwned?.has(id) !== true);\n const missingClaims = claims.filter((c) => !byId.has(c.id));\n const missing = missingClaims.map((c) => c.id);\n if (runnerHit.length + unknown.length + missing.length + twice.length > 0) {\n const parts = [\n runnerHit.length > 0\n ? `${runnerHit.length} answer(s) name scenario claim(s) the test runner owns under --results: ${runnerHit.join(\", \")} — the report answers those; take them out of the answers file`\n : \"\",\n unknown.length > 0 ? `${unknown.length} answer(s) name a claim that is not on the checklist: ${unknown.join(\", \")}` : \"\",\n missing.length > 0 ? `${missing.length} claim(s) have no answer: ${missing.join(\", \")}` : \"\",\n twice.length > 0 ? `${twice.length} claim(s) answered more than once: ${[...new Set(twice)].join(\", \")}` : \"\",\n ].filter((s) => s.length > 0);\n return refuse(\n \"answers-mismatch\",\n `These answers do not match the checklist — ${parts.join(\"; \")}. ` +\n missingAdvice(missingClaims, claims.filter((c) => byId.has(c.id)), context),\n );\n }\n\n const unevidenced = claims\n .map((c) => byId.get(c.id)!)\n .filter((a) => a.verdict === \"confirmed\" && a.evidence.length === 0)\n .map((a) => a.id);\n if (unevidenced.length > 0) {\n return refuse(\n \"answers-unevidenced\",\n `${unevidenced.length} claim(s) are confirmed with no evidence: ${unevidenced.join(\", \")}. A confirmation with nothing behind it is an assertion — give a file:line, or answer 'unconfirmed'.`,\n );\n }\n\n // Checklist order, not the order they were written in: the record reads in the\n // order the questions were asked, whatever the agent did.\n return { ok: true, answers: claims.map((c) => byId.get(c.id)!) };\n}\n\n/**\n * The last sentence of an `answers-mismatch`. \"Re-run and answer the claims it\n * lists\" is sound advice only when the caller CAN answer them. In a fleet the\n * checklist is federated — a feature's claims are filed under the service whose\n * code answers them — so the honest diagnosis for a repo that answered\n * everything it owns is \"the rest is not yours\", together with the form that\n * records the rest. Anything else sends the caller round the same loop.\n */\nfunction missingAdvice(\n missing: Claim[],\n answered: Claim[],\n context?: { feature: string; service?: string },\n): string {\n const generic = \"Re-run `loam verify` and answer the claims it lists.\";\n if (context === undefined || missing.length === 0 || answered.length === 0) return generic;\n // The signature of \"I answered mine and the rest is not mine\": the services\n // that own the unanswered claims and the services that own the answered ones\n // do not overlap. A single-service checklist with one claim forgotten fails\n // this test and keeps the generic advice, which is the right advice there —\n // that claim really is the caller's to answer.\n const owners = [...new Set(missing.map((c) => c.subject))].sort();\n const covered = new Set(answered.map((c) => c.subject));\n if (owners.some((s) => covered.has(s))) return generic;\n const form = `\\`loam verify ${context.feature} --record answers.json --service <svc>\\``;\n const mine = context.service;\n return mine === undefined\n ? `Those claims are owned by ${owners.join(\", \")} — each service attests its own from its own repository (${form}); ` +\n \"this all-at-once form has to answer the whole checklist in one file.\"\n : `Every unanswered claim belongs to a different service (${owners.join(\", \")}) and this repository can only attest '${mine}' — ` +\n `run ${form} in each of those repositories instead.`;\n}\n\nfunction stringList(v: unknown): string[] {\n if (typeof v === \"string\") return v.trim().length > 0 ? [v.trim()] : [];\n if (!Array.isArray(v)) return [];\n return v.map((x) => String(x).trim()).filter((s) => s.length > 0);\n}\n\n/* ------------------------------------------------------------------ */\n/* The record */\n/* ------------------------------------------------------------------ */\n\nexport interface RecordedClaim {\n id: string;\n kind: ClaimKind;\n /** Present in federated records; omitted by legacy records written before schema 2. */\n subject?: string;\n claim: string;\n verdict: Verdict;\n /** Who answered — absent only in records written before `--results` existed. */\n answered_by?: AnsweredBy;\n evidence: string[];\n note?: string;\n}\n\n/**\n * The test report a `--results` run consumed.\n *\n * Nothing can prove a JSON file came from executing a particular commit — a\n * report is bytes, and bytes are writable. What loam can do is stop leaving the\n * question open: it records exactly which file it read (inside the attesting\n * repository, resolved by the same path rules as evidence), the sha256 of the\n * bytes it read, the file's own mtime and how many digest-tagged scenarios it\n * carried. A reviewer holding the repository can tell whether this is that\n * file; before this, `answered_by: runner` named nothing at all.\n */\nexport interface ConsumedReport {\n /** As it was passed to `--results` — repo-relative in a federated record. */\n path: string;\n /** Full sha256 of the bytes read, so `shasum -a 256 <path>` answers the question. */\n digest: string;\n /** ISO-8601 mtime of the report file — when the run that wrote it finished. */\n mtime: string;\n /** How many digest-tagged scenarios the report carried. */\n scenarios: number;\n}\n\n/** A service repository's commit-bound contribution to a federated record. */\nexport interface ServiceAttestation {\n service: string;\n commit: string;\n recorded: string;\n /** Claim ids this commit answered. Kept explicit so stale answers can be pruned safely. */\n claims: string[];\n /** The report that answered this service's scenario claims, when `--results` did. */\n report?: ConsumedReport;\n}\n\nexport interface Verification {\n /** Federated, partial records use schema 2. Absent means the original all-at-once format. */\n schema?: 2;\n feature: string;\n /** The day the answers were recorded. */\n recorded: string;\n /** The checklist digest they answer — how a later reader spots a record gone stale. */\n checklist: string;\n summary: { claims: number; confirmed: number; unconfirmed: number; unanswered?: number };\n claims: RecordedClaim[];\n /** The all-at-once form's consumed report; the federated form files it per attestation. */\n report?: ConsumedReport;\n attestations?: ServiceAttestation[];\n}\n\n/* ------------------------------------------------------------------ */\n/* The verdict */\n/* ------------------------------------------------------------------ */\n\n/**\n * Anything carrying an answer, whichever surface holds it: a recorded claim, or\n * the read view's per-claim status. Structural on purpose — the same questions\n * are asked of both, and neither should have to convert into the other to be\n * counted.\n */\nexport interface AnsweredClaim {\n id: string;\n kind: string;\n verdict: string;\n answered_by?: string;\n}\n\n/**\n * The confirmed `scenario.tested` claims that no test run answered.\n *\n * A scenario claim's whole premise is that a run answers it — its digest IS the\n * tag `loam gherkin` stamps, and `--results` matches the two mechanically. An\n * agent may still answer one, because a legacy service has no runnable suite\n * for months and that fleet is who loam is for; but the answer is somebody's\n * word about a test, and it must not read as the run. A record written before\n * `--results` existed carries no `answered_by` at all: unknown counts as agent\n * here, because the alternative is to credit a run nobody can point at.\n */\nexport function attestedClaims(claims: readonly AnsweredClaim[]): string[] {\n return claims\n .filter((c) => c.kind === \"scenario.tested\" && c.verdict === \"confirmed\" && c.answered_by !== \"runner\")\n .map((c) => c.id);\n}\n\n/** How a set of answers counts up — recounted from the answers, never read off a `summary`. */\nexport interface RecordTally {\n /** Questions asked: answered, plus the ones a federated record leaves for other repos. */\n claims: number;\n confirmed: number;\n unconfirmed: number;\n unanswered: number;\n /** Of the confirmed, the scenario claims on an agent's word. See {@link attestedClaims}. */\n attested: number;\n}\n\n/**\n * `unanswered` is the count the answers themselves cannot show: a federated\n * record holds only what somebody answered, and the claims still owed to other\n * repositories exist on the checklist, not in `claims[]`. Read views that carry\n * an `unanswered` status inside the list pass 0 and are counted in place.\n */\nexport function tallyAnswers(claims: readonly AnsweredClaim[], unanswered = 0): RecordTally {\n const of = (verdict: string): number => claims.filter((c) => c.verdict === verdict).length;\n return {\n claims: claims.length + unanswered,\n confirmed: of(\"confirmed\"),\n unconfirmed: of(\"unconfirmed\"),\n unanswered: of(\"unanswered\") + unanswered,\n attested: attestedClaims(claims).length,\n };\n}\n\n/**\n * The record's own answers, recounted. Never `v.summary`: the summary is what\n * whoever wrote the file said the answers add up to, and every reader that\n * believed it reported a record full of unconfirmed claims as fully confirmed.\n * `readVerificationState` refuses a record whose summary contradicts this, so a\n * caller holding a Verification may use either — and should use this one.\n */\nexport function tallyRecord(v: Verification): RecordTally {\n return tallyAnswers(v.claims, v.summary.unanswered ?? 0);\n}\n\n/**\n * Three states, because two were a lie. `verified` is the strong claim — every\n * question answered, every answer confirmed, and every scenario answered by a\n * digest-matched green run. `attested` is the same completeness resting, for at\n * least one scenario, on an agent's word. `unverified` is everything else:\n * nothing recorded, a record gone stale, a claim unanswered or unconfirmed, or\n * a checklist that asks nothing at all.\n */\nexport const VERIFICATION_VERDICTS = [\"verified\", \"attested\", \"unverified\"] as const;\nexport type VerificationVerdict = (typeof VERIFICATION_VERDICTS)[number];\n\nexport function verificationVerdict(t: RecordTally, stale = false): VerificationVerdict {\n if (stale || t.claims === 0 || t.confirmed !== t.claims) return \"unverified\";\n return t.attested > 0 ? \"attested\" : \"verified\";\n}\n\n/** A finding a verify surface reports about a record. Stable code, prose that may change. */\nexport interface VerifyNotice {\n code: string;\n severity: \"warn\";\n message: string;\n /** The claims it is about, when it is about claims. */\n claims?: string[];\n}\n\n/**\n * The one notice `verify`, `list` and `status` all show: this record's scenario\n * claims were confirmed without a run.\n *\n * It is a warning and it gates nothing — verify has never gated, and a service\n * with no suite yet has to be able to ship. What it must never do is read the\n * same as a green run.\n */\nexport function attestedNotice(claims: readonly AnsweredClaim[], feature: string): VerifyNotice | null {\n const ids = attestedClaims(claims);\n if (ids.length === 0) return null;\n return {\n code: \"verify.scenario-attested\",\n severity: \"warn\",\n message:\n `${ids.length} scenario claim(s) are confirmed on an agent's word, not on a test run: ${ids.join(\", \")}. ` +\n `Answer them mechanically once the suite runs: \\`loam verify ${feature} --results <cucumber.json>\\`.`,\n claims: ids,\n };\n}\n\n/**\n * Where the record lives: inside the feature, so `archive` carries it into\n * `features/archive/` with everything else and a reviewer finds it next to the\n * delta it is about. YAML rather than frontmatter or JSON — it is data, it has\n * to survive without loam, and one claim per block is a diff a person can read.\n */\nexport function verificationPath(featureDir: string): string {\n return join(featureDir, \"verification.yaml\");\n}\n\nexport function buildVerification(\n checklist: Checklist,\n answers: Answer[],\n today: string,\n report?: ConsumedReport,\n): Verification {\n const byId = new Map(answers.map((a) => [a.id, a]));\n const claims: RecordedClaim[] = checklist.claims.map((c) => {\n // Callers hand in an answer per claim — `runnerAnswers` is total over the\n // checklist, and both other callers map the checklist to build theirs. The\n // branch is here because the alternative to a diagnosis is a `TypeError` on\n // `a.verdict`, which cli.ts reports as `internal` with a stack about a\n // property of `undefined` and no word about which claim went missing.\n const a = byId.get(c.id);\n if (a === undefined) throw new Error(`buildVerification: no answer for claim ${c.id}`);\n return {\n id: c.id,\n kind: c.kind,\n claim: c.claim,\n verdict: a.verdict,\n answered_by: a.answered_by,\n evidence: a.evidence,\n ...(a.note === undefined ? {} : { note: a.note }),\n };\n });\n const confirmed = claims.filter((c) => c.verdict === \"confirmed\").length;\n return {\n feature: checklist.feature,\n recorded: today,\n checklist: checklist.digest,\n summary: { claims: claims.length, confirmed, unconfirmed: claims.length - confirmed },\n claims,\n ...(report === undefined ? {} : { report }),\n };\n}\n\n/**\n * An answer the previous record held that the new one does not carry.\n *\n * Dropping these is correct — see `buildFederatedVerification` — but doing it\n * silently is not: a schema-1 record answers the WHOLE checklist, so the first\n * federated write turns a green record into a partial one, and an operator who\n * is not told which answers went missing reads the drop as loam losing data.\n */\nexport interface DiscardedAnswer {\n id: string;\n claim: string;\n /** The service the record filed it under, when it said. Absent in schema-1 records. */\n subject?: string;\n /**\n * `off-checklist` — the feature changed and nobody asks this question any\n * more. `unattested` — the question still stands, but no service attestation\n * binds the answer to a commit, so it cannot be carried into a federated\n * record; the owning repository has to record it again.\n */\n reason: \"off-checklist\" | \"unattested\";\n}\n\nexport interface FederatedBuild {\n verification: Verification;\n /** In checklist-independent record order — see {@link DiscardedAnswer}. */\n discarded: DiscardedAnswer[];\n}\n\n/**\n * Replace one service's contribution while retaining current contributions\n * from other repositories. Claim ids removed from the live checklist are\n * pruned from both the answers and their attestations, so a changed feature can\n * never keep a green answer to a question it no longer asks.\n */\nexport function buildFederatedVerification(\n checklist: Checklist,\n service: string,\n answers: Answer[],\n previous: Verification | null,\n recorded: string,\n commit: string,\n report?: ConsumedReport,\n): FederatedBuild {\n const currentById = new Map(checklist.claims.map((claim) => [claim.id, claim]));\n const localIds = new Set(checklist.claims.filter((claim) => claim.subject === service).map((claim) => claim.id));\n\n // An old all-at-once record remains readable, but its answers have no commit\n // attestation and therefore cannot silently become somebody else's\n // federated contribution. The first schema-2 write starts a clean federation.\n const previouslyAttested = new Set((previous?.attestations ?? []).flatMap((a) => a.claims));\n const retained = (previous?.schema === 2 ? previous.claims : []).filter((claim) => {\n const current = currentById.get(claim.id);\n return current !== undefined && current.subject !== service && previouslyAttested.has(claim.id);\n });\n const local = answers.map((answer): RecordedClaim => {\n // Same contract as `buildVerification`: an answer is built from the live\n // checklist, so it always names a current claim. Off-checklist answers do\n // NOT belong here — `discarded` means \"an answer the PREVIOUS record held\",\n // and routing an incoming stray into it would change what the printed\n // notice tells an operator. A caller that reaches this gets told which\n // answer it was rather than a `TypeError` on `claim.kind`.\n const claim = currentById.get(answer.id);\n if (claim === undefined) {\n throw new Error(`buildFederatedVerification: answer ${answer.id} names no current claim`);\n }\n return {\n id: claim.id,\n kind: claim.kind,\n subject: claim.subject,\n claim: claim.claim,\n verdict: answer.verdict,\n answered_by: answer.answered_by,\n evidence: answer.evidence,\n ...(answer.note === undefined ? {} : { note: answer.note }),\n };\n });\n const byId = new Map([...retained, ...local].map((claim) => [claim.id, claim]));\n const claims = checklist.claims.flatMap((claim) => {\n const answer = byId.get(claim.id);\n if (answer === undefined) return [];\n // Normalize retained legacy entries to the current question text and add\n // the subject schema 2 needs for future independent replacement.\n return [{ ...answer, kind: claim.kind, subject: claim.subject, claim: claim.claim }];\n });\n\n const retainedAttestations = (previous?.attestations ?? []).flatMap((attestation) => {\n if (attestation.service === service) return [];\n const ids = attestation.claims.filter(\n (id) => currentById.get(id)?.subject === attestation.service && byId.has(id),\n );\n return ids.length === 0 ? [] : [{ ...attestation, claims: ids }];\n });\n const attestations: ServiceAttestation[] = [\n ...retainedAttestations,\n {\n service,\n commit,\n recorded,\n claims: checklist.claims.filter((c) => localIds.has(c.id)).map((c) => c.id),\n // Filed with the attestation, not the record: the report is one\n // repository's run, and it is pruned or retained with that repository's\n // answers rather than outliving them.\n ...(report === undefined ? {} : { report }),\n },\n ].sort((a, b) => a.service.localeCompare(b.service));\n\n const confirmed = claims.filter((claim) => claim.verdict === \"confirmed\").length;\n const unconfirmed = claims.length - confirmed;\n const unanswered = checklist.claims.length - claims.length;\n\n // Everything the old record answered that the new one does not. The caller\n // prints it; nothing here decides it is acceptable.\n const discarded = (previous?.claims ?? [])\n .filter((claim) => !byId.has(claim.id))\n .map((claim): DiscardedAnswer => ({\n id: claim.id,\n claim: claim.claim,\n ...(claim.subject === undefined ? {} : { subject: claim.subject }),\n reason: currentById.has(claim.id) ? \"unattested\" : \"off-checklist\",\n }));\n\n return {\n verification: {\n schema: 2,\n feature: checklist.feature,\n recorded,\n checklist: checklist.digest,\n summary: { claims: checklist.claims.length, confirmed, unconfirmed, unanswered },\n claims,\n attestations,\n },\n discarded,\n };\n}\n\n/**\n * The record as a file. The header explains what the reader is looking at,\n * because the whole point is that this is legible to someone who has never run\n * loam — including the part loam cannot vouch for.\n */\nexport function renderVerification(v: Verification): string {\n const header = [\n `# Verification record for ${v.feature} — written by \\`loam verify ${v.feature}\\` (--results / --record).`,\n \"#\",\n \"# Every claim below was derived mechanically from this feature's own artifacts:\",\n \"# delta.likec4, specs/<svc>/spec.md, specs/<svc>/arch.spec.md and specs/<svc>/\",\n \"# openapi.yaml. Each verdict names who answered it: `answered_by: runner` means a\",\n \"# cucumber JSON report's digest-tagged scenarios answered it mechanically;\",\n \"# `answered_by: agent` means somebody's word about the code, which loam did not\",\n \"# check. Nothing gates on either.\",\n \"#\",\n \"# A `scenario.tested` claim confirmed by an agent is ATTESTED, not run: loam\",\n \"# reports it as `verify.scenario-attested` and the feature does not count as\",\n \"# verified until a report answers it. `report:` records the file a --results run\",\n \"# read — its sha256 and mtime say WHICH file, not that it came from that commit;\",\n \"# no digest can say that.\",\n \"#\",\n \"# `checklist` is a digest of the claim ids. If `loam verify` stops reporting the same\",\n \"# one, the feature changed after this was recorded and these answers are stale.\",\n ...(v.schema === 2\n ? [\n \"#\",\n \"# Schema 2 is federated: each service entry under `attestations` binds its claim ids\",\n \"# and file:line evidence to that repository's git commit. Missing claims are honestly\",\n \"# unanswered; another service run may add them without rewriting existing attestations.\",\n ]\n : []),\n \"\",\n ].join(\"\\n\");\n // lineWidth 0: never fold a claim onto a second line — these are grepped and diffed.\n return header + stringify(v, { lineWidth: 0 });\n}\n\n/**\n * What is on disk beside a feature: nothing, something unreadable, or a record.\n *\n * The three are kept apart because they call for opposite handling. \"Absent\" is\n * a feature nobody has verified — verify starts a fresh record over it without\n * a second thought. \"Unreadable\" is a file that IS somebody's record: it parses\n * as garbage, or its shape is not a record's, and every answer and attestation\n * it holds is unaccounted for. Collapsing the two (which this function used to\n * do, returning null for both) meant a hand-edited or half-written\n * verification.yaml read as \"not verified\" and the next `--record` overwrote a\n * whole fleet's attestations without a word.\n */\nexport type VerificationRead =\n | { state: \"absent\" }\n /**\n * `reason` names the YAML line when the parser gave one — the file is the\n * thing to fix. `code` is set for the one unreadable that parses perfectly: a\n * record whose `summary` contradicts its own `claims[]`. That lives here\n * rather than in a state of its own so every reader which already refuses an\n * unreadable record refuses this one too, without a line of new code.\n */\n | { state: \"unreadable\"; reason: string; code?: \"verify.record-miscounted\" }\n | { state: \"ok\"; verification: Verification };\n\nexport async function readVerificationState(featureDir: string): Promise<VerificationRead> {\n const path = verificationPath(featureDir);\n if (!existsSync(path)) return { state: \"absent\" };\n let text: string;\n try {\n text = await readFile(path, \"utf8\");\n } catch (err) {\n return { state: \"unreadable\", reason: err instanceof Error ? err.message : String(err) };\n }\n let doc: unknown;\n try {\n doc = parse(text);\n } catch (err) {\n return { state: \"unreadable\", reason: yamlReason(err) };\n }\n const verification = asVerification(doc);\n if (verification === null) {\n return {\n state: \"unreadable\",\n reason:\n \"it parses as YAML but does not have a verification record's shape (feature, recorded, checklist, summary and claims)\",\n };\n }\n const miscount = summaryDisagreement(verification);\n if (miscount === null) return { state: \"ok\", verification };\n return {\n state: \"unreadable\",\n // Spelled twice on purpose: the field is what a caller branches on, the\n // prose is what a terminal prints and what somebody greps for.\n code: \"verify.record-miscounted\",\n reason: `verify.record-miscounted: ${miscount}`,\n };\n}\n\n/**\n * Does the record's `summary` add up from its own `claims[]`?\n *\n * The summary is what wrote the file; the claims are what it answered. Nothing\n * ever compared them, so every reader that took the shortcut — verify's frozen\n * verdict for a shipped feature, list's column, status's block — reported a\n * record full of unconfirmed claims as fully confirmed. Believing either side\n * over the other would be a guess about which half was tampered with, so a\n * record that contradicts itself is refused like any other file loam cannot\n * use. The code is in the reason: it travels wherever the reason is printed.\n */\nfunction summaryDisagreement(v: Verification): string | null {\n const t = tallyAnswers(v.claims);\n const unanswered = v.summary.unanswered ?? 0;\n const says: string[] = [];\n if (v.summary.confirmed !== t.confirmed) {\n says.push(`says ${v.summary.confirmed} confirmed where claims[] holds ${t.confirmed}`);\n }\n if (v.summary.unconfirmed !== t.unconfirmed) {\n says.push(`says ${v.summary.unconfirmed} unconfirmed where claims[] holds ${t.unconfirmed}`);\n }\n if (v.summary.claims !== v.claims.length + unanswered) {\n says.push(\n `says ${v.summary.claims} claim(s)${unanswered === 0 ? \"\" : ` including ${unanswered} unanswered`} where claims[] holds ${v.claims.length}`,\n );\n }\n return says.length === 0\n ? null\n : `its summary ${says.join(\", \")} — the record contradicts itself, so neither half can be believed`;\n}\n\n/** A YAML failure with its line, when the parser located one: that line is the fix. */\nfunction yamlReason(err: unknown): string {\n const message = err instanceof Error ? err.message.split(\"\\n\")[0]!.trim() : String(err);\n const pos = (err as { linePos?: Array<{ line: number; col: number }> } | null)?.linePos;\n const line = Array.isArray(pos) ? pos[0]?.line : undefined;\n return line === undefined ? `YAML error: ${message}` : `YAML error at line ${line}: ${message}`;\n}\n\n/**\n * The record beside a feature, or null when there is none loam can use. Kept\n * for readers that have nothing to say about the difference (`loam list`'s\n * verification column shows one glyph either way); anything that WRITES must\n * use {@link readVerificationState} instead, or it overwrites what it could not\n * read.\n *\n * A record whose summary contradicts its own claims is one of the nulls now, so\n * no reader can print its counts as fact. Reading it as ABSENT is the safe half\n * of the truth and not the whole of it: a reader that can say why should take\n * {@link readVerificationState} and its `code`.\n */\nexport async function readVerification(featureDir: string): Promise<Verification | null> {\n const read = await readVerificationState(featureDir);\n return read.state === \"ok\" ? read.verification : null;\n}\n\n/**\n * The shape check `readVerification` stands behind: everything its readers\n * dereference (verify's report/frozen views, list's verification column) must\n * be present and typed, or the cast would manufacture a Verification that\n * crashes them. Deliberately no tighter than the readers need: extra keys pass\n * (a human may annotate), `answered_by` stays optional (absent in records that\n * predate `--results`) and is any string, like `kind` — the readers only ever\n * compare them, and a record written by a newer loam must not read as absent.\n */\nfunction asVerification(doc: unknown): Verification | null {\n if (!isRecord(doc)) return null;\n const summary = doc[\"summary\"];\n if (\n typeof doc[\"feature\"] !== \"string\" ||\n typeof doc[\"recorded\"] !== \"string\" ||\n typeof doc[\"checklist\"] !== \"string\" ||\n !isRecord(summary) ||\n !isCount(summary[\"claims\"]) ||\n !isCount(summary[\"confirmed\"]) ||\n !isCount(summary[\"unconfirmed\"]) ||\n (summary[\"unanswered\"] !== undefined && !isCount(summary[\"unanswered\"])) ||\n !Array.isArray(doc[\"claims\"])\n ) {\n return null;\n }\n for (const c of doc[\"claims\"]) {\n if (!isRecord(c)) return null;\n if (typeof c[\"id\"] !== \"string\" || typeof c[\"kind\"] !== \"string\" || typeof c[\"claim\"] !== \"string\") return null;\n if (c[\"subject\"] !== undefined && typeof c[\"subject\"] !== \"string\") return null;\n if (typeof c[\"verdict\"] !== \"string\" || !(VERDICTS as readonly string[]).includes(c[\"verdict\"])) return null;\n // The frozen view iterates evidence per claim — a scalar here would crash it.\n if (!Array.isArray(c[\"evidence\"]) || c[\"evidence\"].some((e) => typeof e !== \"string\")) return null;\n if (c[\"note\"] !== undefined && typeof c[\"note\"] !== \"string\") return null;\n if (c[\"answered_by\"] !== undefined && typeof c[\"answered_by\"] !== \"string\") return null;\n }\n if (doc[\"schema\"] !== undefined && doc[\"schema\"] !== 2) return null;\n if (!isConsumedReport(doc[\"report\"])) return null;\n if (doc[\"attestations\"] !== undefined) {\n if (!Array.isArray(doc[\"attestations\"])) return null;\n for (const a of doc[\"attestations\"]) {\n if (!isRecord(a)) return null;\n if (typeof a[\"service\"] !== \"string\" || typeof a[\"commit\"] !== \"string\" || typeof a[\"recorded\"] !== \"string\") return null;\n if (!/^[0-9a-f]{40,64}$/i.test(a[\"commit\"])) return null;\n if (!Array.isArray(a[\"claims\"]) || a[\"claims\"].some((id) => typeof id !== \"string\")) return null;\n if (!isConsumedReport(a[\"report\"])) return null;\n }\n }\n return doc as unknown as Verification;\n}\n\n/** Absent is fine — most records answer nothing from a report. Present must be whole: it is printed. */\nfunction isConsumedReport(v: unknown): boolean {\n if (v === undefined) return true;\n return (\n isRecord(v) &&\n typeof v[\"path\"] === \"string\" &&\n typeof v[\"digest\"] === \"string\" &&\n typeof v[\"mtime\"] === \"string\" &&\n isCount(v[\"scenarios\"])\n );\n}\n\n/** A summary count: a finite number. YAML hands back strings for anything quoted. */\nfunction isCount(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nexport async function writeVerification(featureDir: string, v: Verification): Promise<string> {\n const path = verificationPath(featureDir);\n await writeFile(path, renderVerification(v), \"utf8\");\n return path;\n}\n"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * One version string, owned by package.json — read once, imported everywhere a
3
+ * version is spoken. `../../package.json` resolves from src/core/ in dev and
4
+ * from dist/core/ in the published layout alike.
5
+ */
6
+ import { createRequire } from "node:module";
7
+ /**
8
+ * The read is checked rather than asserted because the failure it would let
9
+ * through is silent, permanent, and invisible on the machine that caused it: an
10
+ * absent or empty `version` stamps `vundefined` into every file loam generates,
11
+ * `agentsStampVersion` then cannot parse that stamp back, and every repository
12
+ * built by that build reports its agent files stale forever with nothing to
13
+ * point at. A build that cannot say what it is has nothing useful to do, so it
14
+ * says so once, loudly, while its own module is being evaluated.
15
+ */
16
+ function readVersion(pkg) {
17
+ const version = pkg !== null && typeof pkg === "object" && "version" in pkg ? pkg.version : undefined;
18
+ if (typeof version !== "string" || version === "") {
19
+ throw new Error("readVersion: package.json declares no non-empty 'version', so this build cannot say what it is");
20
+ }
21
+ return version;
22
+ }
23
+ export const LOAM_VERSION = readVersion(createRequire(import.meta.url)("../../package.json"));
24
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;GAQG;AACH,SAAS,WAAW,CAAC,GAAY;IAC/B,MAAM,OAAO,GAAG,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,SAAS,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACtG,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACb,gGAAgG,CACjG,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC","sourcesContent":["/**\n * One version string, owned by package.json — read once, imported everywhere a\n * version is spoken. `../../package.json` resolves from src/core/ in dev and\n * from dist/core/ in the published layout alike.\n */\nimport { createRequire } from \"node:module\";\n\n/**\n * The read is checked rather than asserted because the failure it would let\n * through is silent, permanent, and invisible on the machine that caused it: an\n * absent or empty `version` stamps `vundefined` into every file loam generates,\n * `agentsStampVersion` then cannot parse that stamp back, and every repository\n * built by that build reports its agent files stale forever with nothing to\n * point at. A build that cannot say what it is has nothing useful to do, so it\n * says so once, loudly, while its own module is being evaluated.\n */\nfunction readVersion(pkg: unknown): string {\n const version = pkg !== null && typeof pkg === \"object\" && \"version\" in pkg ? pkg.version : undefined;\n if (typeof version !== \"string\" || version === \"\") {\n throw new Error(\n \"readVersion: package.json declares no non-empty 'version', so this build cannot say what it is\",\n );\n }\n return version;\n}\n\nexport const LOAM_VERSION = readVersion(createRequire(import.meta.url)(\"../../package.json\"));\n"]}
package/package.json CHANGED
@@ -1,14 +1,73 @@
1
1
  {
2
2
  "name": "@ybotok/loam",
3
- "version": "0.0.0",
4
- "description": "Placeholder. Publishing this creates the package so npm trusted publishing can be configured; every real release ships through the gated GitHub Actions workflow.",
5
- "license": "MIT",
3
+ "version": "0.1.0-beta.2",
4
+ "description": "Architecture-first spec framework for microservice fleets: reverse-engineer service docs from code, drive cross-service features from C4 deltas.",
5
+ "author": "spentsov",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/ybotok/loam.git"
9
9
  },
10
10
  "homepage": "https://github.com/ybotok/loam#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/ybotok/loam/issues"
13
+ },
14
+ "keywords": [
15
+ "architecture",
16
+ "c4",
17
+ "gherkin",
18
+ "microservices",
19
+ "spec-driven-development"
20
+ ],
21
+ "type": "module",
22
+ "bin": {
23
+ "loam": "dist/cli.js"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "CHANGELOG.md",
28
+ "COMPARISON.md",
29
+ "MIGRATING-from-OpenSpec.md",
30
+ "SCHEMA.md",
31
+ "SECURITY.md"
32
+ ],
11
33
  "publishConfig": {
12
- "access": "public"
34
+ "access": "public",
35
+ "provenance": true
36
+ },
37
+ "engines": {
38
+ "node": ">=22.22.3"
39
+ },
40
+ "scripts": {
41
+ "clean": "node scripts/clean.mjs",
42
+ "build": "npm run clean && tsc",
43
+ "prepack": "npm run build",
44
+ "dev": "tsx src/cli.ts",
45
+ "start": "node dist/cli.js",
46
+ "lint": "oxlint -D correctness --import-plugin --promise-plugin --node-plugin --deny-warnings src test scripts",
47
+ "typecheck": "tsc --noEmit",
48
+ "test": "vitest run",
49
+ "test:coverage": "vitest run --coverage",
50
+ "test:package": "node scripts/package-smoke.mjs",
51
+ "release:check": "node scripts/release-check.mjs",
52
+ "release:check:selftest": "node scripts/release-check.mjs --fixture-ready",
53
+ "release:pack": "node scripts/pack-release.mjs",
54
+ "pilot:run": "node scripts/pilot-harness.mjs",
55
+ "pilot:check": "node scripts/pilot-harness.mjs --check-manifest docs/pilot/manifest.example.json --selftest",
56
+ "test:openspec-corpus": "tsx scripts/check-openspec-corpus.ts",
57
+ "setup": "bash scripts/setup.sh"
58
+ },
59
+ "license": "MIT",
60
+ "dependencies": {
61
+ "commander": "^15.0.0",
62
+ "likec4": "1.59.2",
63
+ "yaml": "^2.9.0"
64
+ },
65
+ "devDependencies": {
66
+ "@types/node": "^22.0.0",
67
+ "@vitest/coverage-v8": "^4.1.10",
68
+ "oxlint": "^1.77.0",
69
+ "tsx": "^4.23.1",
70
+ "typescript": "^7.0.2",
71
+ "vitest": "^4.1.10"
13
72
  }
14
73
  }