@ybotok/loam 0.2.0-alpha.6 → 0.2.0-alpha.7
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 +162 -0
- package/README.md +4 -4
- package/ROADMAP.md +27 -6
- package/SCHEMA.md +152 -32
- package/dist/commands/archive/run.js +49 -22
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/seed/commit.js +34 -1
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/plan.js +64 -13
- package/dist/commands/seed/plan.js.map +1 -1
- package/dist/commands/subsystem/edit.js +4 -2
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +55 -17
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/sync.js +142 -45
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/report.js +55 -5
- package/dist/commands/subsystem/txn/report.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +118 -21
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +51 -8
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/validate/checks/health-deps.js +9 -6
- package/dist/commands/validate/checks/health-deps.js.map +1 -1
- package/dist/commands/validate/fleet/census.js +26 -3
- package/dist/commands/validate/fleet/census.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +12 -9
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +4 -5
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +15 -2
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
- package/dist/commands/validate/fleet/map/consumers.js +43 -122
- package/dist/commands/validate/fleet/map/consumers.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
- package/dist/commands/validate/fleet/views/ids.js +33 -7
- package/dist/commands/validate/fleet/views/ids.js.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +9 -7
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
- package/dist/commands/validate/fleet/views/stale.js +31 -14
- package/dist/commands/validate/fleet/views/stale.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +1 -1
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +27 -14
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +5 -3
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +16 -7
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/workflows/check.js +5 -6
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +12 -6
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/brief/brief.js +25 -8
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/targets.js +64 -0
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/brief/unchecked.js +1 -1
- package/dist/core/brief/unchecked.js.map +1 -1
- package/dist/core/c4/project/architecture.js +31 -9
- package/dist/core/c4/project/architecture.js.map +1 -1
- package/dist/core/c4/project/documents.js +5 -4
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +8 -4
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/project/staged.js +7 -3
- package/dist/core/c4/project/staged.js.map +1 -1
- package/dist/core/deployment/delta.js +12 -10
- package/dist/core/deployment/delta.js.map +1 -1
- package/dist/core/gate/checks.js +5 -1
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +60 -17
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/repo/paths.js +34 -29
- package/dist/core/repo/paths.js.map +1 -1
- package/dist/core/repo/tree/render/body.js +45 -6
- package/dist/core/repo/tree/render/body.js.map +1 -1
- package/dist/core/repo/tree/render/service/header.js +125 -0
- package/dist/core/repo/tree/render/service/header.js.map +1 -0
- package/dist/core/repo/tree/render/service/peers.js +28 -0
- package/dist/core/repo/tree/render/service/peers.js.map +1 -0
- package/dist/core/repo/tree/render/service/view-id.js +67 -0
- package/dist/core/repo/tree/render/service/view-id.js.map +1 -0
- package/dist/core/repo/tree/render/service/views.js +78 -0
- package/dist/core/repo/tree/render/service/views.js.map +1 -0
- package/dist/core/repo/tree/render/stale.js +19 -2
- package/dist/core/repo/tree/render/stale.js.map +1 -1
- package/dist/core/repo/tree/render/views.js +102 -33
- package/dist/core/repo/tree/render/views.js.map +1 -1
- package/dist/core/verify/checklist.js +42 -10
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +41 -1
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +7 -3
- package/examples/docs/architecture/landscape.likec4 +13 -7
- package/examples/docs/architecture/services.likec4 +70 -0
- package/examples/docs/architecture/subsystems.likec4 +3 -0
- package/examples/docs/services/order-service/model.likec4 +10 -8
- package/examples/docs/services/payment-service/model.likec4 +6 -4
- package/examples/docs/services/platform/identity-service/model.likec4 +9 -7
- package/examples/docs/services/platform/notification-service/model.likec4 +4 -5
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/commands/seed/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a seed run will write, decided in full before a byte lands. Split from\n * `./commit.ts` on the phase seam: that module owns the WINDOW — the lock, the\n * interrupted-commit recovery, the two refusals and the transaction — while\n * this one answers the single question \"given the tree as it is and the fleet\n * file as it reads, which files change and how\". Nothing here acquires\n * anything, and nothing here writes: it is called under the lock and hands\n * back a plan the transaction executes or rolls back whole.\n */\nimport { existsSync } from \"node:fs\";\nimport { copyFile, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { architectureDir, architectureProjectDocuments } from \"../../core/c4/project/architecture.js\";\nimport { loadProject } from \"../../core/c4/project/load.js\";\nimport type { FleetSeed } from \"../../core/c4/seed/fleet-file.js\";\nimport { serviceDirOf, type DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport {\n landscapePath,\n servicePathsUnder,\n servicesDir,\n subsystemPathUnder,\n subsystemViewsPath,\n unfiledServicePaths,\n} from \"../../core/repo/paths.js\";\nimport { SUBSYSTEM_MARKER } from \"../../core/repo/tree/marker.js\";\nimport { type MapFacts, renderSubsystemViews, viewsAgree } from \"../../core/repo/tree/render/views.js\";\nimport type { FleetTree, SubsystemEntry, WalkedService } from \"../../core/repo/tree/walk.js\";\nimport { TEMP_FILE_RE } from \"../../core/staging/commit.js\";\nimport { planWrite, type PlannedWrite } from \"../../core/staging/writes.js\";\n\n/** Everything the plan reads. The commit request extends this and adds only `rerun`. */\nexport interface SeedPlanInput {\n docsDir: DocsDir;\n seed: FleetSeed;\n /** The sealed landscape text (stamp on line 1), already proven to parse. */\n landscape: string;\n /**\n * That same text's parsed facts — the loadSource self-check's output. They\n * were first reused for the views join on their own, on the reasoning that\n * the seed template declares no `global` block (`core/c4/seed/template.ts`)\n * and so the render's style question had the answer \"none\"; true of the\n * template and beside the point, because `validate --all` recomputes the\n * expected bytes from the whole `architecture/` PROJECT, and a palette in a\n * sibling document is in that census (`projectFacts` below carries the\n * defect). They are now the render's input only when the landscape IS the\n * whole project — no sibling document exists — and the fallback when the\n * project does not parse.\n */\n map: MapFacts;\n}\n\n/**\n * The facts the generated views are rendered from: the `architecture/` PROJECT\n * as this commit leaves it — the sealed landscape standing in for the file it is\n * about to become, beside every other `.likec4` the directory already holds —\n * and never the sealed landscape alone.\n *\n * WHY THE PROJECT. `subsystem.views-stale` (`validate --all`) and `loam\n * subsystem sync` both recompute the expected bytes from `loadArchitecture` —\n * the landscape merged with every other `architecture/**` document, the\n * generated file excluded — and the render asks that document set which global\n * style ids it declares. A repository's `global { styleGroup subsystems { … } }`\n * naturally lives in a sibling such as `architecture/usecases/palette.likec4`,\n * since seed regenerates the landscape wholesale, so it was in the grader's\n * census and not in seed's: a seed into such a repo wrote a views file the very\n * next `validate --all` — seed's own first `next` command — graded stale, and\n * re-seeding wrote the same bytes again, the one loop no `next` could leave.\n * Reading the same documents the grader reads is what makes the shared render\n * shared in fact rather than in name (commands/subsystem/txn/views.ts's banner).\n *\n * NO SIBLING, NO WORKSPACE: the landscape's own facts, already in hand from the\n * self-check, ARE the project's, and the common case pays one readdir.\n *\n * STAGED, for `core/c4/project/staged.ts`'s reason: `loadProject` copies\n * documents from disk keyed on their path relative to a base, and the landscape\n * here exists only in memory. That module's staging is bound to a feature's\n * merge preview, so the same handful of copies is made here once more — the\n * temp tree is gone before this returns, and nothing under the docs repo is\n * touched.\n *\n * FAIL CLOSED to the landscape's own facts. A project that does not parse (a\n * sibling carrying an error of its own, or a copy the temp directory refused)\n * declares nothing loam can rely on, so the render carries exactly what it\n * carried before the project was asked, and the seed still lands: that sibling\n * is `landscape.invalid`'s to name on the next `validate --all`, and a seed\n * refusing over it would make seed the first command to grade a file it does\n * not own. Nothing is hidden by the fallback: the grader compares the views\n * file only once the project has parsed (`validate/fleet/landscape.ts`), so\n * the repo sees the one finding that names the sibling, never a stale-views\n * finding over a file no command could have rendered differently.\n */\nasync function projectFacts(req: SeedPlanInput): Promise<MapFacts> {\n const arch = architectureDir(req.docsDir);\n const landscape = resolve(landscapePath(req.docsDir));\n // The PROJECT's documents, root `exclude` applied — `loadArchitecture`'s own\n // list minus the landscape, which is staged from memory just below. Walking\n // `architecture/` directly skipped that filter, and here that is a WRONG\n // answer rather than an over-scan: a `palette.likec4` under an excluded path\n // is a document the renderer never loads, so its `global styleGroup` id put\n // `global style subsystems` into the generated views file — a name the fleet\n // project cannot resolve — and the very next `validate --all` (seed's own\n // `next` command) graded that file against the same set THIS one now reads.\n // The W5b defect, one command over.\n const siblings = (await architectureProjectDocuments(req.docsDir)).filter((p) => resolve(p) !== landscape);\n if (siblings.length === 0) return req.map;\n // The temp directory is made INSIDE the try, not one line above it: a full\n // disk, a read-only temp root, or a TMPDIR naming a directory that no longer\n // exists rejects `mkdtemp`, and outside the try that rejection left `seed` as\n // the generic `internal` refusal on a repository where nothing is wrong — the\n // defect docs/CODE-STYLE.md's \"read inside the try that handles the read\" rule\n // was written from.\n let root: string | null = null;\n try {\n root = await mkdtemp(join(tmpdir(), \"loam-seed-\"));\n const staged: string[] = [];\n for (const path of [landscape, ...siblings]) {\n const dest = join(root, relative(arch, path));\n await mkdir(dirname(dest), { recursive: true });\n // The landscape is staged from the bytes this commit writes, never from\n // the file on disk — which may be the scaffold's stub, a stamped\n // predecessor, or absent — so the project parsed is the one the grader\n // will read after the commit.\n if (path === landscape) await writeFile(dest, req.landscape, \"utf8\");\n else await copyFile(path, dest);\n staged.push(dest);\n }\n const project = await loadProject(root, staged);\n return project.clean ? { elements: project.elements, globalStyles: project.globalStyles } : req.map;\n } catch {\n // `loadProject` throws on an error it cannot attribute to a document, and\n // a full disk fails the staging above; both are the fail-closed arm.\n return req.map;\n } finally {\n if (root !== null) await rm(root, { recursive: true, force: true });\n }\n}\n\nexport interface SeedPlan {\n writes: PlannedWrite[];\n /** Service ids split by whether this run creates the directory. */\n services: { created: string[]; existing: string[] };\n}\n\n/**\n * Does this existing service directory hold nothing git would keep? An empty\n * directory is a service to the walk but nothing at all to git, so a landscape\n * declaring it clones into a repo where `validate --all` fails on a service\n * that exists only on the author's disk. Seed's own crash window produces\n * exactly this tree — `stageWrites` creates directories before the journal is\n * written, so a kill in between leaves directories no recovery knows about —\n * and so does a plain `mkdir`. Staging temps do not count as content: they are\n * that same crash's litter, not somebody's file.\n */\nasync function keepsNothing(dir: string): Promise<boolean> {\n try {\n return (await readdir(dir)).every((entry) => TEMP_FILE_RE.test(entry));\n } catch {\n // Unreadable is not empty: the walk found this directory a moment ago, and\n // inventing a write into a directory we cannot list is the wrong guess.\n return false;\n }\n}\n\n/**\n * The complete plan, in memory, before a byte lands: the sealed landscape, one\n * marker per NEW subsystem, one `.gitkeep` per service directory this commit\n * creates — and per existing one holding nothing git would keep (see\n * `keepsNothing`) — and the generated views file rendered from the tree AS\n * THIS COMMIT LEAVES IT, never from a re-walk between two states\n * (commands/subsystem/txn/views.ts's `movedTree` doctrine). That last one is\n * the only write that can be a DELETE: a tree with no subsystems owes no views\n * file, and a leftover is a finding.\n *\n * Existing directories are never moved: fleet.yaml's placement is ignored for\n * a service that already exists — moving is `loam subsystem move <id> --into\n * <name>`'s job, under its own guards — and nothing anywhere is deleted except\n * that one generated file.\n */\nexport async function planSeedWrites(req: SeedPlanInput, tree: FleetTree): Promise<SeedPlan> {\n const { docsDir, seed } = req;\n const writes: PlannedWrite[] = [planWrite(landscapePath(docsDir), req.landscape)];\n\n const existingSubs = new Map(tree.subsystems.map((s) => [s.name, s]));\n const newSubs: SubsystemEntry[] = [];\n for (const sub of seed.subsystems) {\n if (existingSubs.has(sub as string)) continue;\n const dir = subsystemPathUnder(servicesDir(docsDir), sub);\n // A comment line parses to null — a valid empty marker (tree/marker.ts).\n writes.push(planWrite(join(dir, SUBSYSTEM_MARKER), \"# subsystem marker\\n\"));\n newSubs.push({ name: sub as string, path: [sub as string], dir, meta: {} });\n }\n\n const treeDirs = new Map(tree.services.map((s) => [s.id as string, s.dir]));\n // Every existing directory inspected at once — independent reads, so the\n // loop below stays synchronous.\n const invisible = new Map(\n await Promise.all([...treeDirs].map(async ([id, dir]) => [id, await keepsNothing(dir)] as const)),\n );\n const created: string[] = [];\n const existing: string[] = [];\n const newWalked: WalkedService[] = [];\n for (const s of seed.services) {\n const already = treeDirs.get(s.id as string);\n if (already !== undefined) {\n existing.push(s.id);\n // Existing, but git-invisible: give it the `.gitkeep` a created service\n // gets, or the landscape about to be written declares a directory the\n // next clone will not have. `planWrite` makes this a no-op when the file\n // is already there, so a real service is never touched.\n if (invisible.get(s.id as string) === true) writes.push(planWrite(join(already, \".gitkeep\"), \"\"));\n continue;\n }\n created.push(s.id);\n const parent = s.subsystem === null ? null : existingSubs.get(s.subsystem as string) ?? null;\n const dir =\n s.subsystem === null\n ? unfiledServicePaths(docsDir, s.id).dir\n : parent !== null\n ? servicePathsUnder(parent.dir, s.id).dir\n : // A subsystem this same commit creates: the creation spelling,\n // through the same branded joins the subsystem verbs use.\n serviceDirOf(join(subsystemPathUnder(servicesDir(docsDir), s.subsystem), s.id));\n writes.push(planWrite(join(dir, \".gitkeep\"), \"\"));\n newWalked.push({\n // A ServiceId IS a RawServiceId (the checked subtype), so no re-cast.\n id: s.id,\n dir,\n subsystem: s.subsystem === null ? [] : parent !== null ? parent.path : [s.subsystem as string],\n });\n }\n\n const plannedTree: FleetTree = {\n findings: [],\n services: [...tree.services, ...newWalked],\n subsystems: [...tree.subsystems, ...newSubs],\n };\n // The generated views file, including its ABSENCE. `renderSubsystemViews`\n // returning null means the tree has no subsystems, and its contract is that\n // the file must then not exist — `validate` grades a leftover as\n // `subsystem.views-stale`, so a seed that ignored the file would exit 0 and\n // send the caller, via its own `next` list, into a failing gate over a file\n // it had in hand. `content: null` is the delete the transaction understands\n // (commands/subsystem/txn/txn.ts's `\"removed\"`), and a file that already\n // SAYS this is left out of the journal entirely rather than written back\n // over itself.\n //\n // \"Already says this\" is content, not bytes: `viewsAgree` performs the\n // comparison and records the Windows reason for it, beside the generator\n // that mints the bytes. What gets WRITTEN is unchanged — the LF render, as a\n // Buffer — so this decode is a comparison only, never the string round trip\n // to disk that writes.ts forbids; a views file whose bytes are not UTF-8\n // therefore fails the compare and is regenerated, which is the right answer\n // for it anyway.\n const viewsPath = subsystemViewsPath(docsDir);\n const views = renderSubsystemViews(plannedTree, await projectFacts(req));\n const wanted = views === null ? null : Buffer.from(views, \"utf8\");\n const onDisk = existsSync(viewsPath) ? await readFile(viewsPath, \"utf8\") : null;\n if (!viewsAgree(onDisk, views)) writes.push({ path: viewsPath, content: wanted });\n\n return { writes, services: { created: created.sort(), existing: existing.sort() } };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/commands/seed/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a seed run will write, decided in full before a byte lands. Split from\n * `./commit.ts` on the phase seam: that module owns the WINDOW — the lock, the\n * interrupted-commit recovery, the refusals and the transaction — while\n * this one answers the single question \"given the tree as it is and the fleet\n * file as it reads, which files change and how\". Nothing here acquires\n * anything, and nothing here writes: it is called under the lock and hands\n * back a plan the transaction executes or rolls back whole.\n */\nimport { copyFile, mkdir, mkdtemp, readdir, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { architectureDir, architectureProjectDocuments } from \"../../core/c4/project/architecture.js\";\nimport { loadProject } from \"../../core/c4/project/load.js\";\nimport type { FleetSeed } from \"../../core/c4/seed/fleet-file.js\";\nimport { repoPath } from \"../../core/envelope/json.js\";\nimport { serviceDirOf, type DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport {\n landscapePath,\n servicePathsUnder,\n servicesDir,\n serviceViewsPath,\n subsystemPathUnder,\n subsystemViewsPath,\n unfiledServicePaths,\n} from \"../../core/repo/paths.js\";\nimport { SUBSYSTEM_MARKER } from \"../../core/repo/tree/marker.js\";\nimport { readGeneratedFile } from \"../../core/repo/tree/render/service/header.js\";\nimport {\n type MapFacts,\n renderServiceOverviews,\n renderSubsystemViews,\n viewsAgree,\n} from \"../../core/repo/tree/render/views.js\";\nimport type { FleetTree, SubsystemEntry, WalkedService } from \"../../core/repo/tree/walk.js\";\nimport { TEMP_FILE_RE } from \"../../core/staging/commit.js\";\nimport { planWrite, type PlannedWrite } from \"../../core/staging/writes.js\";\n\n/** Everything the plan reads. The commit request extends this and adds only `rerun`. */\nexport interface SeedPlanInput {\n docsDir: DocsDir;\n seed: FleetSeed;\n /** The sealed landscape text (stamp on line 1), already proven to parse. */\n landscape: string;\n /**\n * That same text's parsed facts — the loadSource self-check's output. They\n * were first reused for the views join on their own, on the reasoning that\n * the seed template declares no `global` block (`core/c4/seed/template.ts`)\n * and so the render's style question had the answer \"none\"; true of the\n * template and beside the point, because `validate --all` recomputes the\n * expected bytes from the whole `architecture/` PROJECT, and a palette in a\n * sibling document is in that census (`projectFacts` below carries the\n * defect). They are now the render's input only when the landscape IS the\n * whole project — no sibling document exists — and the fallback when the\n * project does not parse.\n */\n map: MapFacts;\n}\n\n/**\n * The facts the generated views are rendered from: the `architecture/` PROJECT\n * as this commit leaves it — the sealed landscape standing in for the file it is\n * about to become, beside every other `.likec4` the directory already holds —\n * and never the sealed landscape alone.\n *\n * WHY THE PROJECT. `subsystem.views-stale` (`validate --all`) and `loam\n * subsystem sync` both recompute the expected bytes from `loadArchitecture` —\n * the landscape merged with every other `architecture/**` document, the\n * generated file excluded — and the render asks that document set which global\n * style ids it declares. A repository's `global { styleGroup subsystems { … } }`\n * naturally lives in a sibling such as `architecture/usecases/palette.likec4`,\n * since seed regenerates the landscape wholesale, so it was in the grader's\n * census and not in seed's: a seed into such a repo wrote a views file the very\n * next `validate --all` — seed's own first `next` command — graded stale, and\n * re-seeding wrote the same bytes again, the one loop no `next` could leave.\n * Reading the same documents the grader reads is what makes the shared render\n * shared in fact rather than in name (commands/subsystem/txn/views.ts's banner).\n *\n * NO SIBLING, NO WORKSPACE: the landscape's own facts, already in hand from the\n * self-check, ARE the project's, and the common case pays one readdir.\n *\n * STAGED, for `core/c4/project/staged.ts`'s reason: `loadProject` copies\n * documents from disk keyed on their path relative to a base, and the landscape\n * here exists only in memory. That module's staging is bound to a feature's\n * merge preview, so the same handful of copies is made here once more — the\n * temp tree is gone before this returns, and nothing under the docs repo is\n * touched.\n *\n * FAIL CLOSED to the landscape's own facts. A project that does not parse (a\n * sibling carrying an error of its own, or a copy the temp directory refused)\n * declares nothing loam can rely on, so the render carries exactly what it\n * carried before the project was asked, and the seed still lands: that sibling\n * is `landscape.invalid`'s to name on the next `validate --all`, and a seed\n * refusing over it would make seed the first command to grade a file it does\n * not own. Nothing is hidden by the fallback: the grader compares the views\n * file only once the project has parsed (`validate/fleet/landscape.ts`), so\n * the repo sees the one finding that names the sibling, never a stale-views\n * finding over a file no command could have rendered differently.\n */\nasync function projectFacts(req: SeedPlanInput): Promise<MapFacts> {\n const arch = architectureDir(req.docsDir);\n const landscape = resolve(landscapePath(req.docsDir));\n // The PROJECT's documents, root `exclude` applied — `loadArchitecture`'s own\n // list minus the landscape, which is staged from memory just below. Walking\n // `architecture/` directly skipped that filter, and here that is a WRONG\n // answer rather than an over-scan: a `palette.likec4` under an excluded path\n // is a document the renderer never loads, so its `global styleGroup` id put\n // `global style subsystems` into the generated views file — a name the fleet\n // project cannot resolve — and the very next `validate --all` (seed's own\n // `next` command) graded that file against the same set THIS one now reads.\n // The W5b defect, one command over.\n const siblings = (await architectureProjectDocuments(req.docsDir)).filter((p) => resolve(p) !== landscape);\n if (siblings.length === 0) return req.map;\n // The temp directory is made INSIDE the try, not one line above it: a full\n // disk, a read-only temp root, or a TMPDIR naming a directory that no longer\n // exists rejects `mkdtemp`, and outside the try that rejection left `seed` as\n // the generic `internal` refusal on a repository where nothing is wrong — the\n // defect docs/CODE-STYLE.md's \"read inside the try that handles the read\" rule\n // was written from.\n let root: string | null = null;\n try {\n root = await mkdtemp(join(tmpdir(), \"loam-seed-\"));\n const staged: string[] = [];\n for (const path of [landscape, ...siblings]) {\n const dest = join(root, relative(arch, path));\n await mkdir(dirname(dest), { recursive: true });\n // The landscape is staged from the bytes this commit writes, never from\n // the file on disk — which may be the scaffold's stub, a stamped\n // predecessor, or absent — so the project parsed is the one the grader\n // will read after the commit.\n if (path === landscape) await writeFile(dest, req.landscape, \"utf8\");\n else await copyFile(path, dest);\n staged.push(dest);\n }\n const project = await loadProject(root, staged);\n // RELATIONSHIPS travel with the elements, and dropping them is silent: they\n // are what each per-service overview computes its peer set from, so a facts\n // record without them renders every service view peerless — the service and\n // its own interior, and nothing it touches — with no error anywhere\n // (`core/repo/tree/render/views.ts` states the consequence beside the\n // optional field). The fallback `req.map` is the whole self-check document\n // and already carries them.\n return project.clean\n ? { elements: project.elements, globalStyles: project.globalStyles, relationships: project.relationships }\n : req.map;\n } catch {\n // `loadProject` throws on an error it cannot attribute to a document, and\n // a full disk fails the staging above; both are the fail-closed arm.\n return req.map;\n } finally {\n if (root !== null) await rm(root, { recursive: true, force: true });\n }\n}\n\nexport interface SeedPlan {\n writes: PlannedWrite[];\n /** Service ids split by whether this run creates the directory. */\n services: { created: string[]; existing: string[] };\n /**\n * Repo-relative paths of the generated views files that hold a document loam\n * did NOT write — the plan's own refusal, handed to `./commit.ts` rather than\n * spelled here, because this module decides and never announces.\n *\n * Empty on every ordinary run. Non-empty means the whole seed must be refused\n * before a byte lands: see `stageGenerated`.\n */\n authored: string[];\n}\n\n/**\n * Does this existing service directory hold nothing git would keep? An empty\n * directory is a service to the walk but nothing at all to git, so a landscape\n * declaring it clones into a repo where `validate --all` fails on a service\n * that exists only on the author's disk. Seed's own crash window produces\n * exactly this tree — `stageWrites` creates directories before the journal is\n * written, so a kill in between leaves directories no recovery knows about —\n * and so does a plain `mkdir`. Staging temps do not count as content: they are\n * that same crash's litter, not somebody's file.\n */\nasync function keepsNothing(dir: string): Promise<boolean> {\n try {\n return (await readdir(dir)).every((entry) => TEMP_FILE_RE.test(entry));\n } catch {\n // Unreadable is not empty: the walk found this directory a moment ago, and\n // inventing a write into a directory we cannot list is the wrong guess.\n return false;\n }\n}\n\n/**\n * The complete plan, in memory, before a byte lands: the sealed landscape, one\n * marker per NEW subsystem, one `.gitkeep` per service directory this commit\n * creates — and per existing one holding nothing git would keep (see\n * `keepsNothing`) — and the generated views file rendered from the tree AS\n * THIS COMMIT LEAVES IT, never from a re-walk between two states\n * (commands/subsystem/txn/views.ts's `movedTree` doctrine). That last one is\n * the only write that can be a DELETE: a tree with no subsystems owes no views\n * file, and a leftover is a finding.\n *\n * Existing directories are never moved: fleet.yaml's placement is ignored for\n * a service that already exists — moving is `loam subsystem move <id> --into\n * <name>`'s job, under its own guards — and nothing anywhere is deleted except\n * that one generated file.\n */\nexport async function planSeedWrites(req: SeedPlanInput, tree: FleetTree): Promise<SeedPlan> {\n const { docsDir, seed } = req;\n const writes: PlannedWrite[] = [planWrite(landscapePath(docsDir), req.landscape)];\n\n const existingSubs = new Map(tree.subsystems.map((s) => [s.name, s]));\n const newSubs: SubsystemEntry[] = [];\n for (const sub of seed.subsystems) {\n if (existingSubs.has(sub as string)) continue;\n const dir = subsystemPathUnder(servicesDir(docsDir), sub);\n // A comment line parses to null — a valid empty marker (tree/marker.ts).\n writes.push(planWrite(join(dir, SUBSYSTEM_MARKER), \"# subsystem marker\\n\"));\n newSubs.push({ name: sub as string, path: [sub as string], dir, meta: {} });\n }\n\n const treeDirs = new Map(tree.services.map((s) => [s.id as string, s.dir]));\n // Every existing directory inspected at once — independent reads, so the\n // loop below stays synchronous.\n const invisible = new Map(\n await Promise.all([...treeDirs].map(async ([id, dir]) => [id, await keepsNothing(dir)] as const)),\n );\n const created: string[] = [];\n const existing: string[] = [];\n const newWalked: WalkedService[] = [];\n for (const s of seed.services) {\n const already = treeDirs.get(s.id as string);\n if (already !== undefined) {\n existing.push(s.id);\n // Existing, but git-invisible: give it the `.gitkeep` a created service\n // gets, or the landscape about to be written declares a directory the\n // next clone will not have. `planWrite` makes this a no-op when the file\n // is already there, so a real service is never touched.\n if (invisible.get(s.id as string) === true) writes.push(planWrite(join(already, \".gitkeep\"), \"\"));\n continue;\n }\n created.push(s.id);\n const parent = s.subsystem === null ? null : existingSubs.get(s.subsystem as string) ?? null;\n const dir =\n s.subsystem === null\n ? unfiledServicePaths(docsDir, s.id).dir\n : parent !== null\n ? servicePathsUnder(parent.dir, s.id).dir\n : // A subsystem this same commit creates: the creation spelling,\n // through the same branded joins the subsystem verbs use.\n serviceDirOf(join(subsystemPathUnder(servicesDir(docsDir), s.subsystem), s.id));\n writes.push(planWrite(join(dir, \".gitkeep\"), \"\"));\n newWalked.push({\n // A ServiceId IS a RawServiceId (the checked subtype), so no re-cast.\n id: s.id,\n dir,\n subsystem: s.subsystem === null ? [] : parent !== null ? parent.path : [s.subsystem as string],\n });\n }\n\n const plannedTree: FleetTree = {\n findings: [],\n services: [...tree.services, ...newWalked],\n subsystems: [...tree.subsystems, ...newSubs],\n };\n // The generated views file, including its ABSENCE. `renderSubsystemViews`\n // returning null means the tree has no subsystems, and its contract is that\n // the file must then not exist — `validate` grades a leftover as\n // `subsystem.views-stale`, so a seed that ignored the file would exit 0 and\n // send the caller, via its own `next` list, into a failing gate over a file\n // it had in hand. `content: null` is the delete the transaction understands\n // (commands/subsystem/txn/txn.ts's `\"removed\"`), and a file that already\n // SAYS this is left out of the journal entirely rather than written back\n // over itself.\n //\n // \"Already says this\" is content, not bytes: `viewsAgree` performs the\n // comparison and records the Windows reason for it, beside the generator\n // that mints the bytes. What gets WRITTEN is unchanged — the LF render, as a\n // Buffer — so this decode is a comparison only, never the string round trip\n // to disk that writes.ts forbids; a views file whose bytes are not UTF-8\n // therefore fails the compare and is regenerated, which is the right answer\n // for it anyway.\n //\n // BOTH generated files, from ONE read of the project: `architecture/services.likec4`\n // is the same contract one file over — one overview view per service the map\n // draws, absent when it draws none — and a seed that landed only the subsystem\n // views would send its caller, through its own `next` list, into a\n // `subsystem.views-stale` no `seed` could clear. This is where the seeded\n // services first exist AND the sealed landscape first says what they are, so\n // it is the only moment the pair can be rendered together.\n const facts = await projectFacts(req);\n const authored = [\n await stageGenerated(writes, subsystemViewsPath(docsDir), renderSubsystemViews(plannedTree, facts)),\n await stageGenerated(writes, serviceViewsPath(docsDir), renderServiceOverviews(plannedTree, facts)),\n ].filter((path): path is string => path !== null);\n\n return {\n writes,\n services: { created: created.sort(), existing: existing.sort() },\n authored: authored.map((path) => repoPath(docsDir, path)),\n };\n}\n\n/**\n * Append the write for ONE generated file, or nothing when the file already\n * says it — the comparison the block above argues for, spelled once so the two\n * generated files cannot drift into two rules. Answers the path when the file\n * standing there is SOMEBODY ELSE'S, and stages nothing for it.\n *\n * OWNERSHIP FIRST, before the compare, exactly as `commands/subsystem/sync.ts`\n * asks it — through the same reader, from beneath every writer\n * (`core/repo/tree/render/service/header.ts`). Both of these are filenames loam\n * RESERVES inside `architecture/`, a directory the team owns, and seed rewrites\n * them whole; until this line existed a `loam seed` into a repository that kept\n * a hand-authored diagram at one of those paths destroyed it without a word.\n * That is the same promise `seed-landscape-edited` makes one file over — seed\n * never overwrites human work — and it was missing for the two files seed\n * reserves rather than shares.\n *\n * It is checked before the compare and not after, so that \"the file already says\n * this\" can never vouch for a file loam did not write. An authored file cannot\n * in fact agree with a render that opens with loam's mark, but a reader must not\n * have to prove that to know the guard holds.\n */\nasync function stageGenerated(writes: PlannedWrite[], path: string, content: string | null): Promise<string | null> {\n const { text: onDisk, authored } = await readGeneratedFile(path);\n if (authored) return path;\n if (viewsAgree(onDisk, content)) return null;\n writes.push({ path, content: content === null ? null : Buffer.from(content, \"utf8\") });\n return null;\n}\n"]}
|
|
@@ -98,7 +98,8 @@ export async function runNew(docsDir, name, opts, json) {
|
|
|
98
98
|
command: "subsystem",
|
|
99
99
|
created: name,
|
|
100
100
|
path: repoPath(docsDir, dir),
|
|
101
|
-
views,
|
|
101
|
+
views: views.subsystems,
|
|
102
|
+
serviceViews: views.services,
|
|
102
103
|
...(recovered === null ? {} : { recovered }),
|
|
103
104
|
});
|
|
104
105
|
return;
|
|
@@ -175,7 +176,8 @@ export async function runRm(docsDir, name, json) {
|
|
|
175
176
|
removed: name,
|
|
176
177
|
path: repoPath(docsDir, sub.dir),
|
|
177
178
|
directoryRemoved,
|
|
178
|
-
views,
|
|
179
|
+
views: views.subsystems,
|
|
180
|
+
serviceViews: views.services,
|
|
179
181
|
...(recovered === null ? {} : { recovered }),
|
|
180
182
|
});
|
|
181
183
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/commands/subsystem/edit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The two verbs that create and remove GROUPS: `subsystem new` and\n * `subsystem rm`. Both commit the marker AND the regenerated views file in\n * one journaled transaction, because a marker without its view (or a view\n * outliving its marker) is exactly the `subsystem.views-stale` state the\n * generated file's contract refuses to leave behind.\n *\n * `rm` refuses a subsystem with members — services, child subsystems, or any\n * stray non-dot entry beside the marker the tree does not already count —\n * naming each once, as what it is (`subsystem-not-empty`):\n * a destructive command never picks targets the caller did not name, and a\n * directory emptied of its marker but not of its contents would re-enter the\n * walk as a phantom service.\n */\nimport { existsSync } from \"node:fs\";\nimport { readdir, rmdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { stringify } from \"yaml\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { parseSubsystemName } from \"../../core/kernel/ids/subsystem.js\";\nimport { emitJson, fail, repoPath } from \"../../core/envelope/json.js\";\nimport { servicesDir, subsystemPathUnder } from \"../../core/repo/paths.js\";\nimport { findInTree, nearestTreeNames, servicesUnder, subsystemsUnder } from \"../../core/repo/tree/find.js\";\nimport { entryIs } from \"../../core/repo/tree/fs.js\";\nimport { SUBSYSTEM_MARKER } from \"../../core/repo/tree/marker.js\";\nimport type { FleetTree } from \"../../core/repo/tree/walk.js\";\nimport { planWrite } from \"../../core/staging/writes.js\";\nimport { commitWindow, reportViews, type SubsystemTxn } from \"./txn/txn.js\";\nimport type { SubsystemOptions } from \"./subsystem.js\";\n\n/** `subsystem new <name> [--under <parent>] [--title …] [--description …] [--owner …]` */\nexport async function runNew(\n docsDir: DocsDir,\n name: string,\n opts: SubsystemOptions,\n json: boolean,\n): Promise<void> {\n const parsed = parseSubsystemName(name, \"subsystem name\");\n if (!parsed.ok) {\n fail(json, \"invalid-option\", parsed.problem);\n return;\n }\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n // The flat namespace: one name, one meaning, at any depth — a `new` that\n // collides with ANY service or subsystem would be the exact state\n // `subsystem.name-collision` refuses after the fact.\n const taken = findInTree(tree, name);\n if (taken !== null) {\n const where = taken.kind === \"service\" ? taken.service.dir : taken.subsystem.dir;\n fail(\n json,\n \"already-exists\",\n `'${name}' is already a ${taken.kind} (${repoPath(docsDir, where)}) — ` +\n `service ids and subsystem names share one flat namespace, unique at any depth. Pick another name.`,\n );\n return null;\n }\n let parentDir = servicesDir(docsDir);\n let parentPath: string[] = [];\n if (opts.under !== undefined) {\n const parent = findInTree(tree, opts.under);\n if (parent === null) {\n const close = nearestTreeNames(opts.under, tree.subsystems.map((s) => s.name));\n fail(\n json,\n \"unknown-target\",\n `No subsystem '${opts.under}' in the tree.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `loam subsystem list` shows what exists.\"),\n );\n return null;\n }\n if (parent.kind === \"service\") {\n fail(json, \"invalid-option\", `--under names the service '${opts.under}' — a service never contains subsystems.`);\n return null;\n }\n parentDir = parent.subsystem.dir;\n parentPath = parent.subsystem.path;\n }\n const dir = subsystemPathUnder(parentDir, parsed.name);\n // The flat-namespace check above compares exact strings; the FILESYSTEM\n // may not. On a case-folding volume (macOS APFS by default) `Billing`\n // and an existing `services/billing` are one directory, so the marker\n // write below would land subsystem.yaml INSIDE the live service — the\n // exact marker-beside-artifacts state `subsystem.marker-misplaced`\n // refuses after the fact, created by a blessed command. A plain file of\n // the same name reaches here too (files at the root are not walked). So\n // the disk is probed directly and anything already there refuses closed.\n if (existsSync(dir)) {\n fail(\n json,\n \"already-exists\",\n `${repoPath(docsDir, dir)} already exists on disk — the tree resolves no entry by that exact name, ` +\n `so either this filesystem folds case (the directory answers to another spelling of the name) ` +\n `or something non-loam created it. Pick another name, or move what is there first. Nothing was created.`,\n );\n return null;\n }\n const meta = {\n ...(opts.title === undefined ? {} : { title: opts.title }),\n ...(opts.description === undefined ? {} : { description: opts.description }),\n ...(opts.owner === undefined ? {} : { owner: opts.owner }),\n };\n // An empty file is a valid marker — presence classifies, content is metadata.\n const marker = Object.keys(meta).length === 0 ? \"\" : stringify(meta);\n const after: FleetTree = {\n ...tree,\n subsystems: [...tree.subsystems, { name, path: [...parentPath, name], dir, meta }],\n };\n return {\n target: name,\n writes: [planWrite(join(dir, SUBSYSTEM_MARKER), marker)],\n tree: after,\n // A lost exclusive-create race on the marker IS the name being taken.\n racedCode: \"already-exists\",\n what: \"created\",\n report: (views, recovered) => {\n if (json) {\n emitJson({\n command: \"subsystem\",\n created: name,\n path: repoPath(docsDir, dir),\n views,\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n console.log(`created ${repoPath(docsDir, dir)}/ (${SUBSYSTEM_MARKER} written)${reportViews(views)}`);\n },\n };\n });\n}\n\n/** `subsystem rm <name>` — refuses anything but an empty group. */\nexport async function runRm(docsDir: DocsDir, name: string, json: boolean): Promise<void> {\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n const hit = findInTree(tree, name);\n if (hit === null) {\n const close = nearestTreeNames(name, tree.subsystems.map((s) => s.name));\n fail(\n json,\n \"unknown-target\",\n `No subsystem '${name}' in the tree.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `loam subsystem list` shows what exists.\"),\n );\n return null;\n }\n if (hit.kind === \"service\") {\n fail(\n json,\n \"invalid-option\",\n `'${name}' is a service, not a subsystem — loam does not delete services (\\`git rm -r ${repoPath(docsDir, hit.service.dir)}\\` does, and version control keeps the record).`,\n );\n return null;\n }\n const sub = hit.subsystem;\n const services = servicesUnder(tree, sub).map((s) => s.id);\n const children = subsystemsUnder(tree, sub).map((s) => s.name);\n // Stray entries beside the marker count as members too: after the marker\n // is gone the directory would re-enter the walk as an empty \"service\" the\n // moment anything keeps rmdir from removing it. But only entries the tree\n // has NOT already counted — a member service or child subsystem is also a\n // readdir entry here, and listing it a second time as \"file X\" made the\n // count wrong and the same name appear under two kinds. What remains is\n // discriminated by what it is (`entryIs`, the walk's own symlink-aware\n // question): a file, or a directory the tree does not claim (an unmarked\n // group, already its own `subsystem.unmarked` finding).\n const counted = new Set<string>([\n ...servicesUnder(tree, sub).filter((s) => s.subsystem.length === sub.path.length).map((s) => s.id as string),\n ...subsystemsUnder(tree, sub).filter((c) => c.path.length === sub.path.length + 1).map((c) => c.name),\n ]);\n const strays = (await readdir(sub.dir, { withFileTypes: true }))\n .filter((e) => !e.name.startsWith(\".\") && e.name !== SUBSYSTEM_MARKER && !counted.has(e.name))\n .map((e) => `${entryIs(sub.dir, e, \"dir\") ? \"directory\" : \"file\"} ${e.name}`);\n const members = [\n ...services.map((s) => `service ${s}`),\n ...children.map((c) => `subsystem ${c}`),\n ...strays,\n ];\n if (members.length > 0) {\n fail(\n json,\n \"subsystem-not-empty\",\n `'${name}' still holds ${members.length} member(s): ${members.join(\", \")}. ` +\n \"`loam subsystem move <name>... --into <sub|.>` moves them out; a destructive command never picks targets you did not name.\",\n );\n return null;\n }\n const after: FleetTree = { ...tree, subsystems: tree.subsystems.filter((s) => s !== sub) };\n return {\n target: name,\n writes: [{ path: join(sub.dir, SUBSYSTEM_MARKER), content: null }],\n tree: after,\n what: \"removed\",\n report: async (views, recovered) => {\n // The directory itself, after the transaction: the marker is gone, the\n // group was verifiably empty, and an empty marker-less directory would\n // read as an unfiled service. Dotfile litter (.DS_Store) can still\n // defeat rmdir — reported, not swallowed.\n let directoryRemoved = true;\n try {\n await rmdir(sub.dir);\n } catch {\n directoryRemoved = !existsSync(sub.dir);\n }\n if (json) {\n emitJson({\n command: \"subsystem\",\n removed: name,\n path: repoPath(docsDir, sub.dir),\n directoryRemoved,\n views,\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n console.log(`removed subsystem '${name}' (${repoPath(docsDir, sub.dir)}/)${reportViews(views)}`);\n if (!directoryRemoved) {\n console.log(\n `note: the directory itself could not be removed (something non-loam is still inside) — ` +\n `until it is deleted by hand it will list as an empty unfiled service.`,\n );\n }\n },\n };\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/commands/subsystem/edit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The two verbs that create and remove GROUPS: `subsystem new` and\n * `subsystem rm`. Both commit the marker AND the regenerated views file in\n * one journaled transaction, because a marker without its view (or a view\n * outliving its marker) is exactly the `subsystem.views-stale` state the\n * generated file's contract refuses to leave behind.\n *\n * `rm` refuses a subsystem with members — services, child subsystems, or any\n * stray non-dot entry beside the marker the tree does not already count —\n * naming each once, as what it is (`subsystem-not-empty`):\n * a destructive command never picks targets the caller did not name, and a\n * directory emptied of its marker but not of its contents would re-enter the\n * walk as a phantom service.\n */\nimport { existsSync } from \"node:fs\";\nimport { readdir, rmdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { stringify } from \"yaml\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { parseSubsystemName } from \"../../core/kernel/ids/subsystem.js\";\nimport { emitJson, fail, repoPath } from \"../../core/envelope/json.js\";\nimport { servicesDir, subsystemPathUnder } from \"../../core/repo/paths.js\";\nimport { findInTree, nearestTreeNames, servicesUnder, subsystemsUnder } from \"../../core/repo/tree/find.js\";\nimport { entryIs } from \"../../core/repo/tree/fs.js\";\nimport { SUBSYSTEM_MARKER } from \"../../core/repo/tree/marker.js\";\nimport type { FleetTree } from \"../../core/repo/tree/walk.js\";\nimport { planWrite } from \"../../core/staging/writes.js\";\nimport { commitWindow, reportViews, type SubsystemTxn } from \"./txn/txn.js\";\nimport type { SubsystemOptions } from \"./subsystem.js\";\n\n/** `subsystem new <name> [--under <parent>] [--title …] [--description …] [--owner …]` */\nexport async function runNew(\n docsDir: DocsDir,\n name: string,\n opts: SubsystemOptions,\n json: boolean,\n): Promise<void> {\n const parsed = parseSubsystemName(name, \"subsystem name\");\n if (!parsed.ok) {\n fail(json, \"invalid-option\", parsed.problem);\n return;\n }\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n // The flat namespace: one name, one meaning, at any depth — a `new` that\n // collides with ANY service or subsystem would be the exact state\n // `subsystem.name-collision` refuses after the fact.\n const taken = findInTree(tree, name);\n if (taken !== null) {\n const where = taken.kind === \"service\" ? taken.service.dir : taken.subsystem.dir;\n fail(\n json,\n \"already-exists\",\n `'${name}' is already a ${taken.kind} (${repoPath(docsDir, where)}) — ` +\n `service ids and subsystem names share one flat namespace, unique at any depth. Pick another name.`,\n );\n return null;\n }\n let parentDir = servicesDir(docsDir);\n let parentPath: string[] = [];\n if (opts.under !== undefined) {\n const parent = findInTree(tree, opts.under);\n if (parent === null) {\n const close = nearestTreeNames(opts.under, tree.subsystems.map((s) => s.name));\n fail(\n json,\n \"unknown-target\",\n `No subsystem '${opts.under}' in the tree.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `loam subsystem list` shows what exists.\"),\n );\n return null;\n }\n if (parent.kind === \"service\") {\n fail(json, \"invalid-option\", `--under names the service '${opts.under}' — a service never contains subsystems.`);\n return null;\n }\n parentDir = parent.subsystem.dir;\n parentPath = parent.subsystem.path;\n }\n const dir = subsystemPathUnder(parentDir, parsed.name);\n // The flat-namespace check above compares exact strings; the FILESYSTEM\n // may not. On a case-folding volume (macOS APFS by default) `Billing`\n // and an existing `services/billing` are one directory, so the marker\n // write below would land subsystem.yaml INSIDE the live service — the\n // exact marker-beside-artifacts state `subsystem.marker-misplaced`\n // refuses after the fact, created by a blessed command. A plain file of\n // the same name reaches here too (files at the root are not walked). So\n // the disk is probed directly and anything already there refuses closed.\n if (existsSync(dir)) {\n fail(\n json,\n \"already-exists\",\n `${repoPath(docsDir, dir)} already exists on disk — the tree resolves no entry by that exact name, ` +\n `so either this filesystem folds case (the directory answers to another spelling of the name) ` +\n `or something non-loam created it. Pick another name, or move what is there first. Nothing was created.`,\n );\n return null;\n }\n const meta = {\n ...(opts.title === undefined ? {} : { title: opts.title }),\n ...(opts.description === undefined ? {} : { description: opts.description }),\n ...(opts.owner === undefined ? {} : { owner: opts.owner }),\n };\n // An empty file is a valid marker — presence classifies, content is metadata.\n const marker = Object.keys(meta).length === 0 ? \"\" : stringify(meta);\n const after: FleetTree = {\n ...tree,\n subsystems: [...tree.subsystems, { name, path: [...parentPath, name], dir, meta }],\n };\n return {\n target: name,\n writes: [planWrite(join(dir, SUBSYSTEM_MARKER), marker)],\n tree: after,\n // A lost exclusive-create race on the marker IS the name being taken.\n racedCode: \"already-exists\",\n what: \"created\",\n report: (views, recovered) => {\n if (json) {\n emitJson({\n command: \"subsystem\",\n created: name,\n path: repoPath(docsDir, dir),\n views: views.subsystems,\n serviceViews: views.services,\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n console.log(`created ${repoPath(docsDir, dir)}/ (${SUBSYSTEM_MARKER} written)${reportViews(views)}`);\n },\n };\n });\n}\n\n/** `subsystem rm <name>` — refuses anything but an empty group. */\nexport async function runRm(docsDir: DocsDir, name: string, json: boolean): Promise<void> {\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n const hit = findInTree(tree, name);\n if (hit === null) {\n const close = nearestTreeNames(name, tree.subsystems.map((s) => s.name));\n fail(\n json,\n \"unknown-target\",\n `No subsystem '${name}' in the tree.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `loam subsystem list` shows what exists.\"),\n );\n return null;\n }\n if (hit.kind === \"service\") {\n fail(\n json,\n \"invalid-option\",\n `'${name}' is a service, not a subsystem — loam does not delete services (\\`git rm -r ${repoPath(docsDir, hit.service.dir)}\\` does, and version control keeps the record).`,\n );\n return null;\n }\n const sub = hit.subsystem;\n const services = servicesUnder(tree, sub).map((s) => s.id);\n const children = subsystemsUnder(tree, sub).map((s) => s.name);\n // Stray entries beside the marker count as members too: after the marker\n // is gone the directory would re-enter the walk as an empty \"service\" the\n // moment anything keeps rmdir from removing it. But only entries the tree\n // has NOT already counted — a member service or child subsystem is also a\n // readdir entry here, and listing it a second time as \"file X\" made the\n // count wrong and the same name appear under two kinds. What remains is\n // discriminated by what it is (`entryIs`, the walk's own symlink-aware\n // question): a file, or a directory the tree does not claim (an unmarked\n // group, already its own `subsystem.unmarked` finding).\n const counted = new Set<string>([\n ...servicesUnder(tree, sub).filter((s) => s.subsystem.length === sub.path.length).map((s) => s.id as string),\n ...subsystemsUnder(tree, sub).filter((c) => c.path.length === sub.path.length + 1).map((c) => c.name),\n ]);\n const strays = (await readdir(sub.dir, { withFileTypes: true }))\n .filter((e) => !e.name.startsWith(\".\") && e.name !== SUBSYSTEM_MARKER && !counted.has(e.name))\n .map((e) => `${entryIs(sub.dir, e, \"dir\") ? \"directory\" : \"file\"} ${e.name}`);\n const members = [\n ...services.map((s) => `service ${s}`),\n ...children.map((c) => `subsystem ${c}`),\n ...strays,\n ];\n if (members.length > 0) {\n fail(\n json,\n \"subsystem-not-empty\",\n `'${name}' still holds ${members.length} member(s): ${members.join(\", \")}. ` +\n \"`loam subsystem move <name>... --into <sub|.>` moves them out; a destructive command never picks targets you did not name.\",\n );\n return null;\n }\n const after: FleetTree = { ...tree, subsystems: tree.subsystems.filter((s) => s !== sub) };\n return {\n target: name,\n writes: [{ path: join(sub.dir, SUBSYSTEM_MARKER), content: null }],\n tree: after,\n what: \"removed\",\n report: async (views, recovered) => {\n // The directory itself, after the transaction: the marker is gone, the\n // group was verifiably empty, and an empty marker-less directory would\n // read as an unfiled service. Dotfile litter (.DS_Store) can still\n // defeat rmdir — reported, not swallowed.\n let directoryRemoved = true;\n try {\n await rmdir(sub.dir);\n } catch {\n directoryRemoved = !existsSync(sub.dir);\n }\n if (json) {\n emitJson({\n command: \"subsystem\",\n removed: name,\n path: repoPath(docsDir, sub.dir),\n directoryRemoved,\n views: views.subsystems,\n serviceViews: views.services,\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n console.log(`removed subsystem '${name}' (${repoPath(docsDir, sub.dir)}/)${reportViews(views)}`);\n if (!directoryRemoved) {\n console.log(\n `note: the directory itself could not be removed (something non-loam is still inside) — ` +\n `until it is deleted by hand it will list as an empty unfiled service.`,\n );\n }\n },\n };\n });\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `subsystem move` and `subsystem rename` — ONE transaction over N directory
|
|
3
|
-
* renames plus
|
|
3
|
+
* renames plus BOTH generated views files, the first real consumer of the
|
|
4
4
|
* journaled txn's moves half (`core/staging/txn/`). The planning lives in
|
|
5
5
|
* this module; the commit window itself is `./txn/txn.ts`, shared with
|
|
6
6
|
* `new` and `rm`.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* uncommitted changes under a directory being moved — and only on positive
|
|
10
10
|
* evidence: where git will not say, the move proceeds. After a landed move
|
|
11
11
|
* loam performs the roadmap's one blessed git write, a best-effort `git add`
|
|
12
|
-
* of the rename pairs and
|
|
12
|
+
* of the rename pairs and of BOTH regenerated views files, so git records the
|
|
13
13
|
* renames as renames and `subsystem history` stays answerable once the user
|
|
14
14
|
* commits. Nothing is ever committed.
|
|
15
15
|
*/
|
|
@@ -20,7 +20,7 @@ import { parseSubsystemName } from "../../core/kernel/ids/subsystem.js";
|
|
|
20
20
|
import { isRecord } from "../../core/kernel/records.js";
|
|
21
21
|
import { emitJson, fail, repoPath } from "../../core/envelope/json.js";
|
|
22
22
|
import { gitDirtyPaths, gitStageRenames } from "../../core/provenance/gitq/moves.js";
|
|
23
|
-
import { servicesDir, subsystemPathUnder, subsystemViewsPath } from "../../core/repo/paths.js";
|
|
23
|
+
import { servicesDir, serviceViewsPath, subsystemPathUnder, subsystemViewsPath } from "../../core/repo/paths.js";
|
|
24
24
|
import { listFeatures } from "../../core/repo/repo.js";
|
|
25
25
|
import { findInTree, nearestTreeNames, treeNames, withinSubsystem } from "../../core/repo/tree/find.js";
|
|
26
26
|
import { SNAPSHOT_COMPAT_VERSION, SNAPSHOT_MANIFEST, snapshotDir } from "../../core/staging/snapshot.js";
|
|
@@ -87,7 +87,7 @@ export async function runMove(docsDir, names, into, json) {
|
|
|
87
87
|
failedCode: "move-failed",
|
|
88
88
|
what: "moved",
|
|
89
89
|
report: async (views, recovered) => {
|
|
90
|
-
await stageInGit(docsDir, renames);
|
|
90
|
+
await stageInGit(docsDir, renames, views);
|
|
91
91
|
const rows = moved.map((m, i) => ({
|
|
92
92
|
name: m.name,
|
|
93
93
|
kind: m.kind,
|
|
@@ -99,7 +99,8 @@ export async function runMove(docsDir, names, into, json) {
|
|
|
99
99
|
command: "subsystem",
|
|
100
100
|
into,
|
|
101
101
|
moved: rows,
|
|
102
|
-
views,
|
|
102
|
+
views: views.subsystems,
|
|
103
|
+
serviceViews: views.services,
|
|
103
104
|
...(notices.length === 0 ? {} : { warnings: notices }),
|
|
104
105
|
...(recovered === null ? {} : { recovered }),
|
|
105
106
|
});
|
|
@@ -151,14 +152,15 @@ export async function runRename(docsDir, names, json) {
|
|
|
151
152
|
failedCode: "move-failed",
|
|
152
153
|
what: "renamed",
|
|
153
154
|
report: async (views, recovered) => {
|
|
154
|
-
await stageInGit(docsDir, [rename]);
|
|
155
|
+
await stageInGit(docsDir, [rename], views);
|
|
155
156
|
if (json) {
|
|
156
157
|
emitJson({
|
|
157
158
|
command: "subsystem",
|
|
158
159
|
renamed: from,
|
|
159
160
|
to,
|
|
160
161
|
path: repoPath(docsDir, rename.to),
|
|
161
|
-
views,
|
|
162
|
+
views: views.subsystems,
|
|
163
|
+
serviceViews: views.services,
|
|
162
164
|
...(notices.length === 0 ? {} : { warnings: notices }),
|
|
163
165
|
...(recovered === null ? {} : { recovered }),
|
|
164
166
|
});
|
|
@@ -209,17 +211,53 @@ async function refuseUncommitted(docsDir, dirs, json) {
|
|
|
209
211
|
}
|
|
210
212
|
/**
|
|
211
213
|
* Stage the move's whole delta, silently: the rename pairs (both ends — the
|
|
212
|
-
* delete side and the add side) AND
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
214
|
+
* delete side and the add side) AND BOTH regenerated views files. They ride
|
|
215
|
+
* along for the same reason the transaction writes them in the same commit: a
|
|
216
|
+
* user whose next `git commit` captured the renames without them would commit
|
|
217
|
+
* a tree `validate --all` fails (`subsystem.views-stale`) — the exact "failing
|
|
218
|
+
* between two commits" state the roadmap forbids.
|
|
219
|
+
*
|
|
220
|
+
* A generated file that is neither ON DISK nor REMOVED BY THIS COMMIT is left
|
|
221
|
+
* out of the pathspec, and that is a correctness rule rather than tidiness:
|
|
222
|
+
* `gitStageRenames` runs one `git add -A -- <paths>`, and a pathspec matching
|
|
223
|
+
* neither the worktree nor the index makes git refuse the WHOLE invocation
|
|
224
|
+
* (silently — that write is failure-silent by contract) — so naming an absent
|
|
225
|
+
* `architecture/services.likec4` (a fleet whose map draws no service renders
|
|
226
|
+
* none) would take the other paths out of the index with it.
|
|
227
|
+
*
|
|
228
|
+
* THE `removed` ARM IS WHY `existsSync` ALONE IS NOT THE FILTER. A move can be
|
|
229
|
+
* the commit that DELETES a generated file rather than rewriting it: a
|
|
230
|
+
* committed `architecture/subsystems.likec4` standing over a tree that no
|
|
231
|
+
* longer marks any subsystem (the marker went in an earlier commit) must not
|
|
232
|
+
* exist, so the window renders null and the transaction removes it — and by the
|
|
233
|
+
* time this runs the file is gone from disk. Measured 2026-09-07 on exactly that
|
|
234
|
+
* fixture: under a disk-only filter the path never reached the pathspec, git
|
|
235
|
+
* reported the deletion as ` D` (unstaged) while both rename ends went in as
|
|
236
|
+
* `R `, and the user's next `git commit` captured the renames plus a generated
|
|
237
|
+
* file the tree no longer licenses — `subsystem.views-stale` on a commit loam
|
|
238
|
+
* itself produced, the exact state the paragraph above says this staging exists
|
|
239
|
+
* to prevent. A path this commit deleted is still in the INDEX, so it matches
|
|
240
|
+
* the pathspec and `-A` stages the deletion; the commit window's own
|
|
241
|
+
* `ViewsActions` is what says which file that was, so no second guess about disk
|
|
242
|
+
* is needed.
|
|
243
|
+
*
|
|
244
|
+
* TWO INVOCATIONS, not one, and the split is what makes the `removed` arm safe
|
|
245
|
+
* to add: a generated file that was never committed and is deleted here is in
|
|
246
|
+
* neither the worktree nor the index, and folded into one pathspec it would take
|
|
247
|
+
* the renames down with it — the failure this function's first rule exists to
|
|
248
|
+
* refuse. Staging in two calls costs one extra best-effort spawn and cannot
|
|
249
|
+
* cost the renames. git records a rename from the index state at commit time,
|
|
250
|
+
* never from which `git add` put the two ends there.
|
|
217
251
|
*/
|
|
218
|
-
async function stageInGit(docsDir, renames) {
|
|
219
|
-
await gitStageRenames(docsDir, [
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
252
|
+
async function stageInGit(docsDir, renames, views) {
|
|
253
|
+
await gitStageRenames(docsDir, renames.flatMap((r) => [repoPath(docsDir, r.from), repoPath(docsDir, r.to)]));
|
|
254
|
+
const generated = [
|
|
255
|
+
[subsystemViewsPath(docsDir), views.subsystems],
|
|
256
|
+
[serviceViewsPath(docsDir), views.services],
|
|
257
|
+
];
|
|
258
|
+
await gitStageRenames(docsDir, generated
|
|
259
|
+
.filter(([path, action]) => action === "removed" || existsSync(path))
|
|
260
|
+
.map(([path]) => repoPath(docsDir, path)));
|
|
223
261
|
}
|
|
224
262
|
/**
|
|
225
263
|
* Which archived features hold a VERSION-2 snapshot addressing a path under
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"move.js","sourceRoot":"","sources":["../../../src/commands/subsystem/move.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `subsystem move` and `subsystem rename` — ONE transaction over N directory\n * renames plus the generated views file, the first real consumer of the\n * journaled txn's moves half (`core/staging/txn/`). The planning lives in\n * this module; the commit window itself is `./txn/txn.ts`, shared with\n * `new` and `rm`.\n *\n * The ONLY move-specific refusal is `move-uncommitted` — git reports\n * uncommitted changes under a directory being moved — and only on positive\n * evidence: where git will not say, the move proceeds. After a landed move\n * loam performs the roadmap's one blessed git write, a best-effort `git add`\n * of the rename pairs and the regenerated views file, so git records the\n * renames as renames and `subsystem history` stays answerable once the user\n * commits. Nothing is ever committed.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, join, sep } from \"node:path\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { parseSubsystemName } from \"../../core/kernel/ids/subsystem.js\";\nimport { isRecord } from \"../../core/kernel/records.js\";\nimport { emitJson, fail, repoPath } from \"../../core/envelope/json.js\";\nimport { gitDirtyPaths, gitStageRenames } from \"../../core/provenance/gitq/moves.js\";\nimport type { FeatureEntry } from \"../../core/repo/entries.js\";\nimport { servicesDir, subsystemPathUnder, subsystemViewsPath } from \"../../core/repo/paths.js\";\nimport { listFeatures } from \"../../core/repo/repo.js\";\nimport { findInTree, nearestTreeNames, treeNames, withinSubsystem } from \"../../core/repo/tree/find.js\";\nimport type { FleetTree } from \"../../core/repo/tree/walk.js\";\nimport { SNAPSHOT_COMPAT_VERSION, SNAPSHOT_MANIFEST, snapshotDir } from \"../../core/staging/snapshot.js\";\nimport { commitWindow, reportViews, type SubsystemTxn } from \"./txn/txn.js\";\nimport { movedTree } from \"./txn/views.js\";\n\n/** What one name resolved to for a move: where it is, and where it goes. */\ninterface Moved {\n name: string;\n kind: \"service\" | \"subsystem\";\n dir: string;\n /** Path of the CONTAINING directory, as names from services/ down. */\n parent: string[];\n}\n\n/** `subsystem move <name>... --into <sub|.>` — services and whole subtrees alike. */\nexport async function runMove(docsDir: DocsDir, names: string[], into: string, json: boolean): Promise<void> {\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n const seen = new Set<string>();\n for (const name of names) {\n if (seen.has(name)) {\n fail(json, \"invalid-option\", `'${name}' is named twice — each service or subsystem moves once.`);\n return null;\n }\n seen.add(name);\n }\n const target = resolveTarget(tree, into, { docsDir, json });\n if (target === null) return null;\n const moved: Moved[] = [];\n for (const name of names) {\n const hit = findInTree(tree, name);\n if (hit === null) {\n const close = nearestTreeNames(name, treeNames(tree));\n fail(\n json,\n \"unknown-target\",\n `No service or subsystem '${name}' in the tree.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `loam subsystem list` shows what exists.\"),\n );\n return null;\n }\n moved.push(\n hit.kind === \"service\"\n ? { name, kind: \"service\", dir: hit.service.dir, parent: hit.service.subsystem }\n : { name, kind: \"subsystem\", dir: hit.subsystem.dir, parent: hit.subsystem.path.slice(0, -1) },\n );\n }\n for (const m of moved) {\n if (m.kind === \"subsystem\" && withinSubsystem([...m.parent, m.name], target.path)) {\n fail(json, \"invalid-option\", `'${m.name}' cannot move into itself or its own subtree.`);\n return null;\n }\n if (m.parent.join(\"/\") === target.path.join(\"/\")) {\n fail(json, \"invalid-option\", `'${m.name}' is already directly in ${into === \".\" ? \"the services/ root\" : `'${into}'`} — nothing to move.`);\n return null;\n }\n const inside = moved.find(\n (other) => other.kind === \"subsystem\" && other !== m && (m.dir + sep).startsWith(other.dir + sep),\n );\n if (inside !== undefined) {\n fail(json, \"invalid-option\", `'${m.name}' sits inside '${inside.name}', which is also being moved — name only the subtree root.`);\n return null;\n }\n if (existsSync(join(target.dir, m.name))) {\n // Unreachable in a healthy flat namespace; a tree already carrying\n // name collisions can produce it, and refusing beats stacking a\n // second directory of the same name.\n fail(json, \"already-exists\", `${repoPath(docsDir, join(target.dir, m.name))} already exists — fix the name collision first.`);\n return null;\n }\n }\n if (await refuseUncommitted(docsDir, moved.map((m) => m.dir), json)) return null;\n const notices = await v2SnapshotNotices(docsDir, moved.map((m) => m.dir));\n const renames = moved.map((m) => ({ from: m.dir, to: join(target.dir, m.name) }));\n return {\n target: names.join(\", \"),\n writes: [],\n tree: movedTree(tree, renames, servicesDir(docsDir)),\n moves: renames,\n failedCode: \"move-failed\",\n what: \"moved\",\n report: async (views, recovered) => {\n await stageInGit(docsDir, renames);\n const rows = moved.map((m, i) => ({\n name: m.name,\n kind: m.kind,\n from: repoPath(docsDir, renames[i]!.from),\n to: repoPath(docsDir, renames[i]!.to),\n }));\n if (json) {\n emitJson({\n command: \"subsystem\",\n into,\n moved: rows,\n views,\n ...(notices.length === 0 ? {} : { warnings: notices }),\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n for (const row of rows) console.log(`moved ${row.from} -> ${row.to}`);\n for (const n of notices) console.log(`note: ${n}`);\n console.log(`Renames staged where git answers; nothing committed${reportViews(views)}.`);\n },\n };\n });\n}\n\n/** `subsystem rename <old> <new>` — one rename through the same transaction. */\nexport async function runRename(docsDir: DocsDir, names: [string, string], json: boolean): Promise<void> {\n const [from, to] = names;\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n const hit = findInTree(tree, from);\n if (hit === null) {\n const close = nearestTreeNames(from, tree.subsystems.map((s) => s.name));\n fail(\n json,\n \"unknown-target\",\n `No subsystem '${from}' in the tree.` + (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \"\"),\n );\n return null;\n }\n if (hit.kind === \"service\") {\n fail(\n json,\n \"invalid-option\",\n `'${from}' is a service — a service's id IS its identity (loam.json, frontmatter, feature specs all join on it), so loam never renames one.`,\n );\n return null;\n }\n const parsed = parseSubsystemName(to, \"subsystem name\");\n if (!parsed.ok) {\n fail(json, \"invalid-option\", parsed.problem);\n return null;\n }\n const taken = findInTree(tree, to);\n if (taken !== null) {\n const where = taken.kind === \"service\" ? taken.service.dir : taken.subsystem.dir;\n fail(json, \"already-exists\", `'${to}' is already a ${taken.kind} (${repoPath(docsDir, where)}) — the namespace is flat at any depth.`);\n return null;\n }\n if (await refuseUncommitted(docsDir, [hit.subsystem.dir], json)) return null;\n const notices = await v2SnapshotNotices(docsDir, [hit.subsystem.dir]);\n const rename = { from: hit.subsystem.dir, to: subsystemPathUnder(dirname(hit.subsystem.dir), parsed.name) };\n return {\n target: `${from} -> ${to}`,\n writes: [],\n tree: movedTree(tree, [rename], servicesDir(docsDir)),\n moves: [rename],\n failedCode: \"move-failed\",\n what: \"renamed\",\n report: async (views, recovered) => {\n await stageInGit(docsDir, [rename]);\n if (json) {\n emitJson({\n command: \"subsystem\",\n renamed: from,\n to,\n path: repoPath(docsDir, rename.to),\n views,\n ...(notices.length === 0 ? {} : { warnings: notices }),\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n console.log(`renamed subsystem '${from}' -> '${to}' (${repoPath(docsDir, rename.to)}/)${reportViews(views)}`);\n for (const n of notices) console.log(`note: ${n}`);\n },\n };\n });\n}\n\n/** Where `--into` points: the services/ root for `.`, an existing subsystem otherwise. */\nfunction resolveTarget(tree: FleetTree, into: string, at: { docsDir: DocsDir; json: boolean }): { dir: string; path: string[] } | null {\n if (into === \".\") return { dir: servicesDir(at.docsDir), path: [] };\n const hit = findInTree(tree, into);\n if (hit === null) {\n const close = nearestTreeNames(into, tree.subsystems.map((s) => s.name));\n fail(\n at.json,\n \"unknown-target\",\n `No subsystem '${into}' to move into.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `--into .` unfiles; `loam subsystem new` creates.\"),\n );\n return null;\n }\n if (hit.kind === \"service\") {\n fail(at.json, \"invalid-option\", `--into names the service '${into}' — a service never contains other services.`);\n return null;\n }\n return { dir: hit.subsystem.dir, path: hit.subsystem.path };\n}\n\n/**\n * The one move-specific refusal, on positive evidence only: git names\n * uncommitted work under a directory about to be renamed. Null (git will not\n * say) proceeds — a fleet that does not use git must not be refused over it.\n */\nasync function refuseUncommitted(docsDir: DocsDir, dirs: string[], json: boolean): Promise<boolean> {\n const dirty = await gitDirtyPaths(docsDir, dirs.map((d) => repoPath(docsDir, d)));\n if (dirty === null || dirty.length === 0) return false;\n fail(\n json,\n \"move-uncommitted\",\n // \"or untracked\", and `git stash -u` by name: the check fires on ANY\n // porcelain entry, untracked files included, and plain `git stash` does\n // not stash those — advice that said \"stash\" sent the user in a loop,\n // refused again with the same sentence after doing what it asked.\n `git reports ${dirty.length} uncommitted or untracked path(s) under the directory(ies) being moved: ${dirty.join(\", \")}. ` +\n \"Renaming now would sweep them into a move nobody reviewed — commit them, `git stash -u`, or remove them, then re-run. \" +\n \"Nothing was moved.\",\n );\n return true;\n}\n\n/**\n * Stage the move's whole delta, silently: the rename pairs (both ends — the\n * delete side and the add side) AND the regenerated views file. The views\n * file rides along for the same reason the transaction writes it in the same\n * commit: a user whose next `git commit` captured the renames without it\n * would commit a tree `validate --all` fails (`subsystem.views-stale`) — the\n * exact \"failing between two commits\" state the roadmap forbids.\n */\nasync function stageInGit(docsDir: DocsDir, renames: { from: string; to: string }[]): Promise<void> {\n await gitStageRenames(docsDir, [\n ...renames.flatMap((r) => [repoPath(docsDir, r.from), repoPath(docsDir, r.to)]),\n repoPath(docsDir, subsystemViewsPath(docsDir)),\n ]);\n}\n\n/**\n * Which archived features hold a VERSION-2 snapshot addressing a path under\n * one of these directories — the notice, never a refusal, a move prints\n * before it proceeds. A version-2 manifest predates the tree and records\n * literal `services/<id>/…` paths, so after this move an `unarchive --force`\n * of that feature restores into the OLD path, resurrecting the pre-move\n * directory beside the moved one (two claimants, `subsystem.name-collision`\n * — loud, but avoidable by being told now). Version-3 manifests key rows by\n * service id and follow the enumeration, so they are not named. Every\n * failure to read reads as \"nothing to warn about\": a warning needs positive\n * evidence exactly as the move's one refusal does.\n */\nasync function v2SnapshotNotices(docsDir: DocsDir, dirs: string[]): Promise<string[]> {\n const rels = dirs.map((d) => repoPath(docsDir, d));\n const notices: string[] = [];\n let features: FeatureEntry[];\n try {\n features = await listFeatures(docsDir, { includeArchived: true });\n } catch {\n return notices;\n }\n for (const f of features.filter((entry) => entry.archived)) {\n let manifest: unknown;\n try {\n manifest = JSON.parse(await readFile(join(snapshotDir(f.dir), SNAPSHOT_MANIFEST), \"utf8\"));\n } catch {\n continue;\n }\n if (!isRecord(manifest) || manifest.version !== SNAPSHOT_COMPAT_VERSION || !Array.isArray(manifest.files)) continue;\n const addressed = rels.filter((rel) =>\n (manifest.files as unknown[]).some(\n (row) => isRecord(row) && typeof row.path === \"string\" && row.path.startsWith(`${rel}/`),\n ),\n );\n if (addressed.length > 0) {\n notices.push(\n `${f.id}'s archived snapshot is version 2 and addresses ${addressed.join(\", \")} by literal path — ` +\n `\\`loam unarchive ${f.id} --force\\` after this move would restore into the old location, ` +\n `resurrecting the pre-move directory beside the moved one.`,\n );\n }\n }\n return notices;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"move.js","sourceRoot":"","sources":["../../../src/commands/subsystem/move.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `subsystem move` and `subsystem rename` — ONE transaction over N directory\n * renames plus BOTH generated views files, the first real consumer of the\n * journaled txn's moves half (`core/staging/txn/`). The planning lives in\n * this module; the commit window itself is `./txn/txn.ts`, shared with\n * `new` and `rm`.\n *\n * The ONLY move-specific refusal is `move-uncommitted` — git reports\n * uncommitted changes under a directory being moved — and only on positive\n * evidence: where git will not say, the move proceeds. After a landed move\n * loam performs the roadmap's one blessed git write, a best-effort `git add`\n * of the rename pairs and of BOTH regenerated views files, so git records the\n * renames as renames and `subsystem history` stays answerable once the user\n * commits. Nothing is ever committed.\n */\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, join, sep } from \"node:path\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { parseSubsystemName } from \"../../core/kernel/ids/subsystem.js\";\nimport { isRecord } from \"../../core/kernel/records.js\";\nimport { emitJson, fail, repoPath } from \"../../core/envelope/json.js\";\nimport { gitDirtyPaths, gitStageRenames } from \"../../core/provenance/gitq/moves.js\";\nimport type { FeatureEntry } from \"../../core/repo/entries.js\";\nimport { servicesDir, serviceViewsPath, subsystemPathUnder, subsystemViewsPath } from \"../../core/repo/paths.js\";\nimport { listFeatures } from \"../../core/repo/repo.js\";\nimport { findInTree, nearestTreeNames, treeNames, withinSubsystem } from \"../../core/repo/tree/find.js\";\nimport type { FleetTree } from \"../../core/repo/tree/walk.js\";\nimport { SNAPSHOT_COMPAT_VERSION, SNAPSHOT_MANIFEST, snapshotDir } from \"../../core/staging/snapshot.js\";\nimport { commitWindow, reportViews, type SubsystemTxn, type ViewsActions } from \"./txn/txn.js\";\nimport { movedTree } from \"./txn/views.js\";\n\n/** What one name resolved to for a move: where it is, and where it goes. */\ninterface Moved {\n name: string;\n kind: \"service\" | \"subsystem\";\n dir: string;\n /** Path of the CONTAINING directory, as names from services/ down. */\n parent: string[];\n}\n\n/** `subsystem move <name>... --into <sub|.>` — services and whole subtrees alike. */\nexport async function runMove(docsDir: DocsDir, names: string[], into: string, json: boolean): Promise<void> {\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n const seen = new Set<string>();\n for (const name of names) {\n if (seen.has(name)) {\n fail(json, \"invalid-option\", `'${name}' is named twice — each service or subsystem moves once.`);\n return null;\n }\n seen.add(name);\n }\n const target = resolveTarget(tree, into, { docsDir, json });\n if (target === null) return null;\n const moved: Moved[] = [];\n for (const name of names) {\n const hit = findInTree(tree, name);\n if (hit === null) {\n const close = nearestTreeNames(name, treeNames(tree));\n fail(\n json,\n \"unknown-target\",\n `No service or subsystem '${name}' in the tree.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `loam subsystem list` shows what exists.\"),\n );\n return null;\n }\n moved.push(\n hit.kind === \"service\"\n ? { name, kind: \"service\", dir: hit.service.dir, parent: hit.service.subsystem }\n : { name, kind: \"subsystem\", dir: hit.subsystem.dir, parent: hit.subsystem.path.slice(0, -1) },\n );\n }\n for (const m of moved) {\n if (m.kind === \"subsystem\" && withinSubsystem([...m.parent, m.name], target.path)) {\n fail(json, \"invalid-option\", `'${m.name}' cannot move into itself or its own subtree.`);\n return null;\n }\n if (m.parent.join(\"/\") === target.path.join(\"/\")) {\n fail(json, \"invalid-option\", `'${m.name}' is already directly in ${into === \".\" ? \"the services/ root\" : `'${into}'`} — nothing to move.`);\n return null;\n }\n const inside = moved.find(\n (other) => other.kind === \"subsystem\" && other !== m && (m.dir + sep).startsWith(other.dir + sep),\n );\n if (inside !== undefined) {\n fail(json, \"invalid-option\", `'${m.name}' sits inside '${inside.name}', which is also being moved — name only the subtree root.`);\n return null;\n }\n if (existsSync(join(target.dir, m.name))) {\n // Unreachable in a healthy flat namespace; a tree already carrying\n // name collisions can produce it, and refusing beats stacking a\n // second directory of the same name.\n fail(json, \"already-exists\", `${repoPath(docsDir, join(target.dir, m.name))} already exists — fix the name collision first.`);\n return null;\n }\n }\n if (await refuseUncommitted(docsDir, moved.map((m) => m.dir), json)) return null;\n const notices = await v2SnapshotNotices(docsDir, moved.map((m) => m.dir));\n const renames = moved.map((m) => ({ from: m.dir, to: join(target.dir, m.name) }));\n return {\n target: names.join(\", \"),\n writes: [],\n tree: movedTree(tree, renames, servicesDir(docsDir)),\n moves: renames,\n failedCode: \"move-failed\",\n what: \"moved\",\n report: async (views, recovered) => {\n await stageInGit(docsDir, renames, views);\n const rows = moved.map((m, i) => ({\n name: m.name,\n kind: m.kind,\n from: repoPath(docsDir, renames[i]!.from),\n to: repoPath(docsDir, renames[i]!.to),\n }));\n if (json) {\n emitJson({\n command: \"subsystem\",\n into,\n moved: rows,\n views: views.subsystems,\n serviceViews: views.services,\n ...(notices.length === 0 ? {} : { warnings: notices }),\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n for (const row of rows) console.log(`moved ${row.from} -> ${row.to}`);\n for (const n of notices) console.log(`note: ${n}`);\n console.log(`Renames staged where git answers; nothing committed${reportViews(views)}.`);\n },\n };\n });\n}\n\n/** `subsystem rename <old> <new>` — one rename through the same transaction. */\nexport async function runRename(docsDir: DocsDir, names: [string, string], json: boolean): Promise<void> {\n const [from, to] = names;\n await commitWindow(docsDir, json, async (tree): Promise<SubsystemTxn | null> => {\n const hit = findInTree(tree, from);\n if (hit === null) {\n const close = nearestTreeNames(from, tree.subsystems.map((s) => s.name));\n fail(\n json,\n \"unknown-target\",\n `No subsystem '${from}' in the tree.` + (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \"\"),\n );\n return null;\n }\n if (hit.kind === \"service\") {\n fail(\n json,\n \"invalid-option\",\n `'${from}' is a service — a service's id IS its identity (loam.json, frontmatter, feature specs all join on it), so loam never renames one.`,\n );\n return null;\n }\n const parsed = parseSubsystemName(to, \"subsystem name\");\n if (!parsed.ok) {\n fail(json, \"invalid-option\", parsed.problem);\n return null;\n }\n const taken = findInTree(tree, to);\n if (taken !== null) {\n const where = taken.kind === \"service\" ? taken.service.dir : taken.subsystem.dir;\n fail(json, \"already-exists\", `'${to}' is already a ${taken.kind} (${repoPath(docsDir, where)}) — the namespace is flat at any depth.`);\n return null;\n }\n if (await refuseUncommitted(docsDir, [hit.subsystem.dir], json)) return null;\n const notices = await v2SnapshotNotices(docsDir, [hit.subsystem.dir]);\n const rename = { from: hit.subsystem.dir, to: subsystemPathUnder(dirname(hit.subsystem.dir), parsed.name) };\n return {\n target: `${from} -> ${to}`,\n writes: [],\n tree: movedTree(tree, [rename], servicesDir(docsDir)),\n moves: [rename],\n failedCode: \"move-failed\",\n what: \"renamed\",\n report: async (views, recovered) => {\n await stageInGit(docsDir, [rename], views);\n if (json) {\n emitJson({\n command: \"subsystem\",\n renamed: from,\n to,\n path: repoPath(docsDir, rename.to),\n views: views.subsystems,\n serviceViews: views.services,\n ...(notices.length === 0 ? {} : { warnings: notices }),\n ...(recovered === null ? {} : { recovered }),\n });\n return;\n }\n console.log(`renamed subsystem '${from}' -> '${to}' (${repoPath(docsDir, rename.to)}/)${reportViews(views)}`);\n for (const n of notices) console.log(`note: ${n}`);\n },\n };\n });\n}\n\n/** Where `--into` points: the services/ root for `.`, an existing subsystem otherwise. */\nfunction resolveTarget(tree: FleetTree, into: string, at: { docsDir: DocsDir; json: boolean }): { dir: string; path: string[] } | null {\n if (into === \".\") return { dir: servicesDir(at.docsDir), path: [] };\n const hit = findInTree(tree, into);\n if (hit === null) {\n const close = nearestTreeNames(into, tree.subsystems.map((s) => s.name));\n fail(\n at.json,\n \"unknown-target\",\n `No subsystem '${into}' to move into.` +\n (close.length > 0 ? ` Close names: ${close.join(\", \")}.` : \" `--into .` unfiles; `loam subsystem new` creates.\"),\n );\n return null;\n }\n if (hit.kind === \"service\") {\n fail(at.json, \"invalid-option\", `--into names the service '${into}' — a service never contains other services.`);\n return null;\n }\n return { dir: hit.subsystem.dir, path: hit.subsystem.path };\n}\n\n/**\n * The one move-specific refusal, on positive evidence only: git names\n * uncommitted work under a directory about to be renamed. Null (git will not\n * say) proceeds — a fleet that does not use git must not be refused over it.\n */\nasync function refuseUncommitted(docsDir: DocsDir, dirs: string[], json: boolean): Promise<boolean> {\n const dirty = await gitDirtyPaths(docsDir, dirs.map((d) => repoPath(docsDir, d)));\n if (dirty === null || dirty.length === 0) return false;\n fail(\n json,\n \"move-uncommitted\",\n // \"or untracked\", and `git stash -u` by name: the check fires on ANY\n // porcelain entry, untracked files included, and plain `git stash` does\n // not stash those — advice that said \"stash\" sent the user in a loop,\n // refused again with the same sentence after doing what it asked.\n `git reports ${dirty.length} uncommitted or untracked path(s) under the directory(ies) being moved: ${dirty.join(\", \")}. ` +\n \"Renaming now would sweep them into a move nobody reviewed — commit them, `git stash -u`, or remove them, then re-run. \" +\n \"Nothing was moved.\",\n );\n return true;\n}\n\n/**\n * Stage the move's whole delta, silently: the rename pairs (both ends — the\n * delete side and the add side) AND BOTH regenerated views files. They ride\n * along for the same reason the transaction writes them in the same commit: a\n * user whose next `git commit` captured the renames without them would commit\n * a tree `validate --all` fails (`subsystem.views-stale`) — the exact \"failing\n * between two commits\" state the roadmap forbids.\n *\n * A generated file that is neither ON DISK nor REMOVED BY THIS COMMIT is left\n * out of the pathspec, and that is a correctness rule rather than tidiness:\n * `gitStageRenames` runs one `git add -A -- <paths>`, and a pathspec matching\n * neither the worktree nor the index makes git refuse the WHOLE invocation\n * (silently — that write is failure-silent by contract) — so naming an absent\n * `architecture/services.likec4` (a fleet whose map draws no service renders\n * none) would take the other paths out of the index with it.\n *\n * THE `removed` ARM IS WHY `existsSync` ALONE IS NOT THE FILTER. A move can be\n * the commit that DELETES a generated file rather than rewriting it: a\n * committed `architecture/subsystems.likec4` standing over a tree that no\n * longer marks any subsystem (the marker went in an earlier commit) must not\n * exist, so the window renders null and the transaction removes it — and by the\n * time this runs the file is gone from disk. Measured 2026-09-07 on exactly that\n * fixture: under a disk-only filter the path never reached the pathspec, git\n * reported the deletion as ` D` (unstaged) while both rename ends went in as\n * `R `, and the user's next `git commit` captured the renames plus a generated\n * file the tree no longer licenses — `subsystem.views-stale` on a commit loam\n * itself produced, the exact state the paragraph above says this staging exists\n * to prevent. A path this commit deleted is still in the INDEX, so it matches\n * the pathspec and `-A` stages the deletion; the commit window's own\n * `ViewsActions` is what says which file that was, so no second guess about disk\n * is needed.\n *\n * TWO INVOCATIONS, not one, and the split is what makes the `removed` arm safe\n * to add: a generated file that was never committed and is deleted here is in\n * neither the worktree nor the index, and folded into one pathspec it would take\n * the renames down with it — the failure this function's first rule exists to\n * refuse. Staging in two calls costs one extra best-effort spawn and cannot\n * cost the renames. git records a rename from the index state at commit time,\n * never from which `git add` put the two ends there.\n */\nasync function stageInGit(docsDir: DocsDir, renames: { from: string; to: string }[], views: ViewsActions): Promise<void> {\n await gitStageRenames(docsDir, renames.flatMap((r) => [repoPath(docsDir, r.from), repoPath(docsDir, r.to)]));\n const generated = [\n [subsystemViewsPath(docsDir), views.subsystems] as const,\n [serviceViewsPath(docsDir), views.services] as const,\n ];\n await gitStageRenames(\n docsDir,\n generated\n .filter(([path, action]) => action === \"removed\" || existsSync(path))\n .map(([path]) => repoPath(docsDir, path)),\n );\n}\n\n/**\n * Which archived features hold a VERSION-2 snapshot addressing a path under\n * one of these directories — the notice, never a refusal, a move prints\n * before it proceeds. A version-2 manifest predates the tree and records\n * literal `services/<id>/…` paths, so after this move an `unarchive --force`\n * of that feature restores into the OLD path, resurrecting the pre-move\n * directory beside the moved one (two claimants, `subsystem.name-collision`\n * — loud, but avoidable by being told now). Version-3 manifests key rows by\n * service id and follow the enumeration, so they are not named. Every\n * failure to read reads as \"nothing to warn about\": a warning needs positive\n * evidence exactly as the move's one refusal does.\n */\nasync function v2SnapshotNotices(docsDir: DocsDir, dirs: string[]): Promise<string[]> {\n const rels = dirs.map((d) => repoPath(docsDir, d));\n const notices: string[] = [];\n let features: FeatureEntry[];\n try {\n features = await listFeatures(docsDir, { includeArchived: true });\n } catch {\n return notices;\n }\n for (const f of features.filter((entry) => entry.archived)) {\n let manifest: unknown;\n try {\n manifest = JSON.parse(await readFile(join(snapshotDir(f.dir), SNAPSHOT_MANIFEST), \"utf8\"));\n } catch {\n continue;\n }\n if (!isRecord(manifest) || manifest.version !== SNAPSHOT_COMPAT_VERSION || !Array.isArray(manifest.files)) continue;\n const addressed = rels.filter((rel) =>\n (manifest.files as unknown[]).some(\n (row) => isRecord(row) && typeof row.path === \"string\" && row.path.startsWith(`${rel}/`),\n ),\n );\n if (addressed.length > 0) {\n notices.push(\n `${f.id}'s archived snapshot is version 2 and addresses ${addressed.join(\", \")} by literal path — ` +\n `\\`loam unarchive ${f.id} --force\\` after this move would restore into the old location, ` +\n `resurrecting the pre-move directory beside the moved one.`,\n );\n }\n }\n return notices;\n}\n"]}
|