@deskwork/core 0.9.5

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 (188) hide show
  1. package/dist/body-state.d.ts +27 -0
  2. package/dist/body-state.d.ts.map +1 -0
  3. package/dist/body-state.js +62 -0
  4. package/dist/body-state.js.map +1 -0
  5. package/dist/calendar-mutations.d.ts +124 -0
  6. package/dist/calendar-mutations.d.ts.map +1 -0
  7. package/dist/calendar-mutations.js +305 -0
  8. package/dist/calendar-mutations.js.map +1 -0
  9. package/dist/calendar.d.ts +54 -0
  10. package/dist/calendar.d.ts.map +1 -0
  11. package/dist/calendar.js +430 -0
  12. package/dist/calendar.js.map +1 -0
  13. package/dist/cli.d.ts +38 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +72 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/config.d.ts +91 -0
  18. package/dist/config.d.ts.map +1 -0
  19. package/dist/config.js +216 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/content-index.d.ts +74 -0
  22. package/dist/content-index.d.ts.map +1 -0
  23. package/dist/content-index.js +205 -0
  24. package/dist/content-index.js.map +1 -0
  25. package/dist/content-tree-fs-walk.d.ts +54 -0
  26. package/dist/content-tree-fs-walk.d.ts.map +1 -0
  27. package/dist/content-tree-fs-walk.js +112 -0
  28. package/dist/content-tree-fs-walk.js.map +1 -0
  29. package/dist/content-tree-helpers.d.ts +52 -0
  30. package/dist/content-tree-helpers.d.ts.map +1 -0
  31. package/dist/content-tree-helpers.js +116 -0
  32. package/dist/content-tree-helpers.js.map +1 -0
  33. package/dist/content-tree-types.d.ts +175 -0
  34. package/dist/content-tree-types.d.ts.map +1 -0
  35. package/dist/content-tree-types.js +10 -0
  36. package/dist/content-tree-types.js.map +1 -0
  37. package/dist/content-tree.d.ts +93 -0
  38. package/dist/content-tree.d.ts.map +1 -0
  39. package/dist/content-tree.js +385 -0
  40. package/dist/content-tree.js.map +1 -0
  41. package/dist/doctor/index.d.ts +11 -0
  42. package/dist/doctor/index.d.ts.map +1 -0
  43. package/dist/doctor/index.js +10 -0
  44. package/dist/doctor/index.js.map +1 -0
  45. package/dist/doctor/project-rules.d.ts +59 -0
  46. package/dist/doctor/project-rules.d.ts.map +1 -0
  47. package/dist/doctor/project-rules.js +143 -0
  48. package/dist/doctor/project-rules.js.map +1 -0
  49. package/dist/doctor/rules/calendar-uuid-missing.d.ts +19 -0
  50. package/dist/doctor/rules/calendar-uuid-missing.d.ts.map +1 -0
  51. package/dist/doctor/rules/calendar-uuid-missing.js +176 -0
  52. package/dist/doctor/rules/calendar-uuid-missing.js.map +1 -0
  53. package/dist/doctor/rules/duplicate-id.d.ts +27 -0
  54. package/dist/doctor/rules/duplicate-id.d.ts.map +1 -0
  55. package/dist/doctor/rules/duplicate-id.js +157 -0
  56. package/dist/doctor/rules/duplicate-id.js.map +1 -0
  57. package/dist/doctor/rules/legacy-top-level-id-migration.d.ts +40 -0
  58. package/dist/doctor/rules/legacy-top-level-id-migration.d.ts.map +1 -0
  59. package/dist/doctor/rules/legacy-top-level-id-migration.js +232 -0
  60. package/dist/doctor/rules/legacy-top-level-id-migration.js.map +1 -0
  61. package/dist/doctor/rules/missing-frontmatter-id.d.ts +45 -0
  62. package/dist/doctor/rules/missing-frontmatter-id.d.ts.map +1 -0
  63. package/dist/doctor/rules/missing-frontmatter-id.js +283 -0
  64. package/dist/doctor/rules/missing-frontmatter-id.js.map +1 -0
  65. package/dist/doctor/rules/orphan-frontmatter-id.d.ts +18 -0
  66. package/dist/doctor/rules/orphan-frontmatter-id.d.ts.map +1 -0
  67. package/dist/doctor/rules/orphan-frontmatter-id.js +154 -0
  68. package/dist/doctor/rules/orphan-frontmatter-id.js.map +1 -0
  69. package/dist/doctor/rules/schema-rejected.d.ts +20 -0
  70. package/dist/doctor/rules/schema-rejected.d.ts.map +1 -0
  71. package/dist/doctor/rules/schema-rejected.js +44 -0
  72. package/dist/doctor/rules/schema-rejected.js.map +1 -0
  73. package/dist/doctor/rules/slug-collision.d.ts +18 -0
  74. package/dist/doctor/rules/slug-collision.d.ts.map +1 -0
  75. package/dist/doctor/rules/slug-collision.js +65 -0
  76. package/dist/doctor/rules/slug-collision.js.map +1 -0
  77. package/dist/doctor/rules/workflow-stale.d.ts +20 -0
  78. package/dist/doctor/rules/workflow-stale.d.ts.map +1 -0
  79. package/dist/doctor/rules/workflow-stale.js +136 -0
  80. package/dist/doctor/rules/workflow-stale.js.map +1 -0
  81. package/dist/doctor/runner.d.ts +75 -0
  82. package/dist/doctor/runner.d.ts.map +1 -0
  83. package/dist/doctor/runner.js +289 -0
  84. package/dist/doctor/runner.js.map +1 -0
  85. package/dist/doctor/schema-patch.d.ts +21 -0
  86. package/dist/doctor/schema-patch.d.ts.map +1 -0
  87. package/dist/doctor/schema-patch.js +92 -0
  88. package/dist/doctor/schema-patch.js.map +1 -0
  89. package/dist/doctor/types.d.ts +185 -0
  90. package/dist/doctor/types.d.ts.map +1 -0
  91. package/dist/doctor/types.js +13 -0
  92. package/dist/doctor/types.js.map +1 -0
  93. package/dist/frontmatter.d.ts +103 -0
  94. package/dist/frontmatter.d.ts.map +1 -0
  95. package/dist/frontmatter.js +306 -0
  96. package/dist/frontmatter.js.map +1 -0
  97. package/dist/index.d.ts +27 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +27 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/ingest-derive.d.ts +79 -0
  102. package/dist/ingest-derive.d.ts.map +1 -0
  103. package/dist/ingest-derive.js +299 -0
  104. package/dist/ingest-derive.js.map +1 -0
  105. package/dist/ingest-paths.d.ts +37 -0
  106. package/dist/ingest-paths.d.ts.map +1 -0
  107. package/dist/ingest-paths.js +176 -0
  108. package/dist/ingest-paths.js.map +1 -0
  109. package/dist/ingest.d.ts +162 -0
  110. package/dist/ingest.d.ts.map +1 -0
  111. package/dist/ingest.js +269 -0
  112. package/dist/ingest.js.map +1 -0
  113. package/dist/journal.d.ts +49 -0
  114. package/dist/journal.d.ts.map +1 -0
  115. package/dist/journal.js +113 -0
  116. package/dist/journal.js.map +1 -0
  117. package/dist/outline-split.d.ts +38 -0
  118. package/dist/outline-split.d.ts.map +1 -0
  119. package/dist/outline-split.js +84 -0
  120. package/dist/outline-split.js.map +1 -0
  121. package/dist/overrides.d.ts +83 -0
  122. package/dist/overrides.d.ts.map +1 -0
  123. package/dist/overrides.js +88 -0
  124. package/dist/overrides.js.map +1 -0
  125. package/dist/paths.d.ts +183 -0
  126. package/dist/paths.d.ts.map +1 -0
  127. package/dist/paths.js +266 -0
  128. package/dist/paths.js.map +1 -0
  129. package/dist/remark-image-figure.mjs +77 -0
  130. package/dist/remark-strip-first-h1.mjs +26 -0
  131. package/dist/remark-strip-outline.mjs +44 -0
  132. package/dist/rename-slug.d.ts +49 -0
  133. package/dist/rename-slug.d.ts.map +1 -0
  134. package/dist/rename-slug.js +161 -0
  135. package/dist/rename-slug.js.map +1 -0
  136. package/dist/review/handlers.d.ts +55 -0
  137. package/dist/review/handlers.d.ts.map +1 -0
  138. package/dist/review/handlers.js +307 -0
  139. package/dist/review/handlers.js.map +1 -0
  140. package/dist/review/index.d.ts +14 -0
  141. package/dist/review/index.d.ts.map +1 -0
  142. package/dist/review/index.js +13 -0
  143. package/dist/review/index.js.map +1 -0
  144. package/dist/review/journal-mappers.d.ts +35 -0
  145. package/dist/review/journal-mappers.d.ts.map +1 -0
  146. package/dist/review/journal-mappers.js +48 -0
  147. package/dist/review/journal-mappers.js.map +1 -0
  148. package/dist/review/pipeline.d.ts +79 -0
  149. package/dist/review/pipeline.d.ts.map +1 -0
  150. package/dist/review/pipeline.js +234 -0
  151. package/dist/review/pipeline.js.map +1 -0
  152. package/dist/review/render.d.ts +27 -0
  153. package/dist/review/render.d.ts.map +1 -0
  154. package/dist/review/render.js +42 -0
  155. package/dist/review/render.js.map +1 -0
  156. package/dist/review/report.d.ts +50 -0
  157. package/dist/review/report.d.ts.map +1 -0
  158. package/dist/review/report.js +164 -0
  159. package/dist/review/report.js.map +1 -0
  160. package/dist/review/result.d.ts +12 -0
  161. package/dist/review/result.d.ts.map +1 -0
  162. package/dist/review/result.js +12 -0
  163. package/dist/review/result.js.map +1 -0
  164. package/dist/review/start-handlers.d.ts +62 -0
  165. package/dist/review/start-handlers.d.ts.map +1 -0
  166. package/dist/review/start-handlers.js +223 -0
  167. package/dist/review/start-handlers.js.map +1 -0
  168. package/dist/review/types.d.ts +169 -0
  169. package/dist/review/types.d.ts.map +1 -0
  170. package/dist/review/types.js +26 -0
  171. package/dist/review/types.js.map +1 -0
  172. package/dist/review/workflow-paths.d.ts +68 -0
  173. package/dist/review/workflow-paths.d.ts.map +1 -0
  174. package/dist/review/workflow-paths.js +112 -0
  175. package/dist/review/workflow-paths.js.map +1 -0
  176. package/dist/scaffold.d.ts +67 -0
  177. package/dist/scaffold.d.ts.map +1 -0
  178. package/dist/scaffold.js +122 -0
  179. package/dist/scaffold.js.map +1 -0
  180. package/dist/scrapbook.d.ts +229 -0
  181. package/dist/scrapbook.d.ts.map +1 -0
  182. package/dist/scrapbook.js +500 -0
  183. package/dist/scrapbook.js.map +1 -0
  184. package/dist/types.d.ts +197 -0
  185. package/dist/types.d.ts.map +1 -0
  186. package/dist/types.js +120 -0
  187. package/dist/types.js.map +1 -0
  188. package/package.json +160 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Workflow-creation handlers (longform, shortform). Pulled out of
3
+ * `handlers.ts` to keep that file under the 500-line guideline.
4
+ *
5
+ * Both handlers follow the same shape:
6
+ * 1. Validate input (site/slug/platform — same SLUG_RE the rest of
7
+ * deskwork uses).
8
+ * 2. Resolve the calendar entry (idem-key for the workflow tuple).
9
+ * 3. Resolve the on-disk markdown file path. Longform requires the file
10
+ * to already exist (scaffolded by /deskwork:outline). Shortform
11
+ * scaffolds the file in place when missing — frontmatter carries
12
+ * the deskwork-namespaced binding (`deskwork: { id, platform,
13
+ * channel? }`), body starts as `initialMarkdown ?? ''`.
14
+ * 4. Call `createWorkflow` (idempotent on the tuple).
15
+ *
16
+ * Phase 21a: shortform IS the same shape as longform — file on disk is
17
+ * SSOT, journal stores snapshots, the studio's review surface renders
18
+ * both kinds identically.
19
+ */
20
+ import { existsSync, mkdirSync, readFileSync } from 'node:fs';
21
+ import { dirname } from 'node:path';
22
+ import { isPlatform } from "../types.js";
23
+ import { writeFrontmatter } from "../frontmatter.js";
24
+ import { createWorkflow, readWorkflows } from "./pipeline.js";
25
+ import { lookupEntry, resolveLongformFilePath, resolveShortformWorkflowFilePath, } from "./workflow-paths.js";
26
+ import { err, ok } from "./result.js";
27
+ const SLUG_RE = /^[a-z0-9][a-z0-9-]*(\/[a-z0-9][a-z0-9-]*)*$/;
28
+ /**
29
+ * Enqueue a longform draft review from the studio dashboard. Reads the
30
+ * blog post markdown from disk (honoring blogFilenameTemplate) and calls
31
+ * createWorkflow. Idempotent on (entryId | (site, slug), 'longform').
32
+ */
33
+ export function handleStartLongform(projectRoot, config, body) {
34
+ if (!body || typeof body !== 'object')
35
+ return err(400, 'expected JSON object body');
36
+ const b = body;
37
+ if (!b.site)
38
+ return err(400, 'site is required');
39
+ if (!(b.site in config.sites)) {
40
+ const known = Object.keys(config.sites).join(', ');
41
+ return err(400, `unknown site: ${b.site}. Configured: ${known}`);
42
+ }
43
+ if (!b.slug || typeof b.slug !== 'string')
44
+ return err(400, 'slug is required');
45
+ if (!SLUG_RE.test(b.slug)) {
46
+ return err(400, `invalid slug: ${b.slug}. Must match ${SLUG_RE}`);
47
+ }
48
+ const callerEntryId = b.entryId !== undefined && b.entryId !== '' ? b.entryId : undefined;
49
+ const entry = lookupEntry(projectRoot, config, b.site, {
50
+ ...(callerEntryId !== undefined ? { entryId: callerEntryId } : {}),
51
+ slug: b.slug,
52
+ });
53
+ const entryId = callerEntryId ?? entry?.id;
54
+ const path = resolveLongformFilePath(projectRoot, config, b.site, b.slug, {
55
+ ...(entryId !== undefined ? { entryId } : {}),
56
+ ...(entry !== undefined ? { entry } : {}),
57
+ });
58
+ if (!existsSync(path)) {
59
+ return err(404, `blog draft not found at ${path}`);
60
+ }
61
+ const markdown = readFileSync(path, 'utf-8');
62
+ const before = readWorkflows(projectRoot, config).find((w) => {
63
+ const identityMatch = entryId && w.entryId
64
+ ? w.entryId === entryId
65
+ : w.site === b.site && w.slug === b.slug;
66
+ return (identityMatch &&
67
+ w.contentKind === 'longform' &&
68
+ w.state !== 'applied' &&
69
+ w.state !== 'cancelled');
70
+ });
71
+ const workflow = createWorkflow(projectRoot, config, {
72
+ site: b.site,
73
+ slug: b.slug,
74
+ ...(entryId !== undefined && entryId !== '' ? { entryId } : {}),
75
+ contentKind: 'longform',
76
+ initialMarkdown: markdown,
77
+ initialOriginatedBy: 'agent',
78
+ });
79
+ return ok({ workflow, existing: !!before && before.id === workflow.id });
80
+ }
81
+ /**
82
+ * Enqueue a shortform draft review. Mirrors `handleStartLongform`'s shape:
83
+ * resolve the calendar entry by slug, compute the shortform file path,
84
+ * scaffold the file (frontmatter `deskwork: { id, platform, channel? }` +
85
+ * body) when missing, then `createWorkflow` with `contentKind: 'shortform'`
86
+ * and v1 mirroring the file body.
87
+ *
88
+ * Idempotent: if a workflow already exists for the
89
+ * (entryId|site+slug, contentKind, platform, channel) tuple,
90
+ * `createWorkflow` returns it unchanged. If the file already exists on
91
+ * disk (operator resuming), creation is skipped and the body is read.
92
+ *
93
+ * Lifecycle decoupling: shortform is independent of the longform
94
+ * outline → drafting → published path. If the entry has no body file
95
+ * yet (Ideas / Planned / Outlining without scaffold), this handler
96
+ * still works — it creates the entry directory + scrapbook subdirs
97
+ * before writing the shortform file.
98
+ */
99
+ export function handleStartShortform(projectRoot, config, body) {
100
+ if (!body || typeof body !== 'object')
101
+ return err(400, 'expected JSON object body');
102
+ const b = body;
103
+ if (!b.site)
104
+ return err(400, 'site is required');
105
+ if (!(b.site in config.sites)) {
106
+ const known = Object.keys(config.sites).join(', ');
107
+ return err(400, `unknown site: ${b.site}. Configured: ${known}`);
108
+ }
109
+ if (!b.slug || typeof b.slug !== 'string')
110
+ return err(400, 'slug is required');
111
+ if (!SLUG_RE.test(b.slug)) {
112
+ return err(400, `invalid slug: ${b.slug}. Must match ${SLUG_RE}`);
113
+ }
114
+ if (!b.platform)
115
+ return err(400, 'platform is required');
116
+ if (!isPlatform(b.platform)) {
117
+ return err(400, `invalid platform: ${String(b.platform)}`);
118
+ }
119
+ const channel = b.channel !== undefined && b.channel !== '' ? b.channel : undefined;
120
+ const callerEntryId = b.entryId !== undefined && b.entryId !== '' ? b.entryId : undefined;
121
+ const entry = lookupEntry(projectRoot, config, b.site, {
122
+ ...(callerEntryId !== undefined ? { entryId: callerEntryId } : {}),
123
+ slug: b.slug,
124
+ });
125
+ if (!entry) {
126
+ return err(404, `no calendar entry for site=${b.site} slug=${b.slug}`);
127
+ }
128
+ const entryId = callerEntryId ?? entry.id;
129
+ let filePath;
130
+ try {
131
+ filePath = resolveShortformWorkflowFilePath(projectRoot, config, b.site, b.slug, b.platform, channel, {
132
+ ...(entryId !== undefined ? { entryId } : {}),
133
+ entry,
134
+ });
135
+ }
136
+ catch (e) {
137
+ return err(400, e instanceof Error ? e.message : String(e));
138
+ }
139
+ // The shortform path resolver returns undefined when the entry has no
140
+ // body file scaffolded yet (no entry-dir to anchor against). Shortform
141
+ // is decoupled from the lifecycle, so we materialize the directory
142
+ // tree from the slug-template fallback and proceed.
143
+ if (filePath === undefined) {
144
+ const fallback = resolveLongformFilePath(projectRoot, config, b.site, b.slug, { ...(entryId !== undefined ? { entryId } : {}), entry });
145
+ const entryDir = dirname(fallback);
146
+ const filename = channel !== undefined ? `${b.platform}-${channel}.md` : `${b.platform}.md`;
147
+ filePath = `${entryDir}/scrapbook/shortform/${filename}`;
148
+ }
149
+ // Scaffold the file when missing. Frontmatter carries the
150
+ // deskwork-namespaced binding so the studio can render the workflow
151
+ // off the file content directly.
152
+ let markdown;
153
+ if (existsSync(filePath)) {
154
+ markdown = readFileSync(filePath, 'utf-8');
155
+ }
156
+ else {
157
+ mkdirSync(dirname(filePath), { recursive: true });
158
+ const initialBody = b.initialMarkdown ?? '';
159
+ const deskworkMeta = {
160
+ platform: b.platform,
161
+ };
162
+ if (channel !== undefined)
163
+ deskworkMeta.channel = channel;
164
+ if (entryId !== undefined && entryId !== '')
165
+ deskworkMeta.id = entryId;
166
+ const fmData = {
167
+ title: entry.title,
168
+ deskwork: deskworkMeta,
169
+ };
170
+ writeFrontmatter(filePath, fmData, initialBody);
171
+ markdown = initialBody;
172
+ }
173
+ // Idempotent on (entryId|site+slug, contentKind, platform, channel).
174
+ const before = readWorkflows(projectRoot, config).find((w) => {
175
+ const identityMatch = entryId && w.entryId
176
+ ? w.entryId === entryId
177
+ : w.site === b.site && w.slug === b.slug;
178
+ return (identityMatch &&
179
+ w.contentKind === 'shortform' &&
180
+ (w.platform ?? null) === (b.platform ?? null) &&
181
+ (w.channel ?? null) === (channel ?? null) &&
182
+ w.state !== 'applied' &&
183
+ w.state !== 'cancelled');
184
+ });
185
+ const workflow = createWorkflow(projectRoot, config, {
186
+ site: b.site,
187
+ slug: b.slug,
188
+ ...(entryId !== undefined && entryId !== '' ? { entryId } : {}),
189
+ contentKind: 'shortform',
190
+ platform: b.platform,
191
+ ...(channel !== undefined ? { channel } : {}),
192
+ initialMarkdown: markdown,
193
+ initialOriginatedBy: 'agent',
194
+ });
195
+ return ok({
196
+ workflow,
197
+ existing: !!before && before.id === workflow.id,
198
+ filePath,
199
+ });
200
+ }
201
+ // ---------------------------------------------------------------------------
202
+ // Workflow → file dispatch (used by handleCreateVersion)
203
+ // ---------------------------------------------------------------------------
204
+ /**
205
+ * Resolve the on-disk markdown file path for any kind of workflow.
206
+ * Dispatches on `contentKind`. Returns `undefined` only for shortform when
207
+ * the entry directory cannot be derived (no entry id and no body file
208
+ * scaffolded yet).
209
+ */
210
+ export function workflowFilePath(projectRoot, config, workflow) {
211
+ if (workflow.contentKind === 'shortform') {
212
+ if (workflow.platform === undefined) {
213
+ throw new Error(`shortform workflow ${JSON.stringify(workflow)} has no platform`);
214
+ }
215
+ return resolveShortformWorkflowFilePath(projectRoot, config, workflow.site, workflow.slug, workflow.platform, workflow.channel, {
216
+ ...(workflow.entryId !== undefined ? { entryId: workflow.entryId } : {}),
217
+ });
218
+ }
219
+ return resolveLongformFilePath(projectRoot, config, workflow.site, workflow.slug, {
220
+ ...(workflow.entryId !== undefined ? { entryId: workflow.entryId } : {}),
221
+ });
222
+ }
223
+ //# sourceMappingURL=start-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-handlers.js","sourceRoot":"","sources":["../../src/review/start-handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Editorial review pipeline — data model.
3
+ *
4
+ * Ported from audiocontrol.org's scripts/lib/editorial-review/types.ts.
5
+ * The `Site` type was replaced with `string` since deskwork makes sites
6
+ * config-driven rather than a hardcoded enum. Platform stays typed —
7
+ * it's a bounded set in deskwork's editorial types.
8
+ *
9
+ * The review pipeline is a journal-backed workflow system: each post
10
+ * draft gets a workflow, each iteration produces a new version, and
11
+ * every event (version, annotation, state transition) is an append-only
12
+ * history entry. Callers reconstruct state by reading the journal.
13
+ */
14
+ import type { Platform } from '../types.ts';
15
+ export type DraftWorkflowState = 'open' | 'in-review' | 'iterating' | 'approved' | 'applied' | 'cancelled';
16
+ export type ContentKind = 'longform' | 'shortform' | 'outline';
17
+ export type OriginatedBy = 'agent' | 'operator';
18
+ /**
19
+ * Annotation category for comments — used by the voice-drift report to
20
+ * aggregate which skill principles produce the most corrections. `other`
21
+ * is the catch-all.
22
+ */
23
+ export type AnnotationCategory = 'voice-drift' | 'missing-receipt' | 'tutorial-framing' | 'saas-vocabulary' | 'fake-authority' | 'structural' | 'other';
24
+ /** Character-offset range against the raw markdown source. */
25
+ export interface DraftRange {
26
+ start: number;
27
+ end: number;
28
+ }
29
+ interface AnnotationBase {
30
+ /** ISO-8601 timestamp when the annotation was recorded. */
31
+ createdAt: string;
32
+ /** Workflow the annotation belongs to. */
33
+ workflowId: string;
34
+ /** Monotonic id within the workflow (server-assigned). */
35
+ id: string;
36
+ }
37
+ export interface CommentAnnotation extends AnnotationBase {
38
+ type: 'comment';
39
+ /** Draft version the comment was attached to. */
40
+ version: number;
41
+ /** Character range against the raw markdown of that version. */
42
+ range: DraftRange;
43
+ /** Free-text operator comment. */
44
+ text: string;
45
+ /** Optional category for review-report aggregation. */
46
+ category?: AnnotationCategory;
47
+ /**
48
+ * The displayed text the operator selected when making the comment.
49
+ * Used on later versions to re-locate the anchor via `indexOf`: if the
50
+ * quote appears exactly once in the new version the comment rebases,
51
+ * otherwise it renders as unresolved-from-v{N}.
52
+ */
53
+ anchor?: string;
54
+ }
55
+ export interface EditAnnotation extends AnnotationBase {
56
+ type: 'edit';
57
+ /** Version that was edited (the source of the edit). */
58
+ beforeVersion: number;
59
+ /** Full markdown of the new version the edit produced. */
60
+ afterMarkdown: string;
61
+ /** Unified diff of afterMarkdown against the beforeVersion's markdown. */
62
+ diff: string;
63
+ }
64
+ export interface ApproveAnnotation extends AnnotationBase {
65
+ type: 'approve';
66
+ /** Version the operator approved. */
67
+ version: number;
68
+ }
69
+ export interface RejectAnnotation extends AnnotationBase {
70
+ type: 'reject';
71
+ /** Version the operator rejected. */
72
+ version: number;
73
+ /** Optional reason free-text. */
74
+ reason?: string;
75
+ }
76
+ /**
77
+ * Marks a comment annotation as resolved. Emitted separately rather than
78
+ * mutating the comment because the journal is append-only — readers
79
+ * reconstruct resolved state by scanning for the most recent resolve
80
+ * annotation per commentId. `resolved: false` re-opens.
81
+ */
82
+ export interface ResolveAnnotation extends AnnotationBase {
83
+ type: 'resolve';
84
+ commentId: string;
85
+ resolved: boolean;
86
+ }
87
+ /**
88
+ * Per-iteration agent disposition for a comment. Written when a new
89
+ * version addresses (or defers) an operator comment. Latest-wins when
90
+ * rendering the sidebar badge.
91
+ */
92
+ export interface AddressAnnotation extends AnnotationBase {
93
+ type: 'address';
94
+ commentId: string;
95
+ /** Version the disposition was recorded against (the just-produced version). */
96
+ version: number;
97
+ disposition: 'addressed' | 'deferred' | 'wontfix';
98
+ reason?: string;
99
+ }
100
+ export type DraftAnnotation = CommentAnnotation | EditAnnotation | ApproveAnnotation | RejectAnnotation | ResolveAnnotation | AddressAnnotation;
101
+ export interface DraftVersion {
102
+ /** 1-based version number; v1 is the initial draft. */
103
+ version: number;
104
+ /** Full raw markdown of this version. */
105
+ markdown: string;
106
+ /** ISO-8601 when this version was recorded. */
107
+ createdAt: string;
108
+ /** Who produced this version — the agent or the operator (edit mode). */
109
+ originatedBy: OriginatedBy;
110
+ }
111
+ export interface DraftWorkflowItem {
112
+ /** Stable UUID for this workflow. */
113
+ id: string;
114
+ /**
115
+ * Stable UUID of the target calendar entry. Preferred join key for
116
+ * `matchesKey` / `findOpenByKey` — survives slug renames. Optional
117
+ * for legacy workflows created before entry UUIDs landed; those keep
118
+ * joining via the slug fallback.
119
+ */
120
+ entryId?: string;
121
+ /** Which site this draft belongs to (deskwork config site slug). */
122
+ site: string;
123
+ /** Post slug (blog entry slug for longform; calendar entry slug for shortform). */
124
+ slug: string;
125
+ /** longform / shortform / outline. */
126
+ contentKind: ContentKind;
127
+ /** For shortform only: which distribution platform. */
128
+ platform?: Platform;
129
+ /** For shortform only: channel (e.g. `r/synthdiy`). */
130
+ channel?: string;
131
+ /** Current state in the review pipeline. */
132
+ state: DraftWorkflowState;
133
+ /** Version number of the most recent DraftVersion for this workflow. */
134
+ currentVersion: number;
135
+ /** ISO-8601 when the workflow was first created. */
136
+ createdAt: string;
137
+ /** ISO-8601 when last modified (state transition or new version). */
138
+ updatedAt: string;
139
+ }
140
+ /**
141
+ * A single entry in the editorial-review history journal. Discriminated by
142
+ * `kind` so versions, annotations, and workflow transitions interleave in
143
+ * one stream.
144
+ */
145
+ export type DraftHistoryEntry = {
146
+ kind: 'workflow-created';
147
+ at: string;
148
+ workflow: DraftWorkflowItem;
149
+ } | {
150
+ kind: 'workflow-state';
151
+ at: string;
152
+ workflowId: string;
153
+ from: DraftWorkflowState;
154
+ to: DraftWorkflowState;
155
+ } | {
156
+ kind: 'version';
157
+ at: string;
158
+ workflowId: string;
159
+ version: DraftVersion;
160
+ } | {
161
+ kind: 'annotation';
162
+ at: string;
163
+ annotation: DraftAnnotation;
164
+ };
165
+ /** Allowed state transitions. All others are invalid. */
166
+ export declare const VALID_TRANSITIONS: Readonly<Record<DraftWorkflowState, readonly DraftWorkflowState[]>>;
167
+ export declare function isValidTransition(from: DraftWorkflowState, to: DraftWorkflowState): boolean;
168
+ export {};
169
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/review/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Editorial review pipeline — data model.
3
+ *
4
+ * Ported from audiocontrol.org's scripts/lib/editorial-review/types.ts.
5
+ * The `Site` type was replaced with `string` since deskwork makes sites
6
+ * config-driven rather than a hardcoded enum. Platform stays typed —
7
+ * it's a bounded set in deskwork's editorial types.
8
+ *
9
+ * The review pipeline is a journal-backed workflow system: each post
10
+ * draft gets a workflow, each iteration produces a new version, and
11
+ * every event (version, annotation, state transition) is an append-only
12
+ * history entry. Callers reconstruct state by reading the journal.
13
+ */
14
+ /** Allowed state transitions. All others are invalid. */
15
+ export const VALID_TRANSITIONS = {
16
+ open: ['in-review', 'cancelled'],
17
+ 'in-review': ['iterating', 'approved', 'cancelled'],
18
+ iterating: ['in-review', 'cancelled'],
19
+ approved: ['applied', 'cancelled'],
20
+ applied: [],
21
+ cancelled: [],
22
+ };
23
+ export function isValidTransition(from, to) {
24
+ return VALID_TRANSITIONS[from].includes(to);
25
+ }
26
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/review/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoLH,yDAAyD;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAE1B;IACF,IAAI,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC;IAChC,WAAW,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,WAAW,CAAC;IACnD,SAAS,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC;IACrC,QAAQ,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC;IAClC,OAAO,EAAE,EAAE;IACX,SAAS,EAAE,EAAE;CACd,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAC/B,IAAwB,EACxB,EAAsB;IAEtB,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Workflow → on-disk markdown file resolution.
3
+ *
4
+ * Both `handleStartLongform` and `handleCreateVersion` need to translate a
5
+ * (site, slug, contentKind, entryId?, platform?, channel?) tuple into an
6
+ * absolute file path. Pulled out of `handlers.ts` to keep that file under
7
+ * the 500-line guideline and to give the shortform path resolver a clean
8
+ * home alongside its longform sibling.
9
+ *
10
+ * Resolution rules:
11
+ *
12
+ * - `longform` / `outline`: prefer the content index (so writingcontrol-
13
+ * shaped non-template paths work); fall back to the site's
14
+ * `blogFilenameTemplate` for legacy / pre-doctor entries.
15
+ * - `shortform`: defers to `resolveShortformFilePath` in `paths.ts`,
16
+ * which composes the entry directory (via `findEntryFile`) with
17
+ * `scrapbook/shortform/<platform>[-<channel>].md`.
18
+ *
19
+ * The shortform variant returns `undefined` when the entry has no body file
20
+ * scaffolded yet — the caller decides whether to create the entry directory
21
+ * or surface a 404. The longform variant always returns a path because the
22
+ * slug-template fallback is unconditional for it.
23
+ */
24
+ import type { DeskworkConfig } from '../config.ts';
25
+ import type { ContentIndex } from '../content-index.ts';
26
+ import type { CalendarEntry, Platform } from '../types.ts';
27
+ /**
28
+ * Read a calendar entry by id or slug for a given site, returning
29
+ * `undefined` when the calendar is missing or the entry can't be found.
30
+ */
31
+ export declare function lookupEntry(projectRoot: string, config: DeskworkConfig, site: string, match: {
32
+ entryId?: string;
33
+ slug?: string;
34
+ }): CalendarEntry | undefined;
35
+ /**
36
+ * Hint bundle accepted by the longform/outline path resolver. Any one of
37
+ * `entryId`, `entry`, or `index` may be supplied; the resolver fills the
38
+ * rest in.
39
+ */
40
+ export interface LongformPathHint {
41
+ entryId?: string;
42
+ entry?: CalendarEntry;
43
+ index?: ContentIndex;
44
+ }
45
+ /**
46
+ * Resolve the absolute path of the markdown file backing a longform or
47
+ * outline workflow.
48
+ *
49
+ * Precedence:
50
+ * 1. Content index — when an entry id is known (passed in or derived
51
+ * from the workflow's site+slug via the calendar), look up the file
52
+ * whose frontmatter `deskwork.id:` matches. Refactor-proof.
53
+ * 2. Slug-template fallback — when no entry id is available (legacy
54
+ * workflow, pre-doctor entry, ad-hoc draft with no calendar record),
55
+ * fall back to the site's `blogFilenameTemplate`.
56
+ *
57
+ * Always returns a path (the slug-template fallback is unconditional);
58
+ * callers should `existsSync` if they need existence guarantees.
59
+ */
60
+ export declare function resolveLongformFilePath(projectRoot: string, config: DeskworkConfig, site: string, slug: string, hint: LongformPathHint): string;
61
+ /**
62
+ * Resolve the absolute path of the markdown file backing a shortform
63
+ * workflow. Returns `undefined` when the entry has no body file
64
+ * scaffolded yet — the entry directory cannot be derived in that case
65
+ * and the caller decides what to do (create the directory tree, or 404).
66
+ */
67
+ export declare function resolveShortformWorkflowFilePath(projectRoot: string, config: DeskworkConfig, site: string, slug: string, platform: Platform, channel: string | undefined, hint: LongformPathHint): string | undefined;
68
+ //# sourceMappingURL=workflow-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-paths.d.ts","sourceRoot":"","sources":["../../src/review/workflow-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AASxD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC,aAAa,GAAG,SAAS,CAgB3B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,gBAAgB,GACrB,MAAM,CAyBR;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,IAAI,EAAE,gBAAgB,GACrB,MAAM,GAAG,SAAS,CAwBpB"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Workflow → on-disk markdown file resolution.
3
+ *
4
+ * Both `handleStartLongform` and `handleCreateVersion` need to translate a
5
+ * (site, slug, contentKind, entryId?, platform?, channel?) tuple into an
6
+ * absolute file path. Pulled out of `handlers.ts` to keep that file under
7
+ * the 500-line guideline and to give the shortform path resolver a clean
8
+ * home alongside its longform sibling.
9
+ *
10
+ * Resolution rules:
11
+ *
12
+ * - `longform` / `outline`: prefer the content index (so writingcontrol-
13
+ * shaped non-template paths work); fall back to the site's
14
+ * `blogFilenameTemplate` for legacy / pre-doctor entries.
15
+ * - `shortform`: defers to `resolveShortformFilePath` in `paths.ts`,
16
+ * which composes the entry directory (via `findEntryFile`) with
17
+ * `scrapbook/shortform/<platform>[-<channel>].md`.
18
+ *
19
+ * The shortform variant returns `undefined` when the entry has no body file
20
+ * scaffolded yet — the caller decides whether to create the entry directory
21
+ * or surface a 404. The longform variant always returns a path because the
22
+ * slug-template fallback is unconditional for it.
23
+ */
24
+ import { existsSync } from 'node:fs';
25
+ import { buildContentIndex } from "../content-index.js";
26
+ import { resolveCalendarPath, resolveEntryFilePath, resolveShortformFilePath, } from "../paths.js";
27
+ import { readCalendar } from "../calendar.js";
28
+ import { findEntry, findEntryById } from "../calendar-mutations.js";
29
+ /**
30
+ * Read a calendar entry by id or slug for a given site, returning
31
+ * `undefined` when the calendar is missing or the entry can't be found.
32
+ */
33
+ export function lookupEntry(projectRoot, config, site, match) {
34
+ try {
35
+ const calendarPath = resolveCalendarPath(projectRoot, config, site);
36
+ if (!existsSync(calendarPath))
37
+ return undefined;
38
+ const cal = readCalendar(calendarPath);
39
+ if (match.entryId !== undefined && match.entryId !== '') {
40
+ const byId = findEntryById(cal, match.entryId);
41
+ if (byId !== undefined)
42
+ return byId;
43
+ }
44
+ if (match.slug !== undefined && match.slug !== '') {
45
+ return findEntry(cal, match.slug);
46
+ }
47
+ return undefined;
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }
53
+ /**
54
+ * Resolve the absolute path of the markdown file backing a longform or
55
+ * outline workflow.
56
+ *
57
+ * Precedence:
58
+ * 1. Content index — when an entry id is known (passed in or derived
59
+ * from the workflow's site+slug via the calendar), look up the file
60
+ * whose frontmatter `deskwork.id:` matches. Refactor-proof.
61
+ * 2. Slug-template fallback — when no entry id is available (legacy
62
+ * workflow, pre-doctor entry, ad-hoc draft with no calendar record),
63
+ * fall back to the site's `blogFilenameTemplate`.
64
+ *
65
+ * Always returns a path (the slug-template fallback is unconditional);
66
+ * callers should `existsSync` if they need existence guarantees.
67
+ */
68
+ export function resolveLongformFilePath(projectRoot, config, site, slug, hint) {
69
+ let entry = hint.entry;
70
+ let entryId = hint.entryId;
71
+ if (entry === undefined && (entryId === undefined || entryId === '')) {
72
+ entry = lookupEntry(projectRoot, config, site, { slug });
73
+ entryId = entry?.id;
74
+ }
75
+ else if (entry === undefined && entryId !== undefined) {
76
+ entry = lookupEntry(projectRoot, config, site, { entryId });
77
+ }
78
+ else if (entryId === undefined || entryId === '') {
79
+ entryId = entry?.id;
80
+ }
81
+ // Delegate to the top-level helper — same precedence (id-bound path
82
+ // first, slug-template fallback). The studio passes a per-request
83
+ // memoized index; the CLI can let it build per call.
84
+ const idx = hint.index ?? buildContentIndex(projectRoot, config, site);
85
+ const slugForFallback = entry?.slug ?? slug;
86
+ return resolveEntryFilePath(projectRoot, config, site, slugForFallback, entryId, idx);
87
+ }
88
+ /**
89
+ * Resolve the absolute path of the markdown file backing a shortform
90
+ * workflow. Returns `undefined` when the entry has no body file
91
+ * scaffolded yet — the entry directory cannot be derived in that case
92
+ * and the caller decides what to do (create the directory tree, or 404).
93
+ */
94
+ export function resolveShortformWorkflowFilePath(projectRoot, config, site, slug, platform, channel, hint) {
95
+ let entry = hint.entry;
96
+ let entryId = hint.entryId;
97
+ if (entry === undefined && (entryId === undefined || entryId === '')) {
98
+ entry = lookupEntry(projectRoot, config, site, { slug });
99
+ entryId = entry?.id;
100
+ }
101
+ else if (entry === undefined && entryId !== undefined) {
102
+ entry = lookupEntry(projectRoot, config, site, { entryId });
103
+ }
104
+ else if (entryId === undefined || entryId === '') {
105
+ entryId = entry?.id;
106
+ }
107
+ return resolveShortformFilePath(projectRoot, config, site, {
108
+ ...(entryId !== undefined && entryId !== '' ? { id: entryId } : {}),
109
+ slug: entry?.slug ?? slug,
110
+ }, platform, channel, hint.index);
111
+ }
112
+ //# sourceMappingURL=workflow-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-paths.js","sourceRoot":"","sources":["../../src/review/workflow-paths.ts"],"names":[],"mappings":"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"}