yarramate 1.27.0 → 1.28.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/dist/cli-support.d.ts +1 -1
- package/dist/cli-support.js +1 -1
- package/dist/cli.js +12 -3
- package/dist/reconciliation-text.d.ts +13 -0
- package/dist/reconciliation-text.js +76 -0
- package/docs/EVIDENCE.md +7 -0
- package/package.json +1 -1
package/dist/cli-support.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface CliResult {
|
|
|
7
7
|
export declare const isMainModule: (moduleUrl: string, entrypoint: string | undefined) => boolean;
|
|
8
8
|
export declare const packageVersion: string;
|
|
9
9
|
export declare const versionResult: (binary: string) => CliResult;
|
|
10
|
-
export declare const usage = "Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <subject-id>] [--catalogue <catalogue.yaml>] [--facilitate] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> \"<free text>\" | <subject-id> ... | <projection.yaml> [--budget <tokens>] [--neighbours <n>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise \"<topic>\" [--budget <tokens>] [--neighbours <n>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --where \"<free text>\" | <subject-id> ... [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate ask <workspace.yaml> --changed <git-range> [--budget <tokens>] [--neighbours <n>] [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml> [--json]\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export markdown --changed <git-range> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export briefs --changed <git-range> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export rtm <workspace.yaml> --out <directory>\n yarramate export xlsx <projection.yaml> <workspace.yaml> --out <file>\n yarramate import xlsx <workbook.xlsx> <workspace.yaml> [--json]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml> [--changed <git-range>]\n";
|
|
10
|
+
export declare const usage = "Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <subject-id>] [--catalogue <catalogue.yaml>] [--facilitate] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> \"<free text>\" | <subject-id> ... | <projection.yaml> [--budget <tokens>] [--neighbours <n>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise \"<topic>\" [--budget <tokens>] [--neighbours <n>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --where \"<free text>\" | <subject-id> ... [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate ask <workspace.yaml> --changed <git-range> [--budget <tokens>] [--neighbours <n>] [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml> [--json | --text]\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export markdown --changed <git-range> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export briefs --changed <git-range> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export rtm <workspace.yaml> --out <directory>\n yarramate export xlsx <projection.yaml> <workspace.yaml> --out <file>\n yarramate import xlsx <workbook.xlsx> <workspace.yaml> [--json]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml> [--changed <git-range>]\n";
|
|
11
11
|
export declare const diagnosticJson: (diagnostics: unknown) => string;
|
|
12
12
|
export declare const checkResultJson: (ok: boolean, diagnostics: unknown, counted?: {
|
|
13
13
|
readonly documents: number;
|
package/dist/cli-support.js
CHANGED
|
@@ -22,7 +22,7 @@ export const versionResult = (binary) => ({
|
|
|
22
22
|
stdout: `${binary} ${packageVersion}\n`,
|
|
23
23
|
stderr: '',
|
|
24
24
|
});
|
|
25
|
-
export const usage = 'Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <subject-id>] [--catalogue <catalogue.yaml>] [--facilitate] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> "<free text>" | <subject-id> ... | <projection.yaml> [--budget <tokens>] [--neighbours <n>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise "<topic>" [--budget <tokens>] [--neighbours <n>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --where "<free text>" | <subject-id> ... [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate ask <workspace.yaml> --changed <git-range> [--budget <tokens>] [--neighbours <n>] [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml> [--json]\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export markdown --changed <git-range> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export briefs --changed <git-range> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export rtm <workspace.yaml> --out <directory>\n yarramate export xlsx <projection.yaml> <workspace.yaml> --out <file>\n yarramate import xlsx <workbook.xlsx> <workspace.yaml> [--json]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml> [--changed <git-range>]\n';
|
|
25
|
+
export const usage = 'Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <subject-id>] [--catalogue <catalogue.yaml>] [--facilitate] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> "<free text>" | <subject-id> ... | <projection.yaml> [--budget <tokens>] [--neighbours <n>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise "<topic>" [--budget <tokens>] [--neighbours <n>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --where "<free text>" | <subject-id> ... [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate ask <workspace.yaml> --changed <git-range> [--budget <tokens>] [--neighbours <n>] [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml> [--json | --text]\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export markdown --changed <git-range> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export briefs --changed <git-range> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export rtm <workspace.yaml> --out <directory>\n yarramate export xlsx <projection.yaml> <workspace.yaml> --out <file>\n yarramate import xlsx <workbook.xlsx> <workspace.yaml> [--json]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml> [--changed <git-range>]\n';
|
|
26
26
|
export const diagnosticJson = (diagnostics) => `${JSON.stringify({
|
|
27
27
|
format: 'yarramate/diagnostic-result/v1',
|
|
28
28
|
diagnostics,
|
package/dist/cli.js
CHANGED
|
@@ -13,16 +13,22 @@ import { evaluateEvidenceWorkspace, loadEvidence, } from './evidence.js';
|
|
|
13
13
|
import { deriveArtifactCoverage } from './artifact-coverage.js';
|
|
14
14
|
import { deriveAttestationStaleness } from './attestation-staleness.js';
|
|
15
15
|
import { reconcileEvidenceReports } from './reconciliation.js';
|
|
16
|
+
import { reconciliationReportText } from './reconciliation-text.js';
|
|
16
17
|
import { loadWorkspaceManifest } from './workspace.js';
|
|
17
18
|
const runReconciliation = (options, cwd) => {
|
|
18
19
|
// Bare reconcile already emits JSON, so --json changes nothing — but a
|
|
19
20
|
// harness scripting "add --json to every verb" must not hit exit 2 on
|
|
20
21
|
// the one verb that treats it as unknown (#275). Accepted as a no-op.
|
|
21
|
-
|
|
22
|
+
// --text is the same report for a person (#526, ADR 0153); asking for
|
|
23
|
+
// both shapes at once names no output, so it is refused like any other
|
|
24
|
+
// malformed call.
|
|
25
|
+
const text = options.includes('--text');
|
|
26
|
+
const positional = options.filter((option) => option !== '--json' && option !== '--text');
|
|
22
27
|
const [workspacePath] = positional;
|
|
23
28
|
if (positional.length !== 1 ||
|
|
24
29
|
workspacePath === undefined ||
|
|
25
|
-
workspacePath.startsWith('-')
|
|
30
|
+
workspacePath.startsWith('-') ||
|
|
31
|
+
(text && options.includes('--json'))) {
|
|
26
32
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
27
33
|
}
|
|
28
34
|
try {
|
|
@@ -89,9 +95,12 @@ const runReconciliation = (options, cwd) => {
|
|
|
89
95
|
// cwd, so the same command reports the same coverage wherever it was
|
|
90
96
|
// invoked (ADR 0130).
|
|
91
97
|
const coverage = deriveArtifactCoverage(dirname(resolve(cwd, workspacePath)), loadedWorkspace.manifest.coverage);
|
|
98
|
+
const report = reconcileEvidenceReports(loadedWorkspace.workspace.id, evaluation.reports, compilation.graph, staleness, coverage);
|
|
92
99
|
return {
|
|
93
100
|
exitCode: 0,
|
|
94
|
-
stdout:
|
|
101
|
+
stdout: text
|
|
102
|
+
? reconciliationReportText(report)
|
|
103
|
+
: `${JSON.stringify(report, null, 2)}\n`,
|
|
95
104
|
stderr: '',
|
|
96
105
|
};
|
|
97
106
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReconciliationReport } from './reconciliation.js';
|
|
2
|
+
/**
|
|
3
|
+
* The reconciliation report for a person (#526, ADR 0153).
|
|
4
|
+
*
|
|
5
|
+
* The JSON report is the contract (EVIDENCE.md) and stays the verb's
|
|
6
|
+
* default; this is the same report said in lines, in the same order, with
|
|
7
|
+
* nothing added and nothing judged. Every count the summary carries is on
|
|
8
|
+
* the first lines; every finding is one line, plus its message where the
|
|
9
|
+
* provider left one; the lists that follow appear only when they hold
|
|
10
|
+
* something, and the notes close, because they are what the report says
|
|
11
|
+
* about its own limits.
|
|
12
|
+
*/
|
|
13
|
+
export declare const reconciliationReportText: (report: ReconciliationReport) => string;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reconciliation report for a person (#526, ADR 0153).
|
|
3
|
+
*
|
|
4
|
+
* The JSON report is the contract (EVIDENCE.md) and stays the verb's
|
|
5
|
+
* default; this is the same report said in lines, in the same order, with
|
|
6
|
+
* nothing added and nothing judged. Every count the summary carries is on
|
|
7
|
+
* the first lines; every finding is one line, plus its message where the
|
|
8
|
+
* provider left one; the lists that follow appear only when they hold
|
|
9
|
+
* something, and the notes close, because they are what the report says
|
|
10
|
+
* about its own limits.
|
|
11
|
+
*/
|
|
12
|
+
export const reconciliationReportText = (report) => {
|
|
13
|
+
const { summary } = report;
|
|
14
|
+
const lines = [];
|
|
15
|
+
const row = (label, ...cells) => {
|
|
16
|
+
lines.push(`${label.padEnd(16)}${cells.join(' ')}`);
|
|
17
|
+
};
|
|
18
|
+
row('reconciliation', report.workspace);
|
|
19
|
+
row('observations', String(summary.observations), `confirmed ${summary.confirmed}`, `contradicted ${summary.contradicted}`, `unknown ${summary.unknown}`, `not observed ${summary.notObserved}`, ...(summary.unsupportedAbsences === undefined
|
|
20
|
+
? []
|
|
21
|
+
: [`unsupported absences ${summary.unsupportedAbsences}`]));
|
|
22
|
+
if (summary.staleAttestations !== undefined ||
|
|
23
|
+
summary.unconfirmedAttestations !== undefined) {
|
|
24
|
+
row('attestations', `stale ${summary.staleAttestations ?? 0}`, `unconfirmed ${summary.unconfirmedAttestations ?? 0}`);
|
|
25
|
+
}
|
|
26
|
+
row('expectations', `compared ${summary.expectationsCompared}`, `without observation ${summary.expectationsWithoutObservation}`);
|
|
27
|
+
row('subjects', `without evidence ${summary.subjectsWithoutEvidence}`);
|
|
28
|
+
row('artifacts', ...(summary.artifactsInScope === undefined
|
|
29
|
+
? ['not assessed']
|
|
30
|
+
: [
|
|
31
|
+
`in scope ${summary.artifactsInScope}`,
|
|
32
|
+
`unclaimed ${summary.unclaimedArtifacts ?? 0}`,
|
|
33
|
+
]));
|
|
34
|
+
row('findings', String(summary.findings));
|
|
35
|
+
const section = (title, body) => {
|
|
36
|
+
if (body.length === 0)
|
|
37
|
+
return;
|
|
38
|
+
lines.push('', title);
|
|
39
|
+
lines.push(...body);
|
|
40
|
+
};
|
|
41
|
+
section('Findings', report.findings.flatMap((finding) => findingLines(finding)));
|
|
42
|
+
section('Subjects without evidence', (report.unobservedSubjects ?? []).map((id) => ` ${id}`));
|
|
43
|
+
section('Expectations without observation', (report.unobservedExpectations ?? []).map((expectation) => ` ${expectation.subject} ${expectation.provider}/${expectation.key} expected ${expectation.expected} ${expectation.declared.path}:${expectation.declared.line}`));
|
|
44
|
+
section('Unclaimed artifacts', (report.unclaimedArtifacts ?? []).map((path) => ` ${path}`));
|
|
45
|
+
section('Coverage scope', (report.coverageScope ?? []).map((pattern) => ` ${pattern}`));
|
|
46
|
+
section('Notes', (report.notes ?? []).map((note) => ` ${note}`));
|
|
47
|
+
return `${lines.join('\n')}\n`;
|
|
48
|
+
};
|
|
49
|
+
const findingLines = (finding) => {
|
|
50
|
+
const label = finding.result.replace(/-/g, ' ').padEnd(24);
|
|
51
|
+
if (finding.result === 'stale-attestation') {
|
|
52
|
+
const changed = finding.changedAt === undefined
|
|
53
|
+
? ''
|
|
54
|
+
: `; the subject's wording changed ${finding.changedAt}`;
|
|
55
|
+
return [
|
|
56
|
+
` ${label}${finding.target.id} ${finding.attestation.topic}, by ${finding.attestation.by} on ${finding.attestation.on}${changed} ${finding.evidence.uri}`,
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
if (finding.result === 'unconfirmed-attestation') {
|
|
60
|
+
return [
|
|
61
|
+
` ${label}${finding.target.id} ${finding.attestation.topic}, by ${finding.attestation.by}, recorded by ${finding.attestation.recordedBy} on ${finding.attestation.on} ${finding.declared.path}:${finding.declared.line}`,
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
const head = ` ${label}${finding.target.type === 'claim' ? 'claim ' : ''}${finding.target.id} ${finding.provider}, ${finding.evidenceDocument} ${finding.evidence.uri}`;
|
|
65
|
+
const detail = [];
|
|
66
|
+
if (finding.asserted !== undefined) {
|
|
67
|
+
detail.push(` asserts ${finding.asserted.from} -${finding.asserted.kind}-> ${finding.asserted.to}`);
|
|
68
|
+
}
|
|
69
|
+
if (finding.expectation !== undefined) {
|
|
70
|
+
detail.push(` expected ${finding.expectation.key} = ${finding.expectation.expected}, observed ${finding.expectation.observed} ${finding.expectation.declared.path}:${finding.expectation.declared.line}`);
|
|
71
|
+
}
|
|
72
|
+
if (finding.evidence.message !== undefined) {
|
|
73
|
+
detail.push(` ${finding.evidence.message}`);
|
|
74
|
+
}
|
|
75
|
+
return [head, ...detail];
|
|
76
|
+
};
|
package/docs/EVIDENCE.md
CHANGED
|
@@ -115,6 +115,13 @@ This command evaluates every evidence overlay declared by the manifest and
|
|
|
115
115
|
emits deterministic `yarramate/reconciliation-report/v1` JSON. Its normative
|
|
116
116
|
schema is `schema/yarramate-reconciliation-report.schema.json`.
|
|
117
117
|
|
|
118
|
+
`--text` says the same report to a person (ADR 0153): the summary as
|
|
119
|
+
labelled rows, one line per finding with its message beneath, then the
|
|
120
|
+
subjects without evidence, the unclaimed artifacts and the notes, each only
|
|
121
|
+
when it holds something. Nothing is added or judged; the order is the
|
|
122
|
+
report's. The JSON stays the default, and `--json` stays the accepted no-op
|
|
123
|
+
it has been; asking for both at once is refused.
|
|
124
|
+
|
|
118
125
|
The summary counts all observations. The `findings` array contains only
|
|
119
126
|
`contradicted`, `unknown`, and `not-observed` results, ordered by target and
|
|
120
127
|
provider, plus the `stale-attestation` and `unconfirmed-attestation`
|