@topogram/cli 0.3.78 → 0.3.79

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 (79) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/package.json +2 -2
  3. package/src/agent-brief.js +29 -23
  4. package/src/agent-ops/query-builders/change-risk/{import-plan.js → extract-plan.js} +1 -1
  5. package/src/agent-ops/query-builders/change-risk/review-packets.js +5 -5
  6. package/src/agent-ops/query-builders/change-risk.js +1 -1
  7. package/src/agent-ops/query-builders/common.js +2 -2
  8. package/src/agent-ops/query-builders/multi-agent.js +1 -1
  9. package/src/agent-ops/query-builders/workflow-context-shared.js +4 -4
  10. package/src/catalog/provenance.js +1 -1
  11. package/src/cli/catalog-alias.d.ts +2 -0
  12. package/src/cli/catalog-alias.js +2 -2
  13. package/src/cli/command-parsers/core.js +9 -5
  14. package/src/cli/command-parsers/import.js +11 -17
  15. package/src/cli/command-parsers/project.js +0 -3
  16. package/src/cli/commands/catalog/copy.js +3 -3
  17. package/src/cli/commands/catalog/help.js +1 -2
  18. package/src/cli/commands/catalog/list.js +7 -4
  19. package/src/cli/commands/catalog/show.js +4 -4
  20. package/src/cli/commands/copy.js +356 -0
  21. package/src/cli/commands/doctor.js +1 -1
  22. package/src/cli/commands/import/adopt.js +9 -9
  23. package/src/cli/commands/import/check.js +15 -15
  24. package/src/cli/commands/import/diff.js +6 -6
  25. package/src/cli/commands/import/help.js +43 -34
  26. package/src/cli/commands/import/paths.js +3 -3
  27. package/src/cli/commands/import/plan.js +8 -8
  28. package/src/cli/commands/import/refresh.js +25 -24
  29. package/src/cli/commands/import/status-history.js +4 -4
  30. package/src/cli/commands/import/workspace.js +16 -16
  31. package/src/cli/commands/import-runner.js +6 -5
  32. package/src/cli/commands/import.js +4 -1
  33. package/src/cli/commands/init.js +67 -0
  34. package/src/cli/commands/query/{import-adopt.js → extract-adopt.js} +2 -2
  35. package/src/cli/commands/query/runner/change.js +2 -2
  36. package/src/cli/commands/query/runner/{import-adopt.js → extract-adopt.js} +9 -9
  37. package/src/cli/commands/query/runner/index.js +1 -1
  38. package/src/cli/commands/query/runner/workflow.js +7 -7
  39. package/src/cli/commands/query/workspace.js +4 -4
  40. package/src/cli/commands/release-status.js +2 -2
  41. package/src/cli/commands/source.js +2 -2
  42. package/src/cli/commands/template/check.js +2 -2
  43. package/src/cli/commands/template/list-show.js +4 -4
  44. package/src/cli/dispatcher.js +18 -3
  45. package/src/cli/help-dispatch.js +22 -8
  46. package/src/cli/help.js +68 -52
  47. package/src/cli/migration-guidance.js +9 -0
  48. package/src/generator/context/bundle.js +14 -7
  49. package/src/generator/context/diff.js +8 -1
  50. package/src/generator/context/digest.js +10 -1
  51. package/src/generator/context/shared/domain-sdlc.js +5 -1
  52. package/src/generator/context/shared/relationships.js +20 -5
  53. package/src/generator/context/shared/summaries.js +26 -0
  54. package/src/generator/context/shared.d.ts +1 -0
  55. package/src/generator/context/shared.js +1 -0
  56. package/src/generator/context/slice/core.js +9 -5
  57. package/src/generator/context/slice/sdlc.js +31 -2
  58. package/src/generator/context/task-mode.js +3 -3
  59. package/src/import/core/runner/reports.js +4 -4
  60. package/src/import/provenance.js +16 -16
  61. package/src/init-project.js +215 -0
  62. package/src/new-project/constants.js +1 -1
  63. package/src/new-project/create.js +2 -2
  64. package/src/new-project/project-files.js +7 -7
  65. package/src/reconcile/journeys.js +8 -3
  66. package/src/record-blocks.js +125 -0
  67. package/src/resolver/index.js +3 -0
  68. package/src/resolver/journeys.js +74 -0
  69. package/src/resolver/normalize.js +25 -0
  70. package/src/sdlc/adopt.js +1 -1
  71. package/src/validator/common.js +34 -1
  72. package/src/validator/index.js +4 -0
  73. package/src/validator/kinds.d.ts +2 -0
  74. package/src/validator/kinds.js +34 -1
  75. package/src/validator/per-kind/journey.js +233 -0
  76. package/src/workflows/docs-generate.js +4 -1
  77. package/src/workflows/reconcile/bundle-core/index.js +4 -2
  78. package/src/workflows/reconcile/canonical-surface.js +4 -1
  79. package/src/cli/commands/new.js +0 -94
@@ -0,0 +1,125 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {{
5
+ * key: string,
6
+ * value: import("./parser.js").AstValue | null,
7
+ * values: import("./parser.js").AstValue[],
8
+ * loc: import("./parser.js").AstLocation
9
+ * }} TopogramRecordField
10
+ *
11
+ * @typedef {{
12
+ * fields: Map<string, TopogramRecordField[]>,
13
+ * fieldOrder: TopogramRecordField[],
14
+ * loc: import("./parser.js").AstLocation
15
+ * }} TopogramRecordBlock
16
+ */
17
+
18
+ /**
19
+ * @param {import("./parser.js").AstValue[]} values
20
+ * @param {import("./parser.js").AstLocation} loc
21
+ * @returns {import("./parser.js").AstValue | null}
22
+ */
23
+ function valuesToRecordValue(values, loc) {
24
+ if (values.length === 0) {
25
+ return null;
26
+ }
27
+ if (values.length === 1) {
28
+ return values[0];
29
+ }
30
+ return {
31
+ type: "sequence",
32
+ items: values,
33
+ loc
34
+ };
35
+ }
36
+
37
+ /**
38
+ * @param {import("./parser.js").AstBlock} block
39
+ * @returns {TopogramRecordBlock}
40
+ */
41
+ export function parseRecordBlock(block) {
42
+ /** @type {Map<string, TopogramRecordField[]>} */
43
+ const fields = new Map();
44
+ /** @type {TopogramRecordField[]} */
45
+ const fieldOrder = [];
46
+
47
+ for (const entry of block.entries || []) {
48
+ const [keyToken, ...values] = entry.items || [];
49
+ const key = keyToken?.type === "symbol" ? keyToken.value : "";
50
+ const field = {
51
+ key,
52
+ value: valuesToRecordValue(values, entry.loc),
53
+ values,
54
+ loc: entry.loc
55
+ };
56
+ if (!fields.has(key)) {
57
+ fields.set(key, []);
58
+ }
59
+ fields.get(key)?.push(field);
60
+ fieldOrder.push(field);
61
+ }
62
+
63
+ return {
64
+ fields,
65
+ fieldOrder,
66
+ loc: block.loc
67
+ };
68
+ }
69
+
70
+ /**
71
+ * @param {TopogramRecordBlock} record
72
+ * @param {string} key
73
+ * @returns {TopogramRecordField | null}
74
+ */
75
+ export function recordField(record, key) {
76
+ return record.fields.get(key)?.[0] || null;
77
+ }
78
+
79
+ /**
80
+ * @param {TopogramRecordBlock} record
81
+ * @param {string} key
82
+ * @returns {string | null}
83
+ */
84
+ export function recordSymbol(record, key) {
85
+ const value = recordField(record, key)?.value;
86
+ return value?.type === "symbol" ? value.value : null;
87
+ }
88
+
89
+ /**
90
+ * @param {TopogramRecordBlock} record
91
+ * @param {string} key
92
+ * @returns {string | null}
93
+ */
94
+ export function recordString(record, key) {
95
+ const value = recordField(record, key)?.value;
96
+ return value?.type === "string" ? value.value : null;
97
+ }
98
+
99
+ /**
100
+ * @param {TopogramRecordBlock} record
101
+ * @param {string} key
102
+ * @returns {string[]}
103
+ */
104
+ export function recordStringList(record, key) {
105
+ const value = recordField(record, key)?.value;
106
+ if (!value) return [];
107
+ const items = value.type === "list" ? value.items : value.type === "sequence" ? value.items : [value];
108
+ return items
109
+ .map((item) => item.type === "string" ? item.value : null)
110
+ .filter(/** @param {string | null} value */ (value) => value !== null);
111
+ }
112
+
113
+ /**
114
+ * @param {TopogramRecordBlock} record
115
+ * @param {string} key
116
+ * @returns {string[]}
117
+ */
118
+ export function recordSymbolList(record, key) {
119
+ const value = recordField(record, key)?.value;
120
+ if (!value) return [];
121
+ const items = value.type === "list" ? value.items : value.type === "sequence" ? value.items : [value];
122
+ return items
123
+ .map((item) => item.type === "symbol" ? item.value : null)
124
+ .filter(/** @param {string | null} value */ (value) => value !== null);
125
+ }
@@ -111,6 +111,7 @@ export function resolveWorkspace(workspaceAst) {
111
111
  orchestrations: [],
112
112
  operations: [],
113
113
  decisions: [],
114
+ journeys: [],
114
115
  pitches: [],
115
116
  requirements: [],
116
117
  tasks: [],
@@ -128,6 +129,7 @@ export function resolveWorkspace(workspaceAst) {
128
129
  orchestration: "orchestrations",
129
130
  operation: "operations",
130
131
  decision: "decisions",
132
+ journey: "journeys",
131
133
  pitch: "pitches",
132
134
  requirement: "requirements",
133
135
  task: "tasks",
@@ -321,6 +323,7 @@ export function resolveWorkspace(workspaceAst) {
321
323
  orchestrations: [],
322
324
  operations: [],
323
325
  decisions: [],
326
+ journeys: [],
324
327
  pitches: [],
325
328
  requirements: [],
326
329
  tasks: [],
@@ -0,0 +1,74 @@
1
+ // @ts-check
2
+
3
+ import {
4
+ parseRecordBlock,
5
+ recordString,
6
+ recordStringList,
7
+ recordSymbol,
8
+ recordSymbolList
9
+ } from "../record-blocks.js";
10
+
11
+ /**
12
+ * @param {import("../parser.js").AstStatement} statement
13
+ * @param {string} key
14
+ * @returns {import("../parser.js").AstField[]}
15
+ */
16
+ function fieldsByKey(statement, key) {
17
+ return statement.fields.filter((field) => field.key === key);
18
+ }
19
+
20
+ /**
21
+ * @param {import("../parser.js").AstField} field
22
+ * @returns {any}
23
+ */
24
+ function parseStepField(field) {
25
+ if (field.value.type !== "block") {
26
+ return null;
27
+ }
28
+ const record = parseRecordBlock(field.value);
29
+ return {
30
+ id: recordSymbol(record, "id"),
31
+ intent: recordString(record, "intent"),
32
+ commands: recordStringList(record, "commands"),
33
+ expects: recordStringList(record, "expects"),
34
+ after: recordSymbolList(record, "after"),
35
+ notes: recordString(record, "notes"),
36
+ loc: field.loc
37
+ };
38
+ }
39
+
40
+ /**
41
+ * @param {import("../parser.js").AstField} field
42
+ * @returns {any}
43
+ */
44
+ function parseAlternateField(field) {
45
+ if (field.value.type !== "block") {
46
+ return null;
47
+ }
48
+ const record = parseRecordBlock(field.value);
49
+ return {
50
+ id: recordSymbol(record, "id"),
51
+ from: recordSymbol(record, "from"),
52
+ condition: recordString(record, "condition"),
53
+ commands: recordStringList(record, "commands"),
54
+ expects: recordStringList(record, "expects"),
55
+ notes: recordString(record, "notes"),
56
+ loc: field.loc
57
+ };
58
+ }
59
+
60
+ /**
61
+ * @param {import("../parser.js").AstStatement} statement
62
+ * @returns {any[]}
63
+ */
64
+ export function parseJourneySteps(statement) {
65
+ return fieldsByKey(statement, "step").map(parseStepField).filter(Boolean);
66
+ }
67
+
68
+ /**
69
+ * @param {import("../parser.js").AstStatement} statement
70
+ * @returns {any[]}
71
+ */
72
+ export function parseJourneyAlternates(statement) {
73
+ return fieldsByKey(statement, "alternate").map(parseAlternateField).filter(Boolean);
74
+ }
@@ -75,6 +75,7 @@ import {
75
75
  parseProjectionGeneratorDefaultsBlock
76
76
  } from "./projections-db.js";
77
77
  import { parsePlanSteps } from "../sdlc/plan-steps.js";
78
+ import { parseJourneyAlternates, parseJourneySteps } from "./journeys.js";
78
79
 
79
80
  export function normalizeStatement(statement, registry) {
80
81
  const fieldMap = collectFieldMap(statement);
@@ -308,6 +309,30 @@ export function normalizeStatement(statement, registry) {
308
309
  : null,
309
310
  aliases: normalizeDomainScopeList(statement, "aliases")
310
311
  };
312
+ case "journey":
313
+ return {
314
+ ...base,
315
+ actors: symbolValues(getFieldValue(statement, "actors")),
316
+ roles: symbolValues(getFieldValue(statement, "roles")),
317
+ goal: stringValue(getFieldValue(statement, "goal")),
318
+ trigger: stringValue(getFieldValue(statement, "trigger")),
319
+ steps: parseJourneySteps(statement),
320
+ alternates: parseJourneyAlternates(statement),
321
+ successSignals: normalizeDomainScopeList(statement, "success_signals"),
322
+ failureSignals: normalizeDomainScopeList(statement, "failure_signals"),
323
+ relatedCapabilities: symbolValues(getFieldValue(statement, "related_capabilities")),
324
+ relatedEntities: symbolValues(getFieldValue(statement, "related_entities")),
325
+ relatedRules: symbolValues(getFieldValue(statement, "related_rules")),
326
+ relatedWorkflows: symbolValues(getFieldValue(statement, "related_workflows")),
327
+ relatedProjections: symbolValues(getFieldValue(statement, "related_projections")),
328
+ relatedWidgets: symbolValues(getFieldValue(statement, "related_widgets")),
329
+ relatedVerifications: symbolValues(getFieldValue(statement, "related_verifications")),
330
+ relatedDecisions: symbolValues(getFieldValue(statement, "related_decisions")),
331
+ relatedDocs: symbolValues(getFieldValue(statement, "related_docs")),
332
+ tags: normalizeDomainScopeList(statement, "tags"),
333
+ updated: stringValue(getFieldValue(statement, "updated")),
334
+ resolvedDomain: resolveDomainTag(statement, registry)
335
+ };
311
336
  case "pitch":
312
337
  return {
313
338
  ...base,
package/src/sdlc/adopt.js CHANGED
@@ -55,7 +55,7 @@ function scanPressure(root) {
55
55
  export function sdlcAdopt(workspaceRoot) {
56
56
  const root = path.resolve(workspaceRoot);
57
57
  if (!existsSync(resolveTopoRoot(root))) {
58
- return { ok: false, error: `No '${DEFAULT_TOPO_FOLDER_NAME}/' workspace folder at ${root}; run 'topogram new' first` };
58
+ return { ok: false, error: `No '${DEFAULT_TOPO_FOLDER_NAME}/' workspace folder at ${root}; run 'topogram init' or 'topogram copy' first` };
59
59
  }
60
60
  const folders = SDLC_FOLDERS.map((name) => ensureFolder(root, name));
61
61
  const pressure = scanPressure(root);
@@ -205,6 +205,8 @@ function validateFieldShapes(errors, statement, fieldMap) {
205
205
  ensureSingleValueField(errors, statement, fieldMap, "updated", ["string"]);
206
206
  ensureSingleValueField(errors, statement, fieldMap, "notes", ["string"]);
207
207
  ensureSingleValueField(errors, statement, fieldMap, "outcome", ["string"]);
208
+ ensureSingleValueField(errors, statement, fieldMap, "goal", ["string"]);
209
+ ensureSingleValueField(errors, statement, fieldMap, "trigger", ["string"]);
208
210
 
209
211
  const listFields = [
210
212
  "aliases",
@@ -238,7 +240,19 @@ function validateFieldShapes(errors, statement, fieldMap) {
238
240
  "regions",
239
241
  "lookups",
240
242
  "dependencies",
241
- "approvals"
243
+ "approvals",
244
+ "success_signals",
245
+ "failure_signals",
246
+ "tags",
247
+ "related_capabilities",
248
+ "related_entities",
249
+ "related_rules",
250
+ "related_workflows",
251
+ "related_projections",
252
+ "related_widgets",
253
+ "related_verifications",
254
+ "related_decisions",
255
+ "related_docs"
242
256
  ];
243
257
  if (statement.kind === "orchestration") {
244
258
  listFields.push("steps");
@@ -251,6 +265,16 @@ function validateFieldShapes(errors, statement, fieldMap) {
251
265
  if (statement.kind === "plan") {
252
266
  blockFields.push("steps");
253
267
  }
268
+ if (statement.kind === "journey") {
269
+ for (const key of ["step", "alternate"]) {
270
+ const fields = fieldMap.get(key) || [];
271
+ for (const field of fields) {
272
+ if (field.value.type !== "block") {
273
+ pushError(errors, `Field '${key}' on ${statement.kind} ${statement.id} must be block, found ${field.value.type}`, field.loc);
274
+ }
275
+ }
276
+ }
277
+ }
254
278
  for (const key of blockFields) {
255
279
  ensureSingleValueField(errors, statement, fieldMap, key, ["block"]);
256
280
  }
@@ -429,6 +453,15 @@ function validateReferenceKinds(errors, statement, fieldMap, registry) {
429
453
  requirement: null,
430
454
  from_requirement: ["requirement"],
431
455
  affects: ["capability", "entity", "rule", "projection", "widget", "orchestration", "operation"],
456
+ related_capabilities: ["capability"],
457
+ related_entities: ["entity"],
458
+ related_rules: ["rule"],
459
+ related_workflows: null,
460
+ related_projections: ["projection"],
461
+ related_widgets: ["widget"],
462
+ related_verifications: ["verification"],
463
+ related_decisions: ["decision"],
464
+ related_docs: null,
432
465
  introduces_rules: ["rule"],
433
466
  respects_rules: ["rule"],
434
467
  decisions: ["decision"],
@@ -20,6 +20,7 @@ import { validateAcceptanceCriterion } from "./per-kind/acceptance-criterion.js"
20
20
  import { validateTask } from "./per-kind/task.js";
21
21
  import { validatePlan } from "./per-kind/plan.js";
22
22
  import { validateBug } from "./per-kind/bug.js";
23
+ import { validateJourney } from "./per-kind/journey.js";
23
24
 
24
25
  export {
25
26
  STATEMENT_KINDS,
@@ -32,6 +33,7 @@ export {
32
33
  TASK_IDENTIFIER_PATTERN,
33
34
  PLAN_IDENTIFIER_PATTERN,
34
35
  BUG_IDENTIFIER_PATTERN,
36
+ JOURNEY_IDENTIFIER_PATTERN,
35
37
  DOCUMENT_IDENTIFIER_PATTERN,
36
38
  GLOBAL_STATUSES,
37
39
  DECISION_STATUSES,
@@ -48,6 +50,7 @@ export {
48
50
  PLAN_STATUSES,
49
51
  PLAN_STEP_STATUSES,
50
52
  BUG_STATUSES,
53
+ JOURNEY_STATUSES,
51
54
  PRIORITY_VALUES,
52
55
  WORK_TYPES,
53
56
  BUG_SEVERITIES,
@@ -112,6 +115,7 @@ export function validateWorkspace(workspaceAst) {
112
115
  validateTask(errors, statement, fieldMap, registry);
113
116
  validatePlan(errors, statement, fieldMap, registry);
114
117
  validateBug(errors, statement, fieldMap, registry);
118
+ validateJourney(errors, statement, fieldMap, registry);
115
119
  validateExpressions(errors, statement, fieldMap);
116
120
  }
117
121
  }
@@ -8,6 +8,7 @@ export const ACCEPTANCE_CRITERION_IDENTIFIER_PATTERN: RegExp;
8
8
  export const TASK_IDENTIFIER_PATTERN: RegExp;
9
9
  export const PLAN_IDENTIFIER_PATTERN: RegExp;
10
10
  export const BUG_IDENTIFIER_PATTERN: RegExp;
11
+ export const JOURNEY_IDENTIFIER_PATTERN: RegExp;
11
12
  export const DOCUMENT_IDENTIFIER_PATTERN: RegExp;
12
13
  export const GLOBAL_STATUSES: Set<string>;
13
14
  export const DECISION_STATUSES: Set<string>;
@@ -25,6 +26,7 @@ export const TASK_DISPOSITIONS: Set<string>;
25
26
  export const PLAN_STATUSES: Set<string>;
26
27
  export const PLAN_STEP_STATUSES: Set<string>;
27
28
  export const BUG_STATUSES: Set<string>;
29
+ export const JOURNEY_STATUSES: Set<string>;
28
30
  export const PRIORITY_VALUES: Set<string>;
29
31
  export const WORK_TYPES: Set<string>;
30
32
  export const BUG_SEVERITIES: Set<string>;
@@ -16,6 +16,7 @@ export const STATEMENT_KINDS = new Set([
16
16
  "verification",
17
17
  "operation",
18
18
  "domain",
19
+ "journey",
19
20
  "pitch",
20
21
  "requirement",
21
22
  "acceptance_criterion",
@@ -32,6 +33,7 @@ export const ACCEPTANCE_CRITERION_IDENTIFIER_PATTERN = /^ac_[a-z][a-z0-9_]*$/;
32
33
  export const TASK_IDENTIFIER_PATTERN = /^task_[a-z][a-z0-9_]*$/;
33
34
  export const PLAN_IDENTIFIER_PATTERN = /^plan_[a-z][a-z0-9_]*$/;
34
35
  export const BUG_IDENTIFIER_PATTERN = /^bug_[a-z][a-z0-9_]*$/;
36
+ export const JOURNEY_IDENTIFIER_PATTERN = /^journey_[a-z][a-z0-9_]*$/;
35
37
  export const DOCUMENT_IDENTIFIER_PATTERN = /^doc_[a-z][a-z0-9_]*$/;
36
38
 
37
39
  export const GLOBAL_STATUSES = new Set(["draft", "proposed", "active", "deprecated"]);
@@ -46,6 +48,7 @@ export const TASK_STATUSES = new Set(["unclaimed", "claimed", "in-progress", "do
46
48
  export const PLAN_STATUSES = new Set(["draft", "active", "complete", "superseded"]);
47
49
  export const PLAN_STEP_STATUSES = new Set(["pending", "in-progress", "blocked", "done", "skipped"]);
48
50
  export const BUG_STATUSES = new Set(["open", "in-progress", "fixed", "verified", "wont-fix"]);
51
+ export const JOURNEY_STATUSES = new Set(["draft", "canonical", "active", "deprecated"]);
49
52
  export const TASK_DISPOSITIONS = new Set(["active", "follow_up", "deferred", "backlog", "blocker"]);
50
53
 
51
54
  export const PRIORITY_VALUES = new Set(["critical", "high", "medium", "low"]);
@@ -79,7 +82,8 @@ export const STATUS_SETS_BY_KIND = {
79
82
  acceptance_criterion: ACCEPTANCE_CRITERION_STATUSES,
80
83
  task: TASK_STATUSES,
81
84
  plan: PLAN_STATUSES,
82
- bug: BUG_STATUSES
85
+ bug: BUG_STATUSES,
86
+ journey: JOURNEY_STATUSES
83
87
  };
84
88
  export const VERIFICATION_METHODS = new Set(["smoke", "runtime", "contract", "journey", "manual"]);
85
89
  export const CLI_COMMAND_EFFECTS = new Set(["read_only", "writes_workspace", "writes_app", "network", "package_install", "git", "filesystem"]);
@@ -120,6 +124,7 @@ export const DOMAIN_TAGGABLE_KINDS = new Set([
120
124
  "orchestration",
121
125
  "operation",
122
126
  "decision",
127
+ "journey",
123
128
  "pitch",
124
129
  "requirement",
125
130
  "task",
@@ -244,6 +249,34 @@ export const FIELD_SPECS = {
244
249
  required: ["name", "description", "status"],
245
250
  allowed: ["name", "description", "in_scope", "out_of_scope", "owners", "parent_domain", "aliases", "status"]
246
251
  },
252
+ journey: {
253
+ required: ["name", "description", "status", "actors", "goal", "step"],
254
+ allowed: [
255
+ "name",
256
+ "description",
257
+ "status",
258
+ "actors",
259
+ "roles",
260
+ "goal",
261
+ "trigger",
262
+ "step",
263
+ "alternate",
264
+ "success_signals",
265
+ "failure_signals",
266
+ "related_capabilities",
267
+ "related_entities",
268
+ "related_rules",
269
+ "related_workflows",
270
+ "related_projections",
271
+ "related_widgets",
272
+ "related_verifications",
273
+ "related_decisions",
274
+ "related_docs",
275
+ "tags",
276
+ "domain",
277
+ "updated"
278
+ ]
279
+ },
247
280
  pitch: {
248
281
  required: ["name", "description", "status", "priority"],
249
282
  allowed: [