@taskset/cli 6.0.0 → 6.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @taskset/cli
2
2
 
3
+ ## 6.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add operational memory document kinds (`lesson`, `concern`, `audit`), program rollup, optional closeout gates, and taxonomy allowlists.
8
+
9
+ Agents can create and query lessons, concerns, and audits under `.taskset/` with the same document command surface as existing kinds. `taskset task program` summarizes parent-task programs. Optional `closeout`, `taxonomy`, and `doctor` config keep defaults compatible with existing repositories. `taskset sync` creates the new directories.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+ - @taskset/contracts@6.1.0
15
+ - @taskset/core@6.1.0
16
+
3
17
  ## 6.0.0
4
18
 
5
19
  ### Major Changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @taskset/cli
2
2
 
3
- The public command-line adapter for Taskset, the Git-native workspace for plans, research, decisions, runbooks, and executable tasks. Full documentation is on [taskset.false.foundation](https://taskset.false.foundation/), including the [CLI reference](./docs/cli-reference.md), [document types](./docs/document-types.md), and [agent guide](./docs/agents/index.md).
3
+ The public command-line adapter for Taskset, the Git-native workspace for plans, research, decisions, runbooks, lessons, concerns, audits, and executable tasks. Full documentation is on [taskset.false.foundation](https://taskset.false.foundation/), including the [CLI reference](./docs/cli-reference.md), [document types](./docs/document-types.md), [memory model](./docs/memory-model.md), and [agent guide](./docs/agents/index.md).
4
4
 
5
5
  The published package ships the repository `docs/` and `skills/` trees beside the CLI so agents can load offline references from `node_modules/@taskset/cli/docs` and `node_modules/@taskset/cli/skills`.
6
6
 
@@ -29,7 +29,7 @@ The package owns argument tokenization, Zod-backed command validation, output, a
29
29
  - `taskset document create|import|batch|list|show|update|status|delete` (`doc` is an alias)
30
30
  - `taskset snapshot create|list|restore`
31
31
 
32
- `init` creates `.taskset/` for tasks, stories, flows, decisions, research, and runbooks without requiring a config file. Pass `--config` for optional `taskset.config.ts`.
32
+ `init` creates `.taskset/` for tasks, stories, flows, decisions, research, runbooks, lessons, concerns, and audits without requiring a config file. Pass `--config` for optional `taskset.config.ts`.
33
33
 
34
34
  Exit code `0` means success, `1` means a repository or domain failure, and `2` means invalid CLI usage. Commands reserve stdout for requested output and send diagnostics to stderr.
35
35
 
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAuGA,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACzC;AA6uBD,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAu5B/F"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA6GA,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACzC;AAgvBD,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAy7B/F"}
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { parseArgs } from 'node:util';
4
- import { DocumentIdSchema, DocumentStatusSchema, DocumentTimestampSchema, TaskIdSchema, TaskPrioritySchema, TaskRiskSchema, TaskStatusSchema, TaskTimestampSchema, } from '@taskset/contracts';
5
- import { buildDocumentGraph, buildTaskIndex, createDocument, createSnapshot, createTask, DOCUMENT_SORT_DIRECTIONS, DOCUMENT_SORT_KEYS, DocumentQuerySchema, deleteDocument, deleteTask, diagnoseRepository, discoverRepository, executeDocumentBatch, generateViews, importDocument, initializeRepository, listDocuments, listSnapshots, migrateTaskIds, normalizeDocumentKind, normalizeRepositoryPath, queryDocuments, queryTasks, RepositoryPathError, readDocument, readTask, restoreSnapshot, serializeDocumentFile, serializeTaskFile, syncRepository, TASK_SORT_DIRECTIONS, TASK_SORT_KEYS, TaskQuerySchema, updateDocument, updateTask, } from '@taskset/core';
4
+ import { ConcernClassSchema, DocumentIdSchema, DocumentStatusSchema, DocumentTimestampSchema, LessonSeveritySchema, TaskIdSchema, TaskPrioritySchema, TaskRiskSchema, TaskStatusSchema, TaskTimestampSchema, } from '@taskset/contracts';
5
+ import { buildDocumentGraph, buildTaskIndex, createDocument, createSnapshot, createTask, DOCUMENT_SORT_DIRECTIONS, DOCUMENT_SORT_KEYS, DocumentQuerySchema, deleteDocument, deleteTask, diagnoseRepository, discoverRepository, executeDocumentBatch, generateViews, getProgramRollup, importDocument, initializeRepository, listDocuments, listSnapshots, migrateTaskIds, normalizeDocumentKind, normalizeRepositoryPath, queryDocuments, queryTasks, RepositoryPathError, readDocument, readTask, restoreSnapshot, serializeDocumentFile, serializeTaskFile, syncRepository, TASK_SORT_DIRECTIONS, TASK_SORT_KEYS, TaskQuerySchema, updateDocument, updateTask, } from '@taskset/core';
6
6
  import * as z from 'zod';
7
7
  const USAGE = `Usage:
8
8
  taskset init [--config] [--cwd <path>]
@@ -18,8 +18,9 @@ const USAGE = `Usage:
18
18
  taskset task status <task-id> <status> [--json] [--cwd <path>]
19
19
  taskset task delete <task-id> [--remove-dependencies] [--json] [--cwd <path>]
20
20
  taskset task migrate-ids [--json] [--cwd <path>]
21
+ taskset task program <parent-id> [--json] [--cwd <path>]
21
22
  taskset sync [--concurrency <count>] [--json] [--cwd <path>]
22
- taskset document create <story|flow|decision|adr|dr|research|runbook> --title <title> [metadata options]
23
+ taskset document create <story|flow|decision|adr|dr|research|runbook|lesson|antipattern|concern|audit> --title <title> [metadata options]
23
24
  taskset document import <markdown-path> [--type <type>] [--move]
24
25
  taskset document batch <manifest.json> [--concurrency <count>] [--json]
25
26
  taskset document list [type] [query options]
@@ -32,10 +33,12 @@ Metadata options:
32
33
  --status --priority --order --owner --team --estimate --effort --risk --due-date
33
34
  --label --assignee --reviewer --depends-on --related --duplicate
34
35
  --parent --file --directory --project --body
36
+ Lesson/concern options: --severity --related-skill --pack --class --cadence
35
37
  Repeat array options. Task and document updates clear arrays with --clear-dependencies,
36
38
  --clear-labels, --clear-assignees, --clear-reviewers, --clear-related,
37
- --clear-files, --clear-directories, or --clear-projects. Clear scalar
38
- relationships with --clear-parent or --clear-owner.
39
+ --clear-files, --clear-directories, --clear-projects, --clear-related-skills,
40
+ or --clear-packs. Clear scalar relationships with --clear-parent, --clear-owner,
41
+ --clear-severity, --clear-class, or --clear-cadence.
39
42
 
40
43
  List query options:
41
44
  --status --priority --label --owner --assignee --reviewer --team --risk
@@ -106,6 +109,11 @@ const DocumentInputSchema = z.strictObject({
106
109
  parent: TaskIdSchema.optional(),
107
110
  directories: StringListSchema.optional(),
108
111
  projects: StringListSchema.optional(),
112
+ severity: LessonSeveritySchema.optional(),
113
+ relatedSkills: StringListSchema.optional(),
114
+ packs: StringListSchema.optional(),
115
+ class: ConcernClassSchema.optional(),
116
+ cadence: TrimmedStringSchema.optional(),
109
117
  body: z.string().optional(),
110
118
  });
111
119
  const DocumentUpdateSchema = z
@@ -130,6 +138,11 @@ const DocumentUpdateSchema = z
130
138
  parent: TaskIdSchema.nullable().optional(),
131
139
  directories: StringListSchema.optional(),
132
140
  projects: StringListSchema.optional(),
141
+ severity: LessonSeveritySchema.nullable().optional(),
142
+ relatedSkills: StringListSchema.optional(),
143
+ packs: StringListSchema.optional(),
144
+ class: ConcernClassSchema.nullable().optional(),
145
+ cadence: TrimmedStringSchema.nullable().optional(),
133
146
  body: z.string().optional(),
134
147
  })
135
148
  .refine((input) => Object.keys(input).length > 0, 'Document update requires at least one field');
@@ -182,6 +195,14 @@ const UPDATE_CLEAR_PAIRS = [
182
195
  ['directory', 'clear-directories'],
183
196
  ['project', 'clear-projects'],
184
197
  ];
198
+ const DOCUMENT_UPDATE_CLEAR_PAIRS = [
199
+ ...UPDATE_CLEAR_PAIRS,
200
+ ['severity', 'clear-severity'],
201
+ ['related-skill', 'clear-related-skills'],
202
+ ['pack', 'clear-packs'],
203
+ ['class', 'clear-class'],
204
+ ['cadence', 'clear-cadence'],
205
+ ];
185
206
  const DocumentCreateValuesSchema = z.strictObject({
186
207
  title: TrimmedStringSchema,
187
208
  status: DocumentStatusSchema.optional(),
@@ -203,6 +224,11 @@ const DocumentCreateValuesSchema = z.strictObject({
203
224
  parent: TaskIdSchema.optional(),
204
225
  directory: StringListSchema.optional(),
205
226
  project: StringListSchema.optional(),
227
+ severity: LessonSeveritySchema.optional(),
228
+ 'related-skill': StringListSchema.optional(),
229
+ pack: StringListSchema.optional(),
230
+ class: ConcernClassSchema.optional(),
231
+ cadence: TrimmedStringSchema.optional(),
206
232
  body: z.string().optional(),
207
233
  cwd: CwdSchema,
208
234
  json: JsonSchema,
@@ -229,6 +255,11 @@ const DocumentUpdateValuesSchema = z
229
255
  parent: TaskIdSchema.optional(),
230
256
  directory: StringListSchema.optional(),
231
257
  project: StringListSchema.optional(),
258
+ severity: LessonSeveritySchema.optional(),
259
+ 'related-skill': StringListSchema.optional(),
260
+ pack: StringListSchema.optional(),
261
+ class: ConcernClassSchema.optional(),
262
+ cadence: TrimmedStringSchema.optional(),
232
263
  body: z.string().optional(),
233
264
  type: TrimmedStringSchema.optional(),
234
265
  'clear-priority': z.boolean().optional(),
@@ -249,11 +280,16 @@ const DocumentUpdateValuesSchema = z
249
280
  'clear-parent': z.boolean().optional(),
250
281
  'clear-directories': z.boolean().optional(),
251
282
  'clear-projects': z.boolean().optional(),
283
+ 'clear-severity': z.boolean().optional(),
284
+ 'clear-related-skills': z.boolean().optional(),
285
+ 'clear-packs': z.boolean().optional(),
286
+ 'clear-class': z.boolean().optional(),
287
+ 'clear-cadence': z.boolean().optional(),
252
288
  cwd: CwdSchema,
253
289
  json: JsonSchema,
254
290
  })
255
291
  .superRefine((values, context) => {
256
- for (const [valueKey, clearKey] of UPDATE_CLEAR_PAIRS) {
292
+ for (const [valueKey, clearKey] of DOCUMENT_UPDATE_CLEAR_PAIRS) {
257
293
  if (values[valueKey] !== undefined && values[clearKey]) {
258
294
  context.addIssue({
259
295
  code: 'custom',
@@ -280,6 +316,8 @@ const DocumentListValuesSchema = z.strictObject({
280
316
  team: StringListSchema.optional(),
281
317
  risk: z.array(TaskRiskSchema).optional(),
282
318
  project: StringListSchema.optional(),
319
+ class: z.array(ConcernClassSchema).optional(),
320
+ severity: z.array(LessonSeveritySchema).optional(),
283
321
  'depends-on': TaskIdSchema.optional(),
284
322
  related: TaskIdSchema.optional(),
285
323
  duplicate: TaskIdSchema.optional(),
@@ -448,6 +486,14 @@ const metadataOptionDefinitions = {
448
486
  directory: { type: 'string', multiple: true },
449
487
  project: { type: 'string', multiple: true },
450
488
  };
489
+ const documentMetadataOptionDefinitions = {
490
+ ...metadataOptionDefinitions,
491
+ severity: { type: 'string' },
492
+ 'related-skill': { type: 'string', multiple: true },
493
+ pack: { type: 'string', multiple: true },
494
+ class: { type: 'string' },
495
+ cadence: { type: 'string' },
496
+ };
451
497
  const commonOptionDefinitions = {
452
498
  cwd: { type: 'string' },
453
499
  json: { type: 'boolean' },
@@ -618,101 +664,107 @@ function updateInputFromValues(repository, values) {
618
664
  };
619
665
  }
620
666
  function documentUpdateInputFromValues(repository, values) {
621
- return {
622
- ...(values.title !== undefined ? { title: values.title } : {}),
623
- ...(values.status !== undefined ? { status: values.status } : {}),
624
- ...(values['clear-priority']
625
- ? { priority: null }
626
- : values.priority !== undefined
627
- ? { priority: values.priority }
628
- : {}),
629
- ...(values['clear-order']
630
- ? { order: null }
631
- : values.order !== undefined
632
- ? { order: values.order }
633
- : {}),
634
- ...(values['clear-labels']
635
- ? { labels: [] }
636
- : values.label !== undefined
637
- ? { labels: values.label }
638
- : {}),
639
- ...(values['clear-dependencies']
640
- ? { dependsOn: [] }
641
- : values['depends-on'] !== undefined
642
- ? { dependsOn: values['depends-on'] }
643
- : {}),
644
- ...(values['clear-files']
645
- ? { files: [] }
646
- : values.file !== undefined
647
- ? { files: normalizeMutationPaths(repository, values.file) }
648
- : {}),
649
- ...(values['clear-owner']
650
- ? { owner: null }
651
- : values.owner !== undefined
652
- ? { owner: values.owner }
653
- : {}),
654
- ...(values['clear-assignees']
655
- ? { assignees: [] }
656
- : values.assignee !== undefined
657
- ? { assignees: values.assignee }
658
- : {}),
659
- ...(values['clear-reviewers']
660
- ? { reviewers: [] }
661
- : values.reviewer !== undefined
662
- ? { reviewers: values.reviewer }
663
- : {}),
664
- ...(values['clear-team']
665
- ? { team: null }
666
- : values.team !== undefined
667
- ? { team: values.team }
668
- : {}),
669
- ...(values['clear-estimate']
670
- ? { estimate: null }
671
- : values.estimate !== undefined
672
- ? { estimate: values.estimate }
673
- : {}),
674
- ...(values['clear-effort']
675
- ? { effort: null }
676
- : values.effort !== undefined
677
- ? { effort: values.effort }
678
- : {}),
679
- ...(values['clear-risk']
680
- ? { risk: null }
681
- : values.risk !== undefined
682
- ? { risk: values.risk }
683
- : {}),
684
- ...(values['clear-due-date']
685
- ? { dueDate: null }
686
- : values['due-date'] !== undefined
687
- ? { dueDate: values['due-date'] }
688
- : {}),
689
- ...(values['clear-related']
690
- ? { related: [] }
691
- : values.related !== undefined
692
- ? { related: values.related }
693
- : {}),
694
- ...(values['clear-duplicates']
695
- ? { duplicates: [] }
696
- : values.duplicate !== undefined
697
- ? { duplicates: values.duplicate }
698
- : {}),
699
- ...(values['clear-parent']
700
- ? { parent: null }
701
- : values.parent !== undefined
702
- ? { parent: values.parent }
703
- : {}),
704
- ...(values['clear-directories']
705
- ? { directories: [] }
706
- : values.directory !== undefined
707
- ? { directories: normalizeMutationPaths(repository, values.directory) }
708
- : {}),
709
- ...(values['clear-projects']
710
- ? { projects: [] }
711
- : values.project !== undefined
712
- ? { projects: values.project }
713
- : {}),
714
- ...(values.body !== undefined ? { body: values.body } : {}),
715
- };
667
+ const input = {};
668
+ if (values.title !== undefined)
669
+ input.title = values.title;
670
+ if (values.status !== undefined)
671
+ input.status = values.status;
672
+ if (values['clear-priority'])
673
+ input.priority = null;
674
+ else if (values.priority !== undefined)
675
+ input.priority = values.priority;
676
+ if (values['clear-order'])
677
+ input.order = null;
678
+ else if (values.order !== undefined)
679
+ input.order = values.order;
680
+ if (values['clear-labels'])
681
+ input.labels = [];
682
+ else if (values.label !== undefined)
683
+ input.labels = values.label;
684
+ if (values['clear-dependencies'])
685
+ input.dependsOn = [];
686
+ else if (values['depends-on'] !== undefined)
687
+ input.dependsOn = values['depends-on'];
688
+ if (values['clear-files'])
689
+ input.files = [];
690
+ else if (values.file !== undefined)
691
+ input.files = normalizeMutationPaths(repository, values.file);
692
+ if (values['clear-owner'])
693
+ input.owner = null;
694
+ else if (values.owner !== undefined)
695
+ input.owner = values.owner;
696
+ if (values['clear-assignees'])
697
+ input.assignees = [];
698
+ else if (values.assignee !== undefined)
699
+ input.assignees = values.assignee;
700
+ if (values['clear-reviewers'])
701
+ input.reviewers = [];
702
+ else if (values.reviewer !== undefined)
703
+ input.reviewers = values.reviewer;
704
+ if (values['clear-team'])
705
+ input.team = null;
706
+ else if (values.team !== undefined)
707
+ input.team = values.team;
708
+ if (values['clear-estimate'])
709
+ input.estimate = null;
710
+ else if (values.estimate !== undefined)
711
+ input.estimate = values.estimate;
712
+ if (values['clear-effort'])
713
+ input.effort = null;
714
+ else if (values.effort !== undefined)
715
+ input.effort = values.effort;
716
+ if (values['clear-risk'])
717
+ input.risk = null;
718
+ else if (values.risk !== undefined)
719
+ input.risk = values.risk;
720
+ if (values['clear-due-date'])
721
+ input.dueDate = null;
722
+ else if (values['due-date'] !== undefined)
723
+ input.dueDate = values['due-date'];
724
+ if (values['clear-related'])
725
+ input.related = [];
726
+ else if (values.related !== undefined)
727
+ input.related = values.related;
728
+ if (values['clear-duplicates'])
729
+ input.duplicates = [];
730
+ else if (values.duplicate !== undefined)
731
+ input.duplicates = values.duplicate;
732
+ if (values['clear-parent'])
733
+ input.parent = null;
734
+ else if (values.parent !== undefined)
735
+ input.parent = values.parent;
736
+ if (values['clear-directories'])
737
+ input.directories = [];
738
+ else if (values.directory !== undefined) {
739
+ input.directories = normalizeMutationPaths(repository, values.directory);
740
+ }
741
+ if (values['clear-projects'])
742
+ input.projects = [];
743
+ else if (values.project !== undefined)
744
+ input.projects = values.project;
745
+ if (values['clear-severity'])
746
+ input.severity = null;
747
+ else if (values.severity !== undefined)
748
+ input.severity = values.severity;
749
+ if (values['clear-related-skills'])
750
+ input.relatedSkills = [];
751
+ else if (values['related-skill'] !== undefined)
752
+ input.relatedSkills = values['related-skill'];
753
+ if (values['clear-packs'])
754
+ input.packs = [];
755
+ else if (values.pack !== undefined)
756
+ input.packs = values.pack;
757
+ if (values['clear-class'])
758
+ input.class = null;
759
+ else if (values.class !== undefined)
760
+ input.class = values.class;
761
+ if (values['clear-cadence'])
762
+ input.cadence = null;
763
+ else if (values.cadence !== undefined)
764
+ input.cadence = values.cadence;
765
+ if (values.body !== undefined)
766
+ input.body = values.body;
767
+ return input;
716
768
  }
717
769
  export async function runCli(args, context = {}) {
718
770
  const cwd = path.resolve(context.cwd ?? process.cwd());
@@ -776,7 +828,7 @@ export async function runCli(args, context = {}) {
776
828
  stdout(`${JSON.stringify(result, null, 2)}\n`);
777
829
  }
778
830
  else if (result.valid) {
779
- stdout(`Taskset repository is valid (${result.taskCount} tasks)\n`);
831
+ stdout(`Taskset repository is valid (${result.taskCount} tasks, ${result.documentCount} documents)\n`);
780
832
  }
781
833
  else {
782
834
  for (const diagnostic of result.diagnostics) {
@@ -896,7 +948,7 @@ export async function runCli(args, context = {}) {
896
948
  const parsed = parseArgs({
897
949
  args: subcommandArgs,
898
950
  allowPositionals: true,
899
- options: { ...metadataOptionDefinitions, ...commonOptionDefinitions },
951
+ options: { ...documentMetadataOptionDefinitions, ...commonOptionDefinitions },
900
952
  });
901
953
  const [rawType] = requirePositionals(parsed.positionals, 1, 'exactly one document type');
902
954
  const values = parseSchema(DocumentCreateValuesSchema, parsed.values, 'document create options');
@@ -925,6 +977,11 @@ export async function runCli(args, context = {}) {
925
977
  ? { directories: normalizeMutationPaths(repository, values.directory) }
926
978
  : {}),
927
979
  ...(values.project ? { projects: values.project } : {}),
980
+ ...(values.severity ? { severity: values.severity } : {}),
981
+ ...(values['related-skill'] ? { relatedSkills: values['related-skill'] } : {}),
982
+ ...(values.pack ? { packs: values.pack } : {}),
983
+ ...(values.class ? { class: values.class } : {}),
984
+ ...(values.cadence ? { cadence: values.cadence } : {}),
928
985
  ...(values.body !== undefined ? { body: values.body } : {}),
929
986
  }, { onWarning });
930
987
  writeDocumentRecord(record, values.json, stdout);
@@ -974,6 +1031,8 @@ export async function runCli(args, context = {}) {
974
1031
  team: { type: 'string', multiple: true },
975
1032
  risk: { type: 'string', multiple: true },
976
1033
  project: { type: 'string', multiple: true },
1034
+ class: { type: 'string', multiple: true },
1035
+ severity: { type: 'string', multiple: true },
977
1036
  'depends-on': { type: 'string' },
978
1037
  related: { type: 'string' },
979
1038
  duplicate: { type: 'string' },
@@ -1014,6 +1073,8 @@ export async function runCli(args, context = {}) {
1014
1073
  ...(values.team ? { teams: values.team } : {}),
1015
1074
  ...(values.risk ? { risks: values.risk } : {}),
1016
1075
  ...(values.project ? { projects: values.project } : {}),
1076
+ ...(values.class ? { classes: values.class } : {}),
1077
+ ...(values.severity ? { severities: values.severity } : {}),
1017
1078
  ...(values['depends-on'] ? { dependsOn: values['depends-on'] } : {}),
1018
1079
  ...(values.related ? { related: values.related } : {}),
1019
1080
  ...(values.duplicate ? { duplicate: values.duplicate } : {}),
@@ -1099,12 +1160,12 @@ export async function runCli(args, context = {}) {
1099
1160
  return 0;
1100
1161
  }
1101
1162
  if (subcommand === 'update') {
1102
- const clearDefinitions = Object.fromEntries(UPDATE_CLEAR_PAIRS.map(([, clear]) => [clear, { type: 'boolean' }]));
1163
+ const clearDefinitions = Object.fromEntries(DOCUMENT_UPDATE_CLEAR_PAIRS.map(([, clear]) => [clear, { type: 'boolean' }]));
1103
1164
  const parsed = parseArgs({
1104
1165
  args: subcommandArgs,
1105
1166
  allowPositionals: true,
1106
1167
  options: {
1107
- ...metadataOptionDefinitions,
1168
+ ...documentMetadataOptionDefinitions,
1108
1169
  ...clearDefinitions,
1109
1170
  ...commonOptionDefinitions,
1110
1171
  type: { type: 'string' },
@@ -1187,6 +1248,24 @@ export async function runCli(args, context = {}) {
1187
1248
  stdout(`${migration.from}\t${migration.to}\n`);
1188
1249
  return 0;
1189
1250
  }
1251
+ if (subcommand === 'program') {
1252
+ const parsed = parseArgs({
1253
+ args: subcommandArgs,
1254
+ allowPositionals: true,
1255
+ options: commonOptionDefinitions,
1256
+ });
1257
+ const [parentId] = requirePositionals(parsed.positionals, 1, 'exactly one parent task ID');
1258
+ const values = parseSchema(CommonValuesSchema, parsed.values, 'task program options');
1259
+ const repository = await discoverRepository(resolveCommandCwd(cwd, values.cwd));
1260
+ const rollup = await getProgramRollup(repository, parseSchema(TaskIdSchema, parentId, 'parent task ID'));
1261
+ if (values.json) {
1262
+ stdout(`${JSON.stringify(rollup, null, 2)}\n`);
1263
+ }
1264
+ else {
1265
+ stdout(`${rollup.parentId}\t${rollup.status}\tchildren=${rollup.children.total}\topen=${rollup.children.openIds.length}\tconcerns=${rollup.relatedOpenConcerns.length}\tcloseoutReady=${rollup.closeoutReady}\t${rollup.title}\n`);
1266
+ }
1267
+ return 0;
1268
+ }
1190
1269
  if (subcommand === 'create') {
1191
1270
  const parsed = parseArgs({
1192
1271
  args: subcommandArgs,
package/docs/_meta.ts CHANGED
@@ -5,5 +5,9 @@ export default {
5
5
  'cli-reference': 'CLI Reference',
6
6
  'task-files': 'Task Files',
7
7
  'document-types': 'Document Types',
8
+ 'memory-model': 'Memory Model',
9
+ 'security-compliance-tracking': 'Security Tracking',
10
+ 'agent-closeout': 'Agent Closeout',
11
+ 'taxonomy-cookbook': 'Taxonomy Cookbook',
8
12
  agents: 'For Agents',
9
13
  }
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: Follow the agent closeout contract
3
+ description: Subtasks done, related docs created, taxonomy valid, optional lesson and skill promotion.
4
+ contentType: How-to
5
+ navLabel: Agent Closeout
6
+ ---
7
+
8
+ # Follow the agent closeout contract
9
+
10
+ Before marking a Taskset task `done`, agents must leave the repository in a state another agent can trust.
11
+
12
+ ## Required
13
+
14
+ 1. Every checklist item for the outcome is checked (`- [x]`), or intentionally removed with rationale.
15
+ 2. Every child task is `done` or `canceled` (not left `todo` / `doing` / `blocked`).
16
+ 3. Durable outputs exist as documents when the work produced them:
17
+ - evidence → `research`
18
+ - lasting choice → `decision` / `adr`
19
+ - procedure → `runbook`
20
+ - product context → `story` / `flow`
21
+ - recurring mistake → `lesson`
22
+ - residual risk → `concern`
23
+ - inventory / spot-check → `audit`
24
+ 4. Documents and tasks are linked with `--related` (short hex IDs in CLI flags).
25
+ 5. Markdown prose that points at those files uses repository-relative filepaths, not bare hex ids.
26
+ 6. Labels and projects reuse repository taxonomy; do not invent one-off tags when an allowlist exists.
27
+ 7. `taskset doctor --json` reports no errors for the paths you touched.
28
+
29
+ ## Optional config gates
30
+
31
+ When enabled in `taskset.config.ts`, core blocks `done` transitions that violate:
32
+
33
+ ```typescript
34
+ closeout: {
35
+ enforceChildCompletion: true,
36
+ blockDoneWithOpenConcerns: true,
37
+ requireLessonWhenLabeled: ['requires-lesson'],
38
+ }
39
+ ```
40
+
41
+ Defaults are off so existing repositories keep current behavior.
42
+
43
+ ## Skill promotion bridge
44
+
45
+ Lessons may declare skill targets:
46
+
47
+ ```bash
48
+ taskset document create lesson \
49
+ --title "…" \
50
+ --related-skill .agents/skills/foo/SKILL.md \
51
+ --related <task-id>
52
+ ```
53
+
54
+ Contract:
55
+
56
+ - Taskset stores the lesson evidence and correct pattern.
57
+ - The consumer primary skill stores lasting how-to.
58
+ - Agents update the skill in the same change when `--related-skill` is present.
59
+ - Taskset does not auto-edit skills unless a future promote helper is explicitly applied with config opt-in.
60
+
61
+ ## Program closeout
62
+
63
+ For multi-task programs:
64
+
65
+ ```bash
66
+ taskset task program <parent-id> --json
67
+ ```
68
+
69
+ Treat `closeoutReady: false` as a signal to finish children, accept or archive open concerns, accept research, and complete checklist items before marking the parent done.
@@ -2,4 +2,5 @@ export default {
2
2
  index: 'For Agents',
3
3
  workflows: 'Agent Workflows',
4
4
  commands: 'Agent Commands',
5
+ 'query-recipes': 'Query Recipes',
5
6
  }
@@ -27,12 +27,15 @@ This page quotes the contracts agents should rely on. For the full human referen
27
27
  - Exit `1` means repository or domain failure
28
28
  - Exit `2` means usage or validation failure
29
29
 
30
- ## Identifiers
30
+ ## Identifiers and file links
31
31
 
32
32
  - Entity `id` values are immutable 5–6 character lowercase hex strings
33
33
  - Filenames are `{sequence}-{slug}-{id}.md`
34
- - Commands and relationships must use the short `id`
35
- - Never cite the mutable sequence prefix as identity
34
+ - Commands, frontmatter relationships, and JSON handoffs use the short `id`
35
+ - Filename sequence prefixes are display metadata only—never identity
36
+ - Markdown hyperlinks to docs, skills, or `.taskset/` entities must use the
37
+ repository-relative filepath (include the `.md` file). Do not use a bare hex
38
+ id as a link target.
36
39
 
37
40
  ## Array updates and clear flags
38
41
 
@@ -48,6 +51,11 @@ Array options replace the whole stored array. Repeat the singular option once pe
48
51
  - `--clear-projects`
49
52
  - `--clear-parent`
50
53
  - `--clear-owner`
54
+ - `--clear-severity`
55
+ - `--clear-related-skills`
56
+ - `--clear-packs`
57
+ - `--clear-class`
58
+ - `--clear-cadence`
51
59
 
52
60
  Do not guess a clear flag from the singular setter name.
53
61
 
@@ -66,5 +74,21 @@ Use only these kinds:
66
74
  - `decision` (`adr`, `dr` aliases)
67
75
  - `research`
68
76
  - `runbook`
77
+ - `lesson` (`antipattern` alias)
78
+ - `concern`
79
+ - `audit`
69
80
 
70
81
  Document statuses are `draft`, `ready`, `active`, `accepted`, `superseded`, and `archived`.
82
+
83
+ Lesson options: `--severity`, repeatable `--related-skill`, repeatable `--pack`.
84
+ Concern options: `--class`, `--cadence`. List filters include `--class` and `--severity`.
85
+
86
+ ## Program rollup
87
+
88
+ ```bash
89
+ taskset task program <parent-id> --json
90
+ ```
91
+
92
+ Returns child status counts, blocked deps, related open concerns, research not yet accepted, checklist completion, and `closeoutReady`.
93
+
94
+ Copy-paste recipes: [Query recipes](query-recipes.md).