@deskwork/core 0.45.2 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/dist/config.d.ts +14 -4
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +43 -12
  4. package/dist/config.js.map +1 -1
  5. package/dist/content-index.d.ts +25 -0
  6. package/dist/content-index.d.ts.map +1 -1
  7. package/dist/content-index.js +142 -15
  8. package/dist/content-index.js.map +1 -1
  9. package/dist/doctor/index.d.ts +3 -0
  10. package/dist/doctor/index.d.ts.map +1 -1
  11. package/dist/doctor/index.js +3 -0
  12. package/dist/doctor/index.js.map +1 -1
  13. package/dist/doctor/legacy-config.d.ts +98 -0
  14. package/dist/doctor/legacy-config.d.ts.map +1 -0
  15. package/dist/doctor/legacy-config.js +174 -0
  16. package/dist/doctor/legacy-config.js.map +1 -0
  17. package/dist/doctor/project-scope-gate.d.ts +12 -21
  18. package/dist/doctor/project-scope-gate.d.ts.map +1 -1
  19. package/dist/doctor/project-scope-gate.js +13 -25
  20. package/dist/doctor/project-scope-gate.js.map +1 -1
  21. package/dist/doctor/repair.d.ts +11 -0
  22. package/dist/doctor/repair.d.ts.map +1 -1
  23. package/dist/doctor/repair.js +10 -89
  24. package/dist/doctor/repair.js.map +1 -1
  25. package/dist/doctor/rules/duplicate-id.d.ts +7 -2
  26. package/dist/doctor/rules/duplicate-id.d.ts.map +1 -1
  27. package/dist/doctor/rules/duplicate-id.js +8 -5
  28. package/dist/doctor/rules/duplicate-id.js.map +1 -1
  29. package/dist/doctor/rules/duplicate-id.ts +8 -5
  30. package/dist/doctor/rules/legacy-top-level-id-migration.d.ts.map +1 -1
  31. package/dist/doctor/rules/legacy-top-level-id-migration.js +11 -8
  32. package/dist/doctor/rules/legacy-top-level-id-migration.js.map +1 -1
  33. package/dist/doctor/rules/legacy-top-level-id-migration.ts +10 -11
  34. package/dist/doctor/rules/sites-to-lanes-migration.d.ts +39 -0
  35. package/dist/doctor/rules/sites-to-lanes-migration.d.ts.map +1 -0
  36. package/dist/doctor/rules/sites-to-lanes-migration.js +395 -0
  37. package/dist/doctor/rules/sites-to-lanes-migration.js.map +1 -0
  38. package/dist/doctor/rules/sites-to-lanes-migration.ts +449 -0
  39. package/dist/doctor/rules/workflow-stale.d.ts.map +1 -1
  40. package/dist/doctor/rules/workflow-stale.js +5 -2
  41. package/dist/doctor/rules/workflow-stale.js.map +1 -1
  42. package/dist/doctor/rules/workflow-stale.ts +5 -1
  43. package/dist/doctor/runner.d.ts +15 -1
  44. package/dist/doctor/runner.d.ts.map +1 -1
  45. package/dist/doctor/runner.js +48 -41
  46. package/dist/doctor/runner.js.map +1 -1
  47. package/dist/doctor/sites-migration-backfill.d.ts +71 -0
  48. package/dist/doctor/sites-migration-backfill.d.ts.map +1 -0
  49. package/dist/doctor/sites-migration-backfill.js +164 -0
  50. package/dist/doctor/sites-migration-backfill.js.map +1 -0
  51. package/dist/doctor/validate.d.ts.map +1 -1
  52. package/dist/doctor/validate.js +34 -73
  53. package/dist/doctor/validate.js.map +1 -1
  54. package/dist/entry/resolve-artifact.d.ts +80 -0
  55. package/dist/entry/resolve-artifact.d.ts.map +1 -0
  56. package/dist/entry/resolve-artifact.js +102 -0
  57. package/dist/entry/resolve-artifact.js.map +1 -0
  58. package/dist/entry/shortform-path.d.ts +34 -0
  59. package/dist/entry/shortform-path.d.ts.map +1 -0
  60. package/dist/entry/shortform-path.js +50 -0
  61. package/dist/entry/shortform-path.js.map +1 -0
  62. package/dist/index.d.ts +1 -2
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +1 -2
  65. package/dist/index.js.map +1 -1
  66. package/dist/iterate/iterate.d.ts.map +1 -1
  67. package/dist/iterate/iterate.js +21 -26
  68. package/dist/iterate/iterate.js.map +1 -1
  69. package/dist/lanes/bootstrap.d.ts +11 -7
  70. package/dist/lanes/bootstrap.d.ts.map +1 -1
  71. package/dist/lanes/bootstrap.js +42 -17
  72. package/dist/lanes/bootstrap.js.map +1 -1
  73. package/dist/lanes/index.d.ts +1 -0
  74. package/dist/lanes/index.d.ts.map +1 -1
  75. package/dist/lanes/index.js +6 -0
  76. package/dist/lanes/index.js.map +1 -1
  77. package/dist/lanes/loader.d.ts +33 -6
  78. package/dist/lanes/loader.d.ts.map +1 -1
  79. package/dist/lanes/loader.js +44 -11
  80. package/dist/lanes/loader.js.map +1 -1
  81. package/dist/lanes/operations/create.d.ts +11 -2
  82. package/dist/lanes/operations/create.d.ts.map +1 -1
  83. package/dist/lanes/operations/create.js +15 -4
  84. package/dist/lanes/operations/create.js.map +1 -1
  85. package/dist/lanes/operations/list.d.ts +3 -2
  86. package/dist/lanes/operations/list.d.ts.map +1 -1
  87. package/dist/lanes/operations/list.js +3 -2
  88. package/dist/lanes/operations/list.js.map +1 -1
  89. package/dist/lanes/operations/move.d.ts +29 -25
  90. package/dist/lanes/operations/move.d.ts.map +1 -1
  91. package/dist/lanes/operations/move.js +45 -242
  92. package/dist/lanes/operations/move.js.map +1 -1
  93. package/dist/lanes/operations/update.d.ts +15 -5
  94. package/dist/lanes/operations/update.d.ts.map +1 -1
  95. package/dist/lanes/operations/update.js +32 -12
  96. package/dist/lanes/operations/update.js.map +1 -1
  97. package/dist/lanes/scaffold-path.d.ts +88 -0
  98. package/dist/lanes/scaffold-path.d.ts.map +1 -0
  99. package/dist/lanes/scaffold-path.js +122 -0
  100. package/dist/lanes/scaffold-path.js.map +1 -0
  101. package/dist/lanes/types.d.ts +54 -31
  102. package/dist/lanes/types.d.ts.map +1 -1
  103. package/dist/lanes/types.js +60 -21
  104. package/dist/lanes/types.js.map +1 -1
  105. package/dist/outline-split.d.ts +2 -3
  106. package/dist/outline-split.d.ts.map +1 -1
  107. package/dist/outline-split.js +2 -3
  108. package/dist/outline-split.js.map +1 -1
  109. package/dist/paths.d.ts +14 -87
  110. package/dist/paths.d.ts.map +1 -1
  111. package/dist/paths.js +21 -131
  112. package/dist/paths.js.map +1 -1
  113. package/dist/remark-strip-outline.mjs +2 -3
  114. package/dist/rename-slug.d.ts +3 -3
  115. package/dist/rename-slug.d.ts.map +1 -1
  116. package/dist/rename-slug.js +137 -44
  117. package/dist/rename-slug.js.map +1 -1
  118. package/dist/review/handlers.d.ts +3 -3
  119. package/dist/review/handlers.d.ts.map +1 -1
  120. package/dist/review/handlers.js +18 -14
  121. package/dist/review/handlers.js.map +1 -1
  122. package/dist/review/report.d.ts +13 -2
  123. package/dist/review/report.d.ts.map +1 -1
  124. package/dist/review/report.js +48 -18
  125. package/dist/review/report.js.map +1 -1
  126. package/dist/review/start-handlers.d.ts +4 -4
  127. package/dist/review/start-handlers.d.ts.map +1 -1
  128. package/dist/review/start-handlers.js +25 -25
  129. package/dist/review/start-handlers.js.map +1 -1
  130. package/dist/review/workflow-paths.d.ts +24 -26
  131. package/dist/review/workflow-paths.d.ts.map +1 -1
  132. package/dist/review/workflow-paths.js +70 -60
  133. package/dist/review/workflow-paths.js.map +1 -1
  134. package/dist/schema/draft-annotation.d.ts +8 -8
  135. package/dist/schema/entry.d.ts +6 -6
  136. package/dist/schema/journal-events.d.ts +56 -42
  137. package/dist/schema/journal-events.d.ts.map +1 -1
  138. package/dist/schema/journal-events.js +23 -9
  139. package/dist/schema/journal-events.js.map +1 -1
  140. package/dist/sidecar/read.d.ts +8 -0
  141. package/dist/sidecar/read.d.ts.map +1 -1
  142. package/dist/sidecar/read.js +36 -9
  143. package/dist/sidecar/read.js.map +1 -1
  144. package/dist/sidecar/write.d.ts +14 -0
  145. package/dist/sidecar/write.d.ts.map +1 -1
  146. package/dist/sidecar/write.js +25 -0
  147. package/dist/sidecar/write.js.map +1 -1
  148. package/package.json +9 -9
  149. package/dist/body-state.d.ts +0 -27
  150. package/dist/body-state.d.ts.map +0 -1
  151. package/dist/body-state.js +0 -62
  152. package/dist/body-state.js.map +0 -1
  153. package/dist/scaffold.d.ts +0 -67
  154. package/dist/scaffold.d.ts.map +0 -1
  155. package/dist/scaffold.js +0 -122
  156. package/dist/scaffold.js.map +0 -1
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * lane move — relocate an entry from one lane to another.
3
3
  *
4
- * Phase 6 Task 6.1 (graphical-entries). The move:
4
+ * Phase 6 Task 6.1 (graphical-entries); reshaped by Phase 39
5
+ * (sites→lanes retirement). The move:
5
6
  *
6
7
  * 1. Resolves the entry's current lane via the sidecar's `lane`
7
8
  * field. Migration-window default: an entry without a `lane`
@@ -9,11 +10,10 @@
9
10
  * the doctor's lane-back-fill default). The move is refused
10
11
  * when the source lane and target lane are the same. Per
11
12
  * AUDIT-20260530-58, when the resolved source lane config does
12
- * NOT exist on disk the function refuses with an
13
- * entry-named error (no `lane` field → instruct operator to
14
- * run `/deskwork:doctor`; explicit `lane` field → name the
15
- * missing lane id). No silent fallback to the project's
16
- * contentDir — the migration gap is surfaced.
13
+ * NOT exist on disk the function refuses with an entry-named
14
+ * error (no `lane` field → instruct operator to run
15
+ * `/deskwork:doctor`; explicit `lane` field → name the missing
16
+ * lane id).
17
17
  *
18
18
  * 2. Resolves the target lane's pipeline template. The target
19
19
  * stage MUST be in the union of `linearStages ∪
@@ -21,119 +21,38 @@
21
21
  * omits `targetStage`, the move defaults to the target
22
22
  * template's FIRST `linearStages` entry.
23
23
  *
24
- * 3. Relocates the artifact file at
25
- * `<sourceContentDir>/<artifactPath>` to
26
- * `<targetContentDir>/<artifactPath>` (same relative path under
27
- * the lane's contentDir). When the source file does not exist,
28
- * the move is refused — the operator must repair the binding
29
- * before relocating.
30
- *
31
- * 4. Relocates the per-entry scrapbook directory at
32
- * `<sourceContentDir>/<slug>/scrapbook/` (when present) to the
33
- * target lane's parallel location. A missing scrapbook
34
- * directory is normal; the move proceeds.
35
- *
36
- * 5. Rewrites the sidecar with `lane = target`, `currentStage =
24
+ * 3. Rewrites the sidecar with `lane = target`, `currentStage =
37
25
  * targetStage`. Per the PRD's open-question default,
38
26
  * `iterationByStage` is preserved verbatim — no stage-name
39
27
  * remapping. Old keys from the prior lane template become dead
40
28
  * entries that cause no harm (iterate uses `?? 0`).
41
29
  *
42
- * 6. Emits a `lane-move` journal event identifying source / target
43
- * lanes, source / target stages, and the artifact paths.
30
+ * 4. Emits a `lane-move` journal event identifying source / target
31
+ * lanes, source / target stages, and the (unchanged) artifact
32
+ * path for audit.
33
+ *
34
+ * **Phase 39: the move is a METADATA change only.** A lane carries no
35
+ * `contentDir` — location is a property of the ENTRY
36
+ * (`entry.artifactPath`, resolved against the project root), never the
37
+ * lane. So moving an entry between lanes does NOT relocate its artifact
38
+ * file or its per-entry scrapbook; both stay exactly where they are.
39
+ * The lane "spans" whatever directories its entries happen to live in,
40
+ * emergent from the entries. This dissolves the former
41
+ * source/target-`contentDir` relocation (and its cross-device fallback
42
+ * + rollback): there is nothing on disk to move.
44
43
  *
45
- * The function uses `renameSync` for the artifact relocation
46
- * (atomic on the same filesystem); when `renameSync` fails with
47
- * `EXDEV` (cross-device) the fallback is a copy + delete loop so the
48
- * move survives a contentDir that points at a separate mount.
44
+ * The source artifact must still EXIST on disk (when the entry carries
45
+ * an `artifactPath`) moving an entry whose artifact is missing is
46
+ * refused so the operator repairs the binding first.
49
47
  */
50
- import { copyFileSync, cpSync, existsSync, mkdirSync, renameSync, rmSync, unlinkSync, } from 'node:fs';
51
- import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
48
+ import { existsSync } from 'node:fs';
52
49
  import { appendJournalEvent } from "../../journal/append.js";
53
50
  import { writeSidecar } from "../../sidecar/write.js";
54
51
  import { readSidecar } from "../../sidecar/read.js";
55
52
  import { loadPipelineTemplate } from "../../pipelines/loader.js";
53
+ import { resolveStoredArtifactPath } from "../../entry/resolve-artifact.js";
56
54
  import { loadLaneConfig } from "../loader.js";
57
55
  const DEFAULT_LANE_ID = 'default';
58
- /**
59
- * Resolve `<contentDir>` to an absolute path. Lane configs may
60
- * declare `contentDir` as either absolute (taken verbatim) or
61
- * relative (resolved against the project root).
62
- */
63
- function resolveContentDirAbs(projectRoot, contentDir) {
64
- return isAbsolute(contentDir) ? contentDir : resolve(projectRoot, contentDir);
65
- }
66
- /**
67
- * AUDIT-20260530-64 boundary check. Refuses any resolved filesystem
68
- * path that escapes its containing lane contentDir. Used at four
69
- * sites in `moveEntryToLane`: source artifact, target artifact,
70
- * source scrapbook, target scrapbook. Each site joins a contentDir
71
- * with an entry-controlled relative segment (`sidecar.artifactPath`
72
- * or `sidecar.slug`) — without this check, a malformed sidecar with
73
- * `artifactPath: "../outside.md"` or `slug: "../escape"` makes the
74
- * move read + write files outside the lane content tree.
75
- *
76
- * The check uses `path.relative(contentDir, resolvedPath)` and
77
- * refuses any relative path that starts with `..` (or is itself
78
- * `..`) or is absolute. This is the canonical Node pattern for
79
- * "is X inside Y" — works on both POSIX and Windows because the
80
- * underlying `path.resolve` and `path.relative` are platform-aware.
81
- */
82
- function assertPathInsideContentDir(args) {
83
- const { resolvedPath, contentDirAbs, entrySlug, boundary, side } = args;
84
- const rel = relative(contentDirAbs, resolvedPath);
85
- const escapes = rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel);
86
- if (escapes) {
87
- throw new Error(`cannot move entry '${entrySlug}': ${side} ${boundary} resolves to `
88
- + `'${resolvedPath}', which escapes the ${side} lane contentDir `
89
- + `'${contentDirAbs}'. Refusing the move — repair the sidecar `
90
- + `(remove '..' segments / absolute paths from the offending field) `
91
- + `before retrying.`);
92
- }
93
- }
94
- /**
95
- * Type guard for the subset of Node ErrnoException we care about
96
- * (just the `code` string). Keeps the cross-device fallback path
97
- * type-safe without an unchecked `as NodeJS.ErrnoException`.
98
- */
99
- function isErrnoCode(err, expected) {
100
- if (err === null || typeof err !== 'object')
101
- return false;
102
- const maybe = err.code;
103
- return typeof maybe === 'string' && maybe === expected;
104
- }
105
- /**
106
- * Move a path with renameSync, falling back to a caller-supplied
107
- * cross-device strategy on EXDEV. The fallback is responsible for
108
- * both creating the destination and removing the source — the
109
- * helper does not split copy/delete across calls.
110
- *
111
- * The parent directory of `dst` is mkdir'd on every call so callers
112
- * don't have to thread that detail.
113
- */
114
- function tryRenameWithFallback(src, dst, exdevFallback) {
115
- mkdirSync(dirname(dst), { recursive: true });
116
- try {
117
- renameSync(src, dst);
118
- }
119
- catch (err) {
120
- if (!isErrnoCode(err, 'EXDEV'))
121
- throw err;
122
- exdevFallback(src, dst);
123
- }
124
- }
125
- function moveFile(src, dst) {
126
- tryRenameWithFallback(src, dst, (s, d) => {
127
- copyFileSync(s, d);
128
- unlinkSync(s);
129
- });
130
- }
131
- function moveDir(src, dst) {
132
- tryRenameWithFallback(src, dst, (s, d) => {
133
- cpSync(s, d, { recursive: true });
134
- rmSync(s, { recursive: true, force: true });
135
- });
136
- }
137
56
  export async function moveEntryToLane(projectRoot, opts) {
138
57
  const sidecar = await readSidecar(projectRoot, opts.uuid);
139
58
  const sidecarHadLane = sidecar.lane !== undefined;
@@ -154,13 +73,8 @@ export async function moveEntryToLane(projectRoot, opts) {
154
73
  // migration-window state); the error names the entry slug
155
74
  // and directs the operator to `/deskwork:doctor` to back-fill
156
75
  // lane assignments before retrying the move.
157
- //
158
- // Per the project no-fallback rule, the function does NOT silently
159
- // substitute the project's contentDir for the missing lane — that
160
- // would hide the migration gap and produce wrong-place file moves.
161
- let sourceLane;
162
76
  try {
163
- sourceLane = loadLaneConfig(sourceLaneId, projectRoot);
77
+ loadLaneConfig(sourceLaneId, projectRoot);
164
78
  }
165
79
  catch (err) {
166
80
  const detail = err instanceof Error ? err.message : String(err);
@@ -200,98 +114,16 @@ export async function moveEntryToLane(projectRoot, opts) {
200
114
  + `not in target lane "${opts.toLane}" template "${targetTemplate.id}". `
201
115
  + `Allowed stages: ${[...allowed].join(', ')}.`);
202
116
  }
203
- const sourceContentDir = resolveContentDirAbs(projectRoot, sourceLane.contentDir);
204
- const targetContentDir = resolveContentDirAbs(projectRoot, targetLane.contentDir);
205
- // Relocate the artifact file. When `artifactPath` is set on the
206
- // sidecar, the source file is at `<sourceContentDir>/<artifactPath>`;
207
- // we move it to `<targetContentDir>/<artifactPath>` (same relative
208
- // shape under the new contentDir).
209
- //
210
- // Per AUDIT-20260530-64, BOTH the source and the target resolved
211
- // paths are checked against their respective contentDirs before any
212
- // filesystem operation runs. The schema-level refinement on
213
- // `artifactPath` blocks the canonical attack shapes upstream, but
214
- // the boundary check stays as defense-in-depth — the field could be
215
- // populated by a non-deskwork process, or a future code path could
216
- // bypass the schema. The check uses `path.resolve` (via
217
- // `assertPathInsideContentDir`) so even a normalised-looking
218
- // relative path that traverses out is caught.
219
- let fromArtifactAbs;
220
- let toArtifactAbs;
221
- if (sidecar.artifactPath !== undefined) {
222
- fromArtifactAbs = resolve(sourceContentDir, sidecar.artifactPath);
223
- toArtifactAbs = resolve(targetContentDir, sidecar.artifactPath);
224
- assertPathInsideContentDir({
225
- resolvedPath: fromArtifactAbs,
226
- contentDirAbs: sourceContentDir,
227
- entrySlug: sidecar.slug,
228
- boundary: 'artifactPath',
229
- side: 'source',
230
- });
231
- assertPathInsideContentDir({
232
- resolvedPath: toArtifactAbs,
233
- contentDirAbs: targetContentDir,
234
- entrySlug: sidecar.slug,
235
- boundary: 'artifactPath',
236
- side: 'target',
237
- });
238
- if (!existsSync(fromArtifactAbs)) {
239
- throw new Error(`Cannot move entry ${sidecar.slug}: source artifact does not exist at `
240
- + `${fromArtifactAbs}. Repair the binding (e.g. via "deskwork doctor") `
241
- + `before moving.`);
242
- }
243
- if (existsSync(toArtifactAbs)) {
244
- throw new Error(`Cannot move entry ${sidecar.slug}: target artifact already exists at `
245
- + `${toArtifactAbs}. The target lane already holds a file at the same `
246
- + `relative path; resolve the collision (rename / move / remove) before `
247
- + `running lane move.`);
248
- }
249
- }
250
- const sourceScrapbookDir = resolve(sourceContentDir, sidecar.slug, 'scrapbook');
251
- const targetScrapbookDir = resolve(targetContentDir, sidecar.slug, 'scrapbook');
252
- // Per AUDIT-20260530-64, the per-entry scrapbook path is built from
253
- // `sidecar.slug` — also entry-controlled. EntrySchema's `slug` is
254
- // an unconstrained `z.string().min(1)` for back-compat, so a slug
255
- // like `../escape` parses cleanly. Refuse here for the same reason
256
- // as the artifact branch above; check both sides.
257
- assertPathInsideContentDir({
258
- resolvedPath: sourceScrapbookDir,
259
- contentDirAbs: sourceContentDir,
260
- entrySlug: sidecar.slug,
261
- boundary: 'slug-derived scrapbook',
262
- side: 'source',
263
- });
264
- assertPathInsideContentDir({
265
- resolvedPath: targetScrapbookDir,
266
- contentDirAbs: targetContentDir,
267
- entrySlug: sidecar.slug,
268
- boundary: 'slug-derived scrapbook',
269
- side: 'target',
270
- });
271
- // Track which filesystem operations succeeded so the catch below
272
- // can reverse them on a later failure (e.g. writeSidecar throwing
273
- // after the artifact + scrapbook are already in the target lane).
274
- let artifactMoved = false;
275
- let scrapbookMoved = false;
276
- if (fromArtifactAbs !== undefined && toArtifactAbs !== undefined) {
277
- moveFile(fromArtifactAbs, toArtifactAbs);
278
- artifactMoved = true;
279
- }
280
- // Relocate the per-entry scrapbook directory when present. Lives at
281
- // `<contentDir>/<slug>/scrapbook/` per the slug-template convention
282
- // — see packages/core/src/scrapbook/paths.ts (_scrapbookDirSlug).
283
- if (existsSync(sourceScrapbookDir)) {
284
- if (existsSync(targetScrapbookDir)) {
285
- // Rollback the artifact relocation so the operator's state is
286
- // consistent before re-running.
287
- if (artifactMoved && fromArtifactAbs !== undefined && toArtifactAbs !== undefined) {
288
- moveFile(toArtifactAbs, fromArtifactAbs);
289
- }
290
- throw new Error(`Cannot move entry ${sidecar.slug}: target scrapbook directory already `
291
- + `exists at ${targetScrapbookDir}. Resolve the collision before moving.`);
292
- }
293
- moveDir(sourceScrapbookDir, targetScrapbookDir);
294
- scrapbookMoved = true;
117
+ // Resolve the entry's artifact from its STORED `artifactPath` only
118
+ // (Phase 39 — location is an ENTRY property, resolved against the
119
+ // project root, never a lane dir). The move does NOT relocate the
120
+ // file; it stays put. We still require the file to EXIST so the
121
+ // operator repairs a broken binding before moving lanes.
122
+ const artifactAbs = resolveStoredArtifactPath(sidecar, projectRoot);
123
+ if (artifactAbs !== null && !existsSync(artifactAbs)) {
124
+ throw new Error(`Cannot move entry ${sidecar.slug}: source artifact does not exist at `
125
+ + `${artifactAbs}. Repair the binding (e.g. via "deskwork doctor") `
126
+ + `before moving.`);
295
127
  }
296
128
  const at = new Date().toISOString();
297
129
  const fromStage = sidecar.currentStage;
@@ -301,48 +133,17 @@ export async function moveEntryToLane(projectRoot, opts) {
301
133
  currentStage: targetStage,
302
134
  updatedAt: at,
303
135
  };
304
- // Wrap the sidecar write in a rollback. If the sidecar write throws
305
- // AFTER the artifact + scrapbook have been moved, the entry is
306
- // half-moved: filesystem says "target lane" but sidecar still says
307
- // "source lane". Reverse the successful filesystem moves before
308
- // re-throwing so the operator's state is consistent.
309
- try {
310
- await writeSidecar(projectRoot, updated);
311
- }
312
- catch (err) {
313
- try {
314
- if (scrapbookMoved) {
315
- moveDir(targetScrapbookDir, sourceScrapbookDir);
316
- }
317
- if (artifactMoved
318
- && fromArtifactAbs !== undefined
319
- && toArtifactAbs !== undefined) {
320
- moveFile(toArtifactAbs, fromArtifactAbs);
321
- }
322
- }
323
- catch (rollbackErr) {
324
- const rollbackDetail = rollbackErr instanceof Error
325
- ? rollbackErr.message
326
- : String(rollbackErr);
327
- const writeDetail = err instanceof Error ? err.message : String(err);
328
- throw new Error(`Failed to move entry ${sidecar.slug}: sidecar write failed `
329
- + `(${writeDetail}) AND rollback of filesystem moves failed `
330
- + `(${rollbackDetail}). Operator intervention required.`);
331
- }
332
- const detail = err instanceof Error ? err.message : String(err);
333
- throw new Error(`Failed to move entry ${sidecar.slug}: sidecar write failed `
334
- + `(${detail}); filesystem moves rolled back.`);
335
- }
136
+ await writeSidecar(projectRoot, updated);
336
137
  const moveDetails = {
337
138
  fromLane: sourceLaneId,
338
139
  toLane: opts.toLane,
339
140
  fromStage,
340
141
  toStage: targetStage,
341
142
  };
342
- if (fromArtifactAbs !== undefined)
343
- moveDetails.fromArtifactPath = fromArtifactAbs;
344
- if (toArtifactAbs !== undefined)
345
- moveDetails.toArtifactPath = toArtifactAbs;
143
+ if (artifactAbs !== null) {
144
+ moveDetails.fromArtifactPath = artifactAbs;
145
+ moveDetails.toArtifactPath = artifactAbs;
146
+ }
346
147
  await appendJournalEvent(projectRoot, {
347
148
  kind: 'lane-move',
348
149
  at,
@@ -355,8 +156,10 @@ export async function moveEntryToLane(projectRoot, opts) {
355
156
  toLane: opts.toLane,
356
157
  fromStage,
357
158
  toStage: targetStage,
358
- ...(fromArtifactAbs !== undefined && { fromArtifactPath: fromArtifactAbs }),
359
- ...(toArtifactAbs !== undefined && { toArtifactPath: toArtifactAbs }),
159
+ ...(artifactAbs !== null && {
160
+ fromArtifactPath: artifactAbs,
161
+ toArtifactPath: artifactAbs,
162
+ }),
360
163
  };
361
164
  return result;
362
165
  }
@@ -1 +1 @@
1
- {"version":3,"file":"move.js","sourceRoot":"","sources":["../../../src/lanes/operations/move.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"move.js","sourceRoot":"","sources":["../../../src/lanes/operations/move.ts"],"names":[],"mappings":"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"}
@@ -2,9 +2,12 @@
2
2
  * lane update — mutate a subset of fields on an existing lane config.
3
3
  *
4
4
  * Phase 6 Task 6.1 (graphical-entries). Accepts optional patches for
5
- * `name`, `pipelineTemplate`, and `contentDir`. The lane's `id`
6
- * cannot change (it's the filename). The `archivedAt` field is owned
7
- * by `archive` / `restore` and is not mutable through `update`.
5
+ * `name`, `pipelineTemplate`, `scaffoldDefaults`, and `host`. The
6
+ * lane's `id` cannot change (it's the filename). The `archivedAt`
7
+ * field is owned by `archive` / `restore` and is not mutable through
8
+ * `update`. Per Phase 39 (sites→lanes retirement) a lane carries no
9
+ * `contentDir` — the former `--content-dir` patch is replaced by
10
+ * `scaffoldDefaults` (add-time directories, keyed by artifact kind).
8
11
  *
9
12
  * Cross-validation:
10
13
  * - If `pipelineTemplate` is patched, the new template MUST resolve
@@ -19,12 +22,19 @@
19
22
  *
20
23
  * Emits a `lane-update` journal event on success.
21
24
  */
22
- import { type LaneConfig } from '../types.ts';
25
+ import { type ArtifactKind, type LaneConfig } from '../types.ts';
23
26
  export interface UpdateLaneOptions {
24
27
  readonly id: string;
25
28
  readonly name?: string;
26
29
  readonly pipelineTemplate?: string;
27
- readonly contentDir?: string;
30
+ /**
31
+ * Replacement scaffold-default directories, keyed by artifact kind.
32
+ * Replaces the whole `scaffoldDefaults` map when supplied (a lane
33
+ * carries no `contentDir` — Phase 39 sites→lanes retirement).
34
+ */
35
+ readonly scaffoldDefaults?: Partial<Record<ArtifactKind, string>>;
36
+ /** Replacement website host. */
37
+ readonly host?: string;
28
38
  }
29
39
  export interface UpdateLaneResult {
30
40
  readonly lane: LaneConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/lanes/operations/update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C;AAED,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAiE3B"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/lanes/operations/update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAGjE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAClE,gCAAgC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C;AAED,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAiF3B"}
@@ -2,9 +2,12 @@
2
2
  * lane update — mutate a subset of fields on an existing lane config.
3
3
  *
4
4
  * Phase 6 Task 6.1 (graphical-entries). Accepts optional patches for
5
- * `name`, `pipelineTemplate`, and `contentDir`. The lane's `id`
6
- * cannot change (it's the filename). The `archivedAt` field is owned
7
- * by `archive` / `restore` and is not mutable through `update`.
5
+ * `name`, `pipelineTemplate`, `scaffoldDefaults`, and `host`. The
6
+ * lane's `id` cannot change (it's the filename). The `archivedAt`
7
+ * field is owned by `archive` / `restore` and is not mutable through
8
+ * `update`. Per Phase 39 (sites→lanes retirement) a lane carries no
9
+ * `contentDir` — the former `--content-dir` patch is replaced by
10
+ * `scaffoldDefaults` (add-time directories, keyed by artifact kind).
8
11
  *
9
12
  * Cross-validation:
10
13
  * - If `pipelineTemplate` is patched, the new template MUST resolve
@@ -21,7 +24,7 @@
21
24
  */
22
25
  import { appendJournalEvent } from "../../journal/append.js";
23
26
  import { loadPipelineTemplate } from "../../pipelines/loader.js";
24
- import { assertSafeContentDir, loadLaneConfig } from "../loader.js";
27
+ import { assertSafeScaffoldDir, loadLaneConfig } from "../loader.js";
25
28
  import { commitLaneConfig } from "./commit.js";
26
29
  export async function updateLane(projectRoot, opts) {
27
30
  const existing = loadLaneConfig(opts.id, projectRoot);
@@ -31,24 +34,31 @@ export async function updateLane(projectRoot, opts) {
31
34
  if (opts.pipelineTemplate !== undefined) {
32
35
  patches['pipelineTemplate'] = opts.pipelineTemplate;
33
36
  }
34
- if (opts.contentDir !== undefined) {
35
- assertSafeContentDir(projectRoot, opts.contentDir);
36
- patches['contentDir'] = opts.contentDir;
37
+ if (opts.scaffoldDefaults !== undefined) {
38
+ for (const dir of Object.values(opts.scaffoldDefaults)) {
39
+ if (dir !== undefined)
40
+ assertSafeScaffoldDir(projectRoot, dir);
41
+ }
42
+ patches['scaffoldDefaults'] = opts.scaffoldDefaults;
43
+ }
44
+ if (opts.host !== undefined) {
45
+ patches['host'] = opts.host;
37
46
  }
38
47
  const changedFields = Object.keys(patches);
39
48
  if (changedFields.length === 0) {
40
49
  throw new Error(`Cannot update lane "${opts.id}": no patch fields supplied. `
41
- + `Pass at least one of --name, --template, --content-dir.`);
50
+ + `Pass at least one of --name, --template, --scaffold-default, --host.`);
42
51
  }
43
52
  // Cross-validate the patched pipeline template up front so we don't
44
53
  // half-write a broken lane.
45
- if (patches['pipelineTemplate'] !== undefined) {
54
+ const patchedTemplate = patches['pipelineTemplate'];
55
+ if (typeof patchedTemplate === 'string') {
46
56
  try {
47
- loadPipelineTemplate(patches['pipelineTemplate'], projectRoot);
57
+ loadPipelineTemplate(patchedTemplate, projectRoot);
48
58
  }
49
59
  catch (err) {
50
60
  const detail = err instanceof Error ? err.message : String(err);
51
- throw new Error(`Cannot update lane "${opts.id}": pipelineTemplate "${patches['pipelineTemplate']}" `
61
+ throw new Error(`Cannot update lane "${opts.id}": pipelineTemplate "${patchedTemplate}" `
52
62
  + `does not resolve:\n${detail}`);
53
63
  }
54
64
  }
@@ -62,9 +72,19 @@ export async function updateLane(projectRoot, opts) {
62
72
  before[field] = Reflect.get(existing, field);
63
73
  after[field] = patches[field];
64
74
  }
75
+ // Build the patched lane from typed options rather than spreading the
76
+ // `unknown`-valued `patches` bag, so the merge stays type-safe (no
77
+ // `as` cast). `commitLaneConfig` re-validates the result via Zod.
65
78
  const updated = {
66
79
  ...existing,
67
- ...patches,
80
+ ...(opts.name !== undefined && { name: opts.name }),
81
+ ...(opts.pipelineTemplate !== undefined && {
82
+ pipelineTemplate: opts.pipelineTemplate,
83
+ }),
84
+ ...(opts.scaffoldDefaults !== undefined && {
85
+ scaffoldDefaults: opts.scaffoldDefaults,
86
+ }),
87
+ ...(opts.host !== undefined && { host: opts.host }),
68
88
  };
69
89
  const { lane, path } = commitLaneConfig(projectRoot, opts.id, updated, 'update');
70
90
  await appendJournalEvent(projectRoot, {
@@ -1 +1 @@
1
- {"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/lanes/operations/update.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/lanes/operations/update.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * add-time artifactPath composition (Phase 39c-2b, sub-task b).
3
+ *
4
+ * `deskwork add --lane X [--kind markdown] [--layout L]` creates a NEW
5
+ * entry that has no `artifactPath` yet. This module composes that path
6
+ * from the lane's add-time `scaffoldDefaults` (the directory), the
7
+ * requested layout (the on-disk file shape), and the entry's slug, then
8
+ * the caller stamps the result onto the new entry's sidecar — from which
9
+ * point it is authoritative (resolution never recomputes it).
10
+ *
11
+ * MARKDOWN ONLY (operator decision): `deskwork add` supports only
12
+ * markdown entries right now — deskwork has no non-markdown materializer,
13
+ * so a non-markdown entry can't be created. {@link composeAddArtifactPath} therefore throws loudly
14
+ * for any non-markdown kind rather than composing a path that nothing
15
+ * can fulfill. The `ArtifactKind` TYPE still carries the other kinds for
16
+ * graphical-entries; only the add path gates to markdown.
17
+ *
18
+ * Per the sites→lanes retirement design (§ "add-time path composition"):
19
+ *
20
+ * directory ← lane.scaffoldDefaults['markdown'] (FAILS LOUDLY if absent)
21
+ * layout ← --layout flag, else DEFAULT_SCAFFOLD_LAYOUT (index)
22
+ * relativePath ← layoutToContentRelativePath(layout, slug)
23
+ * artifactPath ← posixJoin(directory, relativePath)
24
+ *
25
+ * POSIX join (AUDIT-40): `artifactPath` is persisted and string-compared
26
+ * against the forward-slash paths the rest of the system stores, so the
27
+ * join uses `node:path/posix` (never `node:path.join`, which yields
28
+ * backslashes on Windows).
29
+ *
30
+ * No fallback: a lane that does not declare a `scaffoldDefaults['markdown']`
31
+ * entry is an actionable operator error, not a silent default directory
32
+ * (per the project no-fallbacks rule). The thrown message names the lane
33
+ * id and how to fix it.
34
+ */
35
+ import type { LaneConfig, ArtifactKind } from './types.ts';
36
+ /**
37
+ * On-disk filename shape of a scaffolded markdown artifact:
38
+ *
39
+ * - `index` → `<slug>/index.md` (hub-style directory)
40
+ * - `readme` → `<slug>/README.md` (editorial-private directory)
41
+ * - `flat` → `<slug>.md` (sibling file, no own directory)
42
+ */
43
+ export type ScaffoldLayout = 'index' | 'readme' | 'flat';
44
+ /**
45
+ * The default layout used by `deskwork add` when `--layout` is omitted.
46
+ * `index` reproduces the legacy `{slug}/index.md` template default
47
+ * byte-for-byte — chosen for zero-behavior-change at the sites→lanes
48
+ * cutover.
49
+ *
50
+ * Per design Decision #12 this is a single GLOBAL default. (The
51
+ * superseding per-kind Decision #16 was retired alongside the multi-kind
52
+ * machinery when `add` was gated to markdown-only — a global default is
53
+ * correct again now that only one kind is supported.)
54
+ */
55
+ export declare const DEFAULT_SCAFFOLD_LAYOUT: ScaffoldLayout;
56
+ /**
57
+ * Map a {@link ScaffoldLayout} + slug to the directory-relative path for
58
+ * a MARKDOWN artifact (the only kind `deskwork add` scaffolds).
59
+ */
60
+ export declare function layoutToContentRelativePath(layout: ScaffoldLayout, slug: string): string;
61
+ /**
62
+ * Compose the project-relative `artifactPath` for a NEW markdown entry
63
+ * being scaffolded into a lane.
64
+ *
65
+ * @param lane - The resolved lane config the entry belongs to.
66
+ * @param kind - The entry's artifact kind. ONLY `markdown` is supported;
67
+ * any other kind throws (see module docblock — file creation for
68
+ * non-markdown kinds is not implemented).
69
+ * @param slug - The entry's slug (one or more `/`-separated kebab-case
70
+ * segments).
71
+ * @param layout - The on-disk file shape. When omitted, defaults to
72
+ * {@link DEFAULT_SCAFFOLD_LAYOUT} (`index`).
73
+ * @returns The project-root-relative path to stamp onto the entry's
74
+ * sidecar (e.g. `src/content/blog/my-post/index.md`), joined with
75
+ * forward slashes (AUDIT-40).
76
+ * @throws When `kind` is not `markdown`; or when the lane declares no
77
+ * `scaffoldDefaults['markdown']` entry.
78
+ */
79
+ export declare function composeAddArtifactPath(lane: LaneConfig, kind: ArtifactKind, slug: string, layout?: ScaffoldLayout): string;
80
+ /**
81
+ * Narrow a raw `--layout` flag value to a {@link ScaffoldLayout}.
82
+ * Returns `undefined` for unrecognized values so the CLI caller can
83
+ * raise its own argument-shaped error (with the legal list and exit 2).
84
+ */
85
+ export declare function parseScaffoldLayout(value: string): ScaffoldLayout | undefined;
86
+ /** The legal `--layout` values, for error messages. */
87
+ export declare const SCAFFOLD_LAYOUTS: readonly ScaffoldLayout[];
88
+ //# sourceMappingURL=scaffold-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold-path.d.ts","sourceRoot":"","sources":["../../src/lanes/scaffold-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAAwB,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,GACX,MAAM,CASR;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,cAAc,GACtB,MAAM,CA4BR;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAK7E;AAED,uDAAuD;AACvD,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAIrD,CAAC"}