yarramate 0.3.2 → 0.4.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 +1 -0
- package/dist/adapters/mcp-cli.d.ts +9 -0
- package/dist/adapters/mcp-cli.js +165 -0
- package/dist/cli-support.d.ts +1 -1
- package/dist/cli-support.js +1 -1
- package/dist/cli.js +162 -6
- package/dist/compiler.js +15 -4
- package/dist/new-command.d.ts +2 -0
- package/dist/new-command.js +111 -0
- package/dist/projection.d.ts +1 -0
- package/dist/projection.js +106 -0
- package/dist/source-document.d.ts +3 -1
- package/dist/source-document.js +43 -1
- package/dist/status-command.d.ts +2 -0
- package/dist/status-command.js +171 -0
- package/docs/CONSUMING-YARRAMATE.md +48 -0
- package/package.json +4 -2
- package/schema/yarramate-core-contract.schema.json +11 -2
- package/schema/yarramate-status-result.schema.json +273 -0
- package/skills/yarramate-architecture/SKILL.md +71 -3
- package/skills/yarramate-architecture/agents/openai.yaml +2 -2
- package/skills/yarramate-architecture/references/journey-checklists.md +8 -0
- package/skills/yarramate-architecture/references/native-authoring.md +17 -2
package/dist/projection.js
CHANGED
|
@@ -181,3 +181,109 @@ export function renderProjectionMarkdown(result) {
|
|
|
181
181
|
}
|
|
182
182
|
return `${lines.join('\n')}\n`;
|
|
183
183
|
}
|
|
184
|
+
const estimateTokens = (text) => Math.ceil(text.length / 4);
|
|
185
|
+
export function renderBudgetedContext(result, budgetTokens) {
|
|
186
|
+
const concepts = result.subjects.filter(({ type }) => type === 'concept');
|
|
187
|
+
const relationships = result.subjects.filter(({ type }) => type === 'relationship');
|
|
188
|
+
const title = result.presentation?.title ?? result.projection;
|
|
189
|
+
const header = [
|
|
190
|
+
`context ${result.projection} — ${title}`,
|
|
191
|
+
`subjects: ${concepts.length} concepts, ${relationships.length} relationships`,
|
|
192
|
+
];
|
|
193
|
+
const subjectLines = [];
|
|
194
|
+
for (const concept of concepts) {
|
|
195
|
+
const name = claimValue(result.claims, concept.id, 'yarramate/concept/name');
|
|
196
|
+
const kind = claimValue(result.claims, concept.id, 'yarramate/concept/kind') ??
|
|
197
|
+
'unknown';
|
|
198
|
+
const status = claimValue(result.claims, concept.id, 'yarramate/lifecycle/status');
|
|
199
|
+
subjectLines.push(`- ${concept.id} [${kind.split('#')[1] ?? kind}]` +
|
|
200
|
+
`${name === undefined ? '' : ` ${name}`}` +
|
|
201
|
+
`${status === undefined ? '' : ` (${status})`}`);
|
|
202
|
+
}
|
|
203
|
+
const relationshipLines = [];
|
|
204
|
+
for (const relationship of relationships) {
|
|
205
|
+
const claim = result.claims.find(({ id, object }) => id === relationship.id && 'ref' in object);
|
|
206
|
+
if (claim !== undefined && 'ref' in claim.object) {
|
|
207
|
+
const kind = claim.predicate.split('#')[1] ?? claim.predicate;
|
|
208
|
+
relationshipLines.push(`- ${claim.subject} -${kind}-> ${claim.object.ref}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const descriptionLines = [];
|
|
212
|
+
for (const subject of result.subjects) {
|
|
213
|
+
const description = claimValue(result.claims, subject.id, 'yarramate/concept/description') ??
|
|
214
|
+
claimValue(result.claims, subject.id, 'yarramate/relationship/description');
|
|
215
|
+
if (description !== undefined) {
|
|
216
|
+
descriptionLines.push(`- ${subject.id}: ${description}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const detailPredicates = new Set([
|
|
220
|
+
'yarramate/ownership/owner',
|
|
221
|
+
'yarramate/constraint/requires',
|
|
222
|
+
'yarramate/reference/refers-to',
|
|
223
|
+
'yarramate/access/mode',
|
|
224
|
+
'yarramate/state/present-in',
|
|
225
|
+
]);
|
|
226
|
+
const detailLines = [];
|
|
227
|
+
for (const claim of result.claims) {
|
|
228
|
+
if (!detailPredicates.has(claim.predicate))
|
|
229
|
+
continue;
|
|
230
|
+
const value = 'ref' in claim.object ? claim.object.ref : claim.object.value;
|
|
231
|
+
const predicate = claim.predicate.split('/').pop() ?? claim.predicate;
|
|
232
|
+
detailLines.push(`- ${claim.subject} ${predicate}: ${value}`);
|
|
233
|
+
}
|
|
234
|
+
// Ranked ladder: the two header lines always render; every other line —
|
|
235
|
+
// including the subject skeleton — competes for the remaining budget in
|
|
236
|
+
// priority order, and anything dropped is always announced rather than
|
|
237
|
+
// silently omitted.
|
|
238
|
+
const sections = [
|
|
239
|
+
{ heading: 'relationships:', lines: relationshipLines },
|
|
240
|
+
{ heading: 'descriptions:', lines: descriptionLines },
|
|
241
|
+
{ heading: 'details:', lines: detailLines },
|
|
242
|
+
];
|
|
243
|
+
const rendered = [...header];
|
|
244
|
+
const omitted = [];
|
|
245
|
+
let spent = estimateTokens(rendered.join('\n'));
|
|
246
|
+
let droppedSubjects = 0;
|
|
247
|
+
for (const line of subjectLines) {
|
|
248
|
+
const cost = estimateTokens(line);
|
|
249
|
+
if (spent + cost > budgetTokens) {
|
|
250
|
+
droppedSubjects += 1;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
rendered.push(line);
|
|
254
|
+
spent += cost;
|
|
255
|
+
}
|
|
256
|
+
if (droppedSubjects > 0) {
|
|
257
|
+
omitted.push(`subjects ${droppedSubjects} omitted`);
|
|
258
|
+
}
|
|
259
|
+
for (const section of sections) {
|
|
260
|
+
if (section.lines.length === 0)
|
|
261
|
+
continue;
|
|
262
|
+
const kept = [section.heading];
|
|
263
|
+
let sectionSpent = estimateTokens(section.heading);
|
|
264
|
+
let dropped = 0;
|
|
265
|
+
for (const line of section.lines) {
|
|
266
|
+
const cost = estimateTokens(line);
|
|
267
|
+
if (spent + sectionSpent + cost > budgetTokens) {
|
|
268
|
+
dropped += 1;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
kept.push(line);
|
|
272
|
+
sectionSpent += cost;
|
|
273
|
+
}
|
|
274
|
+
if (kept.length > 1) {
|
|
275
|
+
rendered.push('', ...kept);
|
|
276
|
+
spent += sectionSpent;
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
dropped = section.lines.length;
|
|
280
|
+
}
|
|
281
|
+
if (dropped > 0) {
|
|
282
|
+
omitted.push(`${section.heading.replace(':', '')} ${dropped} omitted`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (omitted.length > 0) {
|
|
286
|
+
rendered.push('', `[budget ${budgetTokens}: ${omitted.join('; ')} — raise --budget or use JSON mode for the complete slice]`);
|
|
287
|
+
}
|
|
288
|
+
return `${rendered.join('\n')}\n`;
|
|
289
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ValidateFunction } from 'ajv';
|
|
1
|
+
import type { ErrorObject, ValidateFunction } from 'ajv';
|
|
2
2
|
import { LineCounter, parseDocument } from 'yaml';
|
|
3
3
|
import type { Diagnostic, WorkspaceSource } from './compiler.js';
|
|
4
4
|
export interface SourceLocation {
|
|
@@ -20,5 +20,7 @@ export type SourceDocumentResult<T> = {
|
|
|
20
20
|
readonly diagnostics: readonly Diagnostic[];
|
|
21
21
|
};
|
|
22
22
|
export declare const diagnosticOrder: (left: Diagnostic, right: Diagnostic) => number;
|
|
23
|
+
export declare const describeSchemaViolation: (error: Pick<ErrorObject, "keyword" | "message" | "params">) => string;
|
|
24
|
+
export declare const closestCandidate: (value: string, candidates: Iterable<string>) => string | undefined;
|
|
23
25
|
export declare function locateSourcePath(sourcePath: string, yaml: ReturnType<typeof parseDocument>, lineCounter: LineCounter, yamlPath: readonly (string | number)[], pointer: string): SourceLocation;
|
|
24
26
|
export declare function loadSourceDocument<T>(source: WorkspaceSource, validate: ValidateFunction, schemaLabel: string): SourceDocumentResult<T>;
|
package/dist/source-document.js
CHANGED
|
@@ -4,6 +4,48 @@ export const diagnosticOrder = (left, right) => left.path.localeCompare(right.pa
|
|
|
4
4
|
left.column - right.column ||
|
|
5
5
|
left.code.localeCompare(right.code) ||
|
|
6
6
|
left.message.localeCompare(right.message);
|
|
7
|
+
export const describeSchemaViolation = (error) => {
|
|
8
|
+
const base = error.message ?? error.keyword;
|
|
9
|
+
if (error.keyword === 'const') {
|
|
10
|
+
return `${base}: expected ${JSON.stringify(error.params.allowedValue)}`;
|
|
11
|
+
}
|
|
12
|
+
if (error.keyword === 'enum' &&
|
|
13
|
+
Array.isArray(error.params.allowedValues)) {
|
|
14
|
+
const allowed = error.params.allowedValues;
|
|
15
|
+
const shown = allowed
|
|
16
|
+
.slice(0, 8)
|
|
17
|
+
.map((value) => JSON.stringify(value))
|
|
18
|
+
.join(', ');
|
|
19
|
+
return `${base}: ${shown}${allowed.length > 8 ? ', …' : ''}`;
|
|
20
|
+
}
|
|
21
|
+
return base;
|
|
22
|
+
};
|
|
23
|
+
const editDistance = (left, right) => {
|
|
24
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
25
|
+
for (let i = 1; i <= left.length; i += 1) {
|
|
26
|
+
let diagonal = previous[0];
|
|
27
|
+
previous[0] = i;
|
|
28
|
+
for (let j = 1; j <= right.length; j += 1) {
|
|
29
|
+
const above = previous[j];
|
|
30
|
+
previous[j] = Math.min(above + 1, previous[j - 1] + 1, diagonal + (left[i - 1] === right[j - 1] ? 0 : 1));
|
|
31
|
+
diagonal = above;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return previous[right.length];
|
|
35
|
+
};
|
|
36
|
+
export const closestCandidate = (value, candidates) => {
|
|
37
|
+
const threshold = Math.max(2, Math.floor(value.length / 4));
|
|
38
|
+
let best;
|
|
39
|
+
let bestDistance = threshold + 1;
|
|
40
|
+
for (const candidate of [...candidates].sort()) {
|
|
41
|
+
const distance = editDistance(value.toLowerCase(), candidate.toLowerCase());
|
|
42
|
+
if (distance < bestDistance) {
|
|
43
|
+
best = candidate;
|
|
44
|
+
bestDistance = distance;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return best;
|
|
48
|
+
};
|
|
7
49
|
export function locateSourcePath(sourcePath, yaml, lineCounter, yamlPath, pointer) {
|
|
8
50
|
const node = yaml.getIn(yamlPath, true);
|
|
9
51
|
const offset = typeof node === 'object' &&
|
|
@@ -62,7 +104,7 @@ export function loadSourceDocument(source, validate, schemaLabel) {
|
|
|
62
104
|
code: 'YM201',
|
|
63
105
|
message: property
|
|
64
106
|
? `Property "${property}" is not allowed`
|
|
65
|
-
: `${schemaLabel} schema violation: ${error
|
|
107
|
+
: `${schemaLabel} schema violation: ${describeSchemaViolation(error)}`,
|
|
66
108
|
...location,
|
|
67
109
|
};
|
|
68
110
|
})
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { parseDocument } from 'yaml';
|
|
4
|
+
import { runCheckCommand } from './check-command.js';
|
|
5
|
+
import { diagnosticJson, humanDiagnostics, usage, } from './cli-support.js';
|
|
6
|
+
import { compileWorkspace } from './compiler.js';
|
|
7
|
+
import { evaluateEvidenceWorkspace, loadEvidence } from './evidence.js';
|
|
8
|
+
import { loadProjection } from './projection.js';
|
|
9
|
+
import { reconcileEvidenceReports, } from './reconciliation.js';
|
|
10
|
+
import { loadWorkspaceManifest } from './workspace.js';
|
|
11
|
+
const plural = (count, singular, pluralForm) => `${count} ${count === 1 ? singular : (pluralForm ?? `${singular}s`)}`;
|
|
12
|
+
export function runStatusCommand(options, cwd) {
|
|
13
|
+
const json = options.includes('--json');
|
|
14
|
+
const paths = options.filter((option) => option !== '--json');
|
|
15
|
+
const [workspacePath] = paths;
|
|
16
|
+
if (paths.length !== 1 ||
|
|
17
|
+
workspacePath === undefined ||
|
|
18
|
+
workspacePath.startsWith('-')) {
|
|
19
|
+
return { exitCode: 2, stdout: '', stderr: usage };
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const manifestSource = readFileSync(resolve(cwd, workspacePath), 'utf8');
|
|
23
|
+
if (parseDocument(manifestSource).get('format') !==
|
|
24
|
+
'yarramate/workspace/v1') {
|
|
25
|
+
return {
|
|
26
|
+
exitCode: 2,
|
|
27
|
+
stdout: '',
|
|
28
|
+
stderr: 'status requires an explicit workspace manifest (yarramate/workspace/v1)\n',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const loadedWorkspace = loadWorkspaceManifest({ path: workspacePath, source: manifestSource }, cwd);
|
|
32
|
+
if (!loadedWorkspace.ok) {
|
|
33
|
+
return {
|
|
34
|
+
exitCode: 1,
|
|
35
|
+
stdout: json
|
|
36
|
+
? diagnosticJson(loadedWorkspace.diagnostics)
|
|
37
|
+
: humanDiagnostics(loadedWorkspace.diagnostics),
|
|
38
|
+
stderr: '',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const workspace = loadedWorkspace.workspace;
|
|
42
|
+
const checked = runCheckCommand([workspacePath, '--json'], cwd);
|
|
43
|
+
const checkPayload = JSON.parse(checked.stdout);
|
|
44
|
+
const projections = workspace.projections.map((path) => {
|
|
45
|
+
const loaded = loadProjection({
|
|
46
|
+
path,
|
|
47
|
+
source: readFileSync(resolve(cwd, path), 'utf8'),
|
|
48
|
+
});
|
|
49
|
+
return loaded.ok
|
|
50
|
+
? {
|
|
51
|
+
id: loaded.projection.id,
|
|
52
|
+
path,
|
|
53
|
+
...(loaded.projection.presentation?.title === undefined
|
|
54
|
+
? {}
|
|
55
|
+
: { title: loaded.projection.presentation.title }),
|
|
56
|
+
}
|
|
57
|
+
: { id: path, path };
|
|
58
|
+
});
|
|
59
|
+
let documents = workspace.documents.map((path) => ({ id: path, path }));
|
|
60
|
+
let states = [];
|
|
61
|
+
let reconciliation;
|
|
62
|
+
if (checkPayload.ok) {
|
|
63
|
+
const compilation = compileWorkspace([...workspace.profiles, ...workspace.documents].map((path) => ({
|
|
64
|
+
path,
|
|
65
|
+
source: readFileSync(resolve(cwd, path), 'utf8'),
|
|
66
|
+
})));
|
|
67
|
+
if (compilation.ok) {
|
|
68
|
+
const sourceByDocument = new Map(compilation.graph.documents.map((document) => [
|
|
69
|
+
document.id,
|
|
70
|
+
document.source,
|
|
71
|
+
]));
|
|
72
|
+
documents = workspace.documents.map((path) => {
|
|
73
|
+
const id = [...sourceByDocument.entries()].find(([, source]) => source === path)?.[0];
|
|
74
|
+
return { id: id ?? path, path };
|
|
75
|
+
});
|
|
76
|
+
states = compilation.graph.claims
|
|
77
|
+
.filter(({ predicate }) => predicate === 'yarramate/state/type')
|
|
78
|
+
.map(({ subject, object }) => ({
|
|
79
|
+
id: subject,
|
|
80
|
+
type: 'value' in object && typeof object.value === 'string'
|
|
81
|
+
? object.value
|
|
82
|
+
: 'baseline',
|
|
83
|
+
}));
|
|
84
|
+
if (workspace.evidence.length > 0) {
|
|
85
|
+
const evidenceDocuments = workspace.evidence.flatMap((path) => {
|
|
86
|
+
const loaded = loadEvidence({
|
|
87
|
+
path,
|
|
88
|
+
source: readFileSync(resolve(cwd, path), 'utf8'),
|
|
89
|
+
});
|
|
90
|
+
return loaded.ok ? [loaded.evidence] : [];
|
|
91
|
+
});
|
|
92
|
+
const evaluation = evaluateEvidenceWorkspace(compilation.graph, evidenceDocuments);
|
|
93
|
+
if (evaluation.ok) {
|
|
94
|
+
reconciliation = reconcileEvidenceReports(workspace.id, evaluation.reports).summary;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const result = {
|
|
100
|
+
format: 'yarramate/status-result/v1',
|
|
101
|
+
workspace: workspace.id,
|
|
102
|
+
ok: checkPayload.ok,
|
|
103
|
+
check: {
|
|
104
|
+
ok: checkPayload.ok,
|
|
105
|
+
diagnostics: checkPayload.ok ? [] : checkPayload.diagnostics,
|
|
106
|
+
...(checkPayload.counted === undefined
|
|
107
|
+
? {}
|
|
108
|
+
: { counted: checkPayload.counted }),
|
|
109
|
+
},
|
|
110
|
+
...(reconciliation === undefined ? {} : { reconciliation }),
|
|
111
|
+
inventory: {
|
|
112
|
+
documents,
|
|
113
|
+
profiles: workspace.profiles,
|
|
114
|
+
states,
|
|
115
|
+
projections,
|
|
116
|
+
evidence: workspace.evidence,
|
|
117
|
+
adapterMappings: workspace.adapterMappings,
|
|
118
|
+
contracts: workspace.contracts,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
if (json) {
|
|
122
|
+
return {
|
|
123
|
+
exitCode: result.ok ? 0 : 1,
|
|
124
|
+
stdout: `${JSON.stringify(result, null, 2)}\n`,
|
|
125
|
+
stderr: '',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const lines = [];
|
|
129
|
+
const counted = result.check.counted;
|
|
130
|
+
lines.push(`Workspace ${result.workspace}: check ${result.ok ? 'ok' : 'failing'}` +
|
|
131
|
+
(counted === undefined
|
|
132
|
+
? ''
|
|
133
|
+
: ` (${plural(counted.concepts, 'concept')}, ` +
|
|
134
|
+
`${plural(counted.relationships, 'relationship')}, ` +
|
|
135
|
+
`${plural(counted.states, 'state')}, ` +
|
|
136
|
+
`${plural(counted.documents, 'document')})`));
|
|
137
|
+
if (!result.ok) {
|
|
138
|
+
lines.push(`Diagnostics: ${plural(result.check.diagnostics.length, 'error')}; run \`yarramate check ${workspacePath}\` for details`);
|
|
139
|
+
}
|
|
140
|
+
if (result.reconciliation !== undefined) {
|
|
141
|
+
lines.push(`Reconciliation: ${plural(result.reconciliation.observations, 'observation')}, ` +
|
|
142
|
+
`${result.reconciliation.confirmed} confirmed, ` +
|
|
143
|
+
`${plural(result.reconciliation.findings, 'finding')}` +
|
|
144
|
+
(result.reconciliation.findings > 0
|
|
145
|
+
? ` (${result.reconciliation.contradicted} contradicted, ` +
|
|
146
|
+
`${result.reconciliation.unknown} unknown, ` +
|
|
147
|
+
`${result.reconciliation.notObserved} not observed)`
|
|
148
|
+
: ''));
|
|
149
|
+
}
|
|
150
|
+
lines.push(`Documents: ${documents.map(({ id }) => id).join(', ') || 'none'}`);
|
|
151
|
+
if (states.length > 0) {
|
|
152
|
+
lines.push(`States: ${states.map(({ id, type }) => `${id} (${type})`).join(', ')}`);
|
|
153
|
+
}
|
|
154
|
+
lines.push(`Projections: ${projections
|
|
155
|
+
.map(({ id, title }) => title === undefined ? id : `${id} — ${title}`)
|
|
156
|
+
.join('; ') || 'none'}`);
|
|
157
|
+
lines.push(`Profiles: ${workspace.profiles.length} · ` +
|
|
158
|
+
`Evidence: ${workspace.evidence.length} · ` +
|
|
159
|
+
`Adapter mappings: ${workspace.adapterMappings.length} · ` +
|
|
160
|
+
`Contracts: ${workspace.contracts.length}`);
|
|
161
|
+
return {
|
|
162
|
+
exitCode: result.ok ? 0 : 1,
|
|
163
|
+
stdout: `${lines.join('\n')}\n`,
|
|
164
|
+
stderr: '',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
169
|
+
return { exitCode: 2, stdout: '', stderr: `${message}\n` };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -143,3 +143,51 @@ yarramate-graphify observe \
|
|
|
143
143
|
Graphify extraction remains a separate installation and operation. The
|
|
144
144
|
adapter observes only explicitly mapped nodes and never promotes them into
|
|
145
145
|
canonical architecture.
|
|
146
|
+
|
|
147
|
+
## MCP server for agent harnesses
|
|
148
|
+
|
|
149
|
+
Harnesses that load MCP servers can connect the bundled read-only adapter:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"mcpServers": {
|
|
154
|
+
"yarramate": {
|
|
155
|
+
"command": "yarramate-mcp"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
It exposes `yarramate_status`, `yarramate_check`, `yarramate_reconcile`,
|
|
162
|
+
and `yarramate_context` (projection path or ad-hoc subjects, with an
|
|
163
|
+
optional token budget). Every tool call executes the same stable CLI in
|
|
164
|
+
the server's working directory; nothing mutates native documents, and
|
|
165
|
+
authoring stays with the CLI and Git review.
|
|
166
|
+
|
|
167
|
+
## Continuous drift signal in CI
|
|
168
|
+
|
|
169
|
+
The repository root ships a composite GitHub Action that checks the
|
|
170
|
+
workspace and reports intent-vs-evidence drift on every pull request:
|
|
171
|
+
|
|
172
|
+
```yaml
|
|
173
|
+
name: architecture
|
|
174
|
+
on: pull_request
|
|
175
|
+
jobs:
|
|
176
|
+
drift:
|
|
177
|
+
runs-on: ubuntu-latest
|
|
178
|
+
steps:
|
|
179
|
+
- uses: actions/checkout@v4
|
|
180
|
+
- uses: actions/setup-node@v4
|
|
181
|
+
with:
|
|
182
|
+
node-version: 22
|
|
183
|
+
- uses: yarrasys/yarramate@main
|
|
184
|
+
with:
|
|
185
|
+
workspace: .yarramate/workspace.yaml
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The job fails on deterministic correctness errors and, by default, when
|
|
189
|
+
reconciliation reports contradicted claims; unknown and not-observed
|
|
190
|
+
findings are reported in the job summary without failing. Set
|
|
191
|
+
`fail-on-contradiction: 'false'` to make the whole signal advisory. The
|
|
192
|
+
action never mutates sources — it runs only the read-only `check` and
|
|
193
|
+
`reconcile` commands, so it is safe as a required check.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yarramate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Tool-neutral semantic architecture engine and guided methodology",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"./schema/likec4-kind-mapping": "./schema/yarramate-likec4-kind-mapping.schema.json",
|
|
59
59
|
"./schema/likec4-check-result": "./schema/yarramate-likec4-check-result.schema.json",
|
|
60
60
|
"./schema/state-comparison": "./schema/yarramate-state-comparison.schema.json",
|
|
61
|
+
"./schema/status-result": "./schema/yarramate-status-result.schema.json",
|
|
61
62
|
"./schema/graph-v2": "./schema/yarramate-graph-v2.schema.json",
|
|
62
63
|
"./schema/workspace": "./schema/yarramate-workspace.schema.json",
|
|
63
64
|
"./schema/evidence": "./schema/yarramate-evidence.schema.json",
|
|
@@ -69,7 +70,8 @@
|
|
|
69
70
|
"bin": {
|
|
70
71
|
"yarramate": "dist/cli.js",
|
|
71
72
|
"yarramate-likec4": "dist/adapters/likec4-cli.js",
|
|
72
|
-
"yarramate-graphify": "dist/adapters/graphify-cli.js"
|
|
73
|
+
"yarramate-graphify": "dist/adapters/graphify-cli.js",
|
|
74
|
+
"yarramate-mcp": "dist/adapters/mcp-cli.js"
|
|
73
75
|
},
|
|
74
76
|
"packageManager": "pnpm@11.7.0",
|
|
75
77
|
"engines": {
|
|
@@ -34,7 +34,11 @@
|
|
|
34
34
|
"items": {
|
|
35
35
|
"type": "object",
|
|
36
36
|
"additionalProperties": false,
|
|
37
|
-
"required": [
|
|
37
|
+
"required": [
|
|
38
|
+
"id",
|
|
39
|
+
"schema",
|
|
40
|
+
"packageExport"
|
|
41
|
+
],
|
|
38
42
|
"properties": {
|
|
39
43
|
"id": {
|
|
40
44
|
"$ref": "#/$defs/formatIdentity"
|
|
@@ -55,14 +59,19 @@
|
|
|
55
59
|
"items": {
|
|
56
60
|
"type": "object",
|
|
57
61
|
"additionalProperties": false,
|
|
58
|
-
"required": [
|
|
62
|
+
"required": [
|
|
63
|
+
"name",
|
|
64
|
+
"binary"
|
|
65
|
+
],
|
|
59
66
|
"properties": {
|
|
60
67
|
"name": {
|
|
61
68
|
"enum": [
|
|
62
69
|
"init",
|
|
63
70
|
"add",
|
|
64
71
|
"connect",
|
|
72
|
+
"new",
|
|
65
73
|
"check",
|
|
74
|
+
"status",
|
|
66
75
|
"compile",
|
|
67
76
|
"view",
|
|
68
77
|
"context",
|