@topogram/cli 0.3.78 → 0.3.80
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 +20 -0
- package/package.json +2 -2
- package/src/agent-brief.js +29 -23
- package/src/agent-ops/query-builders/change-risk/{import-plan.js → extract-plan.js} +1 -1
- package/src/agent-ops/query-builders/change-risk/review-packets.js +5 -5
- package/src/agent-ops/query-builders/change-risk.js +1 -1
- package/src/agent-ops/query-builders/common.js +2 -2
- package/src/agent-ops/query-builders/multi-agent.js +1 -1
- package/src/agent-ops/query-builders/workflow-context-shared.js +4 -4
- package/src/catalog/provenance.js +1 -1
- package/src/cli/catalog-alias.d.ts +2 -0
- package/src/cli/catalog-alias.js +2 -2
- package/src/cli/command-parser.js +2 -0
- package/src/cli/command-parsers/core.js +9 -5
- package/src/cli/command-parsers/extractor.js +40 -0
- package/src/cli/command-parsers/import.js +11 -17
- package/src/cli/command-parsers/project.js +0 -3
- package/src/cli/commands/catalog/copy.js +3 -3
- package/src/cli/commands/catalog/help.js +1 -2
- package/src/cli/commands/catalog/list.js +7 -4
- package/src/cli/commands/catalog/show.js +4 -4
- package/src/cli/commands/copy.js +356 -0
- package/src/cli/commands/doctor.js +1 -1
- package/src/cli/commands/extractor.js +451 -0
- package/src/cli/commands/import/adopt.js +9 -9
- package/src/cli/commands/import/check.js +15 -15
- package/src/cli/commands/import/diff.js +6 -6
- package/src/cli/commands/import/help.js +45 -34
- package/src/cli/commands/import/paths.js +3 -3
- package/src/cli/commands/import/plan.js +8 -8
- package/src/cli/commands/import/refresh.js +25 -24
- package/src/cli/commands/import/status-history.js +4 -4
- package/src/cli/commands/import/workspace.js +24 -18
- package/src/cli/commands/import-runner.js +10 -7
- package/src/cli/commands/import.js +4 -1
- package/src/cli/commands/init.js +67 -0
- package/src/cli/commands/query/{import-adopt.js → extract-adopt.js} +2 -2
- package/src/cli/commands/query/runner/change.js +2 -2
- package/src/cli/commands/query/runner/{import-adopt.js → extract-adopt.js} +9 -9
- package/src/cli/commands/query/runner/index.js +1 -1
- package/src/cli/commands/query/runner/workflow.js +7 -7
- package/src/cli/commands/query/workspace.js +4 -4
- package/src/cli/commands/release-status.js +2 -2
- package/src/cli/commands/source.js +2 -2
- package/src/cli/commands/template/check.js +2 -2
- package/src/cli/commands/template/list-show.js +4 -4
- package/src/cli/dispatcher.js +32 -3
- package/src/cli/help-dispatch.js +33 -8
- package/src/cli/help.js +79 -52
- package/src/cli/migration-guidance.js +9 -0
- package/src/cli/options.js +17 -0
- package/src/extractor/check.js +155 -0
- package/src/extractor/packages.js +295 -0
- package/src/extractor/registry.js +196 -0
- package/src/extractor-policy.js +249 -0
- package/src/generator/check.js +24 -87
- package/src/generator/context/bundle.js +14 -7
- package/src/generator/context/diff.js +8 -1
- package/src/generator/context/digest.js +10 -1
- package/src/generator/context/shared/domain-sdlc.js +5 -1
- package/src/generator/context/shared/relationships.js +20 -5
- package/src/generator/context/shared/summaries.js +26 -0
- package/src/generator/context/shared.d.ts +1 -0
- package/src/generator/context/shared.js +1 -0
- package/src/generator/context/slice/core.js +9 -5
- package/src/generator/context/slice/sdlc.js +31 -2
- package/src/generator/context/task-mode.js +3 -3
- package/src/generator/registry/index.js +16 -75
- package/src/generator-policy.js +9 -57
- package/src/import/core/registry.d.ts +3 -0
- package/src/import/core/registry.js +82 -8
- package/src/import/core/runner/reports.js +4 -4
- package/src/import/core/runner/run.js +2 -0
- package/src/import/core/runner/tracks.js +66 -4
- package/src/import/provenance.js +18 -17
- package/src/init-project.js +215 -0
- package/src/new-project/constants.js +1 -1
- package/src/new-project/create.js +2 -2
- package/src/new-project/project-files.js +7 -7
- package/src/package-adapters/adapter.js +64 -0
- package/src/package-adapters/file-map.js +30 -0
- package/src/package-adapters/index.js +27 -0
- package/src/package-adapters/manifest.js +108 -0
- package/src/package-adapters/policy.js +81 -0
- package/src/package-adapters/spec.js +51 -0
- package/src/reconcile/journeys.js +8 -3
- package/src/record-blocks.js +125 -0
- package/src/resolver/index.js +3 -0
- package/src/resolver/journeys.js +74 -0
- package/src/resolver/normalize.js +25 -0
- package/src/sdlc/adopt.js +1 -1
- package/src/validator/common.js +34 -1
- package/src/validator/index.js +4 -0
- package/src/validator/kinds.d.ts +2 -0
- package/src/validator/kinds.js +34 -1
- package/src/validator/per-kind/journey.js +233 -0
- package/src/workflows/docs-generate.js +4 -1
- package/src/workflows/reconcile/bundle-core/index.js +4 -2
- package/src/workflows/reconcile/canonical-surface.js +4 -1
- package/src/cli/commands/new.js +0 -94
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} spec
|
|
8
|
+
* @param {string} cwd
|
|
9
|
+
* @returns {boolean}
|
|
10
|
+
*/
|
|
11
|
+
export function isPathSpec(spec, cwd) {
|
|
12
|
+
return spec.startsWith(".") || spec.startsWith("/") || fs.existsSync(path.resolve(cwd, spec));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} spec
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
export function packageNameFromSpec(spec) {
|
|
20
|
+
if (spec.startsWith("@")) {
|
|
21
|
+
const versionIndex = spec.indexOf("@", 1);
|
|
22
|
+
return versionIndex > 0 ? spec.slice(0, versionIndex) : spec;
|
|
23
|
+
}
|
|
24
|
+
const versionIndex = spec.indexOf("@");
|
|
25
|
+
return versionIndex > 0 ? spec.slice(0, versionIndex) : spec;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string|null|undefined} rootDir
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
export function packageResolutionBase(rootDir) {
|
|
33
|
+
return path.join(rootDir || process.cwd(), "package.json");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string|null|undefined} packageName
|
|
38
|
+
* @returns {string|null}
|
|
39
|
+
*/
|
|
40
|
+
export function packageInstallCommand(packageName) {
|
|
41
|
+
return packageName ? `npm install -D ${packageName}` : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string|null|undefined} packageName
|
|
46
|
+
* @returns {string|null}
|
|
47
|
+
*/
|
|
48
|
+
export function packageInstallHint(packageName) {
|
|
49
|
+
const command = packageInstallCommand(packageName);
|
|
50
|
+
return command ? `Install it from the project root with: ${command}` : null;
|
|
51
|
+
}
|
|
@@ -28,9 +28,11 @@ function primaryEntityIdForBundle(bundle) {
|
|
|
28
28
|
|
|
29
29
|
function canonicalJourneyCoverage(graph) {
|
|
30
30
|
const journeyDocs = (graph?.docs || []).filter((doc) => doc.kind === "journey");
|
|
31
|
+
const journeyStatements = graph?.byKind?.journey || [];
|
|
32
|
+
const journeys = [...journeyStatements, ...journeyDocs];
|
|
31
33
|
return {
|
|
32
|
-
byEntityId: new Set(
|
|
33
|
-
byCapabilityId: new Set(
|
|
34
|
+
byEntityId: new Set(journeys.flatMap((journey) => journey.relatedEntities || [])),
|
|
35
|
+
byCapabilityId: new Set(journeys.flatMap((journey) => journey.relatedCapabilities || []))
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -42,7 +44,10 @@ function collectJourneyGenerationContext(graph) {
|
|
|
42
44
|
const uiSharedScreens = projections
|
|
43
45
|
.filter((projection) => projection.type === "ui_contract")
|
|
44
46
|
.flatMap((projection) => (projection.uiScreens || []).map((screen) => ({ ...screen, projectionId: projection.id })));
|
|
45
|
-
const canonicalJourneys =
|
|
47
|
+
const canonicalJourneys = [
|
|
48
|
+
...(graph.byKind?.journey || []),
|
|
49
|
+
...(graph.docs || []).filter((doc) => doc.kind === "journey")
|
|
50
|
+
];
|
|
46
51
|
const coveredEntityIds = new Set(canonicalJourneys.flatMap((doc) => doc.relatedEntities || []));
|
|
47
52
|
|
|
48
53
|
return entities
|
|
@@ -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
|
+
}
|
package/src/resolver/index.js
CHANGED
|
@@ -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
|
|
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);
|
package/src/validator/common.js
CHANGED
|
@@ -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"],
|
package/src/validator/index.js
CHANGED
|
@@ -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
|
}
|
package/src/validator/kinds.d.ts
CHANGED
|
@@ -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>;
|
package/src/validator/kinds.js
CHANGED
|
@@ -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: [
|