yarramate 1.32.0 → 1.34.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/catalogues/core-enrichment.yaml +210 -0
- package/dist/adapters/mcp-cli.js +28 -2
- package/dist/brief.js +15 -2
- package/dist/cli-support.d.ts +1 -1
- package/dist/cli-support.js +1 -1
- package/dist/compiler.js +70 -50
- package/dist/export-command.js +48 -8
- package/dist/governance-kinds.d.ts +24 -0
- package/dist/governance-kinds.js +40 -0
- package/dist/governance.d.ts +79 -0
- package/dist/governance.js +164 -0
- package/dist/graph-projection.d.ts +18 -0
- package/dist/graph-projection.js +24 -3
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/projection.d.ts +5 -0
- package/dist/projection.js +4 -0
- package/dist/relationship-reading.d.ts +34 -1
- package/dist/relationship-reading.js +66 -3
- package/dist/responsibility-kinds.d.ts +23 -0
- package/dist/responsibility-kinds.js +43 -0
- package/dist/responsibility.d.ts +99 -0
- package/dist/responsibility.js +239 -0
- package/dist/schema-validators.generated.js +37 -25
- package/dist/shipped-catalogue.generated.d.ts +1 -1
- package/dist/shipped-catalogue.generated.js +1 -1
- package/dist/shipped-profile.d.ts +24 -0
- package/dist/shipped-profile.js +52 -0
- package/dist/tools/export.d.ts +21 -0
- package/dist/tools/export.js +40 -0
- package/dist/tools/table.js +15 -6
- package/dist/tools-entry.d.ts +5 -1
- package/dist/tools-entry.js +5 -1
- package/dist/visual-app/assets/elk-worker.min-B0rP1gRk.js +22 -0
- package/dist/visual-app/assets/elk.bundled-CFiHtVQt.js +24 -0
- package/dist/visual-app/assets/index-BaQ0vy3y.js +373 -0
- package/dist/visual-app/index.html +1 -1
- package/dist/visual-app-lib/editor.js +30875 -30029
- package/dist/visual-app-lib/types/graph-projection.d.ts +18 -0
- package/dist/visual-app-lib/types/projection.d.ts +5 -0
- package/dist/visual-app-lib/types/relationship-reading.d.ts +34 -1
- package/dist/visual-app-lib/types/responsibility-kinds.d.ts +23 -0
- package/dist/visual-app-lib/types/shipped-catalogue.generated.d.ts +1 -1
- package/dist/visual-app-lib/types/shipped-profile.d.ts +24 -0
- package/dist/visual-app-lib/types/visual-app/elk-layout.d.ts +4 -0
- package/dist/visual-app-lib/types/visual-app/graph-canvas.d.ts +10 -1
- package/dist/visual-app-lib/types/visual-app/query-fields.d.ts +3 -2
- package/dist/visual-app-lib/types/visual-app/query-panel.d.ts +3 -1
- package/dist/visual-app-lib/types/visual-app/subject-form.d.ts +5 -0
- package/dist/visual-app-lib/types/visual-app/workspace-state.d.ts +4 -1
- package/docs/CONSUMING-YARRAMATE.md +1 -1
- package/package.json +11 -9
- package/schema/yarramate-governance.schema.json +91 -0
- package/schema/yarramate-projection.schema.json +4 -0
- package/schema/yarramate-responsibility.schema.json +123 -0
- package/schema/yarramate-visual-graph.schema.json +12 -0
- package/skills/yarramate-architecture/SKILL.md +2 -0
- package/dist/visual-app/assets/elk-worker.min-D8OVqK8T.js +0 -22
- package/dist/visual-app/assets/elk.bundled-DwTh4wCo.js +0 -24
- package/dist/visual-app/assets/index-DJYV9FNm.js +0 -373
|
@@ -830,6 +830,216 @@ questions:
|
|
|
830
830
|
Add an owner reference to an accountable actor (evidence such as
|
|
831
831
|
CODEOWNERS may propose it; a human confirms accountability).
|
|
832
832
|
|
|
833
|
+
- id: responsible-missing
|
|
834
|
+
wave: business
|
|
835
|
+
since: "1.33"
|
|
836
|
+
scope: subject
|
|
837
|
+
subjects:
|
|
838
|
+
kinds:
|
|
839
|
+
- yarramate/core@0.1#businessService
|
|
840
|
+
- yarramate/core@0.1#applicationService
|
|
841
|
+
- yarramate/core@0.1#applicationComponent
|
|
842
|
+
- yarramate/core@0.1#capability
|
|
843
|
+
trigger:
|
|
844
|
+
- condition: missing-linkage
|
|
845
|
+
kinds:
|
|
846
|
+
- yarramate/policy@0.2#responsible
|
|
847
|
+
direction: incoming
|
|
848
|
+
counterpartKinds:
|
|
849
|
+
- yarramate/core@0.1#businessActor
|
|
850
|
+
- yarramate/core@0.1#businessRole
|
|
851
|
+
- yarramate/core@0.1#businessCollaboration
|
|
852
|
+
- yarramate/core@0.1#stakeholder
|
|
853
|
+
question: >-
|
|
854
|
+
Who is responsible for {subject.name}?
|
|
855
|
+
askPlain: >-
|
|
856
|
+
Who actually builds, runs or delivers "{subject.name}" day to day?
|
|
857
|
+
materiality: >-
|
|
858
|
+
Accountability says whose desk a failure lands on; responsibility says
|
|
859
|
+
whose hands are on it. A subject with nobody responsible has nobody to
|
|
860
|
+
hand the work to, and a RACI row with no R is a gap the matrix reports.
|
|
861
|
+
authority: either
|
|
862
|
+
resolution: >-
|
|
863
|
+
Add a `responsible` relationship from the actor, role or collaboration
|
|
864
|
+
that does the work to the subject (a support contract or CODEOWNERS may
|
|
865
|
+
propose it; a person confirms it).
|
|
866
|
+
- id: role-idle
|
|
867
|
+
wave: business
|
|
868
|
+
since: "1.33"
|
|
869
|
+
scope: subject
|
|
870
|
+
subjects:
|
|
871
|
+
kinds:
|
|
872
|
+
- yarramate/core@0.1#businessActor
|
|
873
|
+
- yarramate/core@0.1#businessRole
|
|
874
|
+
- yarramate/core@0.1#businessCollaboration
|
|
875
|
+
trigger:
|
|
876
|
+
- condition: missing-reference
|
|
877
|
+
predicate: yarramate/ownership/owner
|
|
878
|
+
direction: incoming
|
|
879
|
+
- condition: missing-relationship
|
|
880
|
+
kinds:
|
|
881
|
+
- yarramate/policy@0.2#responsible
|
|
882
|
+
- yarramate/policy@0.2#consulted
|
|
883
|
+
- yarramate/policy@0.2#informed
|
|
884
|
+
direction: outgoing
|
|
885
|
+
- condition: missing-relationship
|
|
886
|
+
kinds:
|
|
887
|
+
- yarramate/core@0.1#serving
|
|
888
|
+
direction: incoming
|
|
889
|
+
question: >-
|
|
890
|
+
What does {subject.name} answer for?
|
|
891
|
+
askPlain: >-
|
|
892
|
+
"{subject.name}" is in the model but owns nothing, is responsible for
|
|
893
|
+
nothing, and is neither consulted nor informed about anything. What is
|
|
894
|
+
their part, or should they go?
|
|
895
|
+
materiality: >-
|
|
896
|
+
A person with no letter is either missing the edges a delivery depends
|
|
897
|
+
on or a name that outlived its role. A served actor is a consumer, not
|
|
898
|
+
a responsibility holder, and is not asked.
|
|
899
|
+
authority: human
|
|
900
|
+
resolution: >-
|
|
901
|
+
Give the person a letter (an owner claim on what they answer for, or a
|
|
902
|
+
responsible, consulted or informed relationship) or retire the subject.
|
|
903
|
+
- id: risk-threatens-nothing
|
|
904
|
+
wave: motivation
|
|
905
|
+
since: "1.34"
|
|
906
|
+
scope: subject
|
|
907
|
+
subjects:
|
|
908
|
+
kinds:
|
|
909
|
+
- yarramate/policy@0.3#risk
|
|
910
|
+
statuses:
|
|
911
|
+
- planned
|
|
912
|
+
- current
|
|
913
|
+
trigger:
|
|
914
|
+
- condition: missing-linkage
|
|
915
|
+
kinds:
|
|
916
|
+
- yarramate/core@0.1#influence
|
|
917
|
+
direction: outgoing
|
|
918
|
+
counterpartKinds:
|
|
919
|
+
- yarramate/core@0.1#goal
|
|
920
|
+
- yarramate/core@0.1#requirement
|
|
921
|
+
- yarramate/core@0.1#constraint
|
|
922
|
+
question: >-
|
|
923
|
+
What does {subject.name} threaten?
|
|
924
|
+
askPlain: >-
|
|
925
|
+
If "{subject.name}" comes true, which goal, requirement or constraint
|
|
926
|
+
takes the hit?
|
|
927
|
+
materiality: >-
|
|
928
|
+
A risk that threatens nothing named cannot be prioritised or retired:
|
|
929
|
+
its severity has nothing to be measured against, and nobody can say
|
|
930
|
+
when it has passed.
|
|
931
|
+
authority: either
|
|
932
|
+
resolution: >-
|
|
933
|
+
Add an `influence` relationship from the risk to the goal, requirement
|
|
934
|
+
or constraint it threatens.
|
|
935
|
+
- id: risk-unmitigated
|
|
936
|
+
wave: motivation
|
|
937
|
+
since: "1.34"
|
|
938
|
+
scope: subject
|
|
939
|
+
subjects:
|
|
940
|
+
kinds:
|
|
941
|
+
- yarramate/policy@0.3#risk
|
|
942
|
+
statuses:
|
|
943
|
+
- current
|
|
944
|
+
trigger:
|
|
945
|
+
- condition: missing-linkage
|
|
946
|
+
kinds:
|
|
947
|
+
- yarramate/core@0.1#influence
|
|
948
|
+
- yarramate/core@0.1#association
|
|
949
|
+
direction: incoming
|
|
950
|
+
counterpartKinds:
|
|
951
|
+
- yarramate/core@0.1#workPackage
|
|
952
|
+
- yarramate/core@0.1#deliverable
|
|
953
|
+
- yarramate/core@0.1#constraint
|
|
954
|
+
- yarramate/core@0.1#courseOfAction
|
|
955
|
+
question: >-
|
|
956
|
+
What mitigates {subject.name}?
|
|
957
|
+
askPlain: >-
|
|
958
|
+
"{subject.name}" is live. What work, deliverable, rule or decision is in
|
|
959
|
+
place to reduce it?
|
|
960
|
+
materiality: >-
|
|
961
|
+
A live risk with no mitigation is a bet the engagement is making without
|
|
962
|
+
saying so; the log exists to make that visible.
|
|
963
|
+
authority: either
|
|
964
|
+
resolution: >-
|
|
965
|
+
Add an `influence` relationship from the work package, deliverable,
|
|
966
|
+
constraint or decision that mitigates it to the risk, or retire the risk.
|
|
967
|
+
- id: risk-unowned
|
|
968
|
+
wave: motivation
|
|
969
|
+
since: "1.34"
|
|
970
|
+
scope: subject
|
|
971
|
+
subjects:
|
|
972
|
+
kinds:
|
|
973
|
+
- yarramate/policy@0.3#risk
|
|
974
|
+
statuses:
|
|
975
|
+
- planned
|
|
976
|
+
- current
|
|
977
|
+
trigger:
|
|
978
|
+
- condition: missing-claim
|
|
979
|
+
predicate: yarramate/ownership/owner
|
|
980
|
+
question: >-
|
|
981
|
+
Who owns {subject.name}?
|
|
982
|
+
askPlain: >-
|
|
983
|
+
Whose name is on "{subject.name}": who reviews it and decides when it
|
|
984
|
+
has passed?
|
|
985
|
+
materiality: >-
|
|
986
|
+
The risk owner is who a review date is asked of; without one,
|
|
987
|
+
risk-reviewed has no authority to sign it.
|
|
988
|
+
authority: either
|
|
989
|
+
resolution: >-
|
|
990
|
+
Add an owner reference to the actor or role that carries the risk.
|
|
991
|
+
- id: assumption-unconfirmed
|
|
992
|
+
wave: motivation
|
|
993
|
+
since: "1.34"
|
|
994
|
+
scope: subject
|
|
995
|
+
subjects:
|
|
996
|
+
kinds:
|
|
997
|
+
- yarramate/policy@0.3#assumption
|
|
998
|
+
statuses:
|
|
999
|
+
- planned
|
|
1000
|
+
- current
|
|
1001
|
+
trigger:
|
|
1002
|
+
- condition: missing-attestation
|
|
1003
|
+
topic: assumption-confirmed
|
|
1004
|
+
question: >-
|
|
1005
|
+
Has anyone confirmed {subject.name}?
|
|
1006
|
+
askPlain: >-
|
|
1007
|
+
"{subject.name}" is being built on. Has the person who can confirm it
|
|
1008
|
+
said so, and when?
|
|
1009
|
+
materiality: >-
|
|
1010
|
+
An unconfirmed assumption is a risk wearing a calmer name; the
|
|
1011
|
+
confirmation, with its date, is what turns it into a fact the delivery
|
|
1012
|
+
can rest on.
|
|
1013
|
+
authority: human
|
|
1014
|
+
resolution: >-
|
|
1015
|
+
Record an `assumption-confirmed` attestation by the role or actor that
|
|
1016
|
+
can confirm it, dated; or retire the assumption.
|
|
1017
|
+
- id: assumption-bears-on-nothing
|
|
1018
|
+
wave: motivation
|
|
1019
|
+
since: "1.34"
|
|
1020
|
+
scope: subject
|
|
1021
|
+
subjects:
|
|
1022
|
+
kinds:
|
|
1023
|
+
- yarramate/policy@0.3#assumption
|
|
1024
|
+
statuses:
|
|
1025
|
+
- planned
|
|
1026
|
+
- current
|
|
1027
|
+
trigger:
|
|
1028
|
+
- condition: missing-relationship
|
|
1029
|
+
kinds:
|
|
1030
|
+
- yarramate/core@0.1#association
|
|
1031
|
+
direction: outgoing
|
|
1032
|
+
question: >-
|
|
1033
|
+
What does {subject.name} bear on?
|
|
1034
|
+
askPlain: >-
|
|
1035
|
+
If "{subject.name}" turned out false, what in this design would change?
|
|
1036
|
+
materiality: >-
|
|
1037
|
+
An assumption that bears on nothing named is either decorative or
|
|
1038
|
+
missing its edges, and the log cannot say which.
|
|
1039
|
+
authority: either
|
|
1040
|
+
resolution: >-
|
|
1041
|
+
Add an `association` relationship from the assumption to the goal,
|
|
1042
|
+
requirement, constraint or subject it bears on, or retire it.
|
|
833
1043
|
- id: actor-unassigned
|
|
834
1044
|
wave: business
|
|
835
1045
|
since: "0.1"
|
package/dist/adapters/mcp-cli.js
CHANGED
|
@@ -148,8 +148,8 @@ const runExport = (input, tool, root) => {
|
|
|
148
148
|
stderr: '',
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
|
-
// A text kind under `out`: markdown and graph are one file, rtm
|
|
152
|
-
// briefs a directory, as the CLI lays them out.
|
|
151
|
+
// A text kind under `out`: markdown and graph are one file, rtm,
|
|
152
|
+
// responsibility, governance and briefs a directory, as the CLI lays them out.
|
|
153
153
|
if (kind === 'markdown' || kind === 'graph') {
|
|
154
154
|
mkdirSync(dirname(target), { recursive: true });
|
|
155
155
|
writeFileSync(target, outcome.text, 'utf8');
|
|
@@ -172,6 +172,32 @@ const runExport = (input, tool, root) => {
|
|
|
172
172
|
stderr: '',
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
|
+
if (kind === 'governance') {
|
|
176
|
+
const result = outcome.result;
|
|
177
|
+
mkdirSync(target, { recursive: true });
|
|
178
|
+
writeFileSync(join(target, 'GOVERNANCE.md'), outcome.text, 'utf8');
|
|
179
|
+
if (result !== undefined) {
|
|
180
|
+
writeFileSync(join(target, 'governance.json'), `${JSON.stringify(result.log, null, 2)}\n`, 'utf8');
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
exitCode: 0,
|
|
184
|
+
stdout: `Wrote GOVERNANCE.md and governance.json to ${out}\n`,
|
|
185
|
+
stderr: '',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (kind === 'responsibility') {
|
|
189
|
+
const result = outcome.result;
|
|
190
|
+
mkdirSync(target, { recursive: true });
|
|
191
|
+
writeFileSync(join(target, 'RESPONSIBILITY.md'), outcome.text, 'utf8');
|
|
192
|
+
if (result !== undefined) {
|
|
193
|
+
writeFileSync(join(target, 'responsibility.json'), `${JSON.stringify(result.matrix, null, 2)}\n`, 'utf8');
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
exitCode: 0,
|
|
197
|
+
stdout: `Wrote RESPONSIBILITY.md and responsibility.json to ${out}\n`,
|
|
198
|
+
stderr: '',
|
|
199
|
+
};
|
|
200
|
+
}
|
|
175
201
|
if (kind === 'briefs') {
|
|
176
202
|
const result = outcome.result;
|
|
177
203
|
mkdirSync(target, { recursive: true });
|
package/dist/brief.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { conceptKinds } from './profile.js';
|
|
2
|
-
import { RELATIONSHIP_READING, humanizeKind } from './relationship-reading.js';
|
|
2
|
+
import { EXTENSION_READING, RELATIONSHIP_READING, contextualReading, humanizeKind } from './relationship-reading.js';
|
|
3
3
|
const coreKindNames = new Map(conceptKinds.map(({ id, name }) => [id, name]));
|
|
4
4
|
const motivationKindIds = new Set(conceptKinds
|
|
5
5
|
.filter(({ layer }) => layer === 'motivation')
|
|
@@ -66,6 +66,9 @@ const relationshipPhrase = (coreKind, fallbackKind, mode, content) => {
|
|
|
66
66
|
if (coreKind === 'flow') {
|
|
67
67
|
return content === undefined ? 'flows to' : `sends ${content} to`;
|
|
68
68
|
}
|
|
69
|
+
const extension = EXTENSION_READING[fallbackKind];
|
|
70
|
+
if (extension !== undefined)
|
|
71
|
+
return extension;
|
|
69
72
|
const reading = coreKind === undefined ? undefined : RELATIONSHIP_READING[coreKind];
|
|
70
73
|
return reading ?? humanizeKind(fallbackKind);
|
|
71
74
|
};
|
|
@@ -107,8 +110,18 @@ workspaceClaims) {
|
|
|
107
110
|
if (claim === undefined || !('ref' in claim.object))
|
|
108
111
|
continue;
|
|
109
112
|
const description = claimValue(result.claims, subject.id, 'yarramate/relationship/description');
|
|
113
|
+
const coreKind = coreLocalKind(claim.predicate, profileContext?.relationshipKindLineages);
|
|
114
|
+
// A reading the endpoints decide (ADR 0160) speaks before the kind's own.
|
|
115
|
+
const endpointLineage = (id) => {
|
|
116
|
+
const kind = claimValue(result.claims, id, 'yarramate/concept/kind');
|
|
117
|
+
return kind === undefined ? undefined : profileContext?.conceptKindLineages.get(kind);
|
|
118
|
+
};
|
|
119
|
+
const contextual = coreKind === undefined
|
|
120
|
+
? undefined
|
|
121
|
+
: contextualReading(endpointLineage(claim.subject), endpointLineage(claim.object.ref), coreKind);
|
|
110
122
|
const entry = {
|
|
111
|
-
phrase:
|
|
123
|
+
phrase: contextual ??
|
|
124
|
+
relationshipPhrase(coreKind, claim.predicate, claimValue(result.claims, subject.id, 'yarramate/access/mode'), claimValue(result.claims, subject.id, 'yarramate/flow/content')),
|
|
112
125
|
target: claim.object.ref,
|
|
113
126
|
...(description === undefined ? {} : { description }),
|
|
114
127
|
};
|
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 | --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";
|
|
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 responsibility <projection.yaml> <workspace.yaml> --out <directory>\n yarramate export governance <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 | --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';
|
|
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 responsibility <projection.yaml> <workspace.yaml> --out <directory>\n yarramate export governance <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/compiler.js
CHANGED
|
@@ -3,7 +3,7 @@ import { conceptKinds, relationshipPolicies, } from './profile.js';
|
|
|
3
3
|
import { isCoreConceptKindId, matrixEndpointAspects, permittedRelationshipKinds as tablePermittedKinds, } from './relationship-matrix.js';
|
|
4
4
|
import { closestCandidate, describeSchemaViolation, } from './source-document.js';
|
|
5
5
|
import { ATTESTATION_PREDICATE_PREFIX, attestationClaimValue } from './graph-claims.js';
|
|
6
|
-
import {
|
|
6
|
+
import { shippedProfileOf } from './shipped-profile.js';
|
|
7
7
|
import { validateDocument, validateProfile, validatePattern } from './schema-validation.js';
|
|
8
8
|
const coreProfile = 'yarramate/core@0.1';
|
|
9
9
|
/**
|
|
@@ -371,58 +371,78 @@ function compileWorkspaceResolved(parsed) {
|
|
|
371
371
|
const identity = `${value.id}@${value.version}`;
|
|
372
372
|
pendingProfiles.push({ input, value, identity, positionFor });
|
|
373
373
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
374
|
+
// The shipped optional profiles (ADR 0095; policy@0.2 per ADR 0159). Each
|
|
375
|
+
// is injected when a document selects it or a profile extends it, and a
|
|
376
|
+
// shipped profile's own parent follows it in, so `extends:
|
|
377
|
+
// yarramate/policy@0.2` brings 0.1 along. A workspace file that declares
|
|
378
|
+
// the same identity wins; the shipped copy is not added beside it.
|
|
379
|
+
//
|
|
380
|
+
// This probe runs BEFORE the document gate that rejects a source whose
|
|
381
|
+
// schema check failed, so it has to hold its own precondition: a source
|
|
382
|
+
// that composes to anything but a mapping - an empty file, a comment-only
|
|
383
|
+
// one, a bare scalar - selects no profile at all. It used to read
|
|
384
|
+
// `.profile` through an `as` cast, which is what hid the null from the
|
|
385
|
+
// typechecker, and an empty document crashed the whole compile with a
|
|
386
|
+
// `TypeError` instead of the `YM201 must be object` its schema already
|
|
387
|
+
// produces. Every other consumer of a parsed entry checks its diagnostics
|
|
388
|
+
// first (the profile walk above, the pattern walk below); this one could
|
|
389
|
+
// not, because it runs before that gate exists, so it narrows instead.
|
|
390
|
+
const declaredIdentities = new Set(pendingProfiles.map(({ identity }) => identity));
|
|
391
|
+
const requestedIdentities = new Set();
|
|
392
|
+
for (const { entry } of documentInputs) {
|
|
393
|
+
const value = entry.value;
|
|
394
|
+
if (typeof value !== 'object' || value === null)
|
|
395
|
+
continue;
|
|
396
|
+
const profile = value.profile;
|
|
397
|
+
if (typeof profile === 'string')
|
|
398
|
+
requestedIdentities.add(profile);
|
|
399
|
+
}
|
|
400
|
+
for (const { value } of pendingProfiles)
|
|
401
|
+
requestedIdentities.add(value.extends);
|
|
402
|
+
const wantedShipped = [];
|
|
403
|
+
const want = (identity) => {
|
|
404
|
+
if (declaredIdentities.has(identity) || wantedShipped.includes(identity))
|
|
405
|
+
return;
|
|
406
|
+
const shipped = shippedProfileOf(identity);
|
|
407
|
+
if (shipped === undefined)
|
|
408
|
+
return;
|
|
409
|
+
wantedShipped.push(identity);
|
|
410
|
+
want(shipped.extends);
|
|
411
|
+
};
|
|
412
|
+
for (const identity of requestedIdentities)
|
|
413
|
+
want(identity);
|
|
414
|
+
for (const identity of wantedShipped) {
|
|
415
|
+
const shipped = shippedProfileOf(identity);
|
|
416
|
+
const input = {
|
|
417
|
+
path: `yarramate:profile:${identity}`,
|
|
418
|
+
source: shipped.source,
|
|
419
|
+
};
|
|
420
|
+
const { entry, fresh } = parseWorkspaceSource(input);
|
|
421
|
+
const value = entry.value;
|
|
422
|
+
if (entry.schemaDiagnostics.length > 0) {
|
|
423
|
+
profileDiagnostics.push(...entry.schemaDiagnostics);
|
|
424
|
+
}
|
|
425
|
+
else if (!validateProfile(value)) {
|
|
426
|
+
for (const error of validateProfile.errors ?? []) {
|
|
427
|
+
profileDiagnostics.push({
|
|
428
|
+
severity: 'error',
|
|
429
|
+
code: 'YM201',
|
|
430
|
+
message: `Profile schema violation: ${describeSchemaViolation(error)}`,
|
|
431
|
+
path: input.path,
|
|
432
|
+
pointer: error.instancePath || '/',
|
|
433
|
+
line: 1,
|
|
434
|
+
column: 1,
|
|
423
435
|
});
|
|
424
436
|
}
|
|
425
437
|
}
|
|
438
|
+
else {
|
|
439
|
+
pendingProfiles.push({
|
|
440
|
+
input,
|
|
441
|
+
value,
|
|
442
|
+
identity,
|
|
443
|
+
positionFor: positionReader(input.source, entry.positions, fresh),
|
|
444
|
+
});
|
|
445
|
+
}
|
|
426
446
|
}
|
|
427
447
|
let unresolvedProfiles = pendingProfiles.sort((left, right) => left.identity.localeCompare(right.identity) ||
|
|
428
448
|
left.input.path.localeCompare(right.input.path));
|
package/dist/export-command.js
CHANGED
|
@@ -8,7 +8,7 @@ import { humanDiagnostics, usage, } from './cli-support.js';
|
|
|
8
8
|
import { evaluateProjection, renderProjectionMarkdown } from './projection.js';
|
|
9
9
|
import { createFileSystemStore } from './source-store.js';
|
|
10
10
|
import { posixDirectoryOf } from './apply-command.js';
|
|
11
|
-
import { briefsFromResult, exportBriefs, exportGraph, exportMarkdown, exportRtm, exportWorkbook, } from './tools/export.js';
|
|
11
|
+
import { briefsFromResult, exportBriefs, exportGraph, exportMarkdown, exportGovernance, exportResponsibility, exportRtm, exportWorkbook, } from './tools/export.js';
|
|
12
12
|
import { compileOf, readSource, } from './tools/workspace.js';
|
|
13
13
|
import { loadWorkspaceManifest } from './workspace.js';
|
|
14
14
|
// Every kind is derived in `tools/export.ts` (ADR 0156): this command
|
|
@@ -70,7 +70,16 @@ const parseExportOptions = (options) => {
|
|
|
70
70
|
export function runExportCommand(options, cwd) {
|
|
71
71
|
const [kind, ...rest] = options;
|
|
72
72
|
if (kind === undefined ||
|
|
73
|
-
![
|
|
73
|
+
![
|
|
74
|
+
'graph',
|
|
75
|
+
'markdown',
|
|
76
|
+
'briefs',
|
|
77
|
+
'rtm',
|
|
78
|
+
'responsibility',
|
|
79
|
+
'governance',
|
|
80
|
+
'likec4',
|
|
81
|
+
'xlsx',
|
|
82
|
+
].includes(kind)) {
|
|
74
83
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
75
84
|
}
|
|
76
85
|
const parsed = parseExportOptions(rest);
|
|
@@ -113,17 +122,20 @@ export function runExportCommand(options, cwd) {
|
|
|
113
122
|
};
|
|
114
123
|
}
|
|
115
124
|
const usesChanged = parsed.changed !== undefined;
|
|
116
|
-
const
|
|
125
|
+
const wholeWorkspace = kind === 'graph' || kind === 'rtm' || kind === 'governance';
|
|
126
|
+
const expectedPositionals = wholeWorkspace || usesChanged ? 1 : 2;
|
|
117
127
|
const workspacePath = parsed.positionals[expectedPositionals - 1];
|
|
118
|
-
const projectionPath =
|
|
119
|
-
? undefined
|
|
120
|
-
: parsed.positionals[0];
|
|
128
|
+
const projectionPath = wholeWorkspace || usesChanged ? undefined : parsed.positionals[0];
|
|
121
129
|
if (parsed.positionals.length !== expectedPositionals ||
|
|
122
130
|
workspacePath === undefined ||
|
|
123
131
|
parsed.json ||
|
|
124
|
-
(usesChanged && (
|
|
132
|
+
(usesChanged && (wholeWorkspace || kind === 'responsibility')) ||
|
|
125
133
|
(parsed.budget !== undefined && kind !== 'briefs') ||
|
|
126
|
-
((kind === 'briefs' ||
|
|
134
|
+
((kind === 'briefs' ||
|
|
135
|
+
kind === 'rtm' ||
|
|
136
|
+
kind === 'responsibility' ||
|
|
137
|
+
kind === 'governance' ||
|
|
138
|
+
kind === 'xlsx') &&
|
|
127
139
|
parsed.out === undefined)) {
|
|
128
140
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
129
141
|
}
|
|
@@ -171,6 +183,34 @@ export function runExportCommand(options, cwd) {
|
|
|
171
183
|
stderr: '',
|
|
172
184
|
};
|
|
173
185
|
}
|
|
186
|
+
if (kind === 'governance') {
|
|
187
|
+
const exported = exportGovernance(tool);
|
|
188
|
+
if (!exported.ok)
|
|
189
|
+
return failedTool(exported);
|
|
190
|
+
const { markdown, log } = exported.result;
|
|
191
|
+
writeText(join(parsed.out, 'GOVERNANCE.md'), markdown);
|
|
192
|
+
writeText(join(parsed.out, 'governance.json'), `${JSON.stringify(log, null, 2)}\n`);
|
|
193
|
+
const gaps = log.summary.unowned + log.summary.unmitigated + log.summary.unconfirmed + log.summary.unreviewed;
|
|
194
|
+
return {
|
|
195
|
+
exitCode: 0,
|
|
196
|
+
stdout: `Wrote GOVERNANCE.md and governance.json (${log.summary.rows} row${log.summary.rows === 1 ? '' : 's'}, ${gaps} gap${gaps === 1 ? '' : 's'}) to ${parsed.out}\n`,
|
|
197
|
+
stderr: '',
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (kind === 'responsibility') {
|
|
201
|
+
const exported = exportResponsibility(tool, projectionPath);
|
|
202
|
+
if (!exported.ok)
|
|
203
|
+
return failedTool(exported);
|
|
204
|
+
const { markdown, matrix } = exported.result;
|
|
205
|
+
writeText(join(parsed.out, 'RESPONSIBILITY.md'), markdown);
|
|
206
|
+
writeText(join(parsed.out, 'responsibility.json'), `${JSON.stringify(matrix, null, 2)}\n`);
|
|
207
|
+
const gaps = matrix.summary.noAccountable + matrix.summary.noResponsible;
|
|
208
|
+
return {
|
|
209
|
+
exitCode: 0,
|
|
210
|
+
stdout: `Wrote RESPONSIBILITY.md and responsibility.json (${matrix.summary.rows} row${matrix.summary.rows === 1 ? '' : 's'}, ${gaps} gap${gaps === 1 ? '' : 's'}) to ${parsed.out}\n`,
|
|
211
|
+
stderr: '',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
174
214
|
if (kind === 'graph') {
|
|
175
215
|
const exported = exportGraph(tool);
|
|
176
216
|
if (!exported.ok)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The governance concept kinds (#560, ADR 0160): `yarramate/policy@0.3#risk`
|
|
3
|
+
* and `#assumption`, each a subkind of core `assessment`. A decision is the
|
|
4
|
+
* adopter's trial kind under `courseOfAction` until the trial reports, so
|
|
5
|
+
* it is not here. Review dates are attestations on the shipped topics
|
|
6
|
+
* below. Pure, so the canvas projection, the log builder and the brief read
|
|
7
|
+
* one table.
|
|
8
|
+
*/
|
|
9
|
+
export declare const GOVERNANCE_PROFILE = "yarramate/policy@0.3";
|
|
10
|
+
export type GovernanceType = 'risk' | 'assumption';
|
|
11
|
+
export declare const GOVERNANCE_KINDS: Readonly<Record<GovernanceType, string>>;
|
|
12
|
+
/**
|
|
13
|
+
* The attestation topic that records a review of each type: a risk owner
|
|
14
|
+
* or the client role attests `risk-reviewed` on a risk, and the client
|
|
15
|
+
* confirms an assumption with `assumption-confirmed`. How old is too old is
|
|
16
|
+
* the adopter's threshold, not the engine's.
|
|
17
|
+
*/
|
|
18
|
+
export declare const REVIEW_TOPICS: Readonly<Record<GovernanceType, string>>;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a concept kind is a risk or an assumption, read through its
|
|
21
|
+
* lineage (ancestor-first) so a profile's own subkind counts; null for
|
|
22
|
+
* every other kind.
|
|
23
|
+
*/
|
|
24
|
+
export declare const governanceTypeOf: (lineage: readonly string[] | undefined, kind: string) => GovernanceType | null;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The governance concept kinds (#560, ADR 0160): `yarramate/policy@0.3#risk`
|
|
3
|
+
* and `#assumption`, each a subkind of core `assessment`. A decision is the
|
|
4
|
+
* adopter's trial kind under `courseOfAction` until the trial reports, so
|
|
5
|
+
* it is not here. Review dates are attestations on the shipped topics
|
|
6
|
+
* below. Pure, so the canvas projection, the log builder and the brief read
|
|
7
|
+
* one table.
|
|
8
|
+
*/
|
|
9
|
+
export const GOVERNANCE_PROFILE = 'yarramate/policy@0.3';
|
|
10
|
+
export const GOVERNANCE_KINDS = {
|
|
11
|
+
risk: `${GOVERNANCE_PROFILE}#risk`,
|
|
12
|
+
assumption: `${GOVERNANCE_PROFILE}#assumption`,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The attestation topic that records a review of each type: a risk owner
|
|
16
|
+
* or the client role attests `risk-reviewed` on a risk, and the client
|
|
17
|
+
* confirms an assumption with `assumption-confirmed`. How old is too old is
|
|
18
|
+
* the adopter's threshold, not the engine's.
|
|
19
|
+
*/
|
|
20
|
+
export const REVIEW_TOPICS = {
|
|
21
|
+
risk: 'risk-reviewed',
|
|
22
|
+
assumption: 'assumption-confirmed',
|
|
23
|
+
};
|
|
24
|
+
const TYPE_BY_KIND = {
|
|
25
|
+
[GOVERNANCE_KINDS.risk]: 'risk',
|
|
26
|
+
[GOVERNANCE_KINDS.assumption]: 'assumption',
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Whether a concept kind is a risk or an assumption, read through its
|
|
30
|
+
* lineage (ancestor-first) so a profile's own subkind counts; null for
|
|
31
|
+
* every other kind.
|
|
32
|
+
*/
|
|
33
|
+
export const governanceTypeOf = (lineage, kind) => {
|
|
34
|
+
for (const member of lineage ?? [kind]) {
|
|
35
|
+
const type = TYPE_BY_KIND[member];
|
|
36
|
+
if (type !== undefined)
|
|
37
|
+
return type;
|
|
38
|
+
}
|
|
39
|
+
return TYPE_BY_KIND[kind] ?? null;
|
|
40
|
+
};
|