yarramate 0.4.0 → 0.6.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/README.md +3 -1
- package/dist/adapters/graphify-cli.js +4 -1
- package/dist/adapters/likec4-cli.js +210 -51
- package/dist/adapters/likec4-export.d.ts +1 -1
- package/dist/adapters/likec4-prepare.d.ts +6 -0
- package/dist/adapters/likec4-prepare.js +40 -2
- package/dist/adapters/mcp-cli.js +29 -22
- package/dist/check-command.js +70 -4
- package/dist/cli-support.d.ts +6 -1
- package/dist/cli-support.js +12 -2
- package/dist/cli.js +39 -22
- package/dist/compiler.js +33 -7
- package/dist/index.d.ts +1 -1
- package/dist/next-command.d.ts +25 -0
- package/dist/next-command.js +272 -0
- package/dist/profile.d.ts +5 -0
- package/dist/profile.js +4 -0
- package/dist/reconciliation.d.ts +11 -1
- package/dist/reconciliation.js +73 -4
- package/dist/status-command.js +4 -1
- package/package.json +6 -5
- package/schema/yarramate-check-result.schema.json +12 -0
- package/schema/yarramate-core-contract.schema.json +1 -0
- package/schema/yarramate-likec4-generated-project-v2.schema.json +6 -0
- package/schema/yarramate-likec4-generated-project.schema.json +6 -0
- package/schema/yarramate-likec4-project.schema.json +1 -1
- package/schema/yarramate-next-result.schema.json +85 -0
- package/schema/yarramate-reconciliation-report.schema.json +24 -2
- package/schema/yarramate-status-result.schema.json +6 -1
- package/skills/yarramate-architecture/SKILL.md +9 -9
- package/skills/yarramate-architecture/references/native-authoring.md +74 -6
package/dist/check-command.js
CHANGED
|
@@ -8,10 +8,23 @@ import { compileWorkspace } from './compiler.js';
|
|
|
8
8
|
import { checkCoreContract, loadCoreContract, } from './core-contract.js';
|
|
9
9
|
import { evaluateEvidenceWorkspace, loadEvidence, } from './evidence.js';
|
|
10
10
|
import { loadProjection } from './projection.js';
|
|
11
|
+
import { reconcileEvidenceReports, } from './reconciliation.js';
|
|
11
12
|
const Ajv2020 = Ajv2020Module.default;
|
|
13
|
+
const strictFindingMessage = (finding) => {
|
|
14
|
+
const observed = finding.evidence.message === undefined
|
|
15
|
+
? finding.evidence.uri
|
|
16
|
+
: `${finding.evidence.uri}: ${finding.evidence.message}`;
|
|
17
|
+
const assertion = finding.asserted === undefined
|
|
18
|
+
? `Evidence contradicts ${finding.target.type} "${finding.target.id}"`
|
|
19
|
+
: `Evidence contradicts claim "${finding.target.id}": the model asserts ` +
|
|
20
|
+
`${finding.asserted.from} -> ${finding.asserted.to} (${finding.asserted.kind})`;
|
|
21
|
+
return (`${assertion}, but provider "${finding.provider}" observed otherwise ` +
|
|
22
|
+
`(${observed}); align the model or the evidence to pass --strict`);
|
|
23
|
+
};
|
|
12
24
|
export function runCheckCommand(options, cwd) {
|
|
13
25
|
const json = options.includes('--json');
|
|
14
|
-
const
|
|
26
|
+
const strict = options.includes('--strict');
|
|
27
|
+
const paths = options.filter((option) => option !== '--json' && option !== '--strict');
|
|
15
28
|
const unknownOption = paths.find((path) => path.startsWith('-'));
|
|
16
29
|
if (unknownOption !== undefined || paths.length === 0) {
|
|
17
30
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
@@ -171,6 +184,46 @@ export function runCheckCommand(options, cwd) {
|
|
|
171
184
|
const diagnostics = result.ok
|
|
172
185
|
? optionalDiagnostics
|
|
173
186
|
: result.diagnostics;
|
|
187
|
+
// Strict only tightens a passing check: base diagnostics already fail
|
|
188
|
+
// the gate, so contradictions are folded in only once everything else
|
|
189
|
+
// holds, and each one is anchored at the claim the model declares.
|
|
190
|
+
const strictEvaluation = strict && result.ok && ok
|
|
191
|
+
? (() => {
|
|
192
|
+
const reports = evidenceEvaluation !== undefined && evidenceEvaluation.ok
|
|
193
|
+
? evidenceEvaluation.reports
|
|
194
|
+
: [];
|
|
195
|
+
const graph = result.graph;
|
|
196
|
+
const contradicted = reconcileEvidenceReports('strict', reports, graph).findings.filter(({ result: outcome }) => outcome === 'contradicted');
|
|
197
|
+
return {
|
|
198
|
+
observations: reports.reduce((total, report) => total + report.observations.length, 0),
|
|
199
|
+
diagnostics: sortDiagnostics(contradicted.map((finding) => {
|
|
200
|
+
const anchor = graph.claims.find(({ id }) => id === finding.target.id) ??
|
|
201
|
+
graph.claims.find(({ subject, predicate }) => subject === finding.target.id &&
|
|
202
|
+
predicate === 'yarramate/concept/kind') ??
|
|
203
|
+
graph.claims.find(({ subject }) => subject === finding.target.id);
|
|
204
|
+
return {
|
|
205
|
+
severity: 'error',
|
|
206
|
+
code: 'YM901',
|
|
207
|
+
message: strictFindingMessage(finding),
|
|
208
|
+
path: anchor?.source.path ?? finding.evidenceDocument,
|
|
209
|
+
pointer: anchor?.source.pointer ?? '/',
|
|
210
|
+
line: anchor?.source.line ?? 1,
|
|
211
|
+
column: anchor?.source.column ?? 1,
|
|
212
|
+
};
|
|
213
|
+
})),
|
|
214
|
+
};
|
|
215
|
+
})()
|
|
216
|
+
: undefined;
|
|
217
|
+
const strictSummary = strictEvaluation === undefined
|
|
218
|
+
? undefined
|
|
219
|
+
: {
|
|
220
|
+
observations: strictEvaluation.observations,
|
|
221
|
+
contradicted: strictEvaluation.diagnostics.length,
|
|
222
|
+
};
|
|
223
|
+
const strictOk = strictEvaluation === undefined
|
|
224
|
+
? true
|
|
225
|
+
: strictEvaluation.diagnostics.length === 0;
|
|
226
|
+
const finalOk = ok && strictOk;
|
|
174
227
|
const counted = result.ok
|
|
175
228
|
? (() => {
|
|
176
229
|
const states = new Set(result.graph.claims
|
|
@@ -186,8 +239,15 @@ export function runCheckCommand(options, cwd) {
|
|
|
186
239
|
: undefined;
|
|
187
240
|
if (json) {
|
|
188
241
|
return {
|
|
189
|
-
exitCode:
|
|
190
|
-
stdout: checkResultJson(
|
|
242
|
+
exitCode: finalOk ? 0 : 1,
|
|
243
|
+
stdout: checkResultJson(finalOk, finalOk ? [] : ok ? strictEvaluation.diagnostics : diagnostics, finalOk ? counted : undefined, strictSummary),
|
|
244
|
+
stderr: '',
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
if (ok && !strictOk) {
|
|
248
|
+
return {
|
|
249
|
+
exitCode: 1,
|
|
250
|
+
stdout: humanDiagnostics(strictEvaluation.diagnostics),
|
|
191
251
|
stderr: '',
|
|
192
252
|
};
|
|
193
253
|
}
|
|
@@ -227,13 +287,19 @@ export function runCheckCommand(options, cwd) {
|
|
|
227
287
|
]
|
|
228
288
|
: []),
|
|
229
289
|
].join(' and ');
|
|
290
|
+
const strictLine = strictSummary === undefined
|
|
291
|
+
? ''
|
|
292
|
+
: strictSummary.observations === 0
|
|
293
|
+
? 'Strict: no evidence observations to evaluate\n'
|
|
294
|
+
: `Strict: ${strictSummary.observations} ${strictSummary.observations === 1 ? 'observation' : 'observations'}, 0 contradicted\n`;
|
|
230
295
|
return {
|
|
231
296
|
exitCode: 0,
|
|
232
297
|
stdout: `Checked ${checked} (` +
|
|
233
298
|
`${successfulCounts.concepts} ${successfulCounts.concepts === 1 ? 'concept' : 'concepts'}, ` +
|
|
234
299
|
`${successfulCounts.relationships} ${successfulCounts.relationships === 1 ? 'relationship' : 'relationships'}, ` +
|
|
235
300
|
`${successfulCounts.states} ${successfulCounts.states === 1 ? 'state' : 'states'}` +
|
|
236
|
-
'): no errors\n'
|
|
301
|
+
'): no errors\n' +
|
|
302
|
+
strictLine,
|
|
237
303
|
stderr: '',
|
|
238
304
|
};
|
|
239
305
|
}
|
package/dist/cli-support.d.ts
CHANGED
|
@@ -5,13 +5,18 @@ export interface CliResult {
|
|
|
5
5
|
readonly stderr: string;
|
|
6
6
|
}
|
|
7
7
|
export declare const isMainModule: (moduleUrl: string, entrypoint: string | undefined) => boolean;
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const packageVersion: string;
|
|
9
|
+
export declare const versionResult: (binary: string) => CliResult;
|
|
10
|
+
export declare const usage = "Usage:\n yarramate init <directory> [--no-pointer]\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--description <text>] [--status <status>] [--mode <mode>] [--content <text>] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate new projection <projection.yaml> --id <id> [--version <v>] [--title <text>] [--description <text>] [--document <id> ...] [--subject <ref> ...] [--kind <qualified-kind> ...] [--relationships <mode>]\n yarramate status <workspace.yaml> [--json]\n yarramate next <projection.yaml> <workspace.yaml> [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...] [--budget <tokens>]\n yarramate context --subject <document-id>#<local-id> [--subject ...] <source.yaml> [source.yaml ...] [--budget <tokens>]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n";
|
|
9
11
|
export declare const diagnosticJson: (diagnostics: unknown) => string;
|
|
10
12
|
export declare const checkResultJson: (ok: boolean, diagnostics: unknown, counted?: {
|
|
11
13
|
readonly documents: number;
|
|
12
14
|
readonly concepts: number;
|
|
13
15
|
readonly relationships: number;
|
|
14
16
|
readonly states: number;
|
|
17
|
+
}, strict?: {
|
|
18
|
+
readonly observations: number;
|
|
19
|
+
readonly contradicted: number;
|
|
15
20
|
}) => string;
|
|
16
21
|
export declare const humanDiagnostics: (diagnostics: readonly Pick<Diagnostic, "path" | "line" | "column" | "code" | "message">[]) => string;
|
|
17
22
|
export declare const sortDiagnostics: <T extends Diagnostic>(diagnostics: readonly T[]) => T[];
|
package/dist/cli-support.js
CHANGED
|
@@ -3,6 +3,9 @@ import { resolve } from 'node:path';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { parseDocument } from 'yaml';
|
|
5
5
|
import { loadWorkspaceManifest } from './workspace.js';
|
|
6
|
+
import packageManifest from '../package.json' with {
|
|
7
|
+
type: 'json'
|
|
8
|
+
};
|
|
6
9
|
export const isMainModule = (moduleUrl, entrypoint) => {
|
|
7
10
|
if (entrypoint === undefined)
|
|
8
11
|
return false;
|
|
@@ -14,16 +17,23 @@ export const isMainModule = (moduleUrl, entrypoint) => {
|
|
|
14
17
|
return false;
|
|
15
18
|
}
|
|
16
19
|
};
|
|
17
|
-
export const
|
|
20
|
+
export const packageVersion = packageManifest.version;
|
|
21
|
+
export const versionResult = (binary) => ({
|
|
22
|
+
exitCode: 0,
|
|
23
|
+
stdout: `${binary} ${packageVersion}\n`,
|
|
24
|
+
stderr: '',
|
|
25
|
+
});
|
|
26
|
+
export const usage = 'Usage:\n yarramate init <directory> [--no-pointer]\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--description <text>] [--status <status>] [--mode <mode>] [--content <text>] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate new projection <projection.yaml> --id <id> [--version <v>] [--title <text>] [--description <text>] [--document <id> ...] [--subject <ref> ...] [--kind <qualified-kind> ...] [--relationships <mode>]\n yarramate status <workspace.yaml> [--json]\n yarramate next <projection.yaml> <workspace.yaml> [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...] [--budget <tokens>]\n yarramate context --subject <document-id>#<local-id> [--subject ...] <source.yaml> [source.yaml ...] [--budget <tokens>]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n';
|
|
18
27
|
export const diagnosticJson = (diagnostics) => `${JSON.stringify({
|
|
19
28
|
format: 'yarramate/diagnostic-result/v1',
|
|
20
29
|
diagnostics,
|
|
21
30
|
}, null, 2)}\n`;
|
|
22
|
-
export const checkResultJson = (ok, diagnostics, counted) => `${JSON.stringify({
|
|
31
|
+
export const checkResultJson = (ok, diagnostics, counted, strict) => `${JSON.stringify({
|
|
23
32
|
format: 'yarramate/check-result/v1',
|
|
24
33
|
ok,
|
|
25
34
|
diagnostics,
|
|
26
35
|
...(counted === undefined ? {} : { counted }),
|
|
36
|
+
...(strict === undefined ? {} : { strict }),
|
|
27
37
|
}, null, 2)}\n`;
|
|
28
38
|
export const humanDiagnostics = (diagnostics) => diagnostics
|
|
29
39
|
.map((diagnostic) => `${diagnostic.path}:${diagnostic.line}:${diagnostic.column} error ${diagnostic.code} ${diagnostic.message}\n`)
|
package/dist/cli.js
CHANGED
|
@@ -5,9 +5,10 @@ import { isSeq, parseDocument } from 'yaml';
|
|
|
5
5
|
import { compileWorkspace, compileWorkspaceWithProfileContext, } from './compiler.js';
|
|
6
6
|
import { compareArchitectureStates } from './architecture-state.js';
|
|
7
7
|
import { serializeSemanticGraph } from './graph.js';
|
|
8
|
-
import { diagnosticJson, humanDiagnostics, isMainModule, resolveCliWorkspaceSources, usage, } from './cli-support.js';
|
|
8
|
+
import { diagnosticJson, humanDiagnostics, isMainModule, resolveCliWorkspaceSources, usage, versionResult, } from './cli-support.js';
|
|
9
9
|
import { runCheckCommand } from './check-command.js';
|
|
10
10
|
import { runNewCommand } from './new-command.js';
|
|
11
|
+
import { runNextCommand } from './next-command.js';
|
|
11
12
|
import { runStatusCommand } from './status-command.js';
|
|
12
13
|
import { evaluateEvidence, evaluateEvidenceWorkspace, loadEvidence, } from './evidence.js';
|
|
13
14
|
import { reconcileEvidenceReports } from './reconciliation.js';
|
|
@@ -297,7 +298,7 @@ const runReconciliation = (options, cwd) => {
|
|
|
297
298
|
}
|
|
298
299
|
return {
|
|
299
300
|
exitCode: 0,
|
|
300
|
-
stdout: `${JSON.stringify(reconcileEvidenceReports(loadedWorkspace.workspace.id, evaluation.reports), null, 2)}\n`,
|
|
301
|
+
stdout: `${JSON.stringify(reconcileEvidenceReports(loadedWorkspace.workspace.id, evaluation.reports, compilation.graph), null, 2)}\n`,
|
|
301
302
|
stderr: '',
|
|
302
303
|
};
|
|
303
304
|
}
|
|
@@ -353,8 +354,10 @@ const runStateComparison = (options, cwd) => {
|
|
|
353
354
|
}
|
|
354
355
|
};
|
|
355
356
|
const runInit = (options, cwd) => {
|
|
356
|
-
const
|
|
357
|
-
|
|
357
|
+
const positional = options.filter((option) => option !== '--no-pointer');
|
|
358
|
+
const writePointer = positional.length === options.length;
|
|
359
|
+
const target = positional[0];
|
|
360
|
+
if (positional.length !== 1 ||
|
|
358
361
|
target === undefined ||
|
|
359
362
|
target.startsWith('-')) {
|
|
360
363
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
@@ -389,10 +392,8 @@ const runInit = (options, cwd) => {
|
|
|
389
392
|
'projections: []\n' +
|
|
390
393
|
'adapterMappings: []\n' +
|
|
391
394
|
'evidence: []\n', 'utf8');
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
const agentsMarker = '## YarraMate architecture';
|
|
395
|
-
const agentsBlock = `${agentsMarker}\n` +
|
|
395
|
+
const pointerMarker = '## YarraMate architecture';
|
|
396
|
+
const pointerBlock = `${pointerMarker}\n` +
|
|
396
397
|
'\n' +
|
|
397
398
|
'This repository declares its architecture as canonical, versioned\n' +
|
|
398
399
|
'YarraMate documents in `.yarramate/`. When prose documentation and the\n' +
|
|
@@ -403,24 +404,34 @@ const runInit = (options, cwd) => {
|
|
|
403
404
|
'- Bounded task context: `yarramate context <projection.yaml> .yarramate/workspace.yaml`\n' +
|
|
404
405
|
'\n' +
|
|
405
406
|
'Author native documents only; never edit generated output.\n';
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
407
|
+
// Harnesses look in different files: AGENTS.md is the cross-harness
|
|
408
|
+
// convention, while Claude Code auto-loads CLAUDE.md only. Delivering to
|
|
409
|
+
// both is what makes the pointer reach an agent without instruction.
|
|
410
|
+
const pointerNotes = [];
|
|
411
|
+
if (writePointer) {
|
|
412
|
+
for (const pointerFile of ['AGENTS.md', 'CLAUDE.md']) {
|
|
413
|
+
const pointerPath = resolve(workspaceRoot, pointerFile);
|
|
414
|
+
const displayPointerPath = relative(cwd, pointerPath);
|
|
415
|
+
if (!existsSync(pointerPath)) {
|
|
416
|
+
writeFileSync(pointerPath, pointerBlock, 'utf8');
|
|
417
|
+
pointerNotes.push(`Created ${displayPointerPath} with the YarraMate pointer\n`);
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
const existingPointer = readFileSync(pointerPath, 'utf8');
|
|
421
|
+
if (existingPointer.includes(pointerMarker)) {
|
|
422
|
+
pointerNotes.push(`${displayPointerPath} already declares the YarraMate pointer\n`);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
writeFileSync(pointerPath, `${existingPointer.replace(/\n*$/, '\n\n')}${pointerBlock}`, 'utf8');
|
|
426
|
+
pointerNotes.push(`Extended ${displayPointerPath} with the YarraMate pointer\n`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
419
429
|
}
|
|
420
430
|
}
|
|
421
431
|
return {
|
|
422
432
|
exitCode: 0,
|
|
423
|
-
stdout: `Created ${displayPath} and ${displayManifestPath}\n` +
|
|
433
|
+
stdout: `Created ${displayPath} and ${displayManifestPath}\n` +
|
|
434
|
+
pointerNotes.join(''),
|
|
424
435
|
stderr: '',
|
|
425
436
|
};
|
|
426
437
|
};
|
|
@@ -706,6 +717,9 @@ export function runCli(args, cwd = process.cwd()) {
|
|
|
706
717
|
if (command === '--help' || command === '-h' || command === 'help') {
|
|
707
718
|
return { exitCode: 0, stdout: usage, stderr: '' };
|
|
708
719
|
}
|
|
720
|
+
if (command === '--version' || command === '-v') {
|
|
721
|
+
return versionResult('yarramate');
|
|
722
|
+
}
|
|
709
723
|
if (command === 'init') {
|
|
710
724
|
return runInit(options, cwd);
|
|
711
725
|
}
|
|
@@ -744,6 +758,9 @@ export function runCli(args, cwd = process.cwd()) {
|
|
|
744
758
|
if (command === 'new') {
|
|
745
759
|
return runNewCommand(options, cwd);
|
|
746
760
|
}
|
|
761
|
+
if (command === 'next') {
|
|
762
|
+
return runNextCommand(options, cwd);
|
|
763
|
+
}
|
|
747
764
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
748
765
|
}
|
|
749
766
|
if (isMainModule(import.meta.url, process.argv[1])) {
|
package/dist/compiler.js
CHANGED
|
@@ -14,6 +14,23 @@ const validateDocument = new Ajv2020({ allErrors: true }).compile(documentSchema
|
|
|
14
14
|
const validateProfile = new Ajv2020({ allErrors: true }).compile(profileSchema);
|
|
15
15
|
const compareById = (left, right) => left.id.localeCompare(right.id);
|
|
16
16
|
const presenceClaimId = (subject, state) => `${subject}~present-in-${Buffer.from(state, 'utf8').toString('hex')}`;
|
|
17
|
+
const describeAspect = (aspect) => aspect.replace('-', ' ');
|
|
18
|
+
// Candidate order is the policy-matrix declaration order: the resolved kind
|
|
19
|
+
// map inserts core policies first, then extension kinds as declared.
|
|
20
|
+
const candidateKindHint = (kinds, rejected, sourceAspect, targetAspect) => {
|
|
21
|
+
const candidates = [...kinds]
|
|
22
|
+
.filter(([id, kind]) => id !== rejected &&
|
|
23
|
+
(kind.sourceAspects?.includes(sourceAspect) ?? true) &&
|
|
24
|
+
(kind.targetAspects?.includes(targetAspect) ?? true))
|
|
25
|
+
.map(([id]) => id);
|
|
26
|
+
if (candidates.length === 0) {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
const observed = sourceAspect === targetAspect
|
|
30
|
+
? `both endpoints are ${describeAspect(sourceAspect)}`
|
|
31
|
+
: `source is ${describeAspect(sourceAspect)} and target is ${describeAspect(targetAspect)}`;
|
|
32
|
+
return `; ${observed}; valid candidates: ${candidates.join(', ')}`;
|
|
33
|
+
};
|
|
17
34
|
const diagnosticFailure = (diagnostics) => ({
|
|
18
35
|
ok: false,
|
|
19
36
|
diagnostics: [...diagnostics].sort((left, right) => left.path.localeCompare(right.path) ||
|
|
@@ -54,6 +71,7 @@ function compileWorkspaceResolved(sources) {
|
|
|
54
71
|
lineage: [`${coreProfile}#${policy.id}`],
|
|
55
72
|
sourceAspects: policy.sourceAspects,
|
|
56
73
|
targetAspects: policy.targetAspects,
|
|
74
|
+
repair: policy.repair,
|
|
57
75
|
};
|
|
58
76
|
coreRelationshipKinds.set(policy.id, resolved);
|
|
59
77
|
relationshipKindByIdentity.set(resolved.identity, resolved);
|
|
@@ -778,27 +796,35 @@ function compileWorkspaceResolved(sources) {
|
|
|
778
796
|
}
|
|
779
797
|
const policy = selectedProfile.relationshipKinds.get(relationship.kind);
|
|
780
798
|
if (policy !== undefined) {
|
|
781
|
-
|
|
782
|
-
const reference = endpoint === 'source' ? relationship.from : relationship.to;
|
|
799
|
+
const aspectOf = (reference) => {
|
|
783
800
|
const resolvedConcept = conceptByQualifiedId.get(qualifyReference(value.id, reference));
|
|
784
|
-
|
|
801
|
+
return resolvedConcept === undefined
|
|
785
802
|
? undefined
|
|
786
803
|
: profiles
|
|
787
804
|
.get(resolvedConcept.profile)
|
|
788
|
-
?.conceptKinds.get(resolvedConcept.concept.kind);
|
|
805
|
+
?.conceptKinds.get(resolvedConcept.concept.kind)?.aspect;
|
|
806
|
+
};
|
|
807
|
+
const sourceAspect = aspectOf(relationship.from);
|
|
808
|
+
const targetAspect = aspectOf(relationship.to);
|
|
809
|
+
const candidates = sourceAspect === undefined || targetAspect === undefined
|
|
810
|
+
? ''
|
|
811
|
+
: candidateKindHint(selectedProfile.relationshipKinds, relationship.kind, sourceAspect, targetAspect);
|
|
812
|
+
for (const endpoint of ['source', 'target']) {
|
|
813
|
+
const reference = endpoint === 'source' ? relationship.from : relationship.to;
|
|
814
|
+
const aspect = endpoint === 'source' ? sourceAspect : targetAspect;
|
|
789
815
|
const allowed = endpoint === 'source'
|
|
790
816
|
? policy.sourceAspects
|
|
791
817
|
: policy.targetAspects;
|
|
792
|
-
if (
|
|
818
|
+
if (aspect !== undefined &&
|
|
793
819
|
allowed !== undefined &&
|
|
794
|
-
!allowed.includes(
|
|
820
|
+
!allowed.includes(aspect)) {
|
|
795
821
|
const field = endpoint === 'source' ? 'from' : 'to';
|
|
796
822
|
const pointer = `/relationships/${index}/${field}`;
|
|
797
823
|
const source = location(['relationships', index, field], pointer);
|
|
798
824
|
diagnostics.push({
|
|
799
825
|
severity: 'error',
|
|
800
826
|
code: 'YM404',
|
|
801
|
-
message: `Relationship "${relationship.kind}" requires a ${endpoint} with aspect ${allowed.map((
|
|
827
|
+
message: `Relationship "${relationship.kind}" requires a ${endpoint} with aspect ${allowed.map((entry) => `"${entry}"`).join(' or ')}; "${reference}" has aspect "${aspect}"${policy.repair === undefined ? '' : `; ${policy.repair}`}${candidates}`,
|
|
802
828
|
path: input.path,
|
|
803
829
|
pointer,
|
|
804
830
|
line: source.line,
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { checkCoreContract, loadCoreContract, type CoreContract, type CoreContra
|
|
|
4
4
|
export { compareArchitectureStates, type StateComparison, type StateComparisonIssue, type StateComparisonResult, } from './architecture-state.js';
|
|
5
5
|
export { loadWorkspaceManifest, type ResolvedWorkspace, type WorkspaceManifest, type WorkspaceManifestResult, } from './workspace.js';
|
|
6
6
|
export { evaluateEvidence, evaluateEvidenceWorkspace, loadEvidence, type EvidenceDocument, type EvidenceEvaluationResult, type EvidenceLoadResult, type EvidenceLocator, type EvidenceObservation, type EvidenceReport, type EvidenceResult, type EvidenceWorkspaceEvaluationResult, } from './evidence.js';
|
|
7
|
-
export { reconcileEvidenceReports, type ReconciliationFinding, type ReconciliationReport, } from './reconciliation.js';
|
|
7
|
+
export { reconcileEvidenceReports, type AssertedRelationship, type ReconciliationFinding, type ReconciliationReport, } from './reconciliation.js';
|
|
8
8
|
export type { CompilationResult, ContextualCompilationResult, Diagnostic, GraphClaim, GraphSource, SemanticGraph, ResolvedProfileContext, WorkspaceSource, } from './compiler.js';
|
|
9
9
|
export { evaluateProjection, loadProjection, renderProjectionMarkdown, } from './projection.js';
|
|
10
10
|
export { loadAdapterMapping, validateAdapterMapping, validateAdapterMappings, type AdapterMapping, type AdapterMappingLoadResult, type AdapterMappingValidationResult, type AdapterMappingsValidationResult, type AdapterSubjectMapping, } from './adapter-mapping.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type CliResult } from './cli-support.js';
|
|
2
|
+
interface EvidenceCoverage {
|
|
3
|
+
readonly observations: number;
|
|
4
|
+
readonly confirmed: number;
|
|
5
|
+
readonly contradicted: number;
|
|
6
|
+
readonly unknown: number;
|
|
7
|
+
readonly notObserved: number;
|
|
8
|
+
}
|
|
9
|
+
export interface NextSubject {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly kind: string;
|
|
12
|
+
readonly name?: string;
|
|
13
|
+
readonly dependsOn: readonly string[];
|
|
14
|
+
readonly requiredBy: readonly string[];
|
|
15
|
+
readonly evidence: EvidenceCoverage;
|
|
16
|
+
readonly cycle?: true;
|
|
17
|
+
}
|
|
18
|
+
export interface NextResult {
|
|
19
|
+
readonly format: 'yarramate/next-result/v1';
|
|
20
|
+
readonly workspace: string;
|
|
21
|
+
readonly projection: string;
|
|
22
|
+
readonly subjects: readonly NextSubject[];
|
|
23
|
+
}
|
|
24
|
+
export declare function runNextCommand(options: readonly string[], cwd: string): CliResult;
|
|
25
|
+
export {};
|