yarramate 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +39 -12
  2. package/catalogues/core-enrichment.yaml +700 -0
  3. package/dist/adapters/mcp-cli.js +55 -49
  4. package/dist/apply-command.d.ts +2 -0
  5. package/dist/apply-command.js +199 -0
  6. package/dist/{new-command.d.ts → ask-command.d.ts} +1 -1
  7. package/dist/ask-command.js +729 -0
  8. package/dist/brief.d.ts +3 -0
  9. package/dist/brief.js +235 -0
  10. package/dist/cli-support.d.ts +1 -1
  11. package/dist/cli-support.js +1 -1
  12. package/dist/cli.js +22 -590
  13. package/dist/compiler.js +13 -0
  14. package/dist/core-contract.d.ts +1 -1
  15. package/dist/{status-command.d.ts → design-command.d.ts} +1 -1
  16. package/dist/design-command.js +218 -0
  17. package/dist/export-command.d.ts +2 -0
  18. package/dist/export-command.js +228 -0
  19. package/dist/interrogate-command.d.ts +104 -0
  20. package/dist/interrogate-command.js +268 -0
  21. package/dist/next-command.d.ts +5 -8
  22. package/dist/next-command.js +106 -211
  23. package/docs/CONSUMING-YARRAMATE.md +28 -21
  24. package/package.json +13 -11
  25. package/schema/yarramate-apply-result.schema.json +32 -0
  26. package/schema/yarramate-ask-result.schema.json +391 -0
  27. package/schema/yarramate-core-contract.schema.json +5 -11
  28. package/schema/yarramate-design-step.schema.json +152 -0
  29. package/schema/yarramate-document.schema.json +76 -9
  30. package/schema/yarramate-interrogation-report.schema.json +89 -0
  31. package/schema/yarramate-operations.schema.json +303 -0
  32. package/schema/yarramate-question-catalogue.schema.json +431 -0
  33. package/skills/yarramate-architecture/SKILL.md +42 -17
  34. package/skills/yarramate-architecture/references/native-authoring.md +73 -17
  35. package/dist/new-command.js +0 -111
  36. package/dist/status-command.js +0 -174
  37. package/schema/yarramate-next-result.schema.json +0 -85
  38. package/schema/yarramate-status-result.schema.json +0 -278
@@ -0,0 +1,218 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { parseDocument } from 'yaml';
5
+ import { diagnosticJson, humanDiagnostics, usage, } from './cli-support.js';
6
+ import { compileWorkspaceWithProfileContext, } from './compiler.js';
7
+ import { evaluateCatalogue, loadQuestionCatalogue, } from './interrogate-command.js';
8
+ import { evaluateProjection } from './projection.js';
9
+ import { renderBrief } from './brief.js';
10
+ import { loadWorkspaceManifest } from './workspace.js';
11
+ // The catalogue is internal to design: it ships inside the package,
12
+ // versioned with it, and harnesses never pass catalogue paths. The
13
+ // relative hop works from both src/ (dev) and dist/ (shipped).
14
+ const here = dirname(fileURLToPath(import.meta.url));
15
+ const shippedCataloguePath = join(here, '..', 'catalogues', 'core-enrichment.yaml');
16
+ // The top step is the first open question in wave order, then catalogue
17
+ // order within the wave; a subject-scoped question serves its first open
18
+ // subject and reports how many more share it. One question at a time is
19
+ // the discipline; everything else is a read (ask --open).
20
+ const selectStep = (report, subjectFilter) => {
21
+ for (const wave of report.waves) {
22
+ for (const question of wave.questions) {
23
+ if (!question.open)
24
+ continue;
25
+ if (question.subjects === undefined) {
26
+ if (subjectFilter !== undefined)
27
+ continue;
28
+ return {
29
+ questionId: question.id,
30
+ wave: wave.id,
31
+ scope: 'workspace',
32
+ authority: question.authority,
33
+ question: question.question,
34
+ materiality: question.materiality,
35
+ resolution: question.resolution,
36
+ };
37
+ }
38
+ const subjects = subjectFilter === undefined
39
+ ? question.subjects
40
+ : question.subjects.filter(({ id }) => id === subjectFilter);
41
+ const first = subjects[0];
42
+ if (first === undefined)
43
+ continue;
44
+ return {
45
+ questionId: question.id,
46
+ wave: wave.id,
47
+ scope: 'subject',
48
+ authority: question.authority,
49
+ question: first.question,
50
+ materiality: question.materiality,
51
+ resolution: question.resolution,
52
+ subject: {
53
+ id: first.id,
54
+ ...(first.name === undefined ? {} : { name: first.name }),
55
+ },
56
+ ...(subjects.length > 1
57
+ ? { remainingSubjects: subjects.length - 1 }
58
+ : {}),
59
+ };
60
+ }
61
+ }
62
+ return null;
63
+ };
64
+ export function runDesignCommand(options, cwd) {
65
+ const json = options.includes('--json');
66
+ let subjectFilter;
67
+ let cataloguePath;
68
+ const rest = [];
69
+ const withoutJson = options.filter((option) => option !== '--json');
70
+ for (let index = 0; index < withoutJson.length; index += 1) {
71
+ const option = withoutJson[index];
72
+ if (option === '--subject' || option === '--catalogue') {
73
+ const value = withoutJson[index + 1];
74
+ if (value === undefined || value.startsWith('-')) {
75
+ return { exitCode: 2, stdout: '', stderr: usage };
76
+ }
77
+ if (option === '--subject') {
78
+ if (subjectFilter !== undefined) {
79
+ return { exitCode: 2, stdout: '', stderr: usage };
80
+ }
81
+ subjectFilter = value;
82
+ }
83
+ else {
84
+ if (cataloguePath !== undefined) {
85
+ return { exitCode: 2, stdout: '', stderr: usage };
86
+ }
87
+ cataloguePath = value;
88
+ }
89
+ index += 1;
90
+ continue;
91
+ }
92
+ if (option === undefined || option.startsWith('-')) {
93
+ return { exitCode: 2, stdout: '', stderr: usage };
94
+ }
95
+ rest.push(option);
96
+ }
97
+ const [workspacePath] = rest;
98
+ if (rest.length !== 1 || workspacePath === undefined) {
99
+ return { exitCode: 2, stdout: '', stderr: usage };
100
+ }
101
+ try {
102
+ const manifestSource = readFileSync(resolve(cwd, workspacePath), 'utf8');
103
+ if (parseDocument(manifestSource).get('format') !== 'yarramate/workspace/v1') {
104
+ return {
105
+ exitCode: 2,
106
+ stdout: '',
107
+ stderr: 'design requires an explicit workspace manifest (yarramate/workspace/v1)\n',
108
+ };
109
+ }
110
+ const failed = (diagnostics) => ({
111
+ exitCode: 1,
112
+ stdout: json
113
+ ? diagnosticJson(diagnostics)
114
+ : humanDiagnostics(diagnostics),
115
+ stderr: '',
116
+ });
117
+ const loadedWorkspace = loadWorkspaceManifest({ path: workspacePath, source: manifestSource }, cwd);
118
+ if (!loadedWorkspace.ok)
119
+ return failed(loadedWorkspace.diagnostics);
120
+ const workspace = loadedWorkspace.workspace;
121
+ const resolvedCataloguePath = cataloguePath === undefined
122
+ ? shippedCataloguePath
123
+ : resolve(cwd, cataloguePath);
124
+ const loadedCatalogue = loadQuestionCatalogue({
125
+ path: cataloguePath ?? resolvedCataloguePath,
126
+ source: readFileSync(resolvedCataloguePath, 'utf8'),
127
+ });
128
+ if (!loadedCatalogue.ok)
129
+ return failed(loadedCatalogue.diagnostics);
130
+ const compilation = compileWorkspaceWithProfileContext([...workspace.profiles, ...workspace.documents].map((path) => ({
131
+ path,
132
+ source: readFileSync(resolve(cwd, path), 'utf8'),
133
+ })));
134
+ if (!compilation.ok)
135
+ return failed(compilation.diagnostics);
136
+ if (subjectFilter !== undefined) {
137
+ const known = new Set(compilation.graph.subjects.map(({ id }) => id));
138
+ if (!known.has(subjectFilter)) {
139
+ return {
140
+ exitCode: 1,
141
+ stdout: '',
142
+ stderr: `Unknown subject identity: ${subjectFilter} (the compiled workspace declares ${known.size} subjects)\n`,
143
+ };
144
+ }
145
+ }
146
+ const report = evaluateCatalogue(loadedCatalogue.catalogue, compilation.graph, compilation.profileContext);
147
+ const step = selectStep(report, subjectFilter);
148
+ let slice;
149
+ if (step?.subject !== undefined) {
150
+ const projection = evaluateProjection(compilation.graph, {
151
+ format: 'yarramate/projection/v1',
152
+ id: 'design-step',
153
+ version: '0.0',
154
+ query: {
155
+ subjects: [step.subject.id],
156
+ relationships: 'connected',
157
+ },
158
+ presentation: {
159
+ title: step.subject.name ?? step.subject.id,
160
+ description: `The neighbourhood of ${step.subject.id} as declared today.`,
161
+ },
162
+ }, compilation.profileContext);
163
+ slice = renderBrief(projection, compilation.profileContext);
164
+ }
165
+ const result = {
166
+ format: 'yarramate/design-step/v1',
167
+ workspace: workspace.id,
168
+ catalogue: report.catalogue,
169
+ progress: {
170
+ questions: report.summary.questions,
171
+ openQuestions: report.summary.openQuestions,
172
+ open: report.summary.open,
173
+ waves: report.waves.map((wave) => ({
174
+ id: wave.id,
175
+ open: wave.questions.reduce((total, question) => total +
176
+ (question.open ? (question.subjects?.length ?? 1) : 0), 0),
177
+ })),
178
+ },
179
+ step,
180
+ ...(slice === undefined ? {} : { slice }),
181
+ };
182
+ if (json) {
183
+ return {
184
+ exitCode: 0,
185
+ stdout: `${JSON.stringify(result, null, 2)}\n`,
186
+ stderr: '',
187
+ };
188
+ }
189
+ const waveSummary = result.progress.waves
190
+ .map(({ id, open }) => `${id} ${open} open`)
191
+ .join(' · ');
192
+ const lines = [
193
+ `Design interview — workspace ${workspace.id} · catalogue ${report.catalogue}`,
194
+ `Waves: ${waveSummary}`,
195
+ '',
196
+ ];
197
+ if (step === null) {
198
+ lines.push(subjectFilter === undefined
199
+ ? 'Interview complete: no open questions. The model answers everything the catalogue asks.'
200
+ : `Interview complete for ${subjectFilter}: no open questions touch it.`);
201
+ }
202
+ else {
203
+ lines.push(`Q [${step.wave} · ${step.questionId}] (authority: ${step.authority})`, ` ${step.question}`, ` Why it matters: ${step.materiality}`, ` How to answer: ${step.resolution}`);
204
+ if (step.remainingSubjects !== undefined) {
205
+ lines.push(` (${step.remainingSubjects} more subject${step.remainingSubjects === 1 ? '' : 's'} share${step.remainingSubjects === 1 ? 's' : ''} this question)`);
206
+ }
207
+ if (slice !== undefined) {
208
+ lines.push('', 'Subject slice:', '', slice.trimEnd());
209
+ }
210
+ lines.push('', 'Answer by updating the model (one atomic batch):', ` yarramate apply <operations.yaml> ${workspacePath}`, `Then re-run: yarramate design ${workspacePath}`);
211
+ }
212
+ return { exitCode: 0, stdout: `${lines.join('\n')}\n`, stderr: '' };
213
+ }
214
+ catch (error) {
215
+ const message = error instanceof Error ? error.message : String(error);
216
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
217
+ }
218
+ }
@@ -0,0 +1,2 @@
1
+ import { type CliResult } from './cli-support.js';
2
+ export declare function runExportCommand(options: readonly string[], cwd: string): CliResult;
@@ -0,0 +1,228 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { parseDocument } from 'yaml';
6
+ import { renderBrief } from './brief.js';
7
+ import { humanDiagnostics, usage } from './cli-support.js';
8
+ import { compileWorkspaceWithProfileContext, } from './compiler.js';
9
+ import { serializeSemanticGraph } from './graph.js';
10
+ import { evaluateProjection, loadProjection, renderProjectionMarkdown, } from './projection.js';
11
+ import { loadWorkspaceManifest } from './workspace.js';
12
+ // The adapter stays a separate process behind the verb: the core never
13
+ // imports adapter code (the adapter-runtime-dependency exclusion), it
14
+ // hands the invocation to the sibling binary shipped in the same package.
15
+ const here = dirname(fileURLToPath(import.meta.url));
16
+ const likec4AdapterEntry = join(here, 'adapters', 'likec4-cli.js');
17
+ const claimValue = (claims, subject, predicate) => {
18
+ const object = claims.find((claim) => claim.subject === subject && claim.predicate === predicate)?.object;
19
+ return object !== undefined && 'value' in object ? object.value : undefined;
20
+ };
21
+ const briefFileName = (id) => `${id.replaceAll('#', '--')}.md`;
22
+ const parseExportOptions = (options) => {
23
+ const positionals = [];
24
+ let out;
25
+ let budget;
26
+ let json = false;
27
+ for (let index = 0; index < options.length; index += 1) {
28
+ const option = options[index];
29
+ if (option === '--json') {
30
+ json = true;
31
+ continue;
32
+ }
33
+ if (option === '--out' || option === '--budget') {
34
+ const value = options[index + 1];
35
+ if (value === undefined || value.startsWith('-'))
36
+ return undefined;
37
+ if (option === '--out') {
38
+ if (out !== undefined)
39
+ return undefined;
40
+ out = value;
41
+ }
42
+ else {
43
+ if (budget !== undefined || !/^[1-9][0-9]*$/.test(value)) {
44
+ return undefined;
45
+ }
46
+ budget = Number(value);
47
+ }
48
+ index += 1;
49
+ continue;
50
+ }
51
+ if (option === undefined || option.startsWith('-'))
52
+ return undefined;
53
+ positionals.push(option);
54
+ }
55
+ return {
56
+ positionals,
57
+ ...(out === undefined ? {} : { out }),
58
+ ...(budget === undefined ? {} : { budget }),
59
+ json,
60
+ };
61
+ };
62
+ export function runExportCommand(options, cwd) {
63
+ const [kind, ...rest] = options;
64
+ if (kind === undefined ||
65
+ !['graph', 'markdown', 'briefs', 'likec4'].includes(kind)) {
66
+ return { exitCode: 2, stdout: '', stderr: usage };
67
+ }
68
+ const parsed = parseExportOptions(rest);
69
+ if (parsed === undefined) {
70
+ return { exitCode: 2, stdout: '', stderr: usage };
71
+ }
72
+ // likec4 delegates whole: <likec4-project.yaml> <output-dir> <workspace>.
73
+ if (kind === 'likec4') {
74
+ const [projectDefinition, outputDirectory, workspacePath] = parsed.positionals;
75
+ if (parsed.positionals.length !== 3 ||
76
+ projectDefinition === undefined ||
77
+ outputDirectory === undefined ||
78
+ workspacePath === undefined ||
79
+ parsed.out !== undefined ||
80
+ parsed.budget !== undefined ||
81
+ parsed.json) {
82
+ return { exitCode: 2, stdout: '', stderr: usage };
83
+ }
84
+ if (!existsSync(likec4AdapterEntry)) {
85
+ return {
86
+ exitCode: 2,
87
+ stdout: '',
88
+ stderr: `LikeC4 adapter entry not found at ${likec4AdapterEntry}; ` +
89
+ 'run from the installed package or use the yarramate-likec4 binary directly\n',
90
+ };
91
+ }
92
+ const delegated = spawnSync(process.execPath, [
93
+ likec4AdapterEntry,
94
+ 'export-project',
95
+ projectDefinition,
96
+ outputDirectory,
97
+ workspacePath,
98
+ ], { cwd, encoding: 'utf8' });
99
+ const exitCode = delegated.status === 0 ? 0 : delegated.status === 1 ? 1 : 2;
100
+ return {
101
+ exitCode,
102
+ stdout: delegated.stdout ?? '',
103
+ stderr: delegated.stderr ?? '',
104
+ };
105
+ }
106
+ const expectedPositionals = kind === 'graph' ? 1 : 2;
107
+ const workspacePath = parsed.positionals[expectedPositionals - 1];
108
+ const projectionPath = kind === 'graph' ? undefined : parsed.positionals[0];
109
+ if (parsed.positionals.length !== expectedPositionals ||
110
+ workspacePath === undefined ||
111
+ parsed.json ||
112
+ (parsed.budget !== undefined && kind !== 'briefs') ||
113
+ (kind === 'briefs' && parsed.out === undefined)) {
114
+ return { exitCode: 2, stdout: '', stderr: usage };
115
+ }
116
+ try {
117
+ const manifestSource = readFileSync(resolve(cwd, workspacePath), 'utf8');
118
+ if (parseDocument(manifestSource).get('format') !== 'yarramate/workspace/v1') {
119
+ return {
120
+ exitCode: 2,
121
+ stdout: '',
122
+ stderr: 'export requires an explicit workspace manifest (yarramate/workspace/v1)\n',
123
+ };
124
+ }
125
+ const failed = (diagnostics) => ({
126
+ exitCode: 1,
127
+ stdout: humanDiagnostics(diagnostics),
128
+ stderr: '',
129
+ });
130
+ const loadedWorkspace = loadWorkspaceManifest({ path: workspacePath, source: manifestSource }, cwd);
131
+ if (!loadedWorkspace.ok)
132
+ return failed(loadedWorkspace.diagnostics);
133
+ const workspace = loadedWorkspace.workspace;
134
+ const compilation = compileWorkspaceWithProfileContext([...workspace.profiles, ...workspace.documents].map((path) => ({
135
+ path,
136
+ source: readFileSync(resolve(cwd, path), 'utf8'),
137
+ })));
138
+ if (!compilation.ok)
139
+ return failed(compilation.diagnostics);
140
+ if (kind === 'graph') {
141
+ const serialized = serializeSemanticGraph(compilation.graph);
142
+ if (parsed.out === undefined) {
143
+ return { exitCode: 0, stdout: serialized, stderr: '' };
144
+ }
145
+ const outPath = resolve(cwd, parsed.out);
146
+ mkdirSync(dirname(outPath), { recursive: true });
147
+ writeFileSync(outPath, serialized, 'utf8');
148
+ return {
149
+ exitCode: 0,
150
+ stdout: `Wrote graph to ${parsed.out}\n`,
151
+ stderr: '',
152
+ };
153
+ }
154
+ const loadedProjection = loadProjection({
155
+ path: projectionPath,
156
+ source: readFileSync(resolve(cwd, projectionPath), 'utf8'),
157
+ });
158
+ if (!loadedProjection.ok)
159
+ return failed(loadedProjection.diagnostics);
160
+ const result = evaluateProjection(compilation.graph, loadedProjection.projection, compilation.profileContext);
161
+ if (kind === 'markdown') {
162
+ const rendered = renderProjectionMarkdown(result);
163
+ if (parsed.out === undefined) {
164
+ return { exitCode: 0, stdout: rendered, stderr: '' };
165
+ }
166
+ const outPath = resolve(cwd, parsed.out);
167
+ mkdirSync(dirname(outPath), { recursive: true });
168
+ writeFileSync(outPath, rendered, 'utf8');
169
+ return {
170
+ exitCode: 0,
171
+ stdout: `Wrote markdown to ${parsed.out}\n`,
172
+ stderr: '',
173
+ };
174
+ }
175
+ // briefs: the handoff bundle — one brief per projected concept, each
176
+ // the concept's one-hop neighbourhood (ADR 0055), plus an index so N
177
+ // implementers can each pick up one slice.
178
+ const stateIds = new Set(result.claims
179
+ .filter(({ predicate }) => predicate === 'yarramate/state/type')
180
+ .map(({ subject }) => subject));
181
+ const concepts = result.subjects
182
+ .filter(({ id, type }) => type === 'concept' && !stateIds.has(id))
183
+ .map(({ id }) => id)
184
+ .sort((left, right) => left.localeCompare(right));
185
+ const outDirectory = resolve(cwd, parsed.out);
186
+ mkdirSync(outDirectory, { recursive: true });
187
+ const indexLines = [
188
+ `# Briefs — ${result.presentation?.title ?? result.projection}`,
189
+ '',
190
+ `Derived from projection ${result.projection}; one brief per concept,`,
191
+ 'each the concept\'s connected neighbourhood as declared today.',
192
+ '',
193
+ ];
194
+ for (const id of concepts) {
195
+ const slice = evaluateProjection(compilation.graph, {
196
+ format: 'yarramate/projection/v1',
197
+ id: 'export-brief',
198
+ version: '0.0',
199
+ query: { subjects: [id], relationships: 'connected' },
200
+ presentation: {
201
+ title: claimValue(result.claims, id, 'yarramate/concept/name') ?? id,
202
+ description: `The neighbourhood of ${id} as declared today.`,
203
+ },
204
+ }, compilation.profileContext);
205
+ const brief = renderBrief(slice, compilation.profileContext, parsed.budget);
206
+ writeFileSync(join(outDirectory, briefFileName(id)), brief, 'utf8');
207
+ const name = claimValue(result.claims, id, 'yarramate/concept/name');
208
+ const conceptKind = claimValue(result.claims, id, 'yarramate/concept/kind') ?? 'unknown';
209
+ const status = claimValue(result.claims, id, 'yarramate/lifecycle/status');
210
+ indexLines.push(`- [${name ?? id}](${briefFileName(id)}) — ` +
211
+ `${conceptKind.split('#')[1] ?? conceptKind}` +
212
+ `${status === undefined ? '' : ` (${status})`} — \`${id}\``);
213
+ }
214
+ if (concepts.length === 0) {
215
+ indexLines.push('No concepts selected by this projection.');
216
+ }
217
+ writeFileSync(join(outDirectory, 'INDEX.md'), `${indexLines.join('\n')}\n`, 'utf8');
218
+ return {
219
+ exitCode: 0,
220
+ stdout: `Wrote ${concepts.length} brief${concepts.length === 1 ? '' : 's'} and INDEX.md to ${parsed.out}\n`,
221
+ stderr: '',
222
+ };
223
+ }
224
+ catch (error) {
225
+ const message = error instanceof Error ? error.message : String(error);
226
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
227
+ }
228
+ }
@@ -0,0 +1,104 @@
1
+ import { type Diagnostic, type ResolvedProfileContext, type SemanticGraph, type WorkspaceSource } from './compiler.js';
2
+ interface CatalogueSelector {
3
+ readonly kinds: readonly string[];
4
+ readonly kindMatching?: 'exact' | 'descendants';
5
+ readonly statuses?: readonly string[];
6
+ readonly documents?: readonly string[];
7
+ }
8
+ type CatalogueCondition = {
9
+ readonly condition: 'missing-claim';
10
+ readonly predicate: string;
11
+ } | {
12
+ readonly condition: 'missing-relationship';
13
+ readonly kinds: readonly string[];
14
+ readonly direction: 'incoming' | 'outgoing' | 'any';
15
+ readonly kindMatching?: 'exact' | 'descendants';
16
+ } | {
17
+ readonly condition: 'isolated';
18
+ } | {
19
+ readonly condition: 'no-subject-of-kind';
20
+ readonly kinds: readonly string[];
21
+ } | {
22
+ readonly condition: 'no-state-defined';
23
+ } | {
24
+ readonly condition: 'missing-linkage';
25
+ readonly kinds: readonly string[];
26
+ readonly direction: 'incoming' | 'outgoing';
27
+ readonly counterpartKinds: readonly string[];
28
+ readonly kindMatching?: 'exact' | 'descendants';
29
+ } | {
30
+ readonly condition: 'missing-reference';
31
+ readonly predicate: string;
32
+ readonly direction: 'incoming' | 'outgoing';
33
+ } | {
34
+ readonly condition: 'missing-attestation';
35
+ readonly topic: string;
36
+ };
37
+ export interface CatalogueQuestion {
38
+ readonly id: string;
39
+ readonly wave: string;
40
+ readonly scope: 'workspace' | 'subject';
41
+ readonly subjects?: CatalogueSelector;
42
+ readonly trigger: readonly CatalogueCondition[];
43
+ readonly question: string;
44
+ readonly materiality: string;
45
+ readonly resolution: string;
46
+ readonly authority: 'human' | 'agent' | 'either';
47
+ }
48
+ export interface QuestionCatalogue {
49
+ readonly format: 'yarramate/question-catalogue/v1';
50
+ readonly id: string;
51
+ readonly version: string;
52
+ readonly profile: string;
53
+ readonly presentation?: {
54
+ readonly title?: string;
55
+ readonly description?: string;
56
+ };
57
+ readonly waves: readonly {
58
+ readonly id: string;
59
+ readonly name: string;
60
+ readonly description?: string;
61
+ }[];
62
+ readonly questions: readonly CatalogueQuestion[];
63
+ }
64
+ interface OpenSubject {
65
+ readonly id: string;
66
+ readonly name?: string;
67
+ readonly question: string;
68
+ }
69
+ interface ReportQuestion {
70
+ readonly id: string;
71
+ readonly scope: 'workspace' | 'subject';
72
+ readonly authority: 'human' | 'agent' | 'either';
73
+ readonly open: boolean;
74
+ readonly question: string;
75
+ readonly materiality: string;
76
+ readonly resolution: string;
77
+ readonly subjects?: readonly OpenSubject[];
78
+ }
79
+ export interface InterrogationReport {
80
+ readonly format: 'yarramate/interrogation-report/v1';
81
+ readonly workspace: string;
82
+ readonly catalogue: string;
83
+ readonly summary: {
84
+ readonly questions: number;
85
+ readonly openQuestions: number;
86
+ readonly open: number;
87
+ };
88
+ readonly waves: readonly {
89
+ readonly id: string;
90
+ readonly name: string;
91
+ readonly questions: readonly ReportQuestion[];
92
+ }[];
93
+ }
94
+ export declare function evaluateCatalogue(catalogue: QuestionCatalogue, graph: SemanticGraph, profileContext?: ResolvedProfileContext): Omit<InterrogationReport, 'workspace'>;
95
+ export type CatalogueLoadResult = {
96
+ readonly ok: true;
97
+ readonly catalogue: QuestionCatalogue;
98
+ } | {
99
+ readonly ok: false;
100
+ readonly diagnostics: readonly Diagnostic[];
101
+ };
102
+ export declare function loadQuestionCatalogue(catalogueSource: WorkspaceSource): CatalogueLoadResult;
103
+ export declare function renderInterrogationReport(report: InterrogationReport): string;
104
+ export {};