@ybotok/loam 0.1.0-beta.1 → 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.
- package/CHANGELOG.md +49 -0
- package/README.md +6 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.js +17 -3
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/archive.js +24 -8
- package/dist/commands/archive.js.map +1 -1
- package/dist/commands/delta.js +74 -47
- package/dist/commands/delta.js.map +1 -1
- package/dist/commands/dependencies.js +7 -2
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/docs-repo-gate.js +104 -0
- package/dist/commands/docs-repo-gate.js.map +1 -0
- package/dist/commands/explore.js +195 -0
- package/dist/commands/explore.js.map +1 -0
- package/dist/commands/format.js +17 -0
- package/dist/commands/format.js.map +1 -0
- package/dist/commands/gherkin.js +87 -48
- package/dist/commands/gherkin.js.map +1 -1
- package/dist/commands/init.js +3 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/instructions.js +70 -0
- package/dist/commands/instructions.js.map +1 -0
- package/dist/commands/list.js +6 -76
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/migrate-openspec.js +45 -28
- package/dist/commands/migrate-openspec.js.map +1 -1
- package/dist/commands/new.js +35 -34
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/rebase.js +2 -4
- package/dist/commands/rebase.js.map +1 -1
- package/dist/commands/show.js +26 -31
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/status.js +8 -24
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/unarchive.js +12 -5
- package/dist/commands/unarchive.js.map +1 -1
- package/dist/commands/validate.js +115 -133
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/verify.js +33 -17
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/vouch.js +77 -59
- package/dist/commands/vouch.js.map +1 -1
- package/dist/core/agent.js +273 -31
- package/dist/core/agent.js.map +1 -1
- package/dist/core/arch.js +28 -2
- package/dist/core/arch.js.map +1 -1
- package/dist/core/brief.js +3 -3
- package/dist/core/brief.js.map +1 -1
- package/dist/core/coherence.js +37 -13
- package/dist/core/coherence.js.map +1 -1
- package/dist/core/concurrency.js +51 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/config.js +10 -2
- package/dist/core/config.js.map +1 -1
- package/dist/core/delta.js +21 -2
- package/dist/core/delta.js.map +1 -1
- package/dist/core/dependencies.js +46 -42
- package/dist/core/dependencies.js.map +1 -1
- package/dist/core/docs.js +2 -1
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor.js +41 -26
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/document-bytes.js +59 -0
- package/dist/core/document-bytes.js.map +1 -0
- package/dist/core/explore.js +225 -0
- package/dist/core/explore.js.map +1 -0
- package/dist/core/fleet-context.js +16 -63
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/frontmatter.js +1 -5
- package/dist/core/frontmatter.js.map +1 -1
- package/dist/core/gherkin.js +3 -61
- package/dist/core/gherkin.js.map +1 -1
- package/dist/core/health.js +1 -5
- package/dist/core/health.js.map +1 -1
- package/dist/core/ids.js +34 -5
- package/dist/core/ids.js.map +1 -1
- package/dist/core/json.js +8 -1
- package/dist/core/json.js.map +1 -1
- package/dist/core/likec4.js +6 -1
- package/dist/core/likec4.js.map +1 -1
- package/dist/core/maturity.js +66 -0
- package/dist/core/maturity.js.map +1 -0
- package/dist/core/openapi-merge.js +39 -6
- package/dist/core/openapi-merge.js.map +1 -1
- package/dist/core/openapi.js +17 -5
- package/dist/core/openapi.js.map +1 -1
- package/dist/core/openspec-inventory.js +64 -27
- package/dist/core/openspec-inventory.js.map +1 -1
- package/dist/core/path-safety.js +59 -0
- package/dist/core/path-safety.js.map +1 -1
- package/dist/core/provenance.js +35 -10
- package/dist/core/provenance.js.map +1 -1
- package/dist/core/records.js +33 -0
- package/dist/core/records.js.map +1 -0
- package/dist/core/repo.js +75 -1
- package/dist/core/repo.js.map +1 -1
- package/dist/core/report.js +9 -0
- package/dist/core/report.js.map +1 -1
- package/dist/core/results.js +1 -3
- package/dist/core/results.js.map +1 -1
- package/dist/core/spec.js +3 -3
- package/dist/core/spec.js.map +1 -1
- package/dist/core/staging.js +124 -17
- package/dist/core/staging.js.map +1 -1
- package/dist/core/status.js +122 -33
- package/dist/core/status.js.map +1 -1
- package/dist/core/steps.js +72 -0
- package/dist/core/steps.js.map +1 -0
- package/dist/core/verify.js +18 -4
- package/dist/core/verify.js.map +1 -1
- package/dist/core/version.js +17 -1
- package/dist/core/version.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/new.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { stringify as stringifyYaml } from \"yaml\";\nimport { loadConfig } from \"../core/config.js\";\nimport { InvalidIdError, assertServiceId } from \"../core/ids.js\";\nimport { emitJson, fail, repoPath, reportNoConfig } from \"../core/json.js\";\nimport { UnsafePathError, resolveInside } from \"../core/path-safety.js\";\nimport {\n DocsRepoUnavailableError,\n compareIds,\n featureIdFromDirName,\n featuresDir,\n listServices,\n resolveFeature,\n} from \"../core/repo.js\";\n\n/**\n * Feature ids are `<word>-<number>`: the id has to survive being read back off\n * the directory name (FEAT-101-payment-splitting -> FEAT-101), or the feature\n * would answer to a name it was never given.\n */\nconst ID_RE = /^[A-Za-z][A-Za-z0-9]*-\\d+$/;\n\ninterface NewOptions {\n title?: string;\n touches: string[];\n newService: string[];\n json?: boolean;\n}\n\nexport function registerNew(program: Command): void {\n program\n .command(\"new\")\n .argument(\"<featureId>\", \"feature id, e.g. FEAT-101\")\n .description(\"Scaffold a feature: intent, C4 delta, and a requirement delta per service\")\n .option(\"--title <text>\", \"human title; also becomes the directory slug\")\n // `--touches`, not `--service`: everywhere else `--service` selects the ONE\n // operating target (defaulting to config.service), while this is a repeatable\n // list of services the feature touches — a different arity and a different\n // meaning deserve a different name.\n .option(\"--touches <id>\", \"a service this feature touches (repeatable)\", collect, [])\n .option(\"--new-service <id>\", \"a service this feature introduces (repeatable)\", collect, [])\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (featureId: string, opts: NewOptions) => {\n const json = opts.json === true;\n\n const dirName = featureDirName(featureId, opts.title);\n if (!ID_RE.test(featureId) || featureIdFromDirName(dirName) !== featureId) {\n return fail(\n json,\n \"invalid-option\",\n `'${featureId}' is not a usable feature id. Expected <word>-<number>, e.g. FEAT-101 or BUG-42.`,\n );\n }\n\n // Service ids are validated BEFORE the config is even loaded, and long\n // before anything is written: every one of them is interpolated into\n // `specs/<id>/` under the new feature directory, so `--touches ../../x`\n // was a writer pointed outside the docs repo. One grammar (core/ids.ts),\n // the same one adopt/init/vouch refuse on, so a service that is legal to\n // create is legal to name here and nowhere the two disagree.\n for (const [label, ids] of [\n [\"--touches\", opts.touches],\n [\"--new-service\", opts.newService],\n ] as const) {\n for (const id of ids) {\n try {\n assertServiceId(id, label);\n } catch (err) {\n if (!(err instanceof InvalidIdError)) throw err;\n return fail(json, \"invalid-option\", err.message);\n }\n }\n }\n\n const config = await loadConfig();\n if (!config) {\n reportNoConfig(json);\n return;\n }\n const { docsDir } = config;\n\n const existing = await resolveFeature(docsDir, featureId, \"include\");\n if (existing) {\n return fail(\n json,\n \"already-exists\",\n `Feature '${featureId}' already exists at ${repoPath(docsDir, existing.dir)}.`,\n );\n }\n\n // A service named both ways is new — that is the more specific claim.\n const created = new Set(opts.newService);\n const touched = opts.touches.filter((s) => !created.has(s));\n const dir = join(featuresDir(docsDir), dirName);\n\n const files: Record<string, string> = {\n \"intent.md\": intentTemplate(featureId, opts.title),\n \"delta.likec4\": deltaTemplate(featureId, touched, [...created]),\n };\n for (const svc of [...touched, ...created]) {\n files[join(\"specs\", svc, \"spec.md\")] = specTemplate(featureId, svc);\n }\n for (const svc of created) {\n files[join(\"specs\", svc, \"openapi.yaml\")] = openapiTemplate(svc);\n // The architecture axis is scaffolded only for a service this feature\n // INTRODUCES, because that is the case where `c4.uncovered` will fire\n // the moment the delta is written: a brand-new tagged element nothing\n // covers. Handing an author a blank page for the outbox/retries/alerts\n // requirement is how that axis stays empty across a whole fleet.\n files[join(\"specs\", svc, \"arch.spec.md\")] = archSpecTemplate(featureId, svc);\n }\n\n const written: string[] = [];\n for (const [rel, content] of Object.entries(files)) {\n // Belt and braces over the id check above: the id grammar is what makes\n // this safe, and `resolveInside` is what proves it at the moment of the\n // write — including the symlink cases a grammar cannot see.\n let path: string;\n try {\n path = resolveInside(docsDir, join(\"features\", dirName, rel), \"feature file\");\n } catch (err) {\n if (!(err instanceof UnsafePathError)) throw err;\n return fail(json, \"invalid-option\", err.message);\n }\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, \"utf8\");\n written.push(repoPath(docsDir, path));\n }\n\n const notes = await unknownServiceNotes(docsDir, touched);\n\n if (json) {\n emitJson({\n feature: featureId,\n path: repoPath(docsDir, dir),\n created: written,\n // Not an error and not a finding: `--touches` on a service that does\n // not exist yet is legal (adopt it later, or say `--new-service`).\n // It is reported because the silent alternative is a feature whose\n // spec delta will never merge into anything.\n notes,\n });\n return;\n }\n console.log(`${featureId} scaffolded at ${repoPath(docsDir, dir)}`);\n for (const w of written) console.log(` + ${w}`);\n for (const note of notes) console.log(`\\n note: ${note}`);\n console.log(`\\nNext: fill in the delta, then \\`loam validate --feature ${featureId}\\`.`);\n console.log(\n \"If this feature changes no architecture, delete delta.likec4 — a requirements-only\\n\" +\n \"feature is complete without it, and an empty `model {}` validates clean too.\",\n );\n });\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\n/* ------------------------------------------------------------------ */\n/* Hints */\n/* ------------------------------------------------------------------ */\n\n/**\n * `--touches <id>` naming neither a living `services/<id>/` nor a `--new-service`\n * is almost always a typo, and it is silent by construction: the scaffold writes\n * `specs/<id>/spec.md` happily, `loam validate` grades a delta against a living\n * spec that is not there, and the mistake surfaces at archive time as a service\n * appearing from nowhere. A note, not a refusal — naming a service that will be\n * adopted next week is legitimate — but the near-miss is spelled out, because\n * `order-api` vs `orders-api` is exactly the pair a person cannot see.\n */\nasync function unknownServiceNotes(docsDir: string, touched: string[]): Promise<string[]> {\n if (touched.length === 0) return [];\n let known: string[];\n try {\n known = (await listServices(docsDir)).map((s) => s.id);\n } catch (err) {\n // No docs repo to compare against is a different diagnosis entirely, and\n // `loam doctor` owns it. Saying nothing here beats inventing a typo hint\n // out of an enumeration that never ran.\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n return [];\n }\n const notes: string[] = [];\n for (const id of touched) {\n if (known.includes(id)) continue;\n const near = nearestIds(id, known);\n notes.push(\n `--touches '${id}' matches no services/${id}/ in the docs repo` +\n (near.length === 0 ? \"\" : ` — did you mean ${near.map((n) => `'${n}'`).join(\" or \")}?`) +\n ` If ${id} is introduced by this feature, pass --new-service ${id} instead.`,\n );\n }\n return notes;\n}\n\n/** Known ids within a small edit distance of `id`, closest first, at most three. */\nfunction nearestIds(id: string, known: string[]): string[] {\n const budget = Math.max(1, Math.floor(id.length / 4));\n return known\n .map((candidate) => ({ candidate, distance: editDistance(id.toLowerCase(), candidate.toLowerCase()) }))\n .filter((scored) => scored.distance <= budget)\n .sort((a, b) => a.distance - b.distance || compareIds(a.candidate, b.candidate))\n .slice(0, 3)\n .map((scored) => scored.candidate);\n}\n\n/** Plain Levenshtein — ids are short, and the row-at-a-time form keeps it obvious. */\nfunction editDistance(a: string, b: string): number {\n let previous = Array.from({ length: b.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i += 1) {\n const current = [i];\n for (let j = 1; j <= b.length; j += 1) {\n const substitution = previous[j - 1]! + (a[i - 1] === b[j - 1] ? 0 : 1);\n current.push(Math.min(previous[j]! + 1, current[j - 1]! + 1, substitution));\n }\n previous = current;\n }\n return previous[b.length]!;\n}\n\n/* ------------------------------------------------------------------ */\n/* Naming */\n/* ------------------------------------------------------------------ */\n\nfunction featureDirName(featureId: string, title: string | undefined): string {\n const slug = slugify(title ?? \"\");\n return slug ? `${featureId}-${slug}` : featureId;\n}\n\nfunction slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n/** A LikeC4 identifier for a service name: `payment-split-service` -> `paymentSplitService`. */\nfunction identifier(name: string, taken: Set<string>): string {\n const parts = name.split(/[^A-Za-z0-9]+/).filter((p) => p.length > 0);\n const head = (parts[0] ?? \"svc\").replace(/^\\d+/, \"\") || \"svc\";\n const base =\n head[0]!.toLowerCase() +\n head.slice(1) +\n parts.slice(1).map((p) => p[0]!.toUpperCase() + p.slice(1)).join(\"\");\n let id = base;\n for (let n = 2; taken.has(id); n += 1) id = `${base}${n}`;\n taken.add(id);\n return id;\n}\n\n/** `FEAT-101` -> `feat_101`: LikeC4 view names take no dashes. */\nfunction viewName(featureId: string): string {\n return featureId.toLowerCase().replace(/[^a-z0-9]+/g, \"_\");\n}\n\n/* ------------------------------------------------------------------ */\n/* Templates */\n/* ------------------------------------------------------------------ */\n\nfunction intentTemplate(featureId: string, title: string | undefined): string {\n const heading = title ?? featureId;\n // The frontmatter is SERIALIZED, never interpolated. A title is free text a\n // person types — `Checkout: split payments` used to be pasted straight after\n // `title: `, where the colon reopens the mapping and the file stops parsing;\n // `Orders #42 rework` lost everything from the `#` as a YAML comment, so the\n // feature quietly answered to a title nobody wrote. The yaml serializer is the\n // only thing that knows every case, and it is already how migrate-openspec\n // writes the same three keys.\n //\n // `owner` is deliberately NOT a key here. Scaffolding `owner:` writes an\n // explicit null, which is a claim (\"this feature has no owner\") rather than\n // the truth (\"nobody has said yet\") — and every reader then has to tell an\n // absent key from a null one. The prompt survives as a comment.\n const frontmatter = stringifyYaml(\n { feature: featureId, ...(title === undefined ? {} : { title }), status: \"proposed\" },\n { lineWidth: 0 },\n ).trimEnd();\n return `---\n${frontmatter}\n# owner: <the team or person who answers for this>\n---\n\n# ${heading}\n\n## Why\n\n<!-- The problem in business terms. This is what a reviewer reads first, and what\n the requirement deltas below have to be justified by. -->\n\n## Scope\n\n<!-- Which services this touches — and, more usefully, which it deliberately does not. -->\n`;\n}\n\nfunction deltaTemplate(featureId: string, touched: string[], created: string[]): string {\n const taken = new Set<string>();\n const touchedIds = touched.map((s) => [identifier(s, taken), s] as const);\n const createdIds = created.map((s) => [identifier(s, taken), s] as const);\n\n const lines: string[] = [];\n if (touchedIds.length > 0) {\n // Context elements ship COMMENTED OUT, exactly like the example edge below.\n // Declared-and-untagged is the one shape `delta.nothing-tagged` exists to\n // refuse (an author who forgot the tags), so writing these live made every\n // `loam new --touches X` fail its own validator on the very first run —\n // which teaches people that the validator is noise. Uncomment a line when an\n // edge below actually needs the identifier.\n lines.push(\" // Services this feature touches, as context for the diagram. Uncomment the\");\n lines.push(\" // ones an edge below names, and reuse the identifiers from\");\n lines.push(\" // architecture/landscape.likec4 so the merge lines up. They stay UNTAGGED:\");\n lines.push(` // only #${featureId} is the change, and everything else here is context.`);\n for (const [id, name] of touchedIds) lines.push(` // ${id} = softwareSystem '${name}'`);\n lines.push(\"\");\n }\n if (createdIds.length > 0) {\n lines.push(\" // Services this feature introduces. The tag is what `loam archive` folds\");\n lines.push(\" // into the living landscape.\");\n for (const [id, name] of createdIds) {\n lines.push(` ${id} = softwareSystem '${name}' {`);\n lines.push(` #${featureId}`);\n lines.push(` description 'TODO — what this service owns'`);\n lines.push(\" }\");\n }\n lines.push(\"\");\n }\n\n // The example edge uses identifiers already declared above when there are two\n // to join; otherwise placeholders, so it never invents a service.\n const from = touchedIds[0]?.[0] ?? createdIds[0]?.[0] ?? \"consumer\";\n const to = createdIds[0]?.[0] ?? touchedIds[1]?.[0] ?? \"provider\";\n lines.push(\" // New calls. `metadata { op }` is the spine: it names the OpenAPI operationId\");\n lines.push(\" // the call uses, and `loam validate` checks it against the target's contract.\");\n lines.push(\" // Uncomment and adjust (both endpoints have to be declared above):\");\n lines.push(\" //\");\n lines.push(` // ${from} -> ${to} 'Calls createSplit' {`);\n lines.push(` // #${featureId}`);\n lines.push(\" // metadata { op 'createSplit' }\");\n lines.push(\" // }\");\n\n return `// ${featureId} — architecture delta.\n//\n// Everything tagged #${featureId} is exactly what \\`loam archive\\` folds into\n// architecture/landscape.likec4. Everything else here is context for the diagram.\n//\n// If ${featureId} changes no architecture, DELETE this file: a requirements-only\n// feature is complete without it, and an empty \\`model {}\\` is just as legal.\n\nspecification {\n element softwareSystem\n tag ${featureId}\n}\n\nmodel {\n${lines.join(\"\\n\")}\n}\n\nviews {\n view ${viewName(featureId)} {\n include *\n }\n}\n`;\n}\n\nfunction specTemplate(featureId: string, service: string): string {\n return `# ${service} — requirement delta for ${featureId}\n\n<!-- Sections: ADDED / MODIFIED / REMOVED. Delete the ones you do not need.\n A MODIFIED requirement carries its full new text, not a diff.\n Every requirement needs at least one scenario — \\`loam validate\\` gates on it. -->\n\n## ADDED Requirements\n\n### Requirement: TODO — name the behaviour\nRequirement-ID: ${featureId}.${service}.requirement\n\nThe service SHALL <observable behaviour, testable without reading the code>.\n\n<!-- Operations: createSplit\n The operationIds this requirement governs. \\`loam validate\\` checks each one\n against the service's OpenAPI, and \\`loam archive\\` refuses to merge a\n requirement that governs an operation nobody defines. Uncomment when the\n contract exists. -->\n\n#### Scenario: TODO — name the case\n- **Given** <the starting state>\n- **When** <the trigger>\n- **Then** <the observable outcome>\n`;\n}\n\n/**\n * The architecture axis, scaffolded EMPTY on purpose.\n *\n * Same grammar as spec.md, so the template body has to be unparseable as\n * requirements or the scaffold would ship a requirement nobody wrote — the\n * headings are therefore indented inside an HTML comment, which puts them past\n * the line-anchored `## ADDED Requirements` / `### Requirement:` patterns\n * core/spec.ts matches on. Copy the block out of the comment and unindent it.\n */\nfunction archSpecTemplate(featureId: string, service: string): string {\n return `# ${service} — architecture requirement delta for ${featureId}\n\n<!-- The architecture axis: the obligations no business scenario was ever going\n to mention — the outbox, the retries, the timeouts, the alerts.\n\n \\`Covers:\\` is to an arch requirement what \\`Operations:\\` is to a business\n one — it names the MODEL OBJECTS the scenarios below exercise, so coverage\n is derived instead of trusted. Three forms, comma-separated:\n\n Covers: ${service} a C4 element — its id, or the service a\n bound/titled element stands for\n Covers: consumer -> ${service} an edge, each side resolved the same way\n Covers: alert:<id>, sli:<id> a signal declared in health.yaml\n\n Every tagged element and edge in delta.likec4 wants one (\\`c4.uncovered\\`).\n Delete this file if ${featureId} adds no architectural obligation.\n\n Copy the block below out of this comment and unindent it:\n\n ## ADDED Requirements\n\n ### Requirement: TODO — name the architectural obligation\n Requirement-ID: ${featureId}.${service}.arch\n\n The service SHALL <the operational/integration behaviour, observable in test>.\n\n Covers: ${service}\n\n #### Scenario: TODO — name the case\n - **Given** <the starting state>\n - **When** <the trigger>\n - **Then** <the observable outcome>\n-->\n`;\n}\n\nfunction openapiTemplate(service: string): string {\n return `openapi: 3.1.0\ninfo:\n title: ${service}\n version: \"0.1\"\n\n# The operations this feature adds. \\`operationId\\` is the token the C4 edge\n# (metadata { op }) and the requirement (Operations:) both point at — keep the\n# three spellings identical or \\`loam validate\\` will say so.\npaths: {}\n# /splits:\n# post:\n# operationId: createSplit\n# responses:\n# \"201\":\n# description: Created\n`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { stringify as stringifyYaml } from \"yaml\";\nimport { loadConfig } from \"../core/config.js\";\nimport { FEATURE_ID_RULE, InvalidIdError, assertServiceId, isFeatureId } from \"../core/ids.js\";\nimport { emitJson, fail, repoPath, reportNoConfig } from \"../core/json.js\";\nimport { UnsafePathError, resolveInside } from \"../core/path-safety.js\";\nimport {\n DocsRepoUnavailableError,\n featureIdFromDirName,\n featuresDir,\n listServices,\n nearestIds,\n resolveFeature,\n type FeatureEntry,\n} from \"../core/repo.js\";\nimport { docsRepoReady, reportDocsRepoError } from \"./docs-repo-gate.js\";\n\ninterface NewOptions {\n title?: string;\n touches: string[];\n newService: string[];\n json?: boolean;\n}\n\nexport function registerNew(program: Command): void {\n program\n .command(\"new\")\n .argument(\"<featureId>\", \"feature id, e.g. FEAT-101\")\n .description(\"Scaffold a feature: intent, C4 delta, and a requirement delta per service\")\n .option(\"--title <text>\", \"human title; also becomes the directory slug\")\n // `--touches`, not `--service`: everywhere else `--service` selects the ONE\n // operating target (defaulting to config.service), while this is a repeatable\n // list of services the feature touches — a different arity and a different\n // meaning deserve a different name.\n .option(\"--touches <id>\", \"a service this feature touches (repeatable)\", collect, [])\n .option(\"--new-service <id>\", \"a service this feature introduces (repeatable)\", collect, [])\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (featureId: string, opts: NewOptions) => {\n const json = opts.json === true;\n\n const dirName = featureDirName(featureId, opts.title);\n // The round-trip is checked beside the grammar, not inside it: the\n // grammar is a fact about the id, while `featureIdFromDirName` depends on\n // the TITLE this command is about to slug into the directory name. Only\n // `new` creates that directory, so only `new` owes that second half.\n if (!isFeatureId(featureId) || featureIdFromDirName(dirName) !== featureId) {\n return fail(\n json,\n \"invalid-option\",\n `'${featureId}' is not a usable feature id. ${FEATURE_ID_RULE}`,\n );\n }\n\n // Service ids are validated BEFORE the config is even loaded, and long\n // before anything is written: every one of them is interpolated into\n // `specs/<id>/` under the new feature directory, so `--touches ../../x`\n // was a writer pointed outside the docs repo. One grammar (core/ids.ts),\n // the same one adopt/init/vouch refuse on, so a service that is legal to\n // create is legal to name here and nowhere the two disagree.\n for (const [label, ids] of [\n [\"--touches\", opts.touches],\n [\"--new-service\", opts.newService],\n ] as const) {\n for (const id of ids) {\n try {\n assertServiceId(id, label);\n } catch (err) {\n if (!(err instanceof InvalidIdError)) throw err;\n return fail(json, \"invalid-option\", err.message);\n }\n }\n }\n\n const config = await loadConfig();\n if (!config) {\n reportNoConfig(json);\n return;\n }\n const { docsDir } = config;\n // Before a single file is scaffolded: a docsDir that is not a docs repo is\n // refused, never written into. `features/<id>/**` lands happily in any\n // directory, so without this gate `new` reported `ok: true` over a\n // scaffold that no enumeration downstream will ever see — and a docsDir\n // that is simply absent escaped as `internal` (the enumeration below\n // throws) carrying the very sentence `list` and `show` report as\n // `docs-missing`. See docs-repo-gate.ts's docsRepoReady. `services`, not\n // `docs`: every id named by --touches/--new-service is a claim about\n // `services/<id>/`, and the near-miss note below is computed against that\n // directory, so a run without it is guessing.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n\n let existing: FeatureEntry | null;\n try {\n existing = await resolveFeature(docsDir, featureId, \"include\");\n } catch (err) {\n // The gate above already refused both broken states, so reaching here\n // means the docs repo went away between that check and this read. Same\n // breach, same two codes — the alternative is a stack trace under\n // `internal` that names neither.\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n reportDocsRepoError(json, err);\n return;\n }\n if (existing) {\n return fail(\n json,\n \"already-exists\",\n `Feature '${featureId}' already exists at ${repoPath(docsDir, existing.dir)}.`,\n );\n }\n\n // A service named both ways is new — that is the more specific claim.\n const created = new Set(opts.newService);\n const touched = opts.touches.filter((s) => !created.has(s));\n const dir = join(featuresDir(docsDir), dirName);\n\n const files: Record<string, string> = {\n \"intent.md\": intentTemplate(featureId, opts.title),\n \"delta.likec4\": deltaTemplate(featureId, touched, [...created]),\n };\n for (const svc of [...touched, ...created]) {\n files[join(\"specs\", svc, \"spec.md\")] = specTemplate(featureId, svc);\n }\n for (const svc of created) {\n files[join(\"specs\", svc, \"openapi.yaml\")] = openapiTemplate(svc);\n // The architecture axis is scaffolded only for a service this feature\n // INTRODUCES, because that is the case where `c4.uncovered` will fire\n // the moment the delta is written: a brand-new tagged element nothing\n // covers. Handing an author a blank page for the outbox/retries/alerts\n // requirement is how that axis stays empty across a whole fleet.\n files[join(\"specs\", svc, \"arch.spec.md\")] = archSpecTemplate(featureId, svc);\n }\n\n const written: string[] = [];\n for (const [rel, content] of Object.entries(files)) {\n // Belt and braces over the id check above: the id grammar is what makes\n // this safe, and `resolveInside` is what proves it at the moment of the\n // write — including the symlink cases a grammar cannot see.\n let path: string;\n try {\n path = resolveInside(docsDir, join(\"features\", dirName, rel), \"feature file\");\n } catch (err) {\n if (!(err instanceof UnsafePathError)) throw err;\n return fail(json, \"invalid-option\", err.message);\n }\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, content, \"utf8\");\n written.push(repoPath(docsDir, path));\n }\n\n const notes = await unknownServiceNotes(docsDir, touched);\n\n if (json) {\n emitJson({\n feature: featureId,\n path: repoPath(docsDir, dir),\n created: written,\n // Not an error and not a finding: `--touches` on a service that does\n // not exist yet is legal (adopt it later, or say `--new-service`).\n // It is reported because the silent alternative is a feature whose\n // spec delta will never merge into anything.\n notes,\n });\n return;\n }\n console.log(`${featureId} scaffolded at ${repoPath(docsDir, dir)}`);\n for (const w of written) console.log(` + ${w}`);\n for (const note of notes) console.log(`\\n note: ${note}`);\n console.log(`\\nNext: fill in the delta, then \\`loam validate --feature ${featureId}\\`.`);\n console.log(\n \"If this feature changes no architecture, delete delta.likec4 — a requirements-only\\n\" +\n \"feature is complete without it, and an empty `model {}` validates clean too.\",\n );\n });\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\n/* ------------------------------------------------------------------ */\n/* Hints */\n/* ------------------------------------------------------------------ */\n\n/**\n * `--touches <id>` naming neither a living `services/<id>/` nor a `--new-service`\n * is almost always a typo, and it is silent by construction: the scaffold writes\n * `specs/<id>/spec.md` happily, `loam validate` grades a delta against a living\n * spec that is not there, and the mistake surfaces at archive time as a service\n * appearing from nowhere. A note, not a refusal — naming a service that will be\n * adopted next week is legitimate — but the near-miss is spelled out, because\n * `order-api` vs `orders-api` is exactly the pair a person cannot see.\n */\nasync function unknownServiceNotes(docsDir: string, touched: string[]): Promise<string[]> {\n if (touched.length === 0) return [];\n let known: string[];\n try {\n known = (await listServices(docsDir)).map((s) => s.id);\n } catch (err) {\n // No docs repo to compare against is a different diagnosis entirely, and\n // `loam doctor` owns it. Saying nothing here beats inventing a typo hint\n // out of an enumeration that never ran.\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n return [];\n }\n const notes: string[] = [];\n for (const id of touched) {\n if (known.includes(id)) continue;\n const near = nearestIds(id, known);\n notes.push(\n `--touches '${id}' matches no services/${id}/ in the docs repo` +\n (near.length === 0 ? \"\" : ` — did you mean ${near.map((n) => `'${n}'`).join(\" or \")}?`) +\n ` If ${id} is introduced by this feature, pass --new-service ${id} instead.`,\n );\n }\n return notes;\n}\n\n/* ------------------------------------------------------------------ */\n/* Naming */\n/* ------------------------------------------------------------------ */\n\nfunction featureDirName(featureId: string, title: string | undefined): string {\n const slug = slugify(title ?? \"\");\n return slug ? `${featureId}-${slug}` : featureId;\n}\n\nfunction slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n/** A LikeC4 identifier for a service name: `payment-split-service` -> `paymentSplitService`. */\nfunction identifier(name: string, taken: Set<string>): string {\n const parts = name.split(/[^A-Za-z0-9]+/).filter((p) => p.length > 0);\n const head = (parts[0] ?? \"svc\").replace(/^\\d+/, \"\") || \"svc\";\n const base =\n head[0]!.toLowerCase() +\n head.slice(1) +\n parts.slice(1).map((p) => p[0]!.toUpperCase() + p.slice(1)).join(\"\");\n let id = base;\n for (let n = 2; taken.has(id); n += 1) id = `${base}${n}`;\n taken.add(id);\n return id;\n}\n\n/** `FEAT-101` -> `feat_101`: LikeC4 view names take no dashes. */\nfunction viewName(featureId: string): string {\n return featureId.toLowerCase().replace(/[^a-z0-9]+/g, \"_\");\n}\n\n/* ------------------------------------------------------------------ */\n/* Templates */\n/* ------------------------------------------------------------------ */\n\nfunction intentTemplate(featureId: string, title: string | undefined): string {\n const heading = title ?? featureId;\n // The frontmatter is SERIALIZED, never interpolated. A title is free text a\n // person types — `Checkout: split payments` used to be pasted straight after\n // `title: `, where the colon reopens the mapping and the file stops parsing;\n // `Orders #42 rework` lost everything from the `#` as a YAML comment, so the\n // feature quietly answered to a title nobody wrote. The yaml serializer is the\n // only thing that knows every case, and it is already how migrate-openspec\n // writes the same three keys.\n //\n // `owner` is deliberately NOT a key here. Scaffolding `owner:` writes an\n // explicit null, which is a claim (\"this feature has no owner\") rather than\n // the truth (\"nobody has said yet\") — and every reader then has to tell an\n // absent key from a null one. The prompt survives as a comment.\n const frontmatter = stringifyYaml(\n { feature: featureId, ...(title === undefined ? {} : { title }), status: \"proposed\" },\n { lineWidth: 0 },\n ).trimEnd();\n return `---\n${frontmatter}\n# owner: <the team or person who answers for this>\n---\n\n# ${heading}\n\n## Why\n\n<!-- The problem in business terms. This is what a reviewer reads first, and what\n the requirement deltas below have to be justified by. -->\n\n## Scope\n\n<!-- Which services this touches — and, more usefully, which it deliberately does not. -->\n`;\n}\n\nfunction deltaTemplate(featureId: string, touched: string[], created: string[]): string {\n const taken = new Set<string>();\n const touchedIds = touched.map((s) => [identifier(s, taken), s] as const);\n const createdIds = created.map((s) => [identifier(s, taken), s] as const);\n\n const lines: string[] = [];\n if (touchedIds.length > 0) {\n // Context elements ship COMMENTED OUT, exactly like the example edge below.\n // Declared-and-untagged is the one shape `delta.nothing-tagged` exists to\n // refuse (an author who forgot the tags), so writing these live made every\n // `loam new --touches X` fail its own validator on the very first run —\n // which teaches people that the validator is noise. Uncomment a line when an\n // edge below actually needs the identifier.\n lines.push(\" // Services this feature touches, as context for the diagram. Uncomment the\");\n lines.push(\" // ones an edge below names, and reuse the identifiers from\");\n lines.push(\" // architecture/landscape.likec4 so the merge lines up. They stay UNTAGGED:\");\n lines.push(` // only #${featureId} is the change, and everything else here is context.`);\n for (const [id, name] of touchedIds) lines.push(` // ${id} = softwareSystem '${name}'`);\n lines.push(\"\");\n }\n if (createdIds.length > 0) {\n lines.push(\" // Services this feature introduces. The tag is what `loam archive` folds\");\n lines.push(\" // into the living landscape.\");\n for (const [id, name] of createdIds) {\n lines.push(` ${id} = softwareSystem '${name}' {`);\n lines.push(` #${featureId}`);\n lines.push(` description 'TODO — what this service owns'`);\n lines.push(\" }\");\n }\n lines.push(\"\");\n }\n\n // The example edge uses identifiers already declared above when there are two\n // to join; otherwise placeholders, so it never invents a service.\n const from = touchedIds[0]?.[0] ?? createdIds[0]?.[0] ?? \"consumer\";\n const to = createdIds[0]?.[0] ?? touchedIds[1]?.[0] ?? \"provider\";\n lines.push(\" // New calls. `metadata { op }` is the spine: it names the OpenAPI operationId\");\n lines.push(\" // the call uses, and `loam validate` checks it against the target's contract.\");\n lines.push(\" // Uncomment and adjust (both endpoints have to be declared above):\");\n lines.push(\" //\");\n lines.push(` // ${from} -> ${to} 'Calls createSplit' {`);\n lines.push(` // #${featureId}`);\n lines.push(\" // metadata { op 'createSplit' }\");\n lines.push(\" // }\");\n\n return `// ${featureId} — architecture delta.\n//\n// Everything tagged #${featureId} is exactly what \\`loam archive\\` folds into\n// architecture/landscape.likec4. Everything else here is context for the diagram.\n//\n// If ${featureId} changes no architecture, DELETE this file: a requirements-only\n// feature is complete without it, and an empty \\`model {}\\` is just as legal.\n\nspecification {\n element softwareSystem\n tag ${featureId}\n}\n\nmodel {\n${lines.join(\"\\n\")}\n}\n\nviews {\n view ${viewName(featureId)} {\n include *\n }\n}\n`;\n}\n\nfunction specTemplate(featureId: string, service: string): string {\n return `# ${service} — requirement delta for ${featureId}\n\n<!-- Sections: ADDED / MODIFIED / REMOVED. Delete the ones you do not need.\n A MODIFIED requirement carries its full new text, not a diff.\n Every requirement needs at least one scenario — \\`loam validate\\` gates on it. -->\n\n## ADDED Requirements\n\n### Requirement: TODO — name the behaviour\nRequirement-ID: ${featureId}.${service}.requirement\n\nThe service SHALL <observable behaviour, testable without reading the code>.\n\n<!-- Operations: createSplit\n The operationIds this requirement governs. \\`loam validate\\` checks each one\n against the service's OpenAPI, and \\`loam archive\\` refuses to merge a\n requirement that governs an operation nobody defines. Uncomment when the\n contract exists. -->\n\n#### Scenario: TODO — name the case\n- **Given** <the starting state>\n- **When** <the trigger>\n- **Then** <the observable outcome>\n`;\n}\n\n/**\n * The architecture axis, scaffolded EMPTY on purpose.\n *\n * Same grammar as spec.md, so the template body has to be unparseable as\n * requirements or the scaffold would ship a requirement nobody wrote — the\n * headings are therefore indented inside an HTML comment, which puts them past\n * the line-anchored `## ADDED Requirements` / `### Requirement:` patterns\n * core/spec.ts matches on. Copy the block out of the comment and unindent it.\n */\nfunction archSpecTemplate(featureId: string, service: string): string {\n return `# ${service} — architecture requirement delta for ${featureId}\n\n<!-- The architecture axis: the obligations no business scenario was ever going\n to mention — the outbox, the retries, the timeouts, the alerts.\n\n \\`Covers:\\` is to an arch requirement what \\`Operations:\\` is to a business\n one — it names the MODEL OBJECTS the scenarios below exercise, so coverage\n is derived instead of trusted. Three forms, comma-separated:\n\n Covers: ${service} a C4 element — its id, or the service a\n bound/titled element stands for\n Covers: consumer -> ${service} an edge, each side resolved the same way\n Covers: alert:<id>, sli:<id> a signal declared in health.yaml\n\n Every tagged element and edge in delta.likec4 wants one (\\`c4.uncovered\\`).\n Delete this file if ${featureId} adds no architectural obligation.\n\n Copy the block below out of this comment and unindent it:\n\n ## ADDED Requirements\n\n ### Requirement: TODO — name the architectural obligation\n Requirement-ID: ${featureId}.${service}.arch\n\n The service SHALL <the operational/integration behaviour, observable in test>.\n\n Covers: ${service}\n\n #### Scenario: TODO — name the case\n - **Given** <the starting state>\n - **When** <the trigger>\n - **Then** <the observable outcome>\n-->\n`;\n}\n\nfunction openapiTemplate(service: string): string {\n return `openapi: 3.1.0\ninfo:\n title: ${service}\n version: \"0.1\"\n\n# The operations this feature adds. \\`operationId\\` is the token the C4 edge\n# (metadata { op }) and the requirement (Operations:) both point at — keep the\n# three spellings identical or \\`loam validate\\` will say so.\npaths: {}\n# /splits:\n# post:\n# operationId: createSplit\n# responses:\n# \"201\":\n# description: Created\n`;\n}\n"]}
|
package/dist/commands/rebase.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { loadConfig } from "../core/config.js";
|
|
4
|
-
import { decodeDocument, NotUtf8DocumentError } from "../core/
|
|
4
|
+
import { decodeDocument, NotUtf8DocumentError } from "../core/document-bytes.js";
|
|
5
5
|
import { InvalidIdError, assertServiceId } from "../core/ids.js";
|
|
6
6
|
import { emitJson, fail, repoPath, reportNoConfig } from "../core/json.js";
|
|
7
7
|
import { compareIds, featureSpecPaths, missingFeatureMessage, resolveFeature, servicePaths, SPEC_AXES, } from "../core/repo.js";
|
|
8
8
|
import { DocsBusyError, acquireDocsLock, message, rollbackStaged, stageWrites, swapStaged, } from "../core/staging.js";
|
|
9
9
|
import { parseRequirements, readRequirementsDocument, requirementDigest, } from "../core/spec.js";
|
|
10
10
|
import { OpenapiMergeError, pinOpenapiOperations, } from "../core/openapi-merge.js";
|
|
11
|
+
import { plural } from "./format.js";
|
|
11
12
|
export function registerRebase(program) {
|
|
12
13
|
program
|
|
13
14
|
.command("rebase")
|
|
@@ -352,7 +353,4 @@ function applyEdits(raw, edits) {
|
|
|
352
353
|
}
|
|
353
354
|
return lines.map((line) => line.text + line.eol).join("");
|
|
354
355
|
}
|
|
355
|
-
function plural(n, noun) {
|
|
356
|
-
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
357
|
-
}
|
|
358
356
|
//# sourceMappingURL=rebase.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rebase.js","sourceRoot":"","sources":["../../src/commands/rebase.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam rebase` — pin a feature's MODIFIED/REMOVED requirements to the living\n * text they are written against.\n *\n * The counterpart to `loam vouch`: vouch stamps a digest of the CODE a living\n * spec was written from, this stamps a digest of the LIVING REQUIREMENT a delta\n * was written from. Both exist so a later `loam validate` can tell \"still true\"\n * from \"something moved underneath\", and both are worth exactly what they claim\n * — so this command computes every pin from what is on disk right now and never\n * invents one for a requirement that addresses nothing living.\n *\n * It is the fix `delta.baseline-stale` sends people to, which is why the\n * rewrite is line SURGERY rather than a reserialize: a delta document is\n * authored prose — section headings, comments, the order the author chose — and\n * `serializeRequirements` would flatten all of it into a bare requirement run.\n * One line changes per requirement; every other byte is copied.\n *\n * Restamping is the LAST step of resolving a collision, never the resolution\n * itself: a pin says \"I read this version\", so running it without re-reading\n * makes it a lie with a digest on it. The output says so on the way out.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { loadConfig } from \"../core/config.js\";\nimport { decodeDocument, NotUtf8DocumentError } from \"../core/fleet-context.js\";\nimport { InvalidIdError, assertServiceId } from \"../core/ids.js\";\nimport { emitJson, fail, repoPath, reportNoConfig } from \"../core/json.js\";\nimport {\n compareIds,\n featureSpecPaths,\n missingFeatureMessage,\n resolveFeature,\n servicePaths,\n SPEC_AXES,\n type SpecAxis,\n} from \"../core/repo.js\";\nimport {\n DocsBusyError,\n acquireDocsLock,\n message,\n rollbackStaged,\n stageWrites,\n swapStaged,\n type PlannedWrite,\n} from \"../core/staging.js\";\nimport {\n parseRequirements,\n readRequirementsDocument,\n requirementDigest,\n type Requirement,\n} from \"../core/spec.js\";\nimport {\n OpenapiMergeError,\n pinOpenapiOperations,\n type OpenapiPinPlan,\n} from \"../core/openapi-merge.js\";\n\ninterface RebaseOptions {\n service?: string;\n json?: boolean;\n dryRun?: boolean;\n}\n\n/** What happened to one pin. */\ntype PinStatus =\n /** It had none and now has one. */\n | \"pinned\"\n /** It had a pin for an older living version; it now names the current one. */\n | \"repinned\"\n /** It already named the current living version — no write. */\n | \"unchanged\"\n /**\n * It addresses nothing living — a MODIFIED of a requirement another feature\n * in flight still introduces, or an operation this feature is adding. There\n * is no version to be based on, so inventing a pin would be inventing a\n * baseline; `loam validate` already reports the ordering where there is one\n * (`delta.modified-pending`).\n */\n | \"unresolved\"\n /**\n * The operation is written as a YAML alias. One shared value backs every use\n * of an anchor, so stamping \"through\" it would pin every other use too —\n * the same reason the merge refuses to edit aliases in place. Rare, legal,\n * and named rather than silently skipped.\n */\n | \"unwritable\";\n\ninterface PinOutcome {\n service: string;\n /** The axis's filename — \"spec.md\", \"arch.spec.md\" or \"openapi.yaml\". */\n file: string;\n /** MODIFIED/REMOVED for a requirement; the upper-case HTTP method for an operation. */\n kind: string;\n /** The requirement heading, or `/path ('operationId')`. */\n target: string;\n status: PinStatus;\n /** The pin as it was, or null when there was none. */\n from: string | null;\n /** The pin as it now stands, or null when nothing living could be hashed. */\n to: string | null;\n}\n\nexport function registerRebase(program: Command): void {\n program\n .command(\"rebase\")\n .argument(\"<featureId>\", \"feature id, e.g. FEAT-101\")\n .description(\"Pin a feature's MODIFIED/REMOVED requirements to the living text they are written against\")\n .option(\"--service <id>\", \"restrict to one service (default: every service the feature touches)\")\n .option(\"--dry-run\", \"print what would be pinned and write nothing\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (featureId: string, opts: RebaseOptions) => {\n const json = opts.json === true;\n const config = await loadConfig();\n if (!config) {\n reportNoConfig(json);\n return;\n }\n\n // The id grammar on the RAW argument, before it reaches a path join —\n // one grammar for the whole tool (core/ids.ts).\n if (opts.service !== undefined) {\n try {\n assertServiceId(opts.service, \"--service\");\n } catch (err) {\n if (!(err instanceof InvalidIdError)) throw err;\n return fail(json, \"invalid-option\", err.message);\n }\n }\n\n // The living specs must not move between the read that computes a pin and\n // the write that records it: a digest taken over a document an archive\n // replaced a millisecond later is precisely the false \"I read this\" the\n // pin exists to make impossible. One writer per docs repo, refusing\n // rather than queueing, exactly as archive takes it.\n let release: () => Promise<void>;\n try {\n release = await acquireDocsLock(config.docsDir);\n } catch (err) {\n if (!(err instanceof DocsBusyError)) throw err;\n return fail(json, \"docs-busy\", err.message);\n }\n try {\n await rebaseLocked(config.docsDir, featureId, opts);\n } finally {\n await release();\n }\n });\n}\n\nasync function rebaseLocked(docsDir: string, featureId: string, opts: RebaseOptions): Promise<void> {\n const json = opts.json === true;\n const dryRun = opts.dryRun === true;\n\n const feature = await resolveFeature(docsDir, featureId, \"exclude\");\n if (!feature) {\n return fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, featureId));\n }\n const { id } = feature;\n\n if (opts.service !== undefined && !feature.services.includes(opts.service)) {\n // The refusal names the choices, the way `loam delta`'s does: a typo must\n // never be indistinguishable from a feature with nothing to pin.\n return fail(\n json,\n \"unknown-target\",\n `${id} carries no requirement delta for '${opts.service}'` +\n (feature.services.length === 0\n ? \" — it touches no service at all.\"\n : ` — it touches: ${[...feature.services].sort(compareIds).join(\", \")}.`),\n );\n }\n const services = (opts.service === undefined ? [...feature.services] : [opts.service]).sort(compareIds);\n\n const outcomes: PinOutcome[] = [];\n const writes: PlannedWrite[] = [];\n try {\n for (const service of services) {\n for (const axis of SPEC_AXES) {\n const specPath = featureSpecPaths(feature.dir, service)[axis.key];\n if (!existsSync(specPath)) continue;\n const planned = await planAxis(docsDir, service, axis, specPath);\n outcomes.push(...planned.outcomes);\n if (planned.content !== null) writes.push({ path: specPath, content: planned.content });\n }\n // The contract axis. It needs the pin more than the requirement axes do:\n // a requirement delta spells only the requirements it changes, while an\n // openapi delta is a COMPLETE document and spells the whole contract.\n const openapiPath = featureSpecPaths(feature.dir, service).openapi;\n if (existsSync(openapiPath)) {\n const planned = await planOpenapi(docsDir, service, openapiPath);\n outcomes.push(...planned.outcomes);\n if (planned.content !== null) writes.push({ path: openapiPath, content: planned.content });\n }\n }\n } catch (err) {\n if (!(err instanceof NotUtf8DocumentError)) throw err;\n // A pin is a claim about text somebody read. Over a document loam could not\n // decode it would be a digest of mojibake with loam's name on it — the\n // precise false claim this command exists to prevent — and the next archive\n // would land a stale delta over living text on the strength of it. So it\n // refuses by name and writes nothing, exactly as `loam archive` refuses a\n // merge it could not read, under archive's code. The refusal is here, in\n // the PLAN, so it lands before stageWrites: no file is touched, and a\n // feature spanning four services does not get two of them pinned.\n return fail(json, \"merge-failed\", `rebase ${id} failed — nothing was pinned: ${err.message}`);\n }\n\n if (!dryRun && writes.length > 0) {\n // Staged and swapped like every other multi-file write in loam\n // (core/staging.ts): a feature spanning four services is one act, and a\n // full disk between its second and third file must not leave two of them\n // pinned to a reading the other two never saw.\n const staged = await stageWrites(writes);\n try {\n await swapStaged(staged);\n } catch (err) {\n const failures = await rollbackStaged(staged);\n return fail(\n json,\n failures.length === 0 ? \"merge-failed\" : \"rollback-incomplete\",\n failures.length === 0\n ? `rebase ${id} failed and was rolled back — nothing was pinned: ${message(err)}`\n : `rebase ${id} failed and the rollback did not complete; these files are in an unknown state: ${failures.join(\", \")} — ${message(err)}`,\n );\n }\n }\n\n const changed = outcomes.filter((o) => o.status === \"pinned\" || o.status === \"repinned\");\n const unresolved = outcomes.filter((o) => o.status === \"unresolved\");\n\n if (json) {\n emitJson({\n feature: id,\n services,\n dryRun,\n pins: outcomes,\n written: dryRun ? [] : writes.map((w) => repoPath(docsDir, w.path)),\n });\n return;\n }\n\n if (outcomes.length === 0) {\n console.log(\n `${id}: nothing to pin — a baseline only means something for a requirement or an operation that already exists.`,\n );\n return;\n }\n\n console.log(`${id}${dryRun ? \" (dry run)\" : \"\"}\\n`);\n for (const o of outcomes) {\n const what = `${o.service}/${o.file} ${o.kind} ${o.target}`;\n if (o.status === \"unresolved\") {\n console.log(` · ${what} — not in the living docs yet, nothing to pin`);\n } else if (o.status === \"unwritable\") {\n console.log(` ! ${what} — written as a YAML alias; loam will not stamp through a shared anchor`);\n } else if (o.status === \"unchanged\") {\n console.log(` = ${what} — already ${o.to}`);\n } else if (o.status === \"pinned\") {\n console.log(` + ${what} — pinned ${o.to}`);\n } else {\n console.log(` ~ ${what} — ${o.from} → ${o.to}`);\n }\n }\n\n if (changed.length === 0) {\n console.log(`\\nNothing to write: every pin already names the living version.`);\n } else if (dryRun) {\n console.log(`\\n${plural(changed.length, \"pin\")} would be written. Nothing was written.`);\n } else {\n console.log(`\\n${plural(changed.length, \"pin\")} written across ${plural(writes.length, \"file\")}.`);\n // The one sentence that keeps this command honest. A pin is a claim about\n // what its author read, and a `repinned` line means the living text moved\n // under a delta that still says what it said before — restamping does not\n // fold in the change it just stopped reporting.\n const repinned = changed.filter((o) => o.status === \"repinned\");\n if (repinned.length > 0) {\n console.log(\n `\\n⚠ ${plural(repinned.length, \"pin\")} moved since this delta was written. A pin records what you read — ` +\n `re-read those living requirements and operations and fold in what you still mean, or the next archive lands your version over theirs with loam's blessing.`,\n );\n }\n }\n if (unresolved.length > 0) {\n console.log(\n `\\n${plural(unresolved.length, \"item\")} could not be pinned because the living docs do not have them yet — ` +\n `for a requirement, \\`loam dependencies ${id}\\` says which feature introduces it first; for an operation, this feature is adding it.`,\n );\n }\n}\n\ninterface AxisPlan {\n outcomes: PinOutcome[];\n /** The rewritten document, or null when nothing about it changes. */\n content: string | null;\n}\n\n/** Pin every MODIFIED/REMOVED requirement in one delta file against one living document. */\nasync function planAxis(\n docsDir: string,\n service: string,\n axis: SpecAxis,\n specPath: string,\n): Promise<AxisPlan> {\n // Both sides through the decoding read: the delta because line surgery over a\n // document loam mis-decoded would write bytes nobody authored, the living\n // because it is what every digest below is taken over.\n const raw = await readRequirementsDocument(specPath);\n const reqs = parseRequirements(raw);\n const livingPath = servicePaths(docsDir, service)[axis.key];\n const living = existsSync(livingPath)\n ? parseRequirements(await readRequirementsDocument(livingPath))\n : [];\n\n const outcomes: PinOutcome[] = [];\n const edits: LineEdit[] = [];\n for (const r of reqs) {\n if (r.kind !== \"MODIFIED\" && r.kind !== \"REMOVED\") continue;\n // Every requirement parsed from a document carries its heading line\n // (core/spec.ts). One built in memory has no document to write into, and\n // there is nothing truthful to report about pinning it.\n const line = r.line;\n if (line === undefined) continue;\n const base = { service, file: axis.file, kind: r.kind, target: r.name };\n const selected = selectLiving(living, r);\n if (selected === undefined) {\n outcomes.push({ ...base, status: \"unresolved\", from: r.basedOn ?? null, to: null });\n continue;\n }\n const digest = requirementDigest(selected);\n if (r.basedOn === digest) {\n outcomes.push({ ...base, status: \"unchanged\", from: digest, to: digest });\n continue;\n }\n outcomes.push({\n ...base,\n status: r.basedOn === undefined ? \"pinned\" : \"repinned\",\n from: r.basedOn ?? null,\n to: digest,\n });\n edits.push(pinEdit(r, line, digest));\n }\n\n return { outcomes, content: edits.length === 0 ? null : applyEdits(raw, edits) };\n}\n\n/**\n * Pin every operation in one feature contract against the living contract.\n *\n * The pin is always `operationDigest` of the LIVING operation, never of the\n * delta's own. That single rule produces both merge verdicts on its own: an\n * operation the author only QUOTED is byte-equal to living, so its pin equals\n * its own content and the merge skips it; one the author EDITED differs from\n * its pin, so the merge writes it. Nothing here has to guess at intent — the\n * document already records it, and the pin makes it legible.\n *\n * Slot-keyed (path + method), exactly as the merge upserts. An operationId that\n * moved to another slot is a new slot with nothing living behind it, which is\n * `unresolved` and correct: there is no living version of an operation at a\n * path the contract does not serve yet.\n */\nasync function planOpenapi(docsDir: string, service: string, openapiPath: string): Promise<AxisPlan> {\n const livingPath = servicePaths(docsDir, service).openapi;\n // Decoded, not `readFile(…, \"utf8\")`, for the requirement axes' reason: a\n // contract read with U+FFFD substituted in defines no operation loam can\n // match, so every pin would come out `unresolved` — \"this feature adds them\n // all\" — over a living contract that already serves them. Read outside the\n // try below, whose job is the merge's own diagnosis and not this one.\n const living = existsSync(livingPath) ? decodeDocument(await readFile(livingPath), livingPath) : \"\";\n const delta = decodeDocument(await readFile(openapiPath), openapiPath);\n let plan: OpenapiPinPlan;\n try {\n plan = pinOpenapiOperations(delta, living, service);\n } catch (err) {\n // A document loam cannot read is validate's diagnosis to make\n // (`openapi.invalid`), not this command's to guess at — and stamping into a\n // broken parse would write a file nobody asked for.\n if (!(err instanceof OpenapiMergeError)) throw err;\n return { outcomes: [], content: null };\n }\n return {\n outcomes: plan.pins.map((pin) => ({\n service,\n file: \"openapi.yaml\",\n kind: pin.method.toUpperCase(),\n target: pin.operationId.length === 0 ? pin.path : `${pin.path} ('${pin.operationId}')`,\n status: pin.status,\n from: pin.from,\n to: pin.to,\n })),\n content: plan.text,\n };\n}\n\n/**\n * The living requirement a delta requirement addresses — the merge's own\n * resolution order (core/spec.ts `applyRequirementDelta`), so a pin is taken\n * over exactly the requirement archive would rewrite: the stable ID when the\n * delta carries one, the exact heading otherwise, and the FIRST match either\n * way (duplicates are refused upstream by `delta.living-duplicate-requirement`).\n */\nfunction selectLiving(living: Requirement[], r: Requirement): Requirement | undefined {\n if (r.id !== undefined) return living.find((candidate) => candidate.id === r.id);\n return living.find((candidate) => candidate.name === r.name);\n}\n\n/* ------------------------------------------------------------------ */\n/* Line surgery */\n/* ------------------------------------------------------------------ */\n\ninterface LineEdit {\n /** 0-based line index, as `parseRequirements` counts lines. */\n at: number;\n /** Replace that line, or insert a new one before it. */\n mode: \"replace\" | \"insert\";\n text: string;\n}\n\n/**\n * Where this requirement's `Based-On:` line goes, and whether it replaces one.\n *\n * `headingLine` is 1-based and `text` captures every body line after it\n * contiguously (core/spec.ts), so body line `i` is the 0-based document line\n * `headingLine + i` — no rescan of the document, and no second opinion about\n * where a requirement begins. A new pin lands directly under `Requirement-ID:`\n * when there is one, matching what `serializeRequirements` writes, and\n * otherwise as the first body line: the two identity lines are read together,\n * so they are kept together.\n */\nfunction pinEdit(r: Requirement, headingLine: number, digest: string): LineEdit {\n const text = `Based-On: ${digest}`;\n const existing = r.text.findIndex((line) => /^\\s*Based-On:/i.test(line));\n if (existing >= 0) return { at: headingLine + existing, mode: \"replace\", text };\n const afterId = r.text.findIndex((line) => /^\\s*Requirement-ID:/i.test(line));\n return { at: headingLine + afterId + 1, mode: \"insert\", text };\n}\n\n/** One line of the document with the terminator it was written with. */\ninterface Line {\n text: string;\n /** \"\\n\", \"\\r\\n\", or \"\" for a last line the file does not terminate. */\n eol: string;\n}\n\n/**\n * Split into lines that remember their own terminator, indexed exactly as\n * `parseRequirements` counts them.\n */\nfunction toLines(raw: string): Line[] {\n // split with a captured separator yields [content, sep, content, sep, …, content].\n const parts = raw.split(/(\\r?\\n)/);\n const lines: Line[] = [];\n for (let i = 0; i < parts.length; i += 2) lines.push({ text: parts[i]!, eol: parts[i + 1] ?? \"\" });\n return lines;\n}\n\n/**\n * Apply line edits to a document, preserving every byte they do not name.\n *\n * Each line keeps its own terminator, so a CRLF document stays CRLF and a file\n * that ends without a newline does not grow one — rejoining on a single\n * \"detected\" newline would rewrite the line endings of a whole file whose\n * author asked for one line to change. Edits are applied bottom-up so an\n * insertion never shifts the index of one still to come.\n */\nfunction applyEdits(raw: string, edits: LineEdit[]): string {\n const lines = toLines(raw);\n const eol = lines.find((line) => line.eol !== \"\")?.eol ?? \"\\n\";\n for (const edit of [...edits].sort((a, b) => b.at - a.at)) {\n if (edit.mode === \"replace\") {\n lines[edit.at]!.text = edit.text;\n continue;\n }\n const before = edit.at > 0 ? lines[edit.at - 1] : undefined;\n if (before !== undefined && before.eol === \"\") {\n // Inserting past a file that ends without a newline: the old last line\n // needs a terminator, and the new line inherits its unterminated end so\n // the file keeps the shape its author gave it.\n before.eol = eol;\n lines.splice(edit.at, 0, { text: edit.text, eol: \"\" });\n } else {\n lines.splice(edit.at, 0, { text: edit.text, eol: before?.eol ?? eol });\n }\n }\n return lines.map((line) => line.text + line.eol).join(\"\");\n}\n\nfunction plural(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rebase.js","sourceRoot":"","sources":["../../src/commands/rebase.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `loam rebase` — pin a feature's MODIFIED/REMOVED requirements to the living\n * text they are written against.\n *\n * The counterpart to `loam vouch`: vouch stamps a digest of the CODE a living\n * spec was written from, this stamps a digest of the LIVING REQUIREMENT a delta\n * was written from. Both exist so a later `loam validate` can tell \"still true\"\n * from \"something moved underneath\", and both are worth exactly what they claim\n * — so this command computes every pin from what is on disk right now and never\n * invents one for a requirement that addresses nothing living.\n *\n * It is the fix `delta.baseline-stale` sends people to, which is why the\n * rewrite is line SURGERY rather than a reserialize: a delta document is\n * authored prose — section headings, comments, the order the author chose — and\n * `serializeRequirements` would flatten all of it into a bare requirement run.\n * One line changes per requirement; every other byte is copied.\n *\n * Restamping is the LAST step of resolving a collision, never the resolution\n * itself: a pin says \"I read this version\", so running it without re-reading\n * makes it a lie with a digest on it. The output says so on the way out.\n */\nimport type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { loadConfig } from \"../core/config.js\";\nimport { decodeDocument, NotUtf8DocumentError } from \"../core/document-bytes.js\";\nimport { InvalidIdError, assertServiceId } from \"../core/ids.js\";\nimport { emitJson, fail, repoPath, reportNoConfig } from \"../core/json.js\";\nimport {\n compareIds,\n featureSpecPaths,\n missingFeatureMessage,\n resolveFeature,\n servicePaths,\n SPEC_AXES,\n type SpecAxis,\n} from \"../core/repo.js\";\nimport {\n DocsBusyError,\n acquireDocsLock,\n message,\n rollbackStaged,\n stageWrites,\n swapStaged,\n type PlannedWrite,\n} from \"../core/staging.js\";\nimport {\n parseRequirements,\n readRequirementsDocument,\n requirementDigest,\n type Requirement,\n} from \"../core/spec.js\";\nimport {\n OpenapiMergeError,\n pinOpenapiOperations,\n type OpenapiPinPlan,\n} from \"../core/openapi-merge.js\";\nimport { plural } from \"./format.js\";\n\ninterface RebaseOptions {\n service?: string;\n json?: boolean;\n dryRun?: boolean;\n}\n\n/** What happened to one pin. */\ntype PinStatus =\n /** It had none and now has one. */\n | \"pinned\"\n /** It had a pin for an older living version; it now names the current one. */\n | \"repinned\"\n /** It already named the current living version — no write. */\n | \"unchanged\"\n /**\n * It addresses nothing living — a MODIFIED of a requirement another feature\n * in flight still introduces, or an operation this feature is adding. There\n * is no version to be based on, so inventing a pin would be inventing a\n * baseline; `loam validate` already reports the ordering where there is one\n * (`delta.modified-pending`).\n */\n | \"unresolved\"\n /**\n * The operation is written as a YAML alias. One shared value backs every use\n * of an anchor, so stamping \"through\" it would pin every other use too —\n * the same reason the merge refuses to edit aliases in place. Rare, legal,\n * and named rather than silently skipped.\n */\n | \"unwritable\";\n\ninterface PinOutcome {\n service: string;\n /** The axis's filename — \"spec.md\", \"arch.spec.md\" or \"openapi.yaml\". */\n file: string;\n /** MODIFIED/REMOVED for a requirement; the upper-case HTTP method for an operation. */\n kind: string;\n /** The requirement heading, or `/path ('operationId')`. */\n target: string;\n status: PinStatus;\n /** The pin as it was, or null when there was none. */\n from: string | null;\n /** The pin as it now stands, or null when nothing living could be hashed. */\n to: string | null;\n}\n\nexport function registerRebase(program: Command): void {\n program\n .command(\"rebase\")\n .argument(\"<featureId>\", \"feature id, e.g. FEAT-101\")\n .description(\"Pin a feature's MODIFIED/REMOVED requirements to the living text they are written against\")\n .option(\"--service <id>\", \"restrict to one service (default: every service the feature touches)\")\n .option(\"--dry-run\", \"print what would be pinned and write nothing\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (featureId: string, opts: RebaseOptions) => {\n const json = opts.json === true;\n const config = await loadConfig();\n if (!config) {\n reportNoConfig(json);\n return;\n }\n\n // The id grammar on the RAW argument, before it reaches a path join —\n // one grammar for the whole tool (core/ids.ts).\n if (opts.service !== undefined) {\n try {\n assertServiceId(opts.service, \"--service\");\n } catch (err) {\n if (!(err instanceof InvalidIdError)) throw err;\n return fail(json, \"invalid-option\", err.message);\n }\n }\n\n // The living specs must not move between the read that computes a pin and\n // the write that records it: a digest taken over a document an archive\n // replaced a millisecond later is precisely the false \"I read this\" the\n // pin exists to make impossible. One writer per docs repo, refusing\n // rather than queueing, exactly as archive takes it.\n let release: () => Promise<void>;\n try {\n release = await acquireDocsLock(config.docsDir);\n } catch (err) {\n if (!(err instanceof DocsBusyError)) throw err;\n return fail(json, \"docs-busy\", err.message);\n }\n try {\n await rebaseLocked(config.docsDir, featureId, opts);\n } finally {\n await release();\n }\n });\n}\n\nasync function rebaseLocked(docsDir: string, featureId: string, opts: RebaseOptions): Promise<void> {\n const json = opts.json === true;\n const dryRun = opts.dryRun === true;\n\n const feature = await resolveFeature(docsDir, featureId, \"exclude\");\n if (!feature) {\n return fail(json, \"unknown-target\", await missingFeatureMessage(docsDir, featureId));\n }\n const { id } = feature;\n\n if (opts.service !== undefined && !feature.services.includes(opts.service)) {\n // The refusal names the choices, the way `loam delta`'s does: a typo must\n // never be indistinguishable from a feature with nothing to pin.\n return fail(\n json,\n \"unknown-target\",\n `${id} carries no requirement delta for '${opts.service}'` +\n (feature.services.length === 0\n ? \" — it touches no service at all.\"\n : ` — it touches: ${[...feature.services].sort(compareIds).join(\", \")}.`),\n );\n }\n const services = (opts.service === undefined ? [...feature.services] : [opts.service]).sort(compareIds);\n\n const outcomes: PinOutcome[] = [];\n const writes: PlannedWrite[] = [];\n try {\n for (const service of services) {\n for (const axis of SPEC_AXES) {\n const specPath = featureSpecPaths(feature.dir, service)[axis.key];\n if (!existsSync(specPath)) continue;\n const planned = await planAxis(docsDir, service, axis, specPath);\n outcomes.push(...planned.outcomes);\n if (planned.content !== null) writes.push({ path: specPath, content: planned.content });\n }\n // The contract axis. It needs the pin more than the requirement axes do:\n // a requirement delta spells only the requirements it changes, while an\n // openapi delta is a COMPLETE document and spells the whole contract.\n const openapiPath = featureSpecPaths(feature.dir, service).openapi;\n if (existsSync(openapiPath)) {\n const planned = await planOpenapi(docsDir, service, openapiPath);\n outcomes.push(...planned.outcomes);\n if (planned.content !== null) writes.push({ path: openapiPath, content: planned.content });\n }\n }\n } catch (err) {\n if (!(err instanceof NotUtf8DocumentError)) throw err;\n // A pin is a claim about text somebody read. Over a document loam could not\n // decode it would be a digest of mojibake with loam's name on it — the\n // precise false claim this command exists to prevent — and the next archive\n // would land a stale delta over living text on the strength of it. So it\n // refuses by name and writes nothing, exactly as `loam archive` refuses a\n // merge it could not read, under archive's code. The refusal is here, in\n // the PLAN, so it lands before stageWrites: no file is touched, and a\n // feature spanning four services does not get two of them pinned.\n return fail(json, \"merge-failed\", `rebase ${id} failed — nothing was pinned: ${err.message}`);\n }\n\n if (!dryRun && writes.length > 0) {\n // Staged and swapped like every other multi-file write in loam\n // (core/staging.ts): a feature spanning four services is one act, and a\n // full disk between its second and third file must not leave two of them\n // pinned to a reading the other two never saw.\n const staged = await stageWrites(writes);\n try {\n await swapStaged(staged);\n } catch (err) {\n const failures = await rollbackStaged(staged);\n return fail(\n json,\n failures.length === 0 ? \"merge-failed\" : \"rollback-incomplete\",\n failures.length === 0\n ? `rebase ${id} failed and was rolled back — nothing was pinned: ${message(err)}`\n : `rebase ${id} failed and the rollback did not complete; these files are in an unknown state: ${failures.join(\", \")} — ${message(err)}`,\n );\n }\n }\n\n const changed = outcomes.filter((o) => o.status === \"pinned\" || o.status === \"repinned\");\n const unresolved = outcomes.filter((o) => o.status === \"unresolved\");\n\n if (json) {\n emitJson({\n feature: id,\n services,\n dryRun,\n pins: outcomes,\n written: dryRun ? [] : writes.map((w) => repoPath(docsDir, w.path)),\n });\n return;\n }\n\n if (outcomes.length === 0) {\n console.log(\n `${id}: nothing to pin — a baseline only means something for a requirement or an operation that already exists.`,\n );\n return;\n }\n\n console.log(`${id}${dryRun ? \" (dry run)\" : \"\"}\\n`);\n for (const o of outcomes) {\n const what = `${o.service}/${o.file} ${o.kind} ${o.target}`;\n if (o.status === \"unresolved\") {\n console.log(` · ${what} — not in the living docs yet, nothing to pin`);\n } else if (o.status === \"unwritable\") {\n console.log(` ! ${what} — written as a YAML alias; loam will not stamp through a shared anchor`);\n } else if (o.status === \"unchanged\") {\n console.log(` = ${what} — already ${o.to}`);\n } else if (o.status === \"pinned\") {\n console.log(` + ${what} — pinned ${o.to}`);\n } else {\n console.log(` ~ ${what} — ${o.from} → ${o.to}`);\n }\n }\n\n if (changed.length === 0) {\n console.log(`\\nNothing to write: every pin already names the living version.`);\n } else if (dryRun) {\n console.log(`\\n${plural(changed.length, \"pin\")} would be written. Nothing was written.`);\n } else {\n console.log(`\\n${plural(changed.length, \"pin\")} written across ${plural(writes.length, \"file\")}.`);\n // The one sentence that keeps this command honest. A pin is a claim about\n // what its author read, and a `repinned` line means the living text moved\n // under a delta that still says what it said before — restamping does not\n // fold in the change it just stopped reporting.\n const repinned = changed.filter((o) => o.status === \"repinned\");\n if (repinned.length > 0) {\n console.log(\n `\\n⚠ ${plural(repinned.length, \"pin\")} moved since this delta was written. A pin records what you read — ` +\n `re-read those living requirements and operations and fold in what you still mean, or the next archive lands your version over theirs with loam's blessing.`,\n );\n }\n }\n if (unresolved.length > 0) {\n console.log(\n `\\n${plural(unresolved.length, \"item\")} could not be pinned because the living docs do not have them yet — ` +\n `for a requirement, \\`loam dependencies ${id}\\` says which feature introduces it first; for an operation, this feature is adding it.`,\n );\n }\n}\n\ninterface AxisPlan {\n outcomes: PinOutcome[];\n /** The rewritten document, or null when nothing about it changes. */\n content: string | null;\n}\n\n/** Pin every MODIFIED/REMOVED requirement in one delta file against one living document. */\nasync function planAxis(\n docsDir: string,\n service: string,\n axis: SpecAxis,\n specPath: string,\n): Promise<AxisPlan> {\n // Both sides through the decoding read: the delta because line surgery over a\n // document loam mis-decoded would write bytes nobody authored, the living\n // because it is what every digest below is taken over.\n const raw = await readRequirementsDocument(specPath);\n const reqs = parseRequirements(raw);\n const livingPath = servicePaths(docsDir, service)[axis.key];\n const living = existsSync(livingPath)\n ? parseRequirements(await readRequirementsDocument(livingPath))\n : [];\n\n const outcomes: PinOutcome[] = [];\n const edits: LineEdit[] = [];\n for (const r of reqs) {\n if (r.kind !== \"MODIFIED\" && r.kind !== \"REMOVED\") continue;\n // Every requirement parsed from a document carries its heading line\n // (core/spec.ts). One built in memory has no document to write into, and\n // there is nothing truthful to report about pinning it.\n const line = r.line;\n if (line === undefined) continue;\n const base = { service, file: axis.file, kind: r.kind, target: r.name };\n const selected = selectLiving(living, r);\n if (selected === undefined) {\n outcomes.push({ ...base, status: \"unresolved\", from: r.basedOn ?? null, to: null });\n continue;\n }\n const digest = requirementDigest(selected);\n if (r.basedOn === digest) {\n outcomes.push({ ...base, status: \"unchanged\", from: digest, to: digest });\n continue;\n }\n outcomes.push({\n ...base,\n status: r.basedOn === undefined ? \"pinned\" : \"repinned\",\n from: r.basedOn ?? null,\n to: digest,\n });\n edits.push(pinEdit(r, line, digest));\n }\n\n return { outcomes, content: edits.length === 0 ? null : applyEdits(raw, edits) };\n}\n\n/**\n * Pin every operation in one feature contract against the living contract.\n *\n * The pin is always `operationDigest` of the LIVING operation, never of the\n * delta's own. That single rule produces both merge verdicts on its own: an\n * operation the author only QUOTED is byte-equal to living, so its pin equals\n * its own content and the merge skips it; one the author EDITED differs from\n * its pin, so the merge writes it. Nothing here has to guess at intent — the\n * document already records it, and the pin makes it legible.\n *\n * Slot-keyed (path + method), exactly as the merge upserts. An operationId that\n * moved to another slot is a new slot with nothing living behind it, which is\n * `unresolved` and correct: there is no living version of an operation at a\n * path the contract does not serve yet.\n */\nasync function planOpenapi(docsDir: string, service: string, openapiPath: string): Promise<AxisPlan> {\n const livingPath = servicePaths(docsDir, service).openapi;\n // Decoded, not `readFile(…, \"utf8\")`, for the requirement axes' reason: a\n // contract read with U+FFFD substituted in defines no operation loam can\n // match, so every pin would come out `unresolved` — \"this feature adds them\n // all\" — over a living contract that already serves them. Read outside the\n // try below, whose job is the merge's own diagnosis and not this one.\n const living = existsSync(livingPath) ? decodeDocument(await readFile(livingPath), livingPath) : \"\";\n const delta = decodeDocument(await readFile(openapiPath), openapiPath);\n let plan: OpenapiPinPlan;\n try {\n plan = pinOpenapiOperations(delta, living, service);\n } catch (err) {\n // A document loam cannot read is validate's diagnosis to make\n // (`openapi.invalid`), not this command's to guess at — and stamping into a\n // broken parse would write a file nobody asked for.\n if (!(err instanceof OpenapiMergeError)) throw err;\n return { outcomes: [], content: null };\n }\n return {\n outcomes: plan.pins.map((pin) => ({\n service,\n file: \"openapi.yaml\",\n kind: pin.method.toUpperCase(),\n target: pin.operationId.length === 0 ? pin.path : `${pin.path} ('${pin.operationId}')`,\n status: pin.status,\n from: pin.from,\n to: pin.to,\n })),\n content: plan.text,\n };\n}\n\n/**\n * The living requirement a delta requirement addresses — the merge's own\n * resolution order (core/spec.ts `applyRequirementDelta`), so a pin is taken\n * over exactly the requirement archive would rewrite: the stable ID when the\n * delta carries one, the exact heading otherwise, and the FIRST match either\n * way (duplicates are refused upstream by `delta.living-duplicate-requirement`).\n */\nfunction selectLiving(living: Requirement[], r: Requirement): Requirement | undefined {\n if (r.id !== undefined) return living.find((candidate) => candidate.id === r.id);\n return living.find((candidate) => candidate.name === r.name);\n}\n\n/* ------------------------------------------------------------------ */\n/* Line surgery */\n/* ------------------------------------------------------------------ */\n\ninterface LineEdit {\n /** 0-based line index, as `parseRequirements` counts lines. */\n at: number;\n /** Replace that line, or insert a new one before it. */\n mode: \"replace\" | \"insert\";\n text: string;\n}\n\n/**\n * Where this requirement's `Based-On:` line goes, and whether it replaces one.\n *\n * `headingLine` is 1-based and `text` captures every body line after it\n * contiguously (core/spec.ts), so body line `i` is the 0-based document line\n * `headingLine + i` — no rescan of the document, and no second opinion about\n * where a requirement begins. A new pin lands directly under `Requirement-ID:`\n * when there is one, matching what `serializeRequirements` writes, and\n * otherwise as the first body line: the two identity lines are read together,\n * so they are kept together.\n */\nfunction pinEdit(r: Requirement, headingLine: number, digest: string): LineEdit {\n const text = `Based-On: ${digest}`;\n const existing = r.text.findIndex((line) => /^\\s*Based-On:/i.test(line));\n if (existing >= 0) return { at: headingLine + existing, mode: \"replace\", text };\n const afterId = r.text.findIndex((line) => /^\\s*Requirement-ID:/i.test(line));\n return { at: headingLine + afterId + 1, mode: \"insert\", text };\n}\n\n/** One line of the document with the terminator it was written with. */\ninterface Line {\n text: string;\n /** \"\\n\", \"\\r\\n\", or \"\" for a last line the file does not terminate. */\n eol: string;\n}\n\n/**\n * Split into lines that remember their own terminator, indexed exactly as\n * `parseRequirements` counts them.\n */\nfunction toLines(raw: string): Line[] {\n // split with a captured separator yields [content, sep, content, sep, …, content].\n const parts = raw.split(/(\\r?\\n)/);\n const lines: Line[] = [];\n for (let i = 0; i < parts.length; i += 2) lines.push({ text: parts[i]!, eol: parts[i + 1] ?? \"\" });\n return lines;\n}\n\n/**\n * Apply line edits to a document, preserving every byte they do not name.\n *\n * Each line keeps its own terminator, so a CRLF document stays CRLF and a file\n * that ends without a newline does not grow one — rejoining on a single\n * \"detected\" newline would rewrite the line endings of a whole file whose\n * author asked for one line to change. Edits are applied bottom-up so an\n * insertion never shifts the index of one still to come.\n */\nfunction applyEdits(raw: string, edits: LineEdit[]): string {\n const lines = toLines(raw);\n const eol = lines.find((line) => line.eol !== \"\")?.eol ?? \"\\n\";\n for (const edit of [...edits].sort((a, b) => b.at - a.at)) {\n if (edit.mode === \"replace\") {\n lines[edit.at]!.text = edit.text;\n continue;\n }\n const before = edit.at > 0 ? lines[edit.at - 1] : undefined;\n if (before !== undefined && before.eol === \"\") {\n // Inserting past a file that ends without a newline: the old last line\n // needs a terminator, and the new line inherits its unterminated end so\n // the file keeps the shape its author gave it.\n before.eol = eol;\n lines.splice(edit.at, 0, { text: edit.text, eol: \"\" });\n } else {\n lines.splice(edit.at, 0, { text: edit.text, eol: before?.eol ?? eol });\n }\n }\n return lines.map((line) => line.text + line.eol).join(\"\");\n}\n"]}
|
package/dist/commands/show.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { readFile
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { loadConfig } from "../core/config.js";
|
|
4
|
+
import { FleetContext } from "../core/fleet-context.js";
|
|
4
5
|
import { emitJson, fail, repoPath, reportNoConfig } from "../core/json.js";
|
|
5
6
|
import { listField, readFrontmatter, stringField } from "../core/frontmatter.js";
|
|
6
7
|
import { loadFile, serviceResolver } from "../core/likec4.js";
|
|
7
8
|
import { readOpenapi } from "../core/openapi.js";
|
|
8
|
-
import { DocsRepoUnavailableError, featurePaths, featureSpecPaths, landscapePath, listServices, resolveFeature, servicePaths, } from "../core/repo.js";
|
|
9
|
+
import { DocsRepoUnavailableError, countMarkdown, featurePaths, featureSpecPaths, landscapePath, listServices, resolveFeature, servicePaths, } from "../core/repo.js";
|
|
9
10
|
import { parseRequirements } from "../core/spec.js";
|
|
10
|
-
import { docsRepoReady, reportDocsRepoError } from "./
|
|
11
|
+
import { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from "./docs-repo-gate.js";
|
|
12
|
+
import { plural } from "./format.js";
|
|
11
13
|
export function registerShow(program) {
|
|
12
14
|
program
|
|
13
15
|
.command("show")
|
|
@@ -35,17 +37,23 @@ export function registerShow(program) {
|
|
|
35
37
|
// enumeration below refuses with `services-missing` anyway.
|
|
36
38
|
if (!docsRepoReady(json, docsDir, "docs"))
|
|
37
39
|
return;
|
|
40
|
+
// One read index for the invocation. Deciding the target is a service
|
|
41
|
+
// enumerates the fleet, and so does resolving the landscape's edges to the
|
|
42
|
+
// services that own them — `loam show <service>` walked services/ twice for
|
|
43
|
+
// one answer. The context memoises only within this invocation, so a later
|
|
44
|
+
// run still sees whatever is on disk then.
|
|
45
|
+
const context = new FleetContext();
|
|
38
46
|
try {
|
|
39
47
|
// A feature id is distinctive (FEAT-101); a service name is arbitrary. When
|
|
40
48
|
// both could match, the feature wins and --type forces the other reading.
|
|
41
|
-
const feature = forced === "service" ? null : await resolveFeature(docsDir, target, "include");
|
|
49
|
+
const feature = forced === "service" ? null : await resolveFeature(docsDir, target, "include", context);
|
|
42
50
|
if (feature) {
|
|
43
51
|
await showFeature(docsDir, feature, json);
|
|
44
52
|
return;
|
|
45
53
|
}
|
|
46
|
-
const isService = forced !== "feature" && (await listServices(docsDir)).some((s) => s.id === target);
|
|
54
|
+
const isService = forced !== "feature" && (await listServices(docsDir, context)).some((s) => s.id === target);
|
|
47
55
|
if (isService) {
|
|
48
|
-
await showService(docsDir, target, json);
|
|
56
|
+
await showService(docsDir, target, json, context);
|
|
49
57
|
return;
|
|
50
58
|
}
|
|
51
59
|
const looked = forced ? forced : "service or feature";
|
|
@@ -59,14 +67,11 @@ export function registerShow(program) {
|
|
|
59
67
|
// An artifact that exists but cannot be read used to escape as a stack
|
|
60
68
|
// trace (`internal` in --json), naming nothing. `show` reads one target,
|
|
61
69
|
// so it has nothing to fall back on — but it can at least say WHICH file.
|
|
62
|
-
|
|
63
|
-
if (path === undefined)
|
|
64
|
-
throw err;
|
|
65
|
-
fail(json, "repository-unavailable", `${path} could not be read, so '${target}' cannot be shown. ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
reportRepositoryUnavailable(json, err, `'${target}' cannot be shown`, docsDir);
|
|
66
71
|
}
|
|
67
72
|
});
|
|
68
73
|
}
|
|
69
|
-
async function showService(docsDir, id, json) {
|
|
74
|
+
async function showService(docsDir, id, json, context) {
|
|
70
75
|
const paths = servicePaths(docsDir, id);
|
|
71
76
|
const has = {
|
|
72
77
|
model: existsSync(paths.model),
|
|
@@ -85,7 +90,7 @@ async function showService(docsDir, id, json) {
|
|
|
85
90
|
: { errors: [], elements: [], relationships: [] };
|
|
86
91
|
const reqs = has.spec ? parseRequirements(await readFile(paths.spec, "utf8")) : [];
|
|
87
92
|
const archReqs = has.archSpec ? parseRequirements(await readFile(paths.archSpec, "utf8")) : [];
|
|
88
|
-
const adrs = await
|
|
93
|
+
const adrs = await countMarkdown(paths.adrsDir);
|
|
89
94
|
// `readOpenapi`, not `operationIds`: a contract that exists but does not parse
|
|
90
95
|
// came back from operationIds as an EMPTY operation list, indistinguishable
|
|
91
96
|
// from a service with no endpoints. `show` is what a reader opens to find out
|
|
@@ -103,7 +108,7 @@ async function showService(docsDir, id, json) {
|
|
|
103
108
|
sources: listField(fm, "sources"),
|
|
104
109
|
};
|
|
105
110
|
const governs = (op) => reqs.filter((r) => r.operations.includes(op)).map((r) => r.name);
|
|
106
|
-
const { inbound, outbound } = await landscapeEdges(docsDir, id);
|
|
111
|
+
const { inbound, outbound } = await landscapeEdges(docsDir, id, context);
|
|
107
112
|
const requirementJson = (r) => ({
|
|
108
113
|
id: r.id,
|
|
109
114
|
name: r.name,
|
|
@@ -153,20 +158,20 @@ async function showService(docsDir, id, json) {
|
|
|
153
158
|
console.log(" artifacts");
|
|
154
159
|
const modelNote = model.errors.length > 0
|
|
155
160
|
? `${model.errors.length} error(s)`
|
|
156
|
-
: `${
|
|
161
|
+
: `${plural(model.elements.length, "element")} · ${plural(model.relationships.length, "relationship")}`;
|
|
157
162
|
console.log(` ${mark(has.model)} model.likec4 ${has.model ? modelNote : ""}`.trimEnd());
|
|
158
|
-
const specNote = (rs) => `${
|
|
163
|
+
const specNote = (rs) => `${plural(rs.length, "requirement")} · ${plural(scenarioCount(rs), "scenario")}`;
|
|
159
164
|
console.log(` ${mark(has.spec)} spec.md ${has.spec ? specNote(reqs) : ""}`.trimEnd());
|
|
160
165
|
console.log(` ${mark(has.archSpec)} arch.spec.md ${has.archSpec ? specNote(archReqs) : ""}`.trimEnd());
|
|
161
|
-
const apiNote = api.unreadable ? "does not parse" :
|
|
166
|
+
const apiNote = api.unreadable ? "does not parse" : plural(ops.length, "operation");
|
|
162
167
|
console.log(` ${mark(has.openapi)} openapi.yaml ${has.openapi ? apiNote : ""}`.trimEnd());
|
|
163
168
|
console.log(` ${mark(has.runbook)} runbook.md`);
|
|
164
169
|
console.log(` ${mark(has.health)} health.yaml`);
|
|
165
|
-
console.log(` ${mark(adrs > 0)} adrs/ ${adrs > 0 ?
|
|
170
|
+
console.log(` ${mark(adrs > 0)} adrs/ ${adrs > 0 ? plural(adrs, "decision") : ""}`.trimEnd());
|
|
166
171
|
const requirementLine = (r) => {
|
|
167
172
|
const govern = r.operations.length > 0 ? ` → ${r.operations.join(", ")}` : "";
|
|
168
173
|
const covers = r.covers.length > 0 ? ` covers ${r.covers.join(", ")}` : "";
|
|
169
|
-
return ` ${r.name} (${
|
|
174
|
+
return ` ${r.name} (${plural(r.scenarios.length, "scenario")})${govern}${covers}`;
|
|
170
175
|
};
|
|
171
176
|
if (reqs.length > 0) {
|
|
172
177
|
console.log("\n requirements");
|
|
@@ -208,7 +213,7 @@ async function showService(docsDir, id, json) {
|
|
|
208
213
|
}
|
|
209
214
|
}
|
|
210
215
|
/** Inbound/outbound edges for a service, read off the living landscape. */
|
|
211
|
-
async function landscapeEdges(docsDir, service) {
|
|
216
|
+
async function landscapeEdges(docsDir, service, context) {
|
|
212
217
|
const path = landscapePath(docsDir);
|
|
213
218
|
if (!existsSync(path))
|
|
214
219
|
return { inbound: [], outbound: [] };
|
|
@@ -218,7 +223,7 @@ async function landscapeEdges(docsDir, service) {
|
|
|
218
223
|
// Edges are filed under the service an element is BOUND to, not under what the
|
|
219
224
|
// box is titled — otherwise renaming a box empties this list without a word —
|
|
220
225
|
// and an edge into a modelled container belongs to the service that owns it.
|
|
221
|
-
const known = new Set((await listServices(docsDir)).map((s) => s.id));
|
|
226
|
+
const known = new Set((await listServices(docsDir, context)).map((s) => s.id));
|
|
222
227
|
const svcOf = serviceResolver(land.elements, known);
|
|
223
228
|
const inbound = [];
|
|
224
229
|
const outbound = [];
|
|
@@ -280,7 +285,7 @@ async function showFeature(docsDir, feature, json) {
|
|
|
280
285
|
console.log(` ${mark(feature.has.intent)} intent.md`);
|
|
281
286
|
const deltaNote = delta.errors.length > 0
|
|
282
287
|
? `${delta.errors.length} error(s)`
|
|
283
|
-
: `${
|
|
288
|
+
: `${plural(taggedEls.length, "element")} · ${plural(taggedRels.length, "relationship")} tagged ${feature.id}`;
|
|
284
289
|
console.log(` ${mark(feature.has.delta)} delta.likec4 ${feature.has.delta ? deltaNote : ""}`.trimEnd());
|
|
285
290
|
if (services.length > 0) {
|
|
286
291
|
console.log("\n services");
|
|
@@ -301,16 +306,6 @@ async function showFeature(docsDir, feature, json) {
|
|
|
301
306
|
function mark(present) {
|
|
302
307
|
return present ? "✓" : "-";
|
|
303
308
|
}
|
|
304
|
-
function count(n, noun) {
|
|
305
|
-
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
306
|
-
}
|
|
307
|
-
/** ADRs are markdown files under adrs/ — the same rule `list` counts by. */
|
|
308
|
-
async function countAdrs(dir) {
|
|
309
|
-
if (!existsSync(dir))
|
|
310
|
-
return 0;
|
|
311
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
312
|
-
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).length;
|
|
313
|
-
}
|
|
314
309
|
function scenarioCount(reqs) {
|
|
315
310
|
return reqs.reduce((n, r) => n + r.scenarios.length, 0);
|
|
316
311
|
}
|