@cleocode/contracts 2026.4.68 → 2026.4.70

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 (39) hide show
  1. package/dist/__tests__/acceptance-gate.test.d.ts +14 -0
  2. package/dist/__tests__/acceptance-gate.test.d.ts.map +1 -0
  3. package/dist/__tests__/acceptance-gate.test.js +642 -0
  4. package/dist/__tests__/acceptance-gate.test.js.map +1 -0
  5. package/dist/__tests__/attachment.test.d.ts +17 -0
  6. package/dist/__tests__/attachment.test.d.ts.map +1 -0
  7. package/dist/__tests__/attachment.test.js +505 -0
  8. package/dist/__tests__/attachment.test.js.map +1 -0
  9. package/dist/acceptance-gate-schema.d.ts +624 -0
  10. package/dist/acceptance-gate-schema.d.ts.map +1 -0
  11. package/dist/acceptance-gate-schema.js +244 -0
  12. package/dist/acceptance-gate-schema.js.map +1 -0
  13. package/dist/acceptance-gate.d.ts +253 -0
  14. package/dist/acceptance-gate.d.ts.map +1 -0
  15. package/dist/acceptance-gate.js +22 -0
  16. package/dist/acceptance-gate.js.map +1 -0
  17. package/dist/attachment-schema.d.ts +243 -0
  18. package/dist/attachment-schema.d.ts.map +1 -0
  19. package/dist/attachment-schema.js +200 -0
  20. package/dist/attachment-schema.js.map +1 -0
  21. package/dist/attachment.d.ts +224 -0
  22. package/dist/attachment.d.ts.map +1 -0
  23. package/dist/attachment.js +21 -0
  24. package/dist/attachment.js.map +1 -0
  25. package/dist/index.d.ts +5 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +2 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/task.d.ts +23 -4
  30. package/dist/task.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/__tests__/acceptance-gate.test.ts +736 -0
  33. package/src/__tests__/attachment.test.ts +601 -0
  34. package/src/acceptance-gate-schema.ts +283 -0
  35. package/src/acceptance-gate.ts +264 -0
  36. package/src/attachment-schema.ts +223 -0
  37. package/src/attachment.ts +245 -0
  38. package/src/index.ts +49 -0
  39. package/src/task.ts +24 -4
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Zod validation schemas for {@link Attachment} and related types.
3
+ *
4
+ * Kept in a separate file from the plain TypeScript types so that consumers
5
+ * that do not need runtime validation (e.g. type-only imports) pay zero cost.
6
+ *
7
+ * All schemas are named `*Schema` and their inferred types are compatible with
8
+ * the canonical TypeScript interfaces in `./attachment.ts`.
9
+ *
10
+ * Uses `z.discriminatedUnion('kind', [...])` for O(1) variant lookup on the
11
+ * `kind` discriminant.
12
+ *
13
+ * @epic T760
14
+ * @task T795
15
+ */
16
+
17
+ import { z } from 'zod';
18
+
19
+ // ─── Shared field schemas ──────────────────────────────────────────────────────
20
+
21
+ /**
22
+ * Common optional fields shared by all attachment variants.
23
+ *
24
+ * Not exported as a standalone validator — always composed into full variant
25
+ * schemas via `.extend()`.
26
+ */
27
+ const attachmentCommonSchema = z.object({
28
+ /** Free-text description of what this attachment contains. */
29
+ description: z.string().optional(),
30
+ /** Labels for filtering (e.g., `["rfc", "spec"]`). */
31
+ labels: z.array(z.string()).optional(),
32
+ });
33
+
34
+ // ─── Variant schemas ───────────────────────────────────────────────────────────
35
+
36
+ /**
37
+ * Zod schema for {@link LocalFileAttachment}.
38
+ *
39
+ * Validates that `path` is non-empty, `sha256` is a 64-character hex string,
40
+ * and `size` is a non-negative integer.
41
+ */
42
+ export const localFileAttachmentSchema = attachmentCommonSchema.extend({
43
+ kind: z.literal('local-file'),
44
+ /** Path to the file (absolute or project-root-relative, forward-slashes only). */
45
+ path: z.string().min(1),
46
+ /** SHA-256 hex digest (64 hex characters). */
47
+ sha256: z.string().length(64),
48
+ /** IANA MIME type. */
49
+ mime: z.string().min(1),
50
+ /** File size in bytes (non-negative integer). */
51
+ size: z.number().int().nonnegative(),
52
+ });
53
+
54
+ /**
55
+ * Zod schema for {@link UrlAttachment}.
56
+ *
57
+ * Validates that `url` is a well-formed absolute URL. The optional `cachedSha256`
58
+ * is validated as a 64-character hex string when present.
59
+ */
60
+ export const urlAttachmentSchema = attachmentCommonSchema.extend({
61
+ kind: z.literal('url'),
62
+ /** The remote URL — must be a valid absolute URL. */
63
+ url: z.string().url(),
64
+ /** SHA-256 of the cached body (64 hex characters). */
65
+ cachedSha256: z.string().length(64).optional(),
66
+ /** ISO 8601 timestamp when the body was cached. */
67
+ cachedAt: z.string().datetime().optional(),
68
+ /** IANA MIME type detected from the Content-Type header. */
69
+ mime: z.string().optional(),
70
+ });
71
+
72
+ /**
73
+ * Zod schema for {@link BlobAttachment}.
74
+ *
75
+ * Validates that `sha256` is 64 hex characters, `storageKey` is non-empty,
76
+ * and `size` is a non-negative integer.
77
+ */
78
+ export const blobAttachmentSchema = attachmentCommonSchema.extend({
79
+ kind: z.literal('blob'),
80
+ /** SHA-256 hex digest of the uncompressed content (64 hex characters). */
81
+ sha256: z.string().length(64),
82
+ /** Storage key inside `.cleo/attachments/sha256/`. */
83
+ storageKey: z.string().min(1),
84
+ /** IANA MIME type. */
85
+ mime: z.string().min(1),
86
+ /** Size of the uncompressed content in bytes. */
87
+ size: z.number().int().nonnegative(),
88
+ });
89
+
90
+ /**
91
+ * Zod schema for {@link LlmsTxtAttachment}.
92
+ *
93
+ * Validates that `content` is non-empty, `sha256` is 64 hex characters, and
94
+ * `source` is one of the two allowed literal values.
95
+ */
96
+ export const llmsTxtAttachmentSchema = attachmentCommonSchema.extend({
97
+ kind: z.literal('llms-txt'),
98
+ /** Where the llms.txt content originated. */
99
+ source: z.enum(['url', 'generated']),
100
+ /** Full markdown content of the llms.txt (stored inline). */
101
+ content: z.string().min(1),
102
+ /** SHA-256 hex digest of `content` (64 hex characters). */
103
+ sha256: z.string().length(64),
104
+ });
105
+
106
+ /**
107
+ * Zod schema for {@link LlmtxtDocAttachment}.
108
+ *
109
+ * Validates that `slug` is non-empty and `backend` is one of the two allowed
110
+ * literal values. `pinnedVersion` is an opaque string when present.
111
+ */
112
+ export const llmtxtDocAttachmentSchema = attachmentCommonSchema.extend({
113
+ kind: z.literal('llmtxt-doc'),
114
+ /** Document slug from the llmtxt backend. */
115
+ slug: z.string().min(1),
116
+ /** Which backend owns the document. */
117
+ backend: z.enum(['local', 'remote']),
118
+ /**
119
+ * Version string pinned at attach time.
120
+ *
121
+ * Opaque: integer version for local backends, API-assigned string for remote.
122
+ */
123
+ pinnedVersion: z.string().optional(),
124
+ });
125
+
126
+ // ─── Discriminated union ──────────────────────────────────────────────────────
127
+
128
+ /**
129
+ * Zod discriminated-union schema for {@link Attachment}.
130
+ *
131
+ * Uses `z.discriminatedUnion('kind', [...])` for O(1) variant lookup.
132
+ * Parse with `attachmentSchema.parse(input)` or
133
+ * `attachmentSchema.safeParse(input)`.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * const att = attachmentSchema.parse({
138
+ * kind: 'local-file',
139
+ * path: 'docs/rfc-003.md',
140
+ * sha256: 'a'.repeat(64),
141
+ * mime: 'text/markdown',
142
+ * size: 8192,
143
+ * description: 'RFC draft v3',
144
+ * });
145
+ * ```
146
+ */
147
+ export const attachmentSchema = z.discriminatedUnion('kind', [
148
+ localFileAttachmentSchema,
149
+ urlAttachmentSchema,
150
+ blobAttachmentSchema,
151
+ llmsTxtAttachmentSchema,
152
+ llmtxtDocAttachmentSchema,
153
+ ]);
154
+
155
+ /** Inferred TypeScript type from {@link attachmentSchema}. */
156
+ export type AttachmentSchemaInput = z.input<typeof attachmentSchema>;
157
+
158
+ // ─── AttachmentMetadata schema ────────────────────────────────────────────────
159
+
160
+ /**
161
+ * Zod schema for {@link AttachmentMetadata}.
162
+ *
163
+ * Validates the persisted registry row. The embedded `attachment` field is
164
+ * validated recursively through the discriminated union.
165
+ */
166
+ export const attachmentMetadataSchema = z.object({
167
+ /**
168
+ * Unique attachment ID.
169
+ *
170
+ * Pattern: `att_<base62>` or UUID fallback. Must be non-empty.
171
+ */
172
+ id: z.string().min(1),
173
+ /**
174
+ * Content hash — SHA-256 hex (64 chars) for byte-backed kinds, empty string
175
+ * for kinds that delegate content storage externally.
176
+ */
177
+ sha256: z.string(),
178
+ /** The full attachment value (validated through the discriminated union). */
179
+ attachment: attachmentSchema,
180
+ /** ISO 8601 creation timestamp. */
181
+ createdAt: z.string().datetime(),
182
+ /**
183
+ * Reference count — how many `AttachmentRef` rows point at this attachment.
184
+ *
185
+ * Must be a non-negative integer.
186
+ */
187
+ refCount: z.number().int().nonnegative(),
188
+ });
189
+
190
+ /** Inferred TypeScript type from {@link attachmentMetadataSchema}. */
191
+ export type AttachmentMetadataSchemaInput = z.input<typeof attachmentMetadataSchema>;
192
+
193
+ // ─── AttachmentRef schema ─────────────────────────────────────────────────────
194
+
195
+ /**
196
+ * Zod schema for {@link AttachmentRef}.
197
+ *
198
+ * Validates the junction row linking one attachment to one owner. `ownerType`
199
+ * is restricted to the six supported CLEO entity types.
200
+ */
201
+ export const attachmentRefSchema = z.object({
202
+ /** ID of the attachment (→ `attachments.id`). Must be non-empty. */
203
+ attachmentId: z.string().min(1),
204
+ /**
205
+ * The domain entity type that owns this attachment.
206
+ *
207
+ * Restricted to the six supported CLEO entity types.
208
+ */
209
+ ownerType: z.enum(['task', 'observation', 'session', 'decision', 'learning', 'pattern']),
210
+ /** ID of the owning entity. Must be non-empty. */
211
+ ownerId: z.string().min(1),
212
+ /** ISO 8601 timestamp when the ref was created. */
213
+ attachedAt: z.string().datetime(),
214
+ /**
215
+ * Agent identity (or `"human"`) that created the ref.
216
+ *
217
+ * Optional; populated from the active session credential when available.
218
+ */
219
+ attachedBy: z.string().optional(),
220
+ });
221
+
222
+ /** Inferred TypeScript type from {@link attachmentRefSchema}. */
223
+ export type AttachmentRefSchemaInput = z.input<typeof attachmentRefSchema>;
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Attachment discriminated union and metadata types.
3
+ *
4
+ * An `Attachment` represents any content artifact that can be associated
5
+ * with a CLEO owner (task, observation, session, decision, learning, pattern).
6
+ * Five attachment kinds are supported:
7
+ * - `local-file` — a file on disk, tracked by path + SHA-256 hash
8
+ * - `url` — a remote URL, optionally cached to the content-addressed store
9
+ * - `blob` — raw bytes stored content-addressed in `.cleo/attachments/sha256/`
10
+ * - `llms-txt` — a generated or fetched llms.txt site-index snapshot (RFC llmstxt.org)
11
+ * - `llmtxt-doc` — a pointer into an llmtxt document (local or remote backend)
12
+ *
13
+ * `AttachmentMetadata` is the row persisted in the attachment registry.
14
+ * `AttachmentRef` is the junction row linking an attachment to an owner.
15
+ *
16
+ * @epic T760
17
+ * @task T795
18
+ * @see {@link https://github.com/kryptobaseddev/cleo} T760 unified attachments
19
+ */
20
+
21
+ // ─── Attachment variants ──────────────────────────────────────────────────────
22
+
23
+ /**
24
+ * A file on disk inside or adjacent to the CLEO project.
25
+ *
26
+ * The `path` is stored as recorded at attach time; the `sha256` is the content
27
+ * hash computed at that moment. On fetch, the runtime re-hashes the file and
28
+ * emits a warning if the file has drifted from the stored hash.
29
+ */
30
+ export interface LocalFileAttachment {
31
+ kind: 'local-file';
32
+ /** Path to the file (absolute or project-root-relative, forward-slashes only). */
33
+ path: string;
34
+ /** SHA-256 hex digest of the file at attach time. */
35
+ sha256: string;
36
+ /** IANA MIME type (e.g., `"text/markdown"`, `"application/pdf"`). */
37
+ mime: string;
38
+ /** File size in bytes at attach time. */
39
+ size: number;
40
+ /** Optional free-text description of what this file contains. */
41
+ description?: string;
42
+ /** Labels for filtering (e.g., `["rfc", "spec"]`). */
43
+ labels?: string[];
44
+ }
45
+
46
+ /**
47
+ * A remote URL, optionally cached to the content-addressed store.
48
+ *
49
+ * When `--cache` is passed on attach, the body is fetched, hashed, and stored
50
+ * at `.cleo/attachments/sha256/<hash[0..2]>/<hash[2..]>.<ext>`. The
51
+ * `cachedSha256` and `cachedAt` fields are populated on a successful cache.
52
+ */
53
+ export interface UrlAttachment {
54
+ kind: 'url';
55
+ /** The remote URL. Must be a valid absolute URL. */
56
+ url: string;
57
+ /** SHA-256 of the fetched body if the URL has been cached locally. */
58
+ cachedSha256?: string;
59
+ /** ISO 8601 timestamp when the body was cached. */
60
+ cachedAt?: string;
61
+ /** IANA MIME type detected from the Content-Type header. */
62
+ mime?: string;
63
+ /** Optional free-text description. */
64
+ description?: string;
65
+ /** Labels for filtering. */
66
+ labels?: string[];
67
+ }
68
+
69
+ /**
70
+ * Raw bytes stored content-addressed in `.cleo/attachments/sha256/`.
71
+ *
72
+ * No external path dependency — the content is always retrievable by
73
+ * `storageKey` as long as the attachment has a positive `refCount`.
74
+ */
75
+ export interface BlobAttachment {
76
+ kind: 'blob';
77
+ /** SHA-256 hex digest of the uncompressed content. */
78
+ sha256: string;
79
+ /** Storage key inside `.cleo/attachments/sha256/` (e.g., `"ab/abcdef…12.pdf"`). */
80
+ storageKey: string;
81
+ /** IANA MIME type. */
82
+ mime: string;
83
+ /** Size of the uncompressed content in bytes. */
84
+ size: number;
85
+ /** Optional free-text description. */
86
+ description?: string;
87
+ /** Labels for filtering. */
88
+ labels?: string[];
89
+ }
90
+
91
+ /**
92
+ * A generated or fetched llms.txt site-index snapshot (per llmstxt.org spec).
93
+ *
94
+ * The content is stored inline — llms.txt files are small by design.
95
+ * `source: 'url'` means it was fetched from a remote site;
96
+ * `source: 'generated'` means it was produced by `cleo docs generate`.
97
+ */
98
+ export interface LlmsTxtAttachment {
99
+ kind: 'llms-txt';
100
+ /** Where the llms.txt content originated. */
101
+ source: 'url' | 'generated';
102
+ /**
103
+ * Full markdown content of the llms.txt (stored inline; small by design per
104
+ * the llmstxt.org spec).
105
+ */
106
+ content: string;
107
+ /** SHA-256 hex digest of `content`. */
108
+ sha256: string;
109
+ /** Optional free-text description. */
110
+ description?: string;
111
+ /** Labels for filtering. */
112
+ labels?: string[];
113
+ }
114
+
115
+ /**
116
+ * A pointer into an llmtxt document managed by a local or remote backend.
117
+ *
118
+ * `backend: 'local'` — the document lives in `.cleo/llmtxt/` (llmtxt LocalBackend).
119
+ * `backend: 'remote'` — the document lives on an llmtxt.my SaaS instance.
120
+ *
121
+ * This attachment kind delegates content storage to the llmtxt package; CLEO
122
+ * only stores the pointer (slug + backend + optional pinned version).
123
+ */
124
+ export interface LlmtxtDocAttachment {
125
+ kind: 'llmtxt-doc';
126
+ /** Document slug from the llmtxt backend (e.g., `"9fZLOnf5"`). */
127
+ slug: string;
128
+ /** Which backend owns the document. */
129
+ backend: 'local' | 'remote';
130
+ /**
131
+ * Version string pinned at attach time.
132
+ *
133
+ * For `local` backends this is the integer version number serialised as a
134
+ * string; for `remote` backends it is the version identifier returned by the
135
+ * API.
136
+ */
137
+ pinnedVersion?: string;
138
+ /** Optional free-text description. */
139
+ description?: string;
140
+ /** Labels for filtering. */
141
+ labels?: string[];
142
+ }
143
+
144
+ // ─── Discriminated union ──────────────────────────────────────────────────────
145
+
146
+ /**
147
+ * A content artifact attachable to any CLEO owner
148
+ * (task, observation, session, decision, learning, or pattern).
149
+ *
150
+ * The `kind` field is the discriminant. All variants share optional
151
+ * `description` and `labels` fields.
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * const att: Attachment = {
156
+ * kind: 'local-file',
157
+ * path: 'docs/rfc-003.md',
158
+ * sha256: 'a1b2c3...ff',
159
+ * mime: 'text/markdown',
160
+ * size: 8192,
161
+ * description: 'RFC draft v3',
162
+ * labels: ['rfc', 'spec'],
163
+ * };
164
+ * ```
165
+ */
166
+ export type Attachment =
167
+ | LocalFileAttachment
168
+ | UrlAttachment
169
+ | BlobAttachment
170
+ | LlmsTxtAttachment
171
+ | LlmtxtDocAttachment;
172
+
173
+ /** All valid `kind` discriminants for `Attachment`. */
174
+ export type AttachmentKind = Attachment['kind'];
175
+
176
+ // ─── Registry row ─────────────────────────────────────────────────────────────
177
+
178
+ /**
179
+ * Persisted metadata row for one attachment in the attachment registry
180
+ * (`.cleo/attachments/index.db`).
181
+ *
182
+ * The full {@link Attachment} value is stored as a JSON blob in the `attachment`
183
+ * field so the registry does not need a column per variant.
184
+ */
185
+ export interface AttachmentMetadata {
186
+ /**
187
+ * Unique attachment ID.
188
+ *
189
+ * Pattern: `att_<base62>` — generated by `llmtxt.generateId()` or a UUID
190
+ * fallback.
191
+ */
192
+ id: string;
193
+ /**
194
+ * Content hash.
195
+ *
196
+ * For `local-file`, `blob`, and `llms-txt` variants this is the SHA-256
197
+ * of the bytes at attach time. For `url` it is `cachedSha256` when the URL
198
+ * has been cached, or the empty string when it has not. For `llmtxt-doc` it
199
+ * is the empty string (content is managed by the llmtxt backend).
200
+ */
201
+ sha256: string;
202
+ /** The full attachment value (all kind-specific fields). */
203
+ attachment: Attachment;
204
+ /** ISO 8601 timestamp when this attachment was first registered. */
205
+ createdAt: string;
206
+ /**
207
+ * How many {@link AttachmentRef} rows point at this attachment.
208
+ *
209
+ * When `refCount` drops to zero the blob is eligible for GC via
210
+ * `cleo docs attachments gc`. Blobs are NEVER auto-deleted.
211
+ */
212
+ refCount: number;
213
+ }
214
+
215
+ // ─── Junction row ─────────────────────────────────────────────────────────────
216
+
217
+ /**
218
+ * Junction row linking one attachment to one owner.
219
+ *
220
+ * Stored in the `attachment_refs` table of `.cleo/attachments/index.db`.
221
+ * A single attachment can be referenced by multiple owners simultaneously
222
+ * (e.g., the same RFC PDF attached to an epic and to every child task).
223
+ */
224
+ export interface AttachmentRef {
225
+ /** ID of the attachment (→ `attachments.id`). */
226
+ attachmentId: string;
227
+ /**
228
+ * The domain entity type that owns this attachment.
229
+ *
230
+ * Determines which CLEO store to query when resolving the owner's display
231
+ * name (`tasks.db` for task/session/decision/learning/pattern;
232
+ * `brain.db` for observation).
233
+ */
234
+ ownerType: 'task' | 'observation' | 'session' | 'decision' | 'learning' | 'pattern';
235
+ /** The ID of the owning entity (e.g., `"T766"`, `"O-abc123"`, `"ses_..."`). */
236
+ ownerId: string;
237
+ /** ISO 8601 timestamp when the ref was created. */
238
+ attachedAt: string;
239
+ /**
240
+ * Agent identity (or `"human"`) that created the ref.
241
+ *
242
+ * Populated from the active session's agent credential when available.
243
+ */
244
+ attachedBy?: string;
245
+ }
package/src/index.ts CHANGED
@@ -6,6 +6,42 @@
6
6
  * Implementation packages (@cleocode/core, @cleocode/cleo) import from here.
7
7
  */
8
8
 
9
+ // === Acceptance Gate Types (machine-verifiable criteria) ===
10
+ export type {
11
+ AcceptanceGate,
12
+ AcceptanceGateKind,
13
+ AcceptanceGateResult,
14
+ CommandGate,
15
+ FileAssertion,
16
+ FileGate,
17
+ GateBase,
18
+ HttpGate,
19
+ LintGate,
20
+ ManualGate,
21
+ TestGate,
22
+ } from './acceptance-gate.js';
23
+ export type {
24
+ AcceptanceArrayInput,
25
+ AcceptanceGateResultInput,
26
+ AcceptanceGateSchemaInput,
27
+ AcceptanceItemInput,
28
+ FileAssertionInput,
29
+ } from './acceptance-gate-schema.js';
30
+ // === Acceptance Gate Zod Schemas (runtime validation) ===
31
+ export {
32
+ acceptanceArraySchema,
33
+ acceptanceGateResultSchema,
34
+ acceptanceGateSchema,
35
+ acceptanceItemSchema,
36
+ commandGateSchema,
37
+ fileAssertionSchema,
38
+ fileGateSchema,
39
+ gateBaseSchema,
40
+ httpGateSchema,
41
+ lintGateSchema,
42
+ manualGateSchema,
43
+ testGateSchema,
44
+ } from './acceptance-gate-schema.js';
9
45
  // === Provider Adapter Contracts ===
10
46
  export type { AdapterHealthStatus, CLEOProviderAdapter } from './adapter.js';
11
47
  // === Agent Registry (credential management) ===
@@ -34,6 +70,18 @@ export type {
34
70
  CycleTimeDistribution,
35
71
  CycleTimePercentiles,
36
72
  } from './archive.js';
73
+ // === Attachment Types ===
74
+ export type {
75
+ Attachment,
76
+ AttachmentKind,
77
+ AttachmentMetadata,
78
+ AttachmentRef,
79
+ BlobAttachment,
80
+ LlmsTxtAttachment,
81
+ LlmtxtDocAttachment,
82
+ LocalFileAttachment,
83
+ UrlAttachment,
84
+ } from './attachment.js';
37
85
  // === Backup Manifest Types ===
38
86
  export type {
39
87
  BackupDatabaseEntry,
@@ -323,6 +371,7 @@ export {
323
371
  } from './status-registry.js';
324
372
  // === Task Types ===
325
373
  export type {
374
+ AcceptanceItem,
326
375
  CancelledTask,
327
376
  CompletedTask,
328
377
  EpicLifecycle,
package/src/task.ts CHANGED
@@ -21,10 +21,23 @@
21
21
  * @task T4456
22
22
  */
23
23
 
24
+ import type { AcceptanceGate } from './acceptance-gate.js';
24
25
  import type { TaskStatus } from './status-registry.js';
25
26
 
26
27
  export type { TaskStatus };
27
28
 
29
+ /**
30
+ * A single acceptance criterion — either a free-text string (legacy) or a
31
+ * structured {@link AcceptanceGate} (machine-verifiable).
32
+ *
33
+ * Mixed arrays of these are stored as JSON in the `acceptance_json` column.
34
+ *
35
+ * @epic T760
36
+ * @task T780
37
+ * @task T800
38
+ */
39
+ export type AcceptanceItem = string | AcceptanceGate;
40
+
28
41
  /** Task priority levels. */
29
42
  export type TaskPriority = 'critical' | 'high' | 'medium' | 'low';
30
43
 
@@ -169,8 +182,15 @@ export interface Task {
169
182
  /** File paths associated with this task. @defaultValue undefined */
170
183
  files?: string[];
171
184
 
172
- /** Acceptance criteria for completion. @defaultValue undefined */
173
- acceptance?: string[];
185
+ /**
186
+ * Mixed acceptance criteria — free-text strings (legacy) and/or structured
187
+ * {@link AcceptanceGate} objects (machine-verifiable).
188
+ *
189
+ * @epic T760
190
+ * @task T780
191
+ * @defaultValue undefined
192
+ */
193
+ acceptance?: AcceptanceItem[];
174
194
 
175
195
  /** IDs of tasks this task depends on. @defaultValue undefined */
176
196
  depends?: string[];
@@ -296,8 +316,8 @@ export interface TaskCreate {
296
316
  /** File paths associated with this task. @defaultValue undefined */
297
317
  files?: string[];
298
318
 
299
- /** Acceptance criteria. @defaultValue undefined */
300
- acceptance?: string[];
319
+ /** Acceptance criteria (string or AcceptanceGate objects). @defaultValue undefined */
320
+ acceptance?: AcceptanceItem[];
301
321
 
302
322
  /** IDs of tasks this task depends on. @defaultValue undefined */
303
323
  depends?: string[];