@milehimikey/em 1.17.0 → 1.18.1
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/.claude/agents/em-implementer.md +7 -4
- package/.claude/agents/em-validator.md +2 -2
- package/.claude/skills/event-modeling/SKILL.md +1 -1
- package/.claude/skills/event-modeling-conform/SKILL.md +1 -1
- package/.claude/skills/event-modeling-design/SKILL.md +46 -5
- package/.claude/skills/event-modeling-discover/SKILL.md +43 -8
- package/.claude/skills/event-modeling-discover/reference/extract.md +6 -1
- package/.claude/skills/event-modeling-discover/reference/prd-rubric.md +102 -0
- package/.claude/skills/event-modeling-discover/reference/prd-spec-path.md +171 -0
- package/.claude/skills/event-modeling-engagement/SKILL.md +21 -9
- package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
- package/.claude/skills/event-modeling-implement/reference/implement.md +18 -0
- package/.claude/skills/event-modeling-review/SKILL.md +4 -2
- package/.claude/skills/event-modeling-shared/reference/em-dsl.md +94 -6
- package/.claude/skills/event-modeling-shared/reference/methodology.md +268 -0
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +96 -2
- package/.claude/skills/event-modeling-shared/templates/slice.md +11 -3
- package/.claude-plugin/marketplace.json +2 -2
- package/dist/catalog/docModelConsistencyValidate.js +160 -0
- package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
- package/dist/catalog/invariantsValidate.d.ts +2 -0
- package/dist/catalog/invariantsValidate.js +67 -1
- package/dist/catalog/invariantsValidate.js.map +1 -1
- package/dist/catalog/scenarioQualityValidate.d.ts +4 -0
- package/dist/catalog/scenarioQualityValidate.js +148 -0
- package/dist/catalog/scenarioQualityValidate.js.map +1 -0
- package/dist/catalog/scenarios.d.ts +5 -0
- package/dist/catalog/scenarios.js +80 -0
- package/dist/catalog/scenarios.js.map +1 -1
- package/dist/catalog/sliceDoc.d.ts +4 -0
- package/dist/catalog/sliceDoc.js +16 -7
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/catalog/sliceReadyValidate.d.ts +11 -2
- package/dist/catalog/sliceReadyValidate.js +62 -3
- package/dist/catalog/sliceReadyValidate.js.map +1 -1
- package/dist/catalog/sliceSections.d.ts +3 -2
- package/dist/catalog/sliceSections.js +99 -9
- package/dist/catalog/sliceSections.js.map +1 -1
- package/dist/cli/ciInit.d.ts +4 -11
- package/dist/cli/ciInit.js +24 -6
- package/dist/cli/ciInit.js.map +1 -1
- package/dist/cli/coverage.d.ts +9 -0
- package/dist/cli/coverage.js +18 -0
- package/dist/cli/coverage.js.map +1 -1
- package/dist/cli/defer.d.ts +28 -0
- package/dist/cli/defer.js +87 -4
- package/dist/cli/defer.js.map +1 -1
- package/dist/cli/engagement.d.ts +13 -2
- package/dist/cli/engagement.js +60 -5
- package/dist/cli/engagement.js.map +1 -1
- package/dist/cli/engagementFile.d.ts +2 -0
- package/dist/cli/engagementFile.js +3 -1
- package/dist/cli/engagementFile.js.map +1 -1
- package/dist/cli/markImplemented.d.ts +14 -10
- package/dist/cli/markImplemented.js +49 -1
- package/dist/cli/markImplemented.js.map +1 -1
- package/dist/cli/prdTriage.d.ts +109 -0
- package/dist/cli/prdTriage.js +393 -0
- package/dist/cli/prdTriage.js.map +1 -0
- package/dist/cli/render-inputs.d.ts +2 -0
- package/dist/cli/render-inputs.js +4 -0
- package/dist/cli/render-inputs.js.map +1 -1
- package/dist/cli/review.d.ts +17 -2
- package/dist/cli/review.js +39 -10
- package/dist/cli/review.js.map +1 -1
- package/dist/cli/revise.d.ts +2 -0
- package/dist/cli/revise.js +7 -2
- package/dist/cli/revise.js.map +1 -1
- package/dist/cli/shape.d.ts +27 -0
- package/dist/cli/shape.js +77 -0
- package/dist/cli/shape.js.map +1 -0
- package/dist/cli/sliceIndex.d.ts +6 -1
- package/dist/cli/sliceIndex.js +28 -13
- package/dist/cli/sliceIndex.js.map +1 -1
- package/dist/cli/sliceNew.d.ts +4 -1
- package/dist/cli/sliceNew.js +26 -9
- package/dist/cli/sliceNew.js.map +1 -1
- package/dist/cli/status.d.ts +21 -1
- package/dist/cli/status.js +25 -3
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/storylineSuggest.d.ts +26 -0
- package/dist/cli/storylineSuggest.js +252 -0
- package/dist/cli/storylineSuggest.js.map +1 -0
- package/dist/cli/upgrade.d.ts +15 -3
- package/dist/cli/upgrade.js +516 -7
- package/dist/cli/upgrade.js.map +1 -1
- package/dist/cli/upgradeReshape.d.ts +60 -0
- package/dist/cli/upgradeReshape.js +425 -0
- package/dist/cli/upgradeReshape.js.map +1 -0
- package/dist/cli.js +311 -35
- package/dist/cli.js.map +1 -1
- package/dist/emit/dot.d.ts +2 -0
- package/dist/emit/dot.js +44 -8
- package/dist/emit/dot.js.map +1 -1
- package/dist/emit/engagementJson.d.ts +4 -3
- package/dist/emit/engagementJson.js +11 -4
- package/dist/emit/engagementJson.js.map +1 -1
- package/dist/emit/json.d.ts +21 -1
- package/dist/emit/json.js +26 -2
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/queryJson.d.ts +1 -1
- package/dist/emit/queryJson.js +3 -1
- package/dist/emit/queryJson.js.map +1 -1
- package/dist/emit/statusJson.d.ts +1 -1
- package/dist/emit/statusJson.js +6 -1
- package/dist/emit/statusJson.js.map +1 -1
- package/dist/emit/theme.d.ts +1 -0
- package/dist/emit/theme.js +13 -0
- package/dist/emit/theme.js.map +1 -1
- package/dist/emit/validateJson.d.ts +9 -5
- package/dist/emit/validateJson.js +40 -5
- package/dist/emit/validateJson.js.map +1 -1
- package/dist/layout/grid.d.ts +5 -1
- package/dist/layout/grid.js +93 -2
- package/dist/layout/grid.js.map +1 -1
- package/dist/mcp/server.js +46 -11
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/edges.d.ts +7 -0
- package/dist/model/edges.js +33 -16
- package/dist/model/edges.js.map +1 -1
- package/dist/model/model.d.ts +33 -0
- package/dist/model/model.js +87 -3
- package/dist/model/model.js.map +1 -1
- package/dist/model/queryIndex.js +2 -2
- package/dist/model/queryIndex.js.map +1 -1
- package/dist/model/refs.js +8 -5
- package/dist/model/refs.js.map +1 -1
- package/dist/model/rules.d.ts +149 -0
- package/dist/model/rules.js +144 -0
- package/dist/model/rules.js.map +1 -1
- package/dist/model/shapeLint.d.ts +12 -0
- package/dist/model/shapeLint.js +170 -0
- package/dist/model/shapeLint.js.map +1 -0
- package/dist/model/storylines.d.ts +51 -0
- package/dist/model/storylines.js +188 -0
- package/dist/model/storylines.js.map +1 -0
- package/dist/model/validate.d.ts +6 -1
- package/dist/model/validate.js +317 -14
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/againSugar.d.ts +30 -0
- package/dist/parser/againSugar.js +105 -0
- package/dist/parser/againSugar.js.map +1 -0
- package/dist/parser/ast.d.ts +33 -0
- package/dist/parser/lexer.d.ts +2 -0
- package/dist/parser/lexer.js +4 -0
- package/dist/parser/lexer.js.map +1 -1
- package/dist/parser/parser.js +284 -4
- package/dist/parser/parser.js.map +1 -1
- package/dist/query/format.js +1 -0
- package/dist/query/format.js.map +1 -1
- package/dist/query/verbs.d.ts +4 -0
- package/dist/query/verbs.js +30 -3
- package/dist/query/verbs.js.map +1 -1
- package/dist/render/drawEdges.js +100 -4
- package/dist/render/drawEdges.js.map +1 -1
- package/dist/render/render.js +20 -2
- package/dist/render/render.js.map +1 -1
- package/dist/render/sliceDiagram.js +5 -5
- package/dist/render/sliceDiagram.js.map +1 -1
- package/dist/render/storylineBands.d.ts +11 -0
- package/dist/render/storylineBands.js +80 -0
- package/dist/render/storylineBands.js.map +1 -0
- package/dist/render/storylineDiagram.d.ts +14 -0
- package/dist/render/storylineDiagram.js +142 -0
- package/dist/render/storylineDiagram.js.map +1 -0
- package/dist/roster/check.d.ts +32 -0
- package/dist/roster/check.js +92 -0
- package/dist/roster/check.js.map +1 -0
- package/dist/roster/context.d.ts +3 -0
- package/dist/roster/context.js +23 -0
- package/dist/roster/context.js.map +1 -0
- package/dist/roster/init.d.ts +15 -0
- package/dist/roster/init.js +52 -0
- package/dist/roster/init.js.map +1 -0
- package/dist/roster/roster.d.ts +79 -0
- package/dist/roster/roster.js +298 -0
- package/dist/roster/roster.js.map +1 -0
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/agents/em-implementer.md +7 -4
- package/plugin/agents/em-validator.md +2 -2
- package/plugin/shared/reference/em-dsl.md +94 -6
- package/plugin/shared/reference/methodology.md +268 -0
- package/plugin/shared/reference/slice-doc-schema.md +96 -2
- package/plugin/shared/templates/slice.md +11 -3
- package/plugin/skills/conform/SKILL.md +1 -1
- package/plugin/skills/design/SKILL.md +46 -5
- package/plugin/skills/discover/SKILL.md +43 -8
- package/plugin/skills/discover/reference/extract.md +6 -1
- package/plugin/skills/discover/reference/prd-rubric.md +103 -0
- package/plugin/skills/discover/reference/prd-spec-path.md +172 -0
- package/plugin/skills/engagement/SKILL.md +21 -9
- package/plugin/skills/event-modeling/SKILL.md +1 -1
- package/plugin/skills/implement/SKILL.md +1 -1
- package/plugin/skills/implement/reference/implement.md +18 -0
- package/plugin/skills/review/SKILL.md +4 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storylineBands.js","sourceRoot":"","sources":["../../src/render/storylineBands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NormalizedModel, Storyline } from "../model/model.js";
|
|
2
|
+
import { Grid, LayoutOptions } from "../layout/grid.js";
|
|
3
|
+
export interface StorylineDiagramResult {
|
|
4
|
+
model: NormalizedModel;
|
|
5
|
+
grid: Grid;
|
|
6
|
+
dot: string;
|
|
7
|
+
}
|
|
8
|
+
/** The storyline named `name`, or an error message naming the available ones. */
|
|
9
|
+
export declare function resolveStorylineArg(model: NormalizedModel, name: string): {
|
|
10
|
+
storyline: Storyline;
|
|
11
|
+
} | {
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function buildStorylineDiagram(model: NormalizedModel, storyline: Storyline, opts?: LayoutOptions): StorylineDiagramResult;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Renders ONE storyline alone (`em render --storyline <name>`, GH #277): a subset of the
|
|
3
|
+
// normal timeline's columns, not a redrawn pattern shape (that is `--slice`).
|
|
4
|
+
//
|
|
5
|
+
// The columns are the storyline's own slices, in order, preceded by dimmed STUB columns: every
|
|
6
|
+
// element a slice of the storyline reads from outside it (so each arrow still has a source), and
|
|
7
|
+
// for a storyline declared `after "<other>"` the referenced storyline's end state — the latest
|
|
8
|
+
// earlier instance of each read model the storyline continues with `again`. A stub column holds
|
|
9
|
+
// only the referenced elements of its origin slice, never the whole slice, so the picture can
|
|
10
|
+
// never balloon into the rest of the model. Like render/sliceDiagram.ts this builds a small
|
|
11
|
+
// synthetic NormalizedModel from shallow clones of the real Elements (same ids, fields, notes)
|
|
12
|
+
// and runs it through the unmodified layout()/emitDot()/composeSvg() pipeline.
|
|
13
|
+
import { AUTOMATION_KINDS } from "../parser/ast.js";
|
|
14
|
+
import { normalizeName } from "../model/model.js";
|
|
15
|
+
import { semanticEdges } from "../model/edges.js";
|
|
16
|
+
import { findStoryline, latestOnTimeline, composedRank } from "../model/storylines.js";
|
|
17
|
+
import { layout } from "../layout/grid.js";
|
|
18
|
+
import { emitDot } from "../emit/dot.js";
|
|
19
|
+
/** The storyline named `name`, or an error message naming the available ones. */
|
|
20
|
+
export function resolveStorylineArg(model, name) {
|
|
21
|
+
const storylines = model.storylines ?? [];
|
|
22
|
+
const storyline = findStoryline(model, name);
|
|
23
|
+
if (storyline)
|
|
24
|
+
return { storyline };
|
|
25
|
+
const known = storylines.map((s) => `"${s.name}"`).join(", ");
|
|
26
|
+
return {
|
|
27
|
+
error: storylines.length === 0
|
|
28
|
+
? `em render --storyline: this model declares no storylines`
|
|
29
|
+
: `em render --storyline: no storyline "${name}" (declared: ${known})`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function buildStorylineDiagram(model, storyline, opts = {}) {
|
|
33
|
+
const own = [...storyline.sliceIndexes].sort((a, b) => a - b);
|
|
34
|
+
const ownSet = new Set(own);
|
|
35
|
+
const stubs = collectStubs(model, storyline, ownSet);
|
|
36
|
+
// Left to right: the storyline's own slices and every stub column in ONE sequence ordered on
|
|
37
|
+
// the composed timeline (model/storylines.ts's relationOnTimeline — the ordering validation
|
|
38
|
+
// uses), file order as the fallback. The storylines this one comes `after` precede it on that
|
|
39
|
+
// timeline, so their end-state stubs land on the left naturally, while a stub from a slice
|
|
40
|
+
// between two blocks of this storyline is interleaved with its own columns: no arrow in the
|
|
41
|
+
// subset points right-to-left.
|
|
42
|
+
const columns = [
|
|
43
|
+
...own.map((i) => ({ i, stub: false })),
|
|
44
|
+
...[...stubs.keys()].map((i) => ({ i, stub: true })),
|
|
45
|
+
].sort((x, y) => {
|
|
46
|
+
return composedRank(model, x.i) - composedRank(model, y.i);
|
|
47
|
+
});
|
|
48
|
+
const slices = [];
|
|
49
|
+
const elements = [];
|
|
50
|
+
const byId = new Map();
|
|
51
|
+
const byName = new Map();
|
|
52
|
+
const addColumn = (origin, els, isStub) => {
|
|
53
|
+
const index = slices.length;
|
|
54
|
+
const cloned = els.map((el) => ({ ...el, sliceIndex: index }));
|
|
55
|
+
slices.push({
|
|
56
|
+
name: origin.name,
|
|
57
|
+
index,
|
|
58
|
+
source: origin.source,
|
|
59
|
+
storyline: isStub ? undefined : storyline.name,
|
|
60
|
+
stub: isStub || undefined,
|
|
61
|
+
elements: cloned,
|
|
62
|
+
line: origin.line,
|
|
63
|
+
});
|
|
64
|
+
for (const el of cloned) {
|
|
65
|
+
elements.push(el);
|
|
66
|
+
byId.set(el.id, el);
|
|
67
|
+
const key = normalizeName(el.name);
|
|
68
|
+
const bucket = byName.get(key);
|
|
69
|
+
if (bucket)
|
|
70
|
+
bucket.push(el);
|
|
71
|
+
else
|
|
72
|
+
byName.set(key, [el]);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
for (const { i, stub } of columns) {
|
|
76
|
+
addColumn(model.slices[i], stub ? [...stubs.get(i).values()] : model.slices[i].elements, stub);
|
|
77
|
+
}
|
|
78
|
+
const dedupe = (values) => [...new Set(values.filter((v) => !!v))];
|
|
79
|
+
const synthetic = {
|
|
80
|
+
name: `${model.name} — ${storyline.name}`,
|
|
81
|
+
nameDeclared: true,
|
|
82
|
+
personas: dedupe(elements.filter((e) => e.kind === "ui").map((e) => e.persona)),
|
|
83
|
+
contexts: dedupe(elements.filter((e) => e.kind === "event").map((e) => e.context)),
|
|
84
|
+
hasAutomation: elements.some((e) => AUTOMATION_KINDS.has(e.kind)),
|
|
85
|
+
slices,
|
|
86
|
+
storylines: [
|
|
87
|
+
{
|
|
88
|
+
name: storyline.name,
|
|
89
|
+
source: storyline.source,
|
|
90
|
+
after: storyline.after,
|
|
91
|
+
sliceIndexes: slices.filter((s) => !s.stub).map((s) => s.index),
|
|
92
|
+
line: storyline.line,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
elements,
|
|
96
|
+
byId,
|
|
97
|
+
byName,
|
|
98
|
+
// As in sliceDiagram: explicit `arrow` statements are dropped from a subset extraction.
|
|
99
|
+
arrows: [],
|
|
100
|
+
types: model.types,
|
|
101
|
+
typesByName: model.typesByName,
|
|
102
|
+
};
|
|
103
|
+
const grid = layout(synthetic, opts);
|
|
104
|
+
return { model: synthetic, grid, dot: emitDot(synthetic, grid) };
|
|
105
|
+
}
|
|
106
|
+
/** origin slice index -> the elements of that slice the storyline needs as stubs. */
|
|
107
|
+
function collectStubs(model, storyline, ownSet) {
|
|
108
|
+
const stubs = new Map();
|
|
109
|
+
const add = (el) => {
|
|
110
|
+
if (!el || ownSet.has(el.sliceIndex))
|
|
111
|
+
return;
|
|
112
|
+
let bucket = stubs.get(el.sliceIndex);
|
|
113
|
+
if (!bucket)
|
|
114
|
+
stubs.set(el.sliceIndex, (bucket = new Map()));
|
|
115
|
+
bucket.set(el.id, el);
|
|
116
|
+
};
|
|
117
|
+
for (const edge of semanticEdges(model)) {
|
|
118
|
+
const from = model.byId.get(edge.from);
|
|
119
|
+
const to = model.byId.get(edge.to);
|
|
120
|
+
if (!from || !to)
|
|
121
|
+
continue;
|
|
122
|
+
if (edge.source === "loops-to") {
|
|
123
|
+
if (ownSet.has(from.sliceIndex))
|
|
124
|
+
add(to); // the earlier view a loop re-feeds
|
|
125
|
+
}
|
|
126
|
+
else if (ownSet.has(to.sliceIndex)) {
|
|
127
|
+
add(from); // something the storyline reads
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// The referenced storyline's end state: the latest earlier instance of every read model this
|
|
131
|
+
// storyline continues with `again`.
|
|
132
|
+
for (const i of storyline.sliceIndexes) {
|
|
133
|
+
for (const el of model.slices[i].elements) {
|
|
134
|
+
if (el.kind !== "view" || !el.again)
|
|
135
|
+
continue;
|
|
136
|
+
const earlier = model.elements.filter((x) => x.kind === "view" && x.logicalId === el.logicalId && x.id !== el.id);
|
|
137
|
+
add(latestOnTimeline(model, earlier, el.sliceIndex, true));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return stubs;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=storylineDiagram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storylineDiagram.js","sourceRoot":"","sources":["../../src/render/storylineDiagram.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { NormalizedModel } from "../model/model.js";
|
|
2
|
+
import { RefsResult } from "../model/refs.js";
|
|
3
|
+
import { Diagnostic } from "../model/validate.js";
|
|
4
|
+
import { LoadedRoster, RosterRole } from "./roster.js";
|
|
5
|
+
export declare const ROSTER_CHECK_SCHEMA_VERSION = "1.0";
|
|
6
|
+
export interface RosterCheckResult {
|
|
7
|
+
sliceKey: string;
|
|
8
|
+
doc: string;
|
|
9
|
+
role: RosterRole;
|
|
10
|
+
name: string;
|
|
11
|
+
status: string | null;
|
|
12
|
+
contexts: string[];
|
|
13
|
+
ok: boolean;
|
|
14
|
+
/** The refusal text when `ok` is false. */
|
|
15
|
+
message: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface RosterCheckReport {
|
|
18
|
+
roster: {
|
|
19
|
+
state: LoadedRoster["state"];
|
|
20
|
+
path: string | null;
|
|
21
|
+
};
|
|
22
|
+
results: RosterCheckResult[];
|
|
23
|
+
/** Docs whose signer is em's `grandfathered ...` migration marker -- exempt, counted. */
|
|
24
|
+
grandfathered: number;
|
|
25
|
+
diagnostics: Diagnostic[];
|
|
26
|
+
/** No roster, or a valid one with every checked signer permitted. */
|
|
27
|
+
ok: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function checkRoster(model: NormalizedModel, refs: RefsResult, baseDir: string, file: string, loaded: LoadedRoster): RosterCheckReport;
|
|
30
|
+
export declare function grandfatheredNotice(n: number): string | null;
|
|
31
|
+
export declare function formatRosterCheck(report: RosterCheckReport): string[];
|
|
32
|
+
export declare function buildRosterCheckJson(file: string, report: RosterCheckReport): string;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// `em roster check <model>` (GH #259): the whole-model CI enforcement of the ratifier roster. Where
|
|
3
|
+
// `--slice-ready <key>` judges one ready-to-implement slice, this walks EVERY bound slice doc and
|
|
4
|
+
// tests each recorded signer against the roster: `ratifiedBy` on `ready-to-implement` and
|
|
5
|
+
// `implemented` docs, `reviewedBy` on any doc that carries one. em's own `grandfathered ...`
|
|
6
|
+
// placeholder is not a signer and is exempt (counted, so the exemption is visible).
|
|
7
|
+
import { computeModelKey } from "../model/qualifiedRef.js";
|
|
8
|
+
import { resolveSliceDocJoin } from "../catalog/docJoin.js";
|
|
9
|
+
import { makeDiag } from "../model/rules.js";
|
|
10
|
+
import { isGrandfatheredSignoff, rosterRefusal, sliceContexts } from "./roster.js";
|
|
11
|
+
export const ROSTER_CHECK_SCHEMA_VERSION = "1.0";
|
|
12
|
+
const RATIFIED_STATUSES = new Set(["ready-to-implement", "implemented"]);
|
|
13
|
+
export function checkRoster(model, refs, baseDir, file, loaded) {
|
|
14
|
+
if (loaded.state === "absent")
|
|
15
|
+
return { roster: { state: "absent", path: null }, results: [], grandfathered: 0, diagnostics: [], ok: true };
|
|
16
|
+
if (loaded.state === "invalid") {
|
|
17
|
+
return {
|
|
18
|
+
roster: { state: "invalid", path: loaded.path },
|
|
19
|
+
results: [],
|
|
20
|
+
grandfathered: 0,
|
|
21
|
+
diagnostics: loaded.diagnostics.map((d) => makeDiag("roster-invalid", { message: `${loaded.path}: ${d.message}`, line: d.line })),
|
|
22
|
+
ok: false,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const modelKey = computeModelKey(model, file);
|
|
26
|
+
const results = [];
|
|
27
|
+
const diagnostics = [];
|
|
28
|
+
const seenDocs = new Set();
|
|
29
|
+
let grandfathered = 0;
|
|
30
|
+
refs.sliceKeys.forEach((sliceKey, i) => {
|
|
31
|
+
const { doc, continuationOf } = resolveSliceDocJoin(model, refs, model.slices[i], sliceKey, baseDir, (id) => refs.refById.get(id));
|
|
32
|
+
if (continuationOf || doc.reason !== null || seenDocs.has(doc.path))
|
|
33
|
+
return;
|
|
34
|
+
seenDocs.add(doc.path);
|
|
35
|
+
const contexts = sliceContexts(model, i);
|
|
36
|
+
const signers = [
|
|
37
|
+
["ratifiers", doc.status !== null && RATIFIED_STATUSES.has(doc.status) ? doc.ratifiedBy : null],
|
|
38
|
+
["reviewers", doc.reviewedBy],
|
|
39
|
+
];
|
|
40
|
+
let countedGrandfathered = false;
|
|
41
|
+
for (const [role, name] of signers) {
|
|
42
|
+
if (!name)
|
|
43
|
+
continue;
|
|
44
|
+
if (isGrandfatheredSignoff(name, doc.ratifiedHash)) {
|
|
45
|
+
if (!countedGrandfathered)
|
|
46
|
+
grandfathered++;
|
|
47
|
+
countedGrandfathered = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const refusal = rosterRefusal(loaded.roster, role, name, contexts, modelKey);
|
|
51
|
+
const ok = refusal === null;
|
|
52
|
+
results.push({ sliceKey, doc: doc.path, role, name, status: doc.status, contexts, ok, message: refusal });
|
|
53
|
+
if (!ok) {
|
|
54
|
+
diagnostics.push(makeDiag(role === "ratifiers" ? "ratified-by-not-in-roster" : "reviewed-by-not-in-roster", {
|
|
55
|
+
message: `slice "${sliceKey}" (${doc.path}): ${role === "ratifiers" ? "ratifiedBy" : "reviewedBy"} ${refusal} (${loaded.path})`,
|
|
56
|
+
refs: [sliceKey],
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return { roster: { state: "present", path: loaded.path }, results, grandfathered, diagnostics, ok: diagnostics.length === 0 };
|
|
62
|
+
}
|
|
63
|
+
export function grandfatheredNotice(n) {
|
|
64
|
+
return n > 0 ? `${n} doc${n === 1 ? "" : "s"} ${n === 1 ? "carries" : "carry"} the grandfathered placeholder and ${n === 1 ? "is" : "are"} exempt until re-ratified` : null;
|
|
65
|
+
}
|
|
66
|
+
export function formatRosterCheck(report) {
|
|
67
|
+
if (report.roster.state === "absent")
|
|
68
|
+
return ["no ratifiers.yaml found - nothing to check"];
|
|
69
|
+
const lines = [];
|
|
70
|
+
if (report.roster.state === "invalid")
|
|
71
|
+
return lines;
|
|
72
|
+
const bad = report.results.filter((r) => !r.ok).length;
|
|
73
|
+
lines.push(bad === 0
|
|
74
|
+
? `ok - ${report.results.length} recorded signer(s) all on ${report.roster.path}`
|
|
75
|
+
: `${bad} of ${report.results.length} recorded signer(s) not permitted by ${report.roster.path}`);
|
|
76
|
+
const g = grandfatheredNotice(report.grandfathered);
|
|
77
|
+
if (g)
|
|
78
|
+
lines.push(g);
|
|
79
|
+
return lines;
|
|
80
|
+
}
|
|
81
|
+
export function buildRosterCheckJson(file, report) {
|
|
82
|
+
return JSON.stringify({
|
|
83
|
+
rosterCheckSchemaVersion: ROSTER_CHECK_SCHEMA_VERSION,
|
|
84
|
+
file,
|
|
85
|
+
roster: report.roster,
|
|
86
|
+
ok: report.ok,
|
|
87
|
+
grandfathered: report.grandfathered,
|
|
88
|
+
results: report.results,
|
|
89
|
+
diagnostics: report.diagnostics.map((d) => ({ severity: d.severity, code: d.code, message: d.message, line: d.line ?? null, refs: d.refs ?? [] })),
|
|
90
|
+
}, null, 2);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/roster/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Glue between a model file and the roster loader (GH #259): the model key and the enclosing
|
|
3
|
+
// system's model keys (from the nearest system.yaml, when one exists) a roster is validated against.
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { parseManifest } from "../system/manifest.js";
|
|
6
|
+
import { findSystemManifestAbove } from "../cli/systemInputs.js";
|
|
7
|
+
import { rosterModelContext } from "./roster.js";
|
|
8
|
+
export function rosterContextFor(model, file) {
|
|
9
|
+
let systemModelKeys;
|
|
10
|
+
try {
|
|
11
|
+
const manifest = findSystemManifestAbove(file);
|
|
12
|
+
if (manifest !== null) {
|
|
13
|
+
const parsed = parseManifest(readFileSync(manifest, "utf8"));
|
|
14
|
+
if (parsed.ok)
|
|
15
|
+
systemModelKeys = parsed.manifest.models.map((m) => m.key);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// unreadable manifest: models: entries are then only shape-checked
|
|
20
|
+
}
|
|
21
|
+
return rosterModelContext(model, file, systemModelKeys);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/roster/context.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,6FAA6F;AAC7F,qGAAqG;AAErG,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAsB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAErE,MAAM,UAAU,gBAAgB,CAAC,KAAsB,EAAE,IAAY;IACnE,IAAI,eAAqC,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,aAAa,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;YAC7D,IAAI,MAAM,CAAC,EAAE;gBAAE,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;IACrE,CAAC;IACD,OAAO,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NormalizedModel } from "../model/model.js";
|
|
2
|
+
import { RefsResult } from "../model/refs.js";
|
|
3
|
+
/** `em upgrade`'s ratified-signoff step writes this placeholder; it is not a person. */
|
|
4
|
+
export interface ObservedSigners {
|
|
5
|
+
ratifiers: string[];
|
|
6
|
+
reviewers: string[];
|
|
7
|
+
/** Docs carrying the grandfathered placeholder (skipped; counted so the exemption is visible). */
|
|
8
|
+
grandfathered: number;
|
|
9
|
+
}
|
|
10
|
+
/** Distinct, sorted signer names across every slice doc bound in the model. */
|
|
11
|
+
export declare function observeSigners(model: NormalizedModel, refs: RefsResult, baseDir: string): ObservedSigners;
|
|
12
|
+
/** Roster text for `observed`. With no ratifier observed there is nothing to seed, so the
|
|
13
|
+
* `ratifiers:` list is a commented placeholder -- but the file must still be valid, so the
|
|
14
|
+
* caller refuses to write when both lists are empty. */
|
|
15
|
+
export declare function renderRoster(observed: ObservedSigners): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// `em roster init` (GH #259, the upgrade path #290 names): write a starting `ratifiers.yaml` from
|
|
3
|
+
// the names the model's slice docs already record in `ratifiedBy:` / `reviewedBy:`, so adopting the
|
|
4
|
+
// roster does not start by locking out everyone who has already signed something. Entries carry no
|
|
5
|
+
// `contexts` (whole model) -- narrowing them is the host's decision, made by editing the file.
|
|
6
|
+
import { resolveSliceDocJoin } from "../catalog/docJoin.js";
|
|
7
|
+
import { ROSTER_SCHEMA_VERSION, isGrandfatheredSignoff } from "./roster.js";
|
|
8
|
+
/** Distinct, sorted signer names across every slice doc bound in the model. */
|
|
9
|
+
export function observeSigners(model, refs, baseDir) {
|
|
10
|
+
const ratifiers = new Set();
|
|
11
|
+
const reviewers = new Set();
|
|
12
|
+
let grandfathered = 0;
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
refs.sliceKeys.forEach((sliceKey, i) => {
|
|
15
|
+
const { doc } = resolveSliceDocJoin(model, refs, model.slices[i], sliceKey, baseDir, (id) => refs.refById.get(id));
|
|
16
|
+
if (doc.reason !== null || seen.has(doc.path))
|
|
17
|
+
return;
|
|
18
|
+
seen.add(doc.path);
|
|
19
|
+
const gf = (n) => isGrandfatheredSignoff(n, doc.ratifiedHash);
|
|
20
|
+
if (gf(doc.ratifiedBy) || gf(doc.reviewedBy))
|
|
21
|
+
grandfathered++;
|
|
22
|
+
if (doc.ratifiedBy && !gf(doc.ratifiedBy))
|
|
23
|
+
ratifiers.add(doc.ratifiedBy.trim());
|
|
24
|
+
if (doc.reviewedBy && !gf(doc.reviewedBy))
|
|
25
|
+
reviewers.add(doc.reviewedBy.trim());
|
|
26
|
+
});
|
|
27
|
+
const sorted = (s) => [...s].sort((a, b) => a.localeCompare(b));
|
|
28
|
+
return { ratifiers: sorted(ratifiers), reviewers: sorted(reviewers), grandfathered };
|
|
29
|
+
}
|
|
30
|
+
const yamlName = (n) => JSON.stringify(n);
|
|
31
|
+
/** Roster text for `observed`. With no ratifier observed there is nothing to seed, so the
|
|
32
|
+
* `ratifiers:` list is a commented placeholder -- but the file must still be valid, so the
|
|
33
|
+
* caller refuses to write when both lists are empty. */
|
|
34
|
+
export function renderRoster(observed) {
|
|
35
|
+
const lines = [
|
|
36
|
+
"# ratifiers.yaml -- who may sign slice docs for this model (em roster init).",
|
|
37
|
+
"# Seeded from the names already recorded in the slice docs. Narrow an entry with",
|
|
38
|
+
"# `contexts: [Name, ...]` (omit = whole model). Delete the file to lift the restriction.",
|
|
39
|
+
`rosterSchemaVersion: "${ROSTER_SCHEMA_VERSION}"`,
|
|
40
|
+
];
|
|
41
|
+
const list = (key, names) => {
|
|
42
|
+
lines.push(`${key}:`);
|
|
43
|
+
for (const n of names)
|
|
44
|
+
lines.push(` - name: ${yamlName(n)}`);
|
|
45
|
+
};
|
|
46
|
+
if (observed.ratifiers.length > 0)
|
|
47
|
+
list("ratifiers", observed.ratifiers);
|
|
48
|
+
if (observed.reviewers.length > 0)
|
|
49
|
+
list("reviewers", observed.reviewers);
|
|
50
|
+
return lines.join("\n") + "\n";
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/roster/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Diagnostic } from "../model/validate.js";
|
|
2
|
+
import { type NormalizedModel } from "../model/model.js";
|
|
3
|
+
export declare const ROSTER_FILE = "ratifiers.yaml";
|
|
4
|
+
export declare const ROSTER_SCHEMA_VERSION = "1.0";
|
|
5
|
+
export interface RosterEntry {
|
|
6
|
+
name: string;
|
|
7
|
+
/** Contexts this person may sign for; `null` = the whole model. Stored without a leading `@`. */
|
|
8
|
+
contexts: string[] | null;
|
|
9
|
+
/** Model keys (as `em export` reports `model.key`) this entry applies to; `null` = every model
|
|
10
|
+
* the roster governs. */
|
|
11
|
+
models: string[] | null;
|
|
12
|
+
}
|
|
13
|
+
/** em's own migration marker (`em upgrade`'s `ratified-signoff`), not a signer: exempt from roster checks. */
|
|
14
|
+
export declare const GRANDFATHERED_RATIFIER = "grandfathered (unsigned before em 1.14)";
|
|
15
|
+
/** Exempt only the exact marker AND only on a doc with no `ratifiedHash` (the state `em upgrade`
|
|
16
|
+
* leaves it in): a forged "grandfathered by X" or a marker on a hash-bearing doc is a signer and is checked. */
|
|
17
|
+
export declare function isGrandfatheredSignoff(name: string | null | undefined, ratifiedHash: string | null | undefined): boolean;
|
|
18
|
+
export interface Roster {
|
|
19
|
+
rosterSchemaVersion: string;
|
|
20
|
+
/** `null` = the file has no `ratifiers:` key, so ratification is not restricted. */
|
|
21
|
+
ratifiers: RosterEntry[] | null;
|
|
22
|
+
/** `null` = no `reviewers:` key, so review is not restricted. */
|
|
23
|
+
reviewers: RosterEntry[] | null;
|
|
24
|
+
}
|
|
25
|
+
export type ParseRosterResult = {
|
|
26
|
+
ok: true;
|
|
27
|
+
roster: Roster;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
diagnostics: Diagnostic[];
|
|
31
|
+
};
|
|
32
|
+
export declare function normalizeContext(c: string): string;
|
|
33
|
+
/** Parse and shape-check roster text. Every problem is a `roster-invalid` error (never a throw);
|
|
34
|
+
* `ok: false` means the roster cannot be used, and callers refuse rather than fall back to open. */
|
|
35
|
+
export declare function parseRoster(text: string): ParseRosterResult;
|
|
36
|
+
export type LoadedRoster = {
|
|
37
|
+
state: "absent";
|
|
38
|
+
} | {
|
|
39
|
+
state: "present";
|
|
40
|
+
path: string;
|
|
41
|
+
roster: Roster;
|
|
42
|
+
} | {
|
|
43
|
+
state: "invalid";
|
|
44
|
+
path: string;
|
|
45
|
+
diagnostics: Diagnostic[];
|
|
46
|
+
};
|
|
47
|
+
/** The nearest `ratifiers.yaml` walking up from `startDir`, stopping at the nearest directory that
|
|
48
|
+
* holds `.git` (a directory or a file) or at the filesystem root. When no `.git` exists anywhere
|
|
49
|
+
* above, only `startDir` itself is searched -- without a repo root there is no principled place
|
|
50
|
+
* to stop, and a roster in some unrelated parent must not govern the model. */
|
|
51
|
+
export declare function findRosterAbove(startDir: string): string | null;
|
|
52
|
+
/** Discover (or take an explicit `override` path) and parse the roster for a model in `baseDir`.
|
|
53
|
+
* An explicit path that does not exist is `invalid` — asking for a roster and not finding it must
|
|
54
|
+
* not silently mean "no roster". */
|
|
55
|
+
export declare function loadRoster(baseDir: string, override?: string, modelCtx?: RosterModelContext): LoadedRoster;
|
|
56
|
+
/** What a roster is validated against: the model it governs (and, when a system.yaml is known,
|
|
57
|
+
* its model keys). */
|
|
58
|
+
export interface RosterModelContext {
|
|
59
|
+
model: NormalizedModel;
|
|
60
|
+
/** The model key `em export` reports; entries scoped to other models are not checked here. */
|
|
61
|
+
modelKey: string;
|
|
62
|
+
/** Every model key of the enclosing system, when one is known; `models:` entries must name one. */
|
|
63
|
+
systemModelKeys?: string[];
|
|
64
|
+
}
|
|
65
|
+
export declare function rosterModelContext(model: NormalizedModel, file: string, systemModelKeys?: string[]): RosterModelContext;
|
|
66
|
+
/** `roster-invalid` for a `contexts:` entry the model does not declare (case-sensitive) -- checked
|
|
67
|
+
* only for entries that apply to this model -- and for a `models:` key the enclosing system does
|
|
68
|
+
* not have. */
|
|
69
|
+
export declare function validateRosterForModel(roster: Roster, ctx: RosterModelContext): Diagnostic[];
|
|
70
|
+
/** The contexts of a slice: those of its events, plus those of the events ITS OWN views read (each
|
|
71
|
+
* view instance's own `from`, not a union across the read model's instances - so adding a later
|
|
72
|
+
* instance never changes an earlier slice's contexts). A continuation slice (only `again` views)
|
|
73
|
+
* additionally takes the originating instance's own `from`. A slice with neither has none. */
|
|
74
|
+
export declare function sliceContexts(model: NormalizedModel, sliceIndex: number): string[];
|
|
75
|
+
export type RosterRole = "ratifiers" | "reviewers";
|
|
76
|
+
/** `null` = allowed (including: this role is unrestricted). Otherwise the refusal text. */
|
|
77
|
+
export declare function rosterRefusal(roster: Roster, role: RosterRole, name: string, contexts: string[], modelKey?: string): string | null;
|
|
78
|
+
/** Resolve a roster check for a CLI/MCP write path: `null` = proceed, else the refusal message. */
|
|
79
|
+
export declare function checkRosterForWrite(loaded: LoadedRoster, role: RosterRole, name: string, model: NormalizedModel, sliceIndex: number, modelKey?: string): string | null;
|