@popoverai/dotrequirements 0.24.3 → 0.26.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 (75) hide show
  1. package/README.md +7 -8
  2. package/dist/cli.js +8 -1
  3. package/dist/codebase-to-spec/dispatch.d.ts +60 -14
  4. package/dist/codebase-to-spec/dispatch.js +381 -15
  5. package/dist/codebase-to-spec/pack.d.ts +7 -0
  6. package/dist/codebase-to-spec/pack.js +29 -8
  7. package/dist/codebase-to-spec/present.d.ts +9 -0
  8. package/dist/codebase-to-spec/present.js +23 -2
  9. package/dist/codebase-to-spec/prompts/editor.d.ts +1 -1
  10. package/dist/codebase-to-spec/prompts/editor.js +1 -1
  11. package/dist/codebase-to-spec/prompts/specifier.d.ts +1 -1
  12. package/dist/codebase-to-spec/prompts/specifier.js +3 -2
  13. package/dist/codebase-to-spec/schemas.d.ts +153 -0
  14. package/dist/codebase-to-spec/schemas.js +111 -0
  15. package/dist/codebase-to-spec/skill-install.d.ts +42 -29
  16. package/dist/codebase-to-spec/skill-install.js +122 -112
  17. package/dist/codebase-to-spec/version-check.d.ts +31 -0
  18. package/dist/codebase-to-spec/version-check.js +56 -0
  19. package/dist/commands/ai-setup.d.ts +12 -1
  20. package/dist/commands/ai-setup.js +65 -33
  21. package/dist/commands/codebase-to-spec/dispatch-context.d.ts +2 -5
  22. package/dist/commands/codebase-to-spec/dispatch-context.js +2 -5
  23. package/dist/commands/codebase-to-spec/dispatch-editor.d.ts +0 -1
  24. package/dist/commands/codebase-to-spec/dispatch-editor.js +0 -1
  25. package/dist/commands/codebase-to-spec/dispatch-planner.d.ts +0 -1
  26. package/dist/commands/codebase-to-spec/dispatch-planner.js +0 -1
  27. package/dist/commands/codebase-to-spec/dispatch-spec.d.ts +3 -6
  28. package/dist/commands/codebase-to-spec/dispatch-spec.js +3 -6
  29. package/dist/commands/codebase-to-spec/index.js +3 -2
  30. package/dist/commands/codebase-to-spec/pack.d.ts +9 -0
  31. package/dist/commands/codebase-to-spec/pack.js +23 -3
  32. package/dist/commands/codebase-to-spec/skill-install.js +2 -9
  33. package/dist/commands/init.js +6 -1
  34. package/dist/commands/link-resolution.d.ts +79 -0
  35. package/dist/commands/link-resolution.js +141 -0
  36. package/dist/commands/link.d.ts +14 -4
  37. package/dist/commands/link.js +369 -16
  38. package/dist/commands/pull.js +19 -2
  39. package/dist/commands/push.js +36 -2
  40. package/dist/convex.d.ts +5 -3
  41. package/dist/convex.js +5 -3
  42. package/dist/harness/cache.d.ts +0 -14
  43. package/dist/harness/cache.js +1 -41
  44. package/dist/harness/finalize.js +2 -2
  45. package/dist/harness/prepare.js +1 -3
  46. package/dist/harness/requirementsLoader.d.ts +3 -3
  47. package/dist/harness/requirementsLoader.js +13 -8
  48. package/dist/mcp/handlers/authoring.d.ts +5 -5
  49. package/dist/mcp/handlers/authoring.js +9 -9
  50. package/dist/mcp/handlers/push.d.ts +2 -2
  51. package/dist/mcp/handlers/push.js +36 -3
  52. package/dist/mcp/handlers/review.d.ts +4 -4
  53. package/dist/mcp/handlers/review.js +4 -4
  54. package/dist/mcp/handlers/search.d.ts +1 -1
  55. package/dist/mcp/handlers/search.js +1 -1
  56. package/dist/mcp/index.js +29 -0
  57. package/dist/push/core.d.ts +18 -0
  58. package/dist/push/core.js +70 -3
  59. package/dist/push/index.d.ts +1 -1
  60. package/dist/push/index.js +1 -1
  61. package/dist/schema/parser-core.js +5 -1
  62. package/dist/schema/parser.js +5 -1
  63. package/dist/schema/run-marker.d.ts +38 -0
  64. package/dist/schema/run-marker.js +138 -0
  65. package/dist/schema/schemas.d.ts +12 -0
  66. package/dist/schema/schemas.js +1 -0
  67. package/dist/templates/agents/cts-worker.md +3 -3
  68. package/dist/templates/skills/codebase-to-spec/SKILL.md +56 -158
  69. package/dist/templates/workflows/specify-codebase.js +374 -0
  70. package/dist/utils/own-package.d.ts +10 -0
  71. package/dist/utils/own-package.js +13 -0
  72. package/dist/utils/project-selector.d.ts +5 -0
  73. package/dist/utils/project-selector.js +4 -0
  74. package/package.json +3 -3
  75. package/dist/templates/hooks/cts-worker-persona.sh +0 -76
package/dist/push/core.js CHANGED
@@ -14,6 +14,8 @@ import * as path from "node:path";
14
14
  import { ConvexHttpClient } from "convex/browser";
15
15
  import { api } from "../convex.js";
16
16
  import { buildRequirementsFile, getAllRequirements, parseRequirementKey, parseRequirementsFromFile, } from "../schema/index.js";
17
+ /** Base URL of the web app, where synced documents are reviewed. */
18
+ export const WEB_APP_URL = "https://app.dotrequirements.io";
17
19
  // ============================================================================
18
20
  // Parsing
19
21
  // ============================================================================
@@ -170,13 +172,15 @@ export async function executePush(dryRunResult, credentials) {
170
172
  let created = 0;
171
173
  let updated = 0;
172
174
  const errors = [];
175
+ const importWarnings = [];
176
+ const synced = [];
173
177
  for (const { file, result } of pushableFiles) {
174
178
  const doc = file.metadata.document;
175
179
  const fileName = path.basename(file.filePath);
176
180
  // For not_found, clear the ID so we create a new document
177
181
  const effectiveDocId = result.action === "not_found" ? undefined : doc.id;
178
182
  try {
179
- const pushResult = (await client.mutation(api.documents.saveWithRequirements.saveWithRequirements, {
183
+ const rawResult = (await client.mutation(api.documents.saveWithRequirements.saveWithRequirements, {
180
184
  projectAuth: {
181
185
  projectSlug: credentials.projectId,
182
186
  projectSecret: credentials.projectSecret,
@@ -186,8 +190,21 @@ export async function executePush(dryRunResult, credentials) {
186
190
  title: doc.title,
187
191
  markdownContent: file.markdownContent,
188
192
  defaultPrefix: doc.defaultPrefix,
193
+ // IMPORT-2: forward the CTS run marker when the file is stamped
194
+ runMarker: file.metadata.ctsRun,
189
195
  dryRun: false,
190
196
  }));
197
+ // The server returns a structured result iff we sent a runMarker
198
+ const pushResult = typeof rawResult === "string" ? rawResult : rawResult.documentId;
199
+ if (typeof rawResult !== "string") {
200
+ const status = rawResult.importStatus;
201
+ if (status === "invalid_marker" ||
202
+ status === "unsupported_version" ||
203
+ status === "already_used") {
204
+ // IMPORT-3.2/3.3: loud, never fatal
205
+ importWarnings.push({ fileName, status });
206
+ }
207
+ }
191
208
  const isCreate = result.action === "create" || result.action === "not_found";
192
209
  if (isCreate) {
193
210
  // New document - write ID back to file
@@ -206,11 +223,61 @@ export async function executePush(dryRunResult, credentials) {
206
223
  fs.writeFileSync(file.filePath, updatedContent, "utf-8");
207
224
  updated++;
208
225
  }
226
+ // SYNC-LAND-1: every synced document gets its web URL in the result
227
+ synced.push({
228
+ fileName,
229
+ documentId: pushResult,
230
+ url: `${WEB_APP_URL}/documents/${pushResult}`,
231
+ });
209
232
  }
210
233
  catch (err) {
211
- errors.push({ fileName, error: err.message });
234
+ errors.push({ fileName, error: errorDisplayMessage(err) });
235
+ }
236
+ }
237
+ return { created, updated, errors, importWarnings, synced };
238
+ }
239
+ /**
240
+ * Servers throw ConvexError({kind, message}) for expected failures (e.g.
241
+ * LIMITS-4.2's limit error); the client-side Error message embeds that data
242
+ * as JSON. Surface the human-readable message it carries instead of the blob.
243
+ */
244
+ function errorDisplayMessage(err) {
245
+ const data = err.data;
246
+ const fromData = humanMessage(data);
247
+ if (fromData)
248
+ return fromData;
249
+ const message = err instanceof Error ? err.message : String(err);
250
+ // ConvexError messages may embed the data JSON directly or after a
251
+ // "ConvexError:" prefix — try the trailing {...} chunk
252
+ const jsonStart = message.indexOf("{");
253
+ if (jsonStart !== -1) {
254
+ try {
255
+ const parsed = JSON.parse(message.slice(jsonStart));
256
+ const fromMessage = humanMessage(parsed);
257
+ if (fromMessage)
258
+ return fromMessage;
259
+ }
260
+ catch {
261
+ // fall through to the raw message
262
+ }
263
+ }
264
+ return message;
265
+ }
266
+ function humanMessage(data) {
267
+ if (typeof data === "string") {
268
+ try {
269
+ return humanMessage(JSON.parse(data));
212
270
  }
271
+ catch {
272
+ return undefined;
273
+ }
274
+ }
275
+ if (data &&
276
+ typeof data === "object" &&
277
+ "message" in data &&
278
+ typeof data.message === "string") {
279
+ return data.message;
213
280
  }
214
- return { created, updated, errors };
281
+ return undefined;
215
282
  }
216
283
  //# sourceMappingURL=core.js.map
@@ -4,5 +4,5 @@
4
4
  * Shared push logic for syncing local requirements files to the cloud.
5
5
  * Used by both CLI push command and MCP push_requirements tool.
6
6
  */
7
- export { type CloudDocumentMetadata, type ConflictInfo, type DryRunResult, type DryRunResultItem, dryRunPush, executePush, extractMarkdownContent, type FileWithDryRun, type ParsedFile, type PushCredentials, type PushResult, parseFilesForPush, } from "./core.js";
7
+ export { type CloudDocumentMetadata, type ConflictInfo, type DryRunResult, type DryRunResultItem, dryRunPush, executePush, extractMarkdownContent, type FileWithDryRun, type ParsedFile, type PushCredentials, type PushResult, parseFilesForPush, WEB_APP_URL, } from "./core.js";
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -6,5 +6,5 @@
6
6
  */
7
7
  export { dryRunPush, executePush,
8
8
  // Functions
9
- extractMarkdownContent, parseFilesForPush, } from "./core.js";
9
+ extractMarkdownContent, parseFilesForPush, WEB_APP_URL, } from "./core.js";
10
10
  //# sourceMappingURL=index.js.map
@@ -4,7 +4,7 @@
4
4
  * This module contains pure parsing functions with no Node.js dependencies,
5
5
  * making it safe to import in Convex runtime or browser environments.
6
6
  */
7
- import { ValidationError, } from "./schemas.js";
7
+ import { ValidationError, validateKey, } from "./schemas.js";
8
8
  /**
9
9
  * Default delimiter for requirements.
10
10
  * Can be overridden for organization-specific preferences.
@@ -186,6 +186,10 @@ export function extractRequirementBlocks(body) {
186
186
  throw new ValidationError(`Invalid requirement block format - first line must be "KEY: content"`, blockContent.substring(0, 50));
187
187
  }
188
188
  const key = firstLineMatch[1];
189
+ // SYNC-KEY-1: reject malformed keys at parse time so push/validate fail
190
+ // locally instead of erroring server-side. Validation only — the original
191
+ // key is preserved (normalization happens downstream).
192
+ validateKey(key);
189
193
  blocks.push({ key, blockContent: blockContent.trim() });
190
194
  match = blockRegex.exec(body);
191
195
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import * as fs from "node:fs";
5
5
  import YAML from "yaml";
6
- import { ValidationError, validateMetadata, } from "./schemas.js";
6
+ import { ValidationError, validateKey, validateMetadata, } from "./schemas.js";
7
7
  const DELIMITER_PATTERN = "(?:→|->)"; // Non-capturing group for both Unicode and ASCII
8
8
  /**
9
9
  * Parse a criterion line in "position. Label → content" format.
@@ -198,6 +198,10 @@ function extractRequirementBlocks(body) {
198
198
  throw new ValidationError(`Invalid requirement block format - first line must be "KEY: content"`, blockContent.substring(0, 50));
199
199
  }
200
200
  const key = firstLineMatch[1];
201
+ // SYNC-KEY-1: reject malformed keys at parse time so push/validate fail
202
+ // locally instead of erroring server-side. Validation only — the original
203
+ // key is preserved (normalization happens downstream).
204
+ validateKey(key);
201
205
  // Try to find a heading immediately before this block for the title
202
206
  // Look backwards from the match position to find the nearest heading
203
207
  const textBeforeBlock = body.substring(0, match.index);
@@ -0,0 +1,38 @@
1
+ /**
2
+ * CTS run markers: per-run identifiers stamped into the frontmatter of every
3
+ * document a codebase-to-spec run produces. At push time a valid, team-unseen
4
+ * marker grants `imported` authorship to the rows it creates (IMPORT-1, IMPORT-2).
5
+ *
6
+ * Format: 32 hex chars rendered in UUID layout (8-4-4-4-12).
7
+ * [0..23] random component (96 bits)
8
+ * [24] scheme version nibble
9
+ * [25..31] integrity checksum: first 7 hex chars of
10
+ * sha256(`${SALT}:${random}:${version}`)
11
+ *
12
+ * The checksum is an intentionality gate, not security: the salt ships in
13
+ * published source, so a determined forger can reproduce it. Its job is to make
14
+ * casual tampering ("what if I tweak this value?") fail validation, so that
15
+ * successful forgery requires mining the source — documented intent (see
16
+ * docs/working/cts-onboarding-funnel.md, abuse posture).
17
+ *
18
+ * Pure-JS SHA-256 is used (rather than node:crypto or crypto.subtle) so the
19
+ * same module runs identically in Node and the Convex isolate, synchronously.
20
+ */
21
+ export type RunMarkerValidation = {
22
+ valid: true;
23
+ version: string;
24
+ } | {
25
+ valid: false;
26
+ reason: "malformed" | "unsupported_version" | "checksum_mismatch";
27
+ };
28
+ /**
29
+ * Generate a new run marker (IMPORT-1.2, IMPORT-1.3).
30
+ */
31
+ export declare function generateRunMarker(): string;
32
+ /**
33
+ * Validate a run marker's shape and integrity (IMPORT-2.0, IMPORT-3.0).
34
+ * Does NOT consult the import ledger — replay checking is the caller's job.
35
+ */
36
+ export declare function validateRunMarker(marker: string): RunMarkerValidation;
37
+ export declare function sha256Hex(input: string): string;
38
+ //# sourceMappingURL=run-marker.d.ts.map
@@ -0,0 +1,138 @@
1
+ /**
2
+ * CTS run markers: per-run identifiers stamped into the frontmatter of every
3
+ * document a codebase-to-spec run produces. At push time a valid, team-unseen
4
+ * marker grants `imported` authorship to the rows it creates (IMPORT-1, IMPORT-2).
5
+ *
6
+ * Format: 32 hex chars rendered in UUID layout (8-4-4-4-12).
7
+ * [0..23] random component (96 bits)
8
+ * [24] scheme version nibble
9
+ * [25..31] integrity checksum: first 7 hex chars of
10
+ * sha256(`${SALT}:${random}:${version}`)
11
+ *
12
+ * The checksum is an intentionality gate, not security: the salt ships in
13
+ * published source, so a determined forger can reproduce it. Its job is to make
14
+ * casual tampering ("what if I tweak this value?") fail validation, so that
15
+ * successful forgery requires mining the source — documented intent (see
16
+ * docs/working/cts-onboarding-funnel.md, abuse posture).
17
+ *
18
+ * Pure-JS SHA-256 is used (rather than node:crypto or crypto.subtle) so the
19
+ * same module runs identically in Node and the Convex isolate, synchronously.
20
+ */
21
+ const RUN_MARKER_SALT = "dotrequirements-cts-run-marker";
22
+ /** Current marker scheme version (single hex nibble). */
23
+ const SCHEME_VERSION = "1";
24
+ const HEX_32 = /^[0-9a-f]{32}$/;
25
+ const UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
26
+ /**
27
+ * Generate a new run marker (IMPORT-1.2, IMPORT-1.3).
28
+ */
29
+ export function generateRunMarker() {
30
+ const bytes = new Uint8Array(12); // 96 bits → 24 hex chars
31
+ globalThis.crypto.getRandomValues(bytes);
32
+ const random = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
33
+ const checksum = computeChecksum(random, SCHEME_VERSION);
34
+ return toUuidLayout(`${random}${SCHEME_VERSION}${checksum}`);
35
+ }
36
+ /**
37
+ * Validate a run marker's shape and integrity (IMPORT-2.0, IMPORT-3.0).
38
+ * Does NOT consult the import ledger — replay checking is the caller's job.
39
+ */
40
+ export function validateRunMarker(marker) {
41
+ if (typeof marker !== "string" || !UUID_SHAPE.test(marker)) {
42
+ return { valid: false, reason: "malformed" };
43
+ }
44
+ const hex = marker.replaceAll("-", "");
45
+ if (!HEX_32.test(hex)) {
46
+ return { valid: false, reason: "malformed" };
47
+ }
48
+ const random = hex.slice(0, 24);
49
+ const version = hex.slice(24, 25);
50
+ const checksum = hex.slice(25);
51
+ if (version !== SCHEME_VERSION) {
52
+ // IMPORT-1.4: future schemes validate their own versions; anything we
53
+ // don't know is unsupported, not malformed.
54
+ return { valid: false, reason: "unsupported_version" };
55
+ }
56
+ if (computeChecksum(random, version) !== checksum) {
57
+ return { valid: false, reason: "checksum_mismatch" };
58
+ }
59
+ return { valid: true, version };
60
+ }
61
+ function computeChecksum(randomHex, version) {
62
+ return sha256Hex(`${RUN_MARKER_SALT}:${randomHex}:${version}`).slice(0, 7);
63
+ }
64
+ function toUuidLayout(hex32) {
65
+ return `${hex32.slice(0, 8)}-${hex32.slice(8, 12)}-${hex32.slice(12, 16)}-${hex32.slice(16, 20)}-${hex32.slice(20)}`;
66
+ }
67
+ // =============================================================================
68
+ // Pure-JS SHA-256 (FIPS 180-4). Synchronous and runtime-independent.
69
+ // =============================================================================
70
+ const K = new Uint32Array([
71
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
72
+ 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
73
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
74
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
75
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
76
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
77
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
78
+ 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
79
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
80
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
81
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
82
+ ]);
83
+ function rotr(x, n) {
84
+ return (x >>> n) | (x << (32 - n));
85
+ }
86
+ export function sha256Hex(input) {
87
+ const data = new TextEncoder().encode(input);
88
+ const bitLen = data.length * 8;
89
+ // Padding: 0x80, zeros, 64-bit big-endian length
90
+ const padded = new Uint8Array((((data.length + 8) >> 6) << 6) + 64);
91
+ padded.set(data);
92
+ padded[data.length] = 0x80;
93
+ const view = new DataView(padded.buffer);
94
+ view.setUint32(padded.length - 4, bitLen >>> 0, false);
95
+ view.setUint32(padded.length - 8, Math.floor(bitLen / 0x100000000), false);
96
+ const h = new Uint32Array([
97
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
98
+ 0x1f83d9ab, 0x5be0cd19,
99
+ ]);
100
+ const w = new Uint32Array(64);
101
+ for (let offset = 0; offset < padded.length; offset += 64) {
102
+ for (let i = 0; i < 16; i++) {
103
+ w[i] = view.getUint32(offset + i * 4, false);
104
+ }
105
+ for (let i = 16; i < 64; i++) {
106
+ const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
107
+ const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
108
+ w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
109
+ }
110
+ let [a, b, c, d, e, f, g, hh] = h;
111
+ for (let i = 0; i < 64; i++) {
112
+ const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
113
+ const ch = (e & f) ^ (~e & g);
114
+ const temp1 = (hh + S1 + ch + K[i] + w[i]) >>> 0;
115
+ const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
116
+ const maj = (a & b) ^ (a & c) ^ (b & c);
117
+ const temp2 = (S0 + maj) >>> 0;
118
+ hh = g;
119
+ g = f;
120
+ f = e;
121
+ e = (d + temp1) >>> 0;
122
+ d = c;
123
+ c = b;
124
+ b = a;
125
+ a = (temp1 + temp2) >>> 0;
126
+ }
127
+ h[0] = (h[0] + a) >>> 0;
128
+ h[1] = (h[1] + b) >>> 0;
129
+ h[2] = (h[2] + c) >>> 0;
130
+ h[3] = (h[3] + d) >>> 0;
131
+ h[4] = (h[4] + e) >>> 0;
132
+ h[5] = (h[5] + f) >>> 0;
133
+ h[6] = (h[6] + g) >>> 0;
134
+ h[7] = (h[7] + hh) >>> 0;
135
+ }
136
+ return Array.from(h, (x) => x.toString(16).padStart(8, "0")).join("");
137
+ }
138
+ //# sourceMappingURL=run-marker.js.map
@@ -60,6 +60,7 @@ export declare function buildRequirementKey(prefix: string, number: number): str
60
60
  export declare const MetadataSchema: z.ZodObject<{
61
61
  version: z.ZodOptional<z.ZodNumber>;
62
62
  pulledAt: z.ZodOptional<z.ZodString>;
63
+ ctsRun: z.ZodOptional<z.ZodString>;
63
64
  document: z.ZodOptional<z.ZodObject<{
64
65
  id: z.ZodOptional<z.ZodString>;
65
66
  title: z.ZodString;
@@ -76,6 +77,7 @@ export declare const MetadataSchema: z.ZodObject<{
76
77
  }, "strip", z.ZodTypeAny, {
77
78
  version?: number | undefined;
78
79
  pulledAt?: string | undefined;
80
+ ctsRun?: string | undefined;
79
81
  document?: {
80
82
  title: string;
81
83
  id?: string | undefined;
@@ -84,6 +86,7 @@ export declare const MetadataSchema: z.ZodObject<{
84
86
  }, {
85
87
  version?: number | undefined;
86
88
  pulledAt?: string | undefined;
89
+ ctsRun?: string | undefined;
87
90
  document?: {
88
91
  title: string;
89
92
  id?: string | undefined;
@@ -146,6 +149,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
146
149
  _meta: z.ZodObject<{
147
150
  version: z.ZodOptional<z.ZodNumber>;
148
151
  pulledAt: z.ZodOptional<z.ZodString>;
152
+ ctsRun: z.ZodOptional<z.ZodString>;
149
153
  document: z.ZodOptional<z.ZodObject<{
150
154
  id: z.ZodOptional<z.ZodString>;
151
155
  title: z.ZodString;
@@ -162,6 +166,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
162
166
  }, "strip", z.ZodTypeAny, {
163
167
  version?: number | undefined;
164
168
  pulledAt?: string | undefined;
169
+ ctsRun?: string | undefined;
165
170
  document?: {
166
171
  title: string;
167
172
  id?: string | undefined;
@@ -170,6 +175,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
170
175
  }, {
171
176
  version?: number | undefined;
172
177
  pulledAt?: string | undefined;
178
+ ctsRun?: string | undefined;
173
179
  document?: {
174
180
  title: string;
175
181
  id?: string | undefined;
@@ -180,6 +186,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
180
186
  _meta: z.ZodObject<{
181
187
  version: z.ZodOptional<z.ZodNumber>;
182
188
  pulledAt: z.ZodOptional<z.ZodString>;
189
+ ctsRun: z.ZodOptional<z.ZodString>;
183
190
  document: z.ZodOptional<z.ZodObject<{
184
191
  id: z.ZodOptional<z.ZodString>;
185
192
  title: z.ZodString;
@@ -196,6 +203,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
196
203
  }, "strip", z.ZodTypeAny, {
197
204
  version?: number | undefined;
198
205
  pulledAt?: string | undefined;
206
+ ctsRun?: string | undefined;
199
207
  document?: {
200
208
  title: string;
201
209
  id?: string | undefined;
@@ -204,6 +212,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
204
212
  }, {
205
213
  version?: number | undefined;
206
214
  pulledAt?: string | undefined;
215
+ ctsRun?: string | undefined;
207
216
  document?: {
208
217
  title: string;
209
218
  id?: string | undefined;
@@ -214,6 +223,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
214
223
  _meta: z.ZodObject<{
215
224
  version: z.ZodOptional<z.ZodNumber>;
216
225
  pulledAt: z.ZodOptional<z.ZodString>;
226
+ ctsRun: z.ZodOptional<z.ZodString>;
217
227
  document: z.ZodOptional<z.ZodObject<{
218
228
  id: z.ZodOptional<z.ZodString>;
219
229
  title: z.ZodString;
@@ -230,6 +240,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
230
240
  }, "strip", z.ZodTypeAny, {
231
241
  version?: number | undefined;
232
242
  pulledAt?: string | undefined;
243
+ ctsRun?: string | undefined;
233
244
  document?: {
234
245
  title: string;
235
246
  id?: string | undefined;
@@ -238,6 +249,7 @@ export declare const RequirementsFileSchema: z.ZodObject<{
238
249
  }, {
239
250
  version?: number | undefined;
240
251
  pulledAt?: string | undefined;
252
+ ctsRun?: string | undefined;
241
253
  document?: {
242
254
  title: string;
243
255
  id?: string | undefined;
@@ -112,6 +112,7 @@ export function buildRequirementKey(prefix, number) {
112
112
  export const MetadataSchema = z.object({
113
113
  version: z.number().optional(), // Informational - incremented on push
114
114
  pulledAt: z.string().optional(), // ISO 8601 timestamp - used for conflict detection
115
+ ctsRun: z.string().optional(), // Run marker stamped by codebase-to-spec (IMPORT-1); grants imported authorship at document creation (IMPORT-2)
115
116
  document: z
116
117
  .object({
117
118
  id: z.string().optional(), // Cloud document link - filled by push when creating
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: cts-worker
3
- description: Generic worker subagent for the codebase-to-spec conversational orchestrator. Used for planner, specifier, editor, and reviewer dispatches. The per-dispatch persona is injected by the cts-worker-persona PreToolUse hook at dispatch time.
3
+ description: Generic worker subagent for the codebase-to-spec workflow. Handles planner, outline-reviewer, specifier, reviewer, and editor dispatches. Each worker fetches its own role-specific instructions from the CLI at dispatch time.
4
4
  tools: Bash, Read, Edit, Write, Glob, Grep
5
5
  ---
6
6
 
7
- You are a codebase-to-spec worker. Your dispatched prompt is composed externally and supplied to you at dispatch time. It will specify your role for this dispatch (planner, specifier, editor, or reviewer), the artifacts you're working with, and the output convention you must follow.
7
+ You are a codebase-to-spec worker. The prompt you receive tells you which CLI dispatch to fetch: it will instruct you to run `{{DOTREQ_CLI}} cts dispatch-context <dispatch-id>` (the exact invocation is given in your prompt), read the `"prompt"` field of the JSON it prints, and follow those instructions exactly.
8
8
 
9
- Follow the dispatched prompt exactly. Do not improvise behavior beyond what it asks. When your work is done, submit via the convention specified in the dispatch (typically a `dotrequirements cts submit` call).
9
+ Those fetched instructions are your real task — which files to read, what to write and where, and how to validate and style-check your output via Bash. Follow them exactly and do not improvise beyond what they ask. When your prompt also asks you to return a structured result, return exactly that shape.