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.
Files changed (60) hide show
  1. package/catalogues/core-enrichment.yaml +210 -0
  2. package/dist/adapters/mcp-cli.js +28 -2
  3. package/dist/brief.js +15 -2
  4. package/dist/cli-support.d.ts +1 -1
  5. package/dist/cli-support.js +1 -1
  6. package/dist/compiler.js +70 -50
  7. package/dist/export-command.js +48 -8
  8. package/dist/governance-kinds.d.ts +24 -0
  9. package/dist/governance-kinds.js +40 -0
  10. package/dist/governance.d.ts +79 -0
  11. package/dist/governance.js +164 -0
  12. package/dist/graph-projection.d.ts +18 -0
  13. package/dist/graph-projection.js +24 -3
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.js +4 -0
  16. package/dist/projection.d.ts +5 -0
  17. package/dist/projection.js +4 -0
  18. package/dist/relationship-reading.d.ts +34 -1
  19. package/dist/relationship-reading.js +66 -3
  20. package/dist/responsibility-kinds.d.ts +23 -0
  21. package/dist/responsibility-kinds.js +43 -0
  22. package/dist/responsibility.d.ts +99 -0
  23. package/dist/responsibility.js +239 -0
  24. package/dist/schema-validators.generated.js +37 -25
  25. package/dist/shipped-catalogue.generated.d.ts +1 -1
  26. package/dist/shipped-catalogue.generated.js +1 -1
  27. package/dist/shipped-profile.d.ts +24 -0
  28. package/dist/shipped-profile.js +52 -0
  29. package/dist/tools/export.d.ts +21 -0
  30. package/dist/tools/export.js +40 -0
  31. package/dist/tools/table.js +15 -6
  32. package/dist/tools-entry.d.ts +5 -1
  33. package/dist/tools-entry.js +5 -1
  34. package/dist/visual-app/assets/elk-worker.min-B0rP1gRk.js +22 -0
  35. package/dist/visual-app/assets/elk.bundled-CFiHtVQt.js +24 -0
  36. package/dist/visual-app/assets/index-BaQ0vy3y.js +373 -0
  37. package/dist/visual-app/index.html +1 -1
  38. package/dist/visual-app-lib/editor.js +30875 -30029
  39. package/dist/visual-app-lib/types/graph-projection.d.ts +18 -0
  40. package/dist/visual-app-lib/types/projection.d.ts +5 -0
  41. package/dist/visual-app-lib/types/relationship-reading.d.ts +34 -1
  42. package/dist/visual-app-lib/types/responsibility-kinds.d.ts +23 -0
  43. package/dist/visual-app-lib/types/shipped-catalogue.generated.d.ts +1 -1
  44. package/dist/visual-app-lib/types/shipped-profile.d.ts +24 -0
  45. package/dist/visual-app-lib/types/visual-app/elk-layout.d.ts +4 -0
  46. package/dist/visual-app-lib/types/visual-app/graph-canvas.d.ts +10 -1
  47. package/dist/visual-app-lib/types/visual-app/query-fields.d.ts +3 -2
  48. package/dist/visual-app-lib/types/visual-app/query-panel.d.ts +3 -1
  49. package/dist/visual-app-lib/types/visual-app/subject-form.d.ts +5 -0
  50. package/dist/visual-app-lib/types/visual-app/workspace-state.d.ts +4 -1
  51. package/docs/CONSUMING-YARRAMATE.md +1 -1
  52. package/package.json +11 -9
  53. package/schema/yarramate-governance.schema.json +91 -0
  54. package/schema/yarramate-projection.schema.json +4 -0
  55. package/schema/yarramate-responsibility.schema.json +123 -0
  56. package/schema/yarramate-visual-graph.schema.json +12 -0
  57. package/skills/yarramate-architecture/SKILL.md +2 -0
  58. package/dist/visual-app/assets/elk-worker.min-D8OVqK8T.js +0 -22
  59. package/dist/visual-app/assets/elk.bundled-DwTh4wCo.js +0 -24
  60. package/dist/visual-app/assets/index-DJYV9FNm.js +0 -373
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The governance log (#560, ADR 0160): the RAID log's risks and
3
+ * assumptions as the model holds them, derived the way the responsibility
4
+ * matrix is derived and never authored. One row per risk or assumption:
5
+ * its owner and status, what it threatens or bears on, what mitigates it,
6
+ * the latest review attestation, what it supersedes, and the groupings it
7
+ * aggregates into (an adopter's severity or likelihood classes, which the
8
+ * engine carries without knowing the scale). Issues and open questions are
9
+ * the adopter's and the interrogation's; the engine never sees an issue.
10
+ *
11
+ * Deterministic: identical inputs give identical bytes, so CI can diff it.
12
+ */
13
+ import type { ResolvedProfileContext, SemanticGraph } from './compiler.js';
14
+ import { type GovernanceType } from './governance-kinds.js';
15
+ export interface GovernanceSource {
16
+ readonly path: string;
17
+ readonly line: number;
18
+ }
19
+ export interface GovernanceRef {
20
+ readonly id: string;
21
+ readonly name: string;
22
+ }
23
+ /** The latest review attestation on a row, by date. */
24
+ export interface GovernanceReview {
25
+ readonly topic: string;
26
+ readonly on: string;
27
+ readonly by: string;
28
+ readonly source: GovernanceSource;
29
+ }
30
+ export interface GovernanceRow {
31
+ readonly subject: string;
32
+ readonly name: string;
33
+ readonly kind: string;
34
+ readonly type: GovernanceType;
35
+ readonly status: string | null;
36
+ readonly description?: string;
37
+ readonly owner: GovernanceRef | null;
38
+ /** A risk's outgoing `influence` targets: what it threatens. */
39
+ readonly threatens: readonly GovernanceRef[];
40
+ /** An assumption's outgoing `association` targets: what it bears on. */
41
+ readonly bearsOn: readonly GovernanceRef[];
42
+ /** Incoming `influence` or `association` sources on a risk: what mitigates it. */
43
+ readonly mitigatedBy: readonly GovernanceRef[];
44
+ readonly review: GovernanceReview | null;
45
+ readonly supersedes: readonly string[];
46
+ /** The groupings that aggregate this row: an adopter's rating classes. */
47
+ readonly groupedBy: readonly GovernanceRef[];
48
+ }
49
+ export interface GovernanceLog {
50
+ readonly format: 'yarramate/governance/v1';
51
+ readonly workspace: string;
52
+ readonly summary: {
53
+ readonly rows: number;
54
+ readonly risks: number;
55
+ readonly assumptions: number;
56
+ readonly unowned: number;
57
+ readonly unmitigated: number;
58
+ readonly unconfirmed: number;
59
+ readonly unreviewed: number;
60
+ };
61
+ readonly rows: readonly GovernanceRow[];
62
+ readonly gaps: {
63
+ /** Rows with no owner. */
64
+ readonly unowned: readonly string[];
65
+ /** Current risks nothing mitigates. */
66
+ readonly unmitigated: readonly string[];
67
+ /** Assumptions with no `assumption-confirmed` attestation. */
68
+ readonly unconfirmed: readonly string[];
69
+ /** Risks with no `risk-reviewed` attestation. How old is too old is the adopter's. */
70
+ readonly unreviewed: readonly string[];
71
+ };
72
+ }
73
+ export interface GovernanceOptions {
74
+ /** The subjects that may get a row; absent, every risk and assumption. */
75
+ readonly rows?: readonly string[];
76
+ }
77
+ export declare function buildGovernanceLog(workspace: string, graph: SemanticGraph, profileContext: ResolvedProfileContext, options?: GovernanceOptions): GovernanceLog;
78
+ /** The log as one markdown table a person reads, with the gaps under it. */
79
+ export declare function renderGovernanceMarkdown(log: GovernanceLog): string;
@@ -0,0 +1,164 @@
1
+ import { ATTESTATION_PREDICATE_PREFIX, parseAttestationClaimValue, } from './graph-claims.js';
2
+ import { REVIEW_TOPICS, governanceTypeOf, } from './governance-kinds.js';
3
+ const CORE = 'yarramate/core@0.1#';
4
+ const documentOf = (subject) => {
5
+ const hash = subject.indexOf('#');
6
+ return hash === -1 ? '' : subject.slice(0, hash);
7
+ };
8
+ const bySubjectOrder = (left, right) => documentOf(left).localeCompare(documentOf(right)) ||
9
+ left.localeCompare(right);
10
+ const citation = (claim) => ({
11
+ path: claim.source.path,
12
+ line: claim.source.line,
13
+ });
14
+ export function buildGovernanceLog(workspace, graph, profileContext, options = {}) {
15
+ const conceptIds = new Set(graph.subjects.filter(({ type }) => type === 'concept').map(({ id }) => id));
16
+ const relationshipIds = new Set(graph.subjects
17
+ .filter(({ type }) => type === 'relationship')
18
+ .map(({ id }) => id));
19
+ const values = new Map();
20
+ const refs = new Map();
21
+ const attestations = new Map();
22
+ const definingClaims = [];
23
+ for (const claim of graph.claims) {
24
+ if (relationshipIds.has(claim.id) && 'ref' in claim.object) {
25
+ definingClaims.push(claim);
26
+ continue;
27
+ }
28
+ if ('ref' in claim.object) {
29
+ const key = `${claim.subject} ${claim.predicate}`;
30
+ refs.set(key, [...(refs.get(key) ?? []), claim.object.ref]);
31
+ continue;
32
+ }
33
+ if (claim.predicate.startsWith(ATTESTATION_PREDICATE_PREFIX)) {
34
+ attestations.set(claim.subject, [
35
+ ...(attestations.get(claim.subject) ?? []),
36
+ claim,
37
+ ]);
38
+ continue;
39
+ }
40
+ values.set(`${claim.subject} ${claim.predicate}`, claim.object.value);
41
+ }
42
+ const value = (subject, predicate) => values.get(`${subject} ${predicate}`);
43
+ const refsOf = (subject, predicate) => refs.get(`${subject} ${predicate}`) ?? [];
44
+ const kindOf = (subject) => value(subject, 'yarramate/concept/kind') ?? '';
45
+ const nameOf = (subject) => value(subject, 'yarramate/concept/name') ?? subject;
46
+ const refOf = (id) => ({ id, name: nameOf(id) });
47
+ const typeOf = (subject) => {
48
+ const kind = kindOf(subject);
49
+ return governanceTypeOf(profileContext.conceptKindLineages.get(kind), kind);
50
+ };
51
+ const coreKindOf = (relationshipKind) => (profileContext.relationshipKindLineages.get(relationshipKind) ?? [
52
+ relationshipKind,
53
+ ])[0];
54
+ const rowIds = (options.rows ?? [...conceptIds])
55
+ .filter((id) => conceptIds.has(id) && typeOf(id) !== null)
56
+ .sort(bySubjectOrder);
57
+ const rowSet = new Set(rowIds);
58
+ const outgoing = new Map();
59
+ const incoming = new Map();
60
+ for (const claim of definingClaims) {
61
+ if (!('ref' in claim.object))
62
+ continue;
63
+ const core = coreKindOf(claim.predicate);
64
+ if (rowSet.has(claim.subject)) {
65
+ outgoing.set(claim.subject, [
66
+ ...(outgoing.get(claim.subject) ?? []),
67
+ { core, to: claim.object.ref },
68
+ ]);
69
+ }
70
+ if (rowSet.has(claim.object.ref)) {
71
+ incoming.set(claim.object.ref, [
72
+ ...(incoming.get(claim.object.ref) ?? []),
73
+ { core, from: claim.subject },
74
+ ]);
75
+ }
76
+ }
77
+ const targets = (subject, core) => (outgoing.get(subject) ?? [])
78
+ .filter((edge) => edge.core === `${CORE}${core}`)
79
+ .map(({ to }) => to)
80
+ .sort(bySubjectOrder)
81
+ .map(refOf);
82
+ const sources = (subject, cores) => (incoming.get(subject) ?? [])
83
+ .filter((edge) => cores.some((core) => edge.core === `${CORE}${core}`))
84
+ .map(({ from }) => from)
85
+ .sort(bySubjectOrder)
86
+ .map(refOf);
87
+ const latestReview = (subject, topic) => {
88
+ let latest = null;
89
+ for (const claim of attestations.get(subject) ?? []) {
90
+ if (claim.predicate !== `${ATTESTATION_PREDICATE_PREFIX}${topic}`)
91
+ continue;
92
+ if (!('value' in claim.object))
93
+ continue;
94
+ const parts = parseAttestationClaimValue(claim.object.value);
95
+ if (parts === undefined)
96
+ continue;
97
+ if (latest === null || parts.on >= latest.on) {
98
+ latest = { topic, on: parts.on, by: parts.by, source: citation(claim) };
99
+ }
100
+ }
101
+ return latest;
102
+ };
103
+ const rows = rowIds.map((subject) => {
104
+ const type = typeOf(subject);
105
+ const owner = refsOf(subject, 'yarramate/ownership/owner')[0];
106
+ const description = value(subject, 'yarramate/concept/description');
107
+ return {
108
+ subject,
109
+ name: nameOf(subject),
110
+ kind: kindOf(subject),
111
+ type,
112
+ status: value(subject, 'yarramate/lifecycle/status') ?? null,
113
+ ...(description === undefined ? {} : { description }),
114
+ owner: owner === undefined ? null : refOf(owner),
115
+ threatens: type === 'risk' ? targets(subject, 'influence') : [],
116
+ bearsOn: type === 'assumption' ? targets(subject, 'association') : [],
117
+ mitigatedBy: type === 'risk' ? sources(subject, ['influence', 'association']) : [],
118
+ review: latestReview(subject, REVIEW_TOPICS[type]),
119
+ supersedes: [...refsOf(subject, 'yarramate/lineage/supersedes')].sort(),
120
+ groupedBy: sources(subject, ['aggregation']),
121
+ };
122
+ });
123
+ const ids = (predicate) => rows.filter(predicate).map(({ subject }) => subject);
124
+ const unowned = ids((row) => row.owner === null);
125
+ const unmitigated = ids((row) => row.type === 'risk' && row.status === 'current' && row.mitigatedBy.length === 0);
126
+ const unconfirmed = ids((row) => row.type === 'assumption' && row.review === null);
127
+ const unreviewed = ids((row) => row.type === 'risk' && row.review === null);
128
+ return {
129
+ format: 'yarramate/governance/v1',
130
+ workspace,
131
+ summary: {
132
+ rows: rows.length,
133
+ risks: rows.filter(({ type }) => type === 'risk').length,
134
+ assumptions: rows.filter(({ type }) => type === 'assumption').length,
135
+ unowned: unowned.length,
136
+ unmitigated: unmitigated.length,
137
+ unconfirmed: unconfirmed.length,
138
+ unreviewed: unreviewed.length,
139
+ },
140
+ rows,
141
+ gaps: { unowned, unmitigated, unconfirmed, unreviewed },
142
+ };
143
+ }
144
+ const escapeCell = (text) => text.replaceAll('|', '\\|');
145
+ const names = (entries) => entries.map(({ name }) => escapeCell(name)).join(', ');
146
+ const list = (ids) => ids.length === 0 ? 'none' : ids.map((id) => `\`${id}\``).join(', ');
147
+ /** The log as one markdown table a person reads, with the gaps under it. */
148
+ export function renderGovernanceMarkdown(log) {
149
+ const lines = ['# Governance log', ''];
150
+ lines.push(`Workspace \`${log.workspace}\`: ${log.summary.risks} risk${log.summary.risks === 1 ? '' : 's'}, ${log.summary.assumptions} assumption${log.summary.assumptions === 1 ? '' : 's'}. Derived from the record; edit the native documents, not this file.`, '');
151
+ if (log.rows.length === 0) {
152
+ lines.push('_No rows._', '');
153
+ }
154
+ else {
155
+ lines.push('| Subject | Type | Status | Owner | Threatens / bears on | Mitigated by | Review | Grouped by |', '|---|---|---|---|---|---|---|---|');
156
+ for (const row of log.rows) {
157
+ const review = row.review === null ? '' : `${row.review.topic} ${row.review.on} by ${escapeCell(row.review.by)}`;
158
+ lines.push(`| ${escapeCell(row.name)} (\`${row.subject}\`) | ${row.type} | ${row.status ?? ''} | ${row.owner === null ? '' : escapeCell(row.owner.name)} | ${names(row.type === 'risk' ? row.threatens : row.bearsOn)} | ${names(row.mitigatedBy)} | ${review} | ${names(row.groupedBy)} |`);
159
+ }
160
+ lines.push('');
161
+ }
162
+ lines.push('## Gaps', '', `- Unowned: ${list(log.gaps.unowned)}`, `- Unmitigated (current risks): ${list(log.gaps.unmitigated)}`, `- Unconfirmed assumptions: ${list(log.gaps.unconfirmed)}`, `- Unreviewed risks: ${list(log.gaps.unreviewed)}`, '');
163
+ return `${lines.join('\n')}\n`;
164
+ }
@@ -67,6 +67,23 @@ export interface CanvasEdge {
67
67
  readonly kind: string;
68
68
  readonly kindLabel: string;
69
69
  readonly coreKindLabel: string;
70
+ /**
71
+ * The kind whose reading the edge label speaks (ADR 0159): an extension
72
+ * identity with a reading of its own, else `coreKindLabel`.
73
+ */
74
+ readonly readingKind?: string;
75
+ /**
76
+ * The responsibility letter this edge carries (#557): R, C or I for the
77
+ * shipped responsibility kinds and their subkinds, null for every other
78
+ * kind. The canvas draws these only when the view asks (`showResponsibility`).
79
+ */
80
+ readonly responsibility?: ResponsibilityLetter | null;
81
+ /**
82
+ * A reading the endpoints decide (ADR 0160): "threatens" for an
83
+ * influence from a risk, "mitigates" for one into a risk, "bears on" for
84
+ * an association from an assumption. Absent, the kind's own reading.
85
+ */
86
+ readonly reading?: string;
70
87
  readonly from: string;
71
88
  readonly to: string;
72
89
  readonly name: string | null;
@@ -84,4 +101,5 @@ export interface CanvasGraph {
84
101
  readonly nodes: readonly CanvasNode[];
85
102
  readonly edges: readonly CanvasEdge[];
86
103
  }
104
+ import { type ResponsibilityLetter } from './responsibility-kinds.js';
87
105
  export declare function projectGraphForCanvas(graph: SemanticGraph, profileContext: ResolvedProfileContext): CanvasGraph;
@@ -1,5 +1,7 @@
1
1
  import { ATTESTATION_PREDICATE_PREFIX, parseAttestationClaimValue, parseConstraintExpectsValue, } from './graph-claims.js';
2
2
  import { kindLabelOf } from './kind-label.js';
3
+ import { contextualReading, readingKindOf } from './relationship-reading.js';
4
+ import { responsibilityLetterOf } from './responsibility-kinds.js';
3
5
  const CONCEPT_KIND_PREDICATE = 'yarramate/concept/kind';
4
6
  const CONCEPT_NAME_PREDICATE = 'yarramate/concept/name';
5
7
  const CONCEPT_DESCRIPTION_PREDICATE = 'yarramate/concept/description';
@@ -160,7 +162,7 @@ const projectConcept = (subjectId, claims, profileContext) => {
160
162
  attestations,
161
163
  };
162
164
  };
163
- const projectRelationship = (relationshipId, allClaims, ownClaims, profileContext) => {
165
+ const projectRelationship = (relationshipId, allClaims, ownClaims, profileContext, conceptKindOf = new Map()) => {
164
166
  // A relationship's defining claim is asymmetric: its id is the
165
167
  // relationship's subject id, but its own `subject` field is the
166
168
  // from-concept, so it lives in the from-concept's claim group, not this
@@ -170,6 +172,15 @@ const projectRelationship = (relationshipId, allClaims, ownClaims, profileContex
170
172
  throw new Error(`Relationship "${relationshipId}" is missing its defining claim`);
171
173
  }
172
174
  const kind = definingClaim.predicate;
175
+ const lineage = profileContext.relationshipKindLineages.get(kind);
176
+ const coreKindLabel = kindLabelOf(lineage?.[0] ?? kind);
177
+ const endpointLineage = (subject) => {
178
+ const conceptKind = conceptKindOf.get(subject);
179
+ return conceptKind === undefined
180
+ ? undefined
181
+ : profileContext.conceptKindLineages.get(conceptKind);
182
+ };
183
+ const reading = contextualReading(endpointLineage(definingClaim.subject), endpointLineage(claimRef(definingClaim)), coreKindLabel);
173
184
  const nameClaim = ownClaims.find((claim) => claim.predicate === RELATIONSHIP_NAME_PREDICATE);
174
185
  const descriptionClaim = ownClaims.find((claim) => claim.predicate === RELATIONSHIP_DESCRIPTION_PREDICATE);
175
186
  const modeClaim = ownClaims.find((claim) => claim.predicate === ACCESS_MODE_PREDICATE);
@@ -181,7 +192,10 @@ const projectRelationship = (relationshipId, allClaims, ownClaims, profileContex
181
192
  document: definingClaim.source.path,
182
193
  kind,
183
194
  kindLabel: kindLabelOf(kind),
184
- coreKindLabel: kindLabelOf(profileContext.relationshipKindLineages.get(kind)?.[0] ?? kind),
195
+ coreKindLabel,
196
+ readingKind: readingKindOf(lineage, coreKindLabel),
197
+ responsibility: responsibilityLetterOf(lineage, kind),
198
+ ...(reading === undefined ? {} : { reading }),
185
199
  from: definingClaim.subject,
186
200
  to: claimRef(definingClaim),
187
201
  name: nameClaim === undefined ? null : claimValue(nameClaim),
@@ -210,6 +224,13 @@ const projectRelationship = (relationshipId, allClaims, ownClaims, profileContex
210
224
  };
211
225
  export function projectGraphForCanvas(graph, profileContext) {
212
226
  const claimsBySubject = groupClaimsBySubject(graph.claims);
227
+ // Each concept's authored kind, for the readings an edge's endpoints decide.
228
+ const conceptKindOf = new Map();
229
+ for (const claim of graph.claims) {
230
+ if (claim.predicate === CONCEPT_KIND_PREDICATE && 'value' in claim.object) {
231
+ conceptKindOf.set(claim.subject, claim.object.value);
232
+ }
233
+ }
213
234
  const nodes = [];
214
235
  const edges = [];
215
236
  for (const subject of graph.subjects) {
@@ -218,7 +239,7 @@ export function projectGraphForCanvas(graph, profileContext) {
218
239
  nodes.push(projectConcept(subject.id, ownClaims, profileContext));
219
240
  }
220
241
  else {
221
- edges.push(projectRelationship(subject.id, graph.claims, ownClaims, profileContext));
242
+ edges.push(projectRelationship(subject.id, graph.claims, ownClaims, profileContext, conceptKindOf));
222
243
  }
223
244
  }
224
245
  nodes.sort(compareById);
package/dist/index.d.ts CHANGED
@@ -10,6 +10,10 @@ export { constraintExpectsPredicate, reconcileEvidenceReports, type AssertedRela
10
10
  export { deriveAttestationStaleness } from './attestation-staleness.js';
11
11
  export { deriveArtifactCoverage, type ArtifactCoverage, type CoverageScopePattern, } from './artifact-coverage.js';
12
12
  export { buildRtm, renderRtmMarkdown, type RequirementsTraceabilityMatrix, type RtmAttestation, type RtmContextEntry, type RtmDescopedEntry, type RtmEvidenceVerdict, type RtmLineageEntry, type RtmRealizer, type RtmRow, type RtmSource, } from './rtm.js';
13
+ export { buildResponsibilityMatrix, renderResponsibilityMarkdown, type RaciLetter, type ResponsibilityCell, type ResponsibilityCellSource, type ResponsibilityMatrix, type ResponsibilityOptions, type ResponsibilityPerson, type ResponsibilityRow, type ResponsibilitySource, } from './responsibility.js';
14
+ export { PEOPLE_KINDS, RESPONSIBILITY_KINDS, RESPONSIBILITY_PROFILE, responsibilityLetterOf, type ResponsibilityLetter, } from './responsibility-kinds.js';
15
+ export { buildGovernanceLog, renderGovernanceMarkdown, type GovernanceLog, type GovernanceOptions, type GovernanceRef, type GovernanceReview, type GovernanceRow, type GovernanceSource, } from './governance.js';
16
+ export { GOVERNANCE_KINDS, GOVERNANCE_PROFILE, REVIEW_TOPICS, governanceTypeOf, type GovernanceType, } from './governance-kinds.js';
13
17
  export type { CompilationCache, CompilationResult, ContextualCompilationResult, IncrementalCompilationResult, ParsedWorkspaceSource, PatternMembership, PatternPortShape, PatternShape, PatternSlotShape, PatternVacancy, PatternWireShape, Diagnostic, GraphClaim, GraphSource, SemanticGraph, ResolvedProfileContext, WorkspaceSource, } from './compiler.js';
14
18
  export { canonicalProjection, evaluateProjection, explainProjection, instanceClosureOf, loadProjection, renderProjectionMarkdown, type ConceptFacet, type ProjectionExclusion, } from './projection.js';
15
19
  export { loadAdapterMapping, validateAdapterMapping, validateAdapterMappings, type AdapterMapping, type AdapterMappingLoadResult, type AdapterMappingValidationResult, type AdapterMappingsValidationResult, type AdapterSubjectMapping, } from './adapter-mapping.js';
package/dist/index.js CHANGED
@@ -10,6 +10,10 @@ export { constraintExpectsPredicate, reconcileEvidenceReports, } from './reconci
10
10
  export { deriveAttestationStaleness } from './attestation-staleness.js';
11
11
  export { deriveArtifactCoverage, } from './artifact-coverage.js';
12
12
  export { buildRtm, renderRtmMarkdown, } from './rtm.js';
13
+ export { buildResponsibilityMatrix, renderResponsibilityMarkdown, } from './responsibility.js';
14
+ export { PEOPLE_KINDS, RESPONSIBILITY_KINDS, RESPONSIBILITY_PROFILE, responsibilityLetterOf, } from './responsibility-kinds.js';
15
+ export { buildGovernanceLog, renderGovernanceMarkdown, } from './governance.js';
16
+ export { GOVERNANCE_KINDS, GOVERNANCE_PROFILE, REVIEW_TOPICS, governanceTypeOf, } from './governance-kinds.js';
13
17
  export { canonicalProjection, evaluateProjection, explainProjection, instanceClosureOf, loadProjection, renderProjectionMarkdown, } from './projection.js';
14
18
  export { loadAdapterMapping, validateAdapterMapping, validateAdapterMappings, } from './adapter-mapping.js';
15
19
  export { createFileSystemStore, } from './source-store.js';
@@ -84,6 +84,11 @@ export interface ProjectionDefinition {
84
84
  readonly showLifecycle?: boolean;
85
85
  readonly showEvidence?: boolean;
86
86
  readonly showOwnership?: boolean;
87
+ /**
88
+ * Whether responsibility edges draw on the canvas (#557, ADR 0159). Off
89
+ * when absent: the subject's properties read the letters either way.
90
+ */
91
+ readonly showResponsibility?: boolean;
87
92
  /**
88
93
  * Whether an unnamed relationship is labelled with its reading - "serves",
89
94
  * "served by", "realizes" - or left to its line style and arrowhead
@@ -71,6 +71,7 @@ export function canonicalProjection(projection) {
71
71
  ...(presentation.showLifecycle === undefined ? {} : { showLifecycle: presentation.showLifecycle }),
72
72
  ...(presentation.showEvidence === undefined ? {} : { showEvidence: presentation.showEvidence }),
73
73
  ...(presentation.showOwnership === undefined ? {} : { showOwnership: presentation.showOwnership }),
74
+ ...(presentation.showResponsibility === undefined ? {} : { showResponsibility: presentation.showResponsibility }),
74
75
  ...(presentation.showKindLabels === undefined ? {} : { showKindLabels: presentation.showKindLabels }),
75
76
  ...(presentation.notation === undefined ? {} : { notation: presentation.notation }),
76
77
  },
@@ -637,6 +638,9 @@ export function evaluateProjection(graph, projection, profileContext, membership
637
638
  ...(projection.presentation.showOwnership === undefined
638
639
  ? {}
639
640
  : { showOwnership: projection.presentation.showOwnership }),
641
+ ...(projection.presentation.showResponsibility === undefined
642
+ ? {}
643
+ : { showResponsibility: projection.presentation.showResponsibility }),
640
644
  ...(projection.presentation.showKindLabels === undefined
641
645
  ? {}
642
646
  : { showKindLabels: projection.presentation.showKindLabels }),
@@ -13,6 +13,39 @@
13
13
  * reads an access.
14
14
  */
15
15
  export declare const RELATIONSHIP_READING: Readonly<Record<string, string>>;
16
+ /**
17
+ * Readings for the extension kinds the package ships (ADR 0159), keyed by
18
+ * their full identity: a profile's subkind of one of these reads the same,
19
+ * which `readingKindOf` resolves through the lineage.
20
+ */
21
+ export declare const EXTENSION_READING: Readonly<Record<string, string>>;
22
+ /**
23
+ * The kind whose reading an edge speaks: the most specific member of the
24
+ * lineage (ancestor-first) that has a reading of its own, else the core kind.
25
+ */
26
+ export declare const readingKindOf: (lineage: readonly string[] | undefined, coreKind: string) => string;
27
+ /**
28
+ * Readings that depend on the SOURCE kind as well as the relationship
29
+ * (ADR 0160): a risk "threatens" what it influences, where anything else
30
+ * "influences" it; an assumption "bears on" what it associates. Keyed by
31
+ * the source kind's identity, then the relationship's core kind, and
32
+ * resolved through the source's lineage so a profile's subkind of `risk`
33
+ * threatens too.
34
+ */
35
+ export declare const SOURCE_CONTEXTUAL_READING: Readonly<Record<string, Readonly<Record<string, string>>>>;
36
+ /**
37
+ * Readings that depend on the TARGET kind: a work package, deliverable,
38
+ * constraint or decision "mitigates" the risk it influences. Consulted
39
+ * after the source table, so a risk that influences another risk still
40
+ * "threatens" it.
41
+ */
42
+ export declare const TARGET_CONTEXTUAL_READING: Readonly<Record<string, Readonly<Record<string, string>>>>;
43
+ /**
44
+ * The reading an edge speaks when its endpoints decide it, or undefined
45
+ * when the relationship kind alone does (the tables above are consulted
46
+ * source first). `coreKind` is the relationship's core kind label.
47
+ */
48
+ export declare const contextualReading: (sourceLineage: readonly string[] | undefined, targetLineage: readonly string[] | undefined, coreKind: string) => string | undefined;
16
49
  export declare const REVERSED_READING: Readonly<Record<string, string>>;
17
50
  /**
18
51
  * `acme/p@1#applicationComponent` reads "application component" and
@@ -25,4 +58,4 @@ export declare const humanizeKind: (kind: string) => string;
25
58
  * and gets it only where one exists; a kind with no passive reading keeps its
26
59
  * active one, so a caller can pass the layering's answer straight through.
27
60
  */
28
- export declare const relationshipReading: (coreKind: string, reversed?: boolean) => string;
61
+ export declare const relationshipReading: (kind: string, reversed?: boolean) => string;
@@ -25,6 +25,68 @@ export const RELATIONSHIP_READING = {
25
25
  influence: 'influences',
26
26
  association: 'is associated with',
27
27
  };
28
+ /**
29
+ * Readings for the extension kinds the package ships (ADR 0159), keyed by
30
+ * their full identity: a profile's subkind of one of these reads the same,
31
+ * which `readingKindOf` resolves through the lineage.
32
+ */
33
+ export const EXTENSION_READING = {
34
+ 'yarramate/policy@0.2#responsible': 'is responsible for',
35
+ 'yarramate/policy@0.2#consulted': 'is consulted on',
36
+ 'yarramate/policy@0.2#informed': 'is informed of',
37
+ };
38
+ /**
39
+ * The kind whose reading an edge speaks: the most specific member of the
40
+ * lineage (ancestor-first) that has a reading of its own, else the core kind.
41
+ */
42
+ export const readingKindOf = (lineage, coreKind) => {
43
+ if (lineage !== undefined) {
44
+ for (let index = lineage.length - 1; index >= 0; index -= 1) {
45
+ const member = lineage[index];
46
+ if (EXTENSION_READING[member] !== undefined)
47
+ return member;
48
+ }
49
+ }
50
+ return coreKind;
51
+ };
52
+ /**
53
+ * Readings that depend on the SOURCE kind as well as the relationship
54
+ * (ADR 0160): a risk "threatens" what it influences, where anything else
55
+ * "influences" it; an assumption "bears on" what it associates. Keyed by
56
+ * the source kind's identity, then the relationship's core kind, and
57
+ * resolved through the source's lineage so a profile's subkind of `risk`
58
+ * threatens too.
59
+ */
60
+ export const SOURCE_CONTEXTUAL_READING = {
61
+ 'yarramate/policy@0.3#risk': { influence: 'threatens' },
62
+ 'yarramate/policy@0.3#assumption': { association: 'bears on' },
63
+ };
64
+ /**
65
+ * Readings that depend on the TARGET kind: a work package, deliverable,
66
+ * constraint or decision "mitigates" the risk it influences. Consulted
67
+ * after the source table, so a risk that influences another risk still
68
+ * "threatens" it.
69
+ */
70
+ export const TARGET_CONTEXTUAL_READING = {
71
+ 'yarramate/policy@0.3#risk': { influence: 'mitigates' },
72
+ };
73
+ const lookupContextual = (table, lineage, coreKind) => {
74
+ if (lineage === undefined)
75
+ return undefined;
76
+ for (let index = lineage.length - 1; index >= 0; index -= 1) {
77
+ const phrase = table[lineage[index]]?.[coreKind];
78
+ if (phrase !== undefined)
79
+ return phrase;
80
+ }
81
+ return undefined;
82
+ };
83
+ /**
84
+ * The reading an edge speaks when its endpoints decide it, or undefined
85
+ * when the relationship kind alone does (the tables above are consulted
86
+ * source first). `coreKind` is the relationship's core kind label.
87
+ */
88
+ export const contextualReading = (sourceLineage, targetLineage, coreKind) => lookupContextual(SOURCE_CONTEXTUAL_READING, sourceLineage, coreKind) ??
89
+ lookupContextual(TARGET_CONTEXTUAL_READING, targetLineage, coreKind);
28
90
  export const REVERSED_READING = {
29
91
  serving: 'served by',
30
92
  realization: 'realized by',
@@ -47,6 +109,7 @@ export const humanizeKind = (kind) => {
47
109
  * and gets it only where one exists; a kind with no passive reading keeps its
48
110
  * active one, so a caller can pass the layering's answer straight through.
49
111
  */
50
- export const relationshipReading = (coreKind, reversed = false) => (reversed ? REVERSED_READING[coreKind] : undefined) ??
51
- RELATIONSHIP_READING[coreKind] ??
52
- humanizeKind(coreKind);
112
+ export const relationshipReading = (kind, reversed = false) => (reversed ? REVERSED_READING[kind] : undefined) ??
113
+ EXTENSION_READING[kind] ??
114
+ RELATIONSHIP_READING[kind] ??
115
+ humanizeKind(kind);
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The three responsibility relationship kinds and the letters they carry
3
+ * (#557, ADR 0159): `yarramate/policy@0.2#responsible`, `#consulted` and
4
+ * `#informed`, each a subkind of core `association` from a person to the
5
+ * subject they answer for. Accountable is not a kind: it is the existing
6
+ * `yarramate/ownership/owner` claim. Pure, so the canvas projection, the
7
+ * matrix builder and the editor read one table.
8
+ */
9
+ export declare const RESPONSIBILITY_PROFILE = "yarramate/policy@0.2";
10
+ export type ResponsibilityLetter = 'R' | 'C' | 'I';
11
+ export declare const RESPONSIBILITY_KINDS: Readonly<Record<'responsible' | 'consulted' | 'informed', string>>;
12
+ export declare const RESPONSIBILITY_LETTERS: Readonly<Record<string, ResponsibilityLetter>>;
13
+ export declare const OWNER_PREDICATE = "yarramate/ownership/owner";
14
+ /** The core kinds a person subject may be, ancestors of any profile's own. */
15
+ export declare const PEOPLE_KINDS: readonly string[];
16
+ /**
17
+ * The letter a relationship kind carries, read through its lineage
18
+ * (ancestor-first) so a profile's own subkind of `responsible` is still an
19
+ * R; `null` for every other kind.
20
+ */
21
+ export declare const responsibilityLetterOf: (lineage: readonly string[] | undefined, kind: string) => ResponsibilityLetter | null;
22
+ /** Whether a concept kind is a person, read through its lineage. */
23
+ export declare const isPeopleKind: (lineage: readonly string[] | undefined, kind: string) => boolean;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The three responsibility relationship kinds and the letters they carry
3
+ * (#557, ADR 0159): `yarramate/policy@0.2#responsible`, `#consulted` and
4
+ * `#informed`, each a subkind of core `association` from a person to the
5
+ * subject they answer for. Accountable is not a kind: it is the existing
6
+ * `yarramate/ownership/owner` claim. Pure, so the canvas projection, the
7
+ * matrix builder and the editor read one table.
8
+ */
9
+ export const RESPONSIBILITY_PROFILE = 'yarramate/policy@0.2';
10
+ export const RESPONSIBILITY_KINDS = {
11
+ responsible: `${RESPONSIBILITY_PROFILE}#responsible`,
12
+ consulted: `${RESPONSIBILITY_PROFILE}#consulted`,
13
+ informed: `${RESPONSIBILITY_PROFILE}#informed`,
14
+ };
15
+ export const RESPONSIBILITY_LETTERS = {
16
+ [RESPONSIBILITY_KINDS.responsible]: 'R',
17
+ [RESPONSIBILITY_KINDS.consulted]: 'C',
18
+ [RESPONSIBILITY_KINDS.informed]: 'I',
19
+ };
20
+ export const OWNER_PREDICATE = 'yarramate/ownership/owner';
21
+ /** The core kinds a person subject may be, ancestors of any profile's own. */
22
+ export const PEOPLE_KINDS = [
23
+ 'yarramate/core@0.1#businessActor',
24
+ 'yarramate/core@0.1#businessRole',
25
+ 'yarramate/core@0.1#businessCollaboration',
26
+ 'yarramate/core@0.1#stakeholder',
27
+ ];
28
+ /**
29
+ * The letter a relationship kind carries, read through its lineage
30
+ * (ancestor-first) so a profile's own subkind of `responsible` is still an
31
+ * R; `null` for every other kind.
32
+ */
33
+ export const responsibilityLetterOf = (lineage, kind) => {
34
+ for (const member of lineage ?? [kind]) {
35
+ const letter = RESPONSIBILITY_LETTERS[member];
36
+ if (letter !== undefined)
37
+ return letter;
38
+ }
39
+ return RESPONSIBILITY_LETTERS[kind] ?? null;
40
+ };
41
+ /** Whether a concept kind is a person, read through its lineage. */
42
+ export const isPeopleKind = (lineage, kind) => (lineage ?? [kind]).some((member) => PEOPLE_KINDS.includes(member)) ||
43
+ PEOPLE_KINDS.includes(kind);