@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,170 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Advisory "shape" lints (GH #292): patterns that are legal but usually mean a reaction was
|
|
3
|
+
// split by outcome instead of modeled as one slice whose command may record several events.
|
|
4
|
+
// - automation-split-by-outcome: sibling reaction slices fed by the same view whose events
|
|
5
|
+
// differ only by an outcome word (Payment Succeeded / Payment Failed).
|
|
6
|
+
// - retry-slice-of: a slice whose command is `Retry <X>` where `<X>` is another command.
|
|
7
|
+
// Name matching is on normalised, tokenised names; the outcome vocabulary is a constant list
|
|
8
|
+
// (a future config knob — see the PR Decisions).
|
|
9
|
+
import { AUTOMATION_KINDS } from "../parser/ast.js";
|
|
10
|
+
import { normalizeName } from "./model.js";
|
|
11
|
+
import { viewSourceNames } from "./continuation.js";
|
|
12
|
+
import { pushDiag } from "./rules.js";
|
|
13
|
+
/** Outcome words that mark a failure-ish or non-success branch on their own, normalised
|
|
14
|
+
* (`timed out` is a two-token phrase). A constant list; a config knob is future work. */
|
|
15
|
+
export const STRONG_OUTCOME_WORDS = new Set([
|
|
16
|
+
"succeeded", "failed", "rejected", "retried", "declined",
|
|
17
|
+
]);
|
|
18
|
+
/** Domain verbs that double as outcome words. They only count as outcome words when the rest
|
|
19
|
+
* of the name matches a sibling exactly, and never make a group on their own: `Order
|
|
20
|
+
* Cancelled` beside `Order Completed` is two decisions, not two outcomes of one. */
|
|
21
|
+
export const WEAK_OUTCOME_WORDS = new Set(["completed", "cancelled", "canceled", "expired"]);
|
|
22
|
+
export const OUTCOME_WORDS = new Set([...STRONG_OUTCOME_WORDS, ...WEAK_OUTCOME_WORDS]);
|
|
23
|
+
/** Outcome verbs and nominals as they appear inside a command name. */
|
|
24
|
+
const COMMAND_OUTCOME_TOKENS = new Set([
|
|
25
|
+
"fail", "failure", "retry", "time", "out", "expire", "reject", "decline", "succeed", "record", "mark", "to", "the", "of",
|
|
26
|
+
]);
|
|
27
|
+
/** Light verb-form normalisation: fulfilled / fulfilment / fulfil share a root, as do
|
|
28
|
+
* captured / capture. */
|
|
29
|
+
function root(word) {
|
|
30
|
+
let w = word;
|
|
31
|
+
for (const suf of ["ization", "ation", "ment", "ed", "ing", "s"]) {
|
|
32
|
+
if (w.length - suf.length >= 3 && w.endsWith(suf)) {
|
|
33
|
+
w = w.slice(0, -suf.length);
|
|
34
|
+
if (suf === "ization")
|
|
35
|
+
w += "iz";
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (w.length > 3 && w.endsWith("e"))
|
|
40
|
+
w = w.slice(0, -1);
|
|
41
|
+
return w.replace(/([^aeiou])\1$/, "$1");
|
|
42
|
+
}
|
|
43
|
+
/** Name tokens (root-normalised) with outcome words and the `timed out` phrase removed. */
|
|
44
|
+
function stemTokens(name) {
|
|
45
|
+
const raw = normalizeName(name).split(" ").filter(Boolean);
|
|
46
|
+
const tokens = [];
|
|
47
|
+
let strong = false;
|
|
48
|
+
let weak = false;
|
|
49
|
+
for (let i = 0; i < raw.length; i++) {
|
|
50
|
+
if (raw[i] === "timed" && raw[i + 1] === "out") {
|
|
51
|
+
strong = true;
|
|
52
|
+
i++;
|
|
53
|
+
}
|
|
54
|
+
else if (STRONG_OUTCOME_WORDS.has(raw[i]))
|
|
55
|
+
strong = true;
|
|
56
|
+
else if (WEAK_OUTCOME_WORDS.has(raw[i]))
|
|
57
|
+
weak = true;
|
|
58
|
+
else
|
|
59
|
+
tokens.push(root(raw[i]));
|
|
60
|
+
}
|
|
61
|
+
return { tokens, strong, weak };
|
|
62
|
+
}
|
|
63
|
+
const REACTION_KINDS = new Set(["automation", "processor", "saga", "translation"]);
|
|
64
|
+
export function shapeDiagnostics(model, refs, diags) {
|
|
65
|
+
const refOf = (id) => refs.refById.get(id);
|
|
66
|
+
// --- automation-split-by-outcome ---
|
|
67
|
+
const rs = [];
|
|
68
|
+
model.slices.forEach((slice, index) => {
|
|
69
|
+
const reaction = slice.elements.find((e) => REACTION_KINDS.has(e.kind) && (e.from?.length ?? 0) > 0);
|
|
70
|
+
const command = slice.elements.find((e) => e.kind === "command");
|
|
71
|
+
const events = slice.elements.filter((e) => e.kind === "event");
|
|
72
|
+
if (!reaction || !command || events.length === 0)
|
|
73
|
+
return;
|
|
74
|
+
const feeds = new Set();
|
|
75
|
+
for (const src of reaction.from ?? []) {
|
|
76
|
+
const key = normalizeName(src);
|
|
77
|
+
feeds.add(`view:${key}`);
|
|
78
|
+
for (const v of model.byName.get(key)?.filter((e) => e.kind === "view") ?? []) {
|
|
79
|
+
for (const ev of viewSourceNames(model, v))
|
|
80
|
+
feeds.add(`event:${normalizeName(ev)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
rs.push({ index, reaction, command, events, feeds, context: events[0].context ?? "" });
|
|
84
|
+
});
|
|
85
|
+
const commandTokens = (c) => {
|
|
86
|
+
const out = new Set();
|
|
87
|
+
for (const t of normalizeName(c.name).split(" ")) {
|
|
88
|
+
if (COMMAND_OUTCOME_TOKENS.has(t) || OUTCOME_WORDS.has(t))
|
|
89
|
+
continue;
|
|
90
|
+
out.add(root(t));
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
};
|
|
94
|
+
// Commands share a stem when, minus outcome vocabulary, they overlap on two or more tokens
|
|
95
|
+
// (verb included) or are identical: `Fulfil Order` / `Fail Order Fulfilment`.
|
|
96
|
+
const commandsShareStem = (a, b) => {
|
|
97
|
+
const ta = commandTokens(a);
|
|
98
|
+
const tb = commandTokens(b);
|
|
99
|
+
const shared = [...ta].filter((t) => tb.has(t)).length;
|
|
100
|
+
return shared >= 2 || (shared >= 1 && shared === ta.size && shared === tb.size);
|
|
101
|
+
};
|
|
102
|
+
const siblings = (a, b) => {
|
|
103
|
+
if (a.context !== b.context)
|
|
104
|
+
return false;
|
|
105
|
+
if (![...a.feeds].some((f) => b.feeds.has(f)))
|
|
106
|
+
return false;
|
|
107
|
+
if (!commandsShareStem(a.command, b.command))
|
|
108
|
+
return false;
|
|
109
|
+
return a.events.some((ea) => b.events.some((eb) => {
|
|
110
|
+
if (normalizeName(ea.name) === normalizeName(eb.name))
|
|
111
|
+
return false;
|
|
112
|
+
const sa = stemTokens(ea.name);
|
|
113
|
+
const sb = stemTokens(eb.name);
|
|
114
|
+
// At least one side carries an unambiguous outcome word; the other may be plain
|
|
115
|
+
// (the success sibling, `Order Fulfilled`) or carry any outcome word.
|
|
116
|
+
return sa.tokens.join(" ") === sb.tokens.join(" ") && (sa.strong || sb.strong);
|
|
117
|
+
}));
|
|
118
|
+
};
|
|
119
|
+
const parent = rs.map((_, i) => i);
|
|
120
|
+
const find = (i) => (parent[i] === i ? i : (parent[i] = find(parent[i])));
|
|
121
|
+
for (let i = 0; i < rs.length; i++) {
|
|
122
|
+
for (let j = i + 1; j < rs.length; j++)
|
|
123
|
+
if (siblings(rs[i], rs[j]))
|
|
124
|
+
parent[find(j)] = find(i);
|
|
125
|
+
}
|
|
126
|
+
const groups = new Map();
|
|
127
|
+
rs.forEach((r, i) => groups.set(find(i), [...(groups.get(find(i)) ?? []), r]));
|
|
128
|
+
for (const group of groups.values()) {
|
|
129
|
+
if (group.length < 2)
|
|
130
|
+
continue;
|
|
131
|
+
const first = group[0];
|
|
132
|
+
const eventNames = [...new Set(group.flatMap((g) => g.events.map((e) => e.name)))];
|
|
133
|
+
const sliceNames = group.map((g) => `"${model.slices[g.index].name}"`).join(", ");
|
|
134
|
+
pushDiag(diags, "automation-split-by-outcome", {
|
|
135
|
+
message: `slices ${sliceNames} are fed by the same read model and differ only by outcome ` +
|
|
136
|
+
`(${eventNames.join(", ")}); model one Automation slice where ${first.reaction.kind} ` +
|
|
137
|
+
`"${first.reaction.name}" triggers one command recording the outcomes as alternative ` +
|
|
138
|
+
`events (\`event ${eventNames.join(" | ")}\`) instead of one slice per outcome ` +
|
|
139
|
+
`(docs/patterns.md#automation)`,
|
|
140
|
+
line: first.reaction.line,
|
|
141
|
+
refs: [
|
|
142
|
+
...group.map((g) => refs.sliceKeys[g.index]),
|
|
143
|
+
...group.flatMap((g) => [refOf(g.reaction.id), refOf(g.command.id)]),
|
|
144
|
+
],
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
// --- retry-slice-of ---
|
|
148
|
+
const commands = model.elements.filter((e) => e.kind === "command");
|
|
149
|
+
for (const cmd of commands) {
|
|
150
|
+
const own = model.slices[cmd.sliceIndex].elements;
|
|
151
|
+
// State Change slices only: no reaction, and this is the slice's sole command.
|
|
152
|
+
if (own.some((e) => AUTOMATION_KINDS.has(e.kind)) || own.filter((e) => e.kind === "command").length !== 1)
|
|
153
|
+
continue;
|
|
154
|
+
const toks = normalizeName(cmd.name).split(" ");
|
|
155
|
+
if (toks[0] !== "retry" || toks.length < 2)
|
|
156
|
+
continue;
|
|
157
|
+
const rest = toks.slice(1).join(" ");
|
|
158
|
+
const target = commands.find((c) => c.sliceIndex !== cmd.sliceIndex && normalizeName(c.name) === rest);
|
|
159
|
+
if (!target)
|
|
160
|
+
continue;
|
|
161
|
+
pushDiag(diags, "retry-slice-of", {
|
|
162
|
+
message: `command "${cmd.name}" (slice ${cmd.sliceIndex + 1}) only retries command "${target.name}"; ` +
|
|
163
|
+
`a retry is the same command issued again — model it as another trigger of "${target.name}", ` +
|
|
164
|
+
`not a slice of its own (docs/patterns.md#automation)`,
|
|
165
|
+
line: cmd.line,
|
|
166
|
+
refs: [refOf(cmd.id), refOf(target.id)],
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=shapeLint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shapeLint.js","sourceRoot":"","sources":["../../src/model/shapeLint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Storyline, NormalizedModel } from "./model.js";
|
|
2
|
+
export type TimelineRelation = "before" | "same" | "after";
|
|
3
|
+
/** A slice's position in the composed total order (see `composedRanks`). */
|
|
4
|
+
export declare function composedRank(model: NormalizedModel, sliceIndex: number): number;
|
|
5
|
+
/** The storyline a slice was declared in, if any. */
|
|
6
|
+
export declare function storylineOfSlice(model: NormalizedModel, sliceIndex: number): Storyline | undefined;
|
|
7
|
+
/** Storyline lookup by (case/whitespace-insensitive) name. */
|
|
8
|
+
export declare function findStoryline(model: NormalizedModel, name: string): Storyline | undefined;
|
|
9
|
+
/** The storylines `storyline` comes after, transitively: root first, the directly referenced one
|
|
10
|
+
* last. `[]` when it has no (valid) `after`. */
|
|
11
|
+
export declare function afterChain(model: NormalizedModel, storyline: Storyline): Storyline[];
|
|
12
|
+
/**
|
|
13
|
+
* Where slice `target` sits on the timeline relative to slice `viewer`: "before" (strictly
|
|
14
|
+
* earlier), "same" (the same slice) or "after" (strictly later) — a comparison of the two slices'
|
|
15
|
+
* composed ranks (`composedRanks`), so it is a strict total order: every pair is comparable and
|
|
16
|
+
* the answer is antisymmetric.
|
|
17
|
+
*/
|
|
18
|
+
export declare function relationOnTimeline(model: NormalizedModel, target: number, viewer: number): TimelineRelation;
|
|
19
|
+
/** True when `target` is strictly later than `viewer` on the timeline. */
|
|
20
|
+
export declare function isLaterOnTimeline(model: NormalizedModel, target: number, viewer: number): boolean;
|
|
21
|
+
/** True when `target` is strictly earlier than `viewer` on the timeline. */
|
|
22
|
+
export declare function isEarlierOnTimeline(model: NormalizedModel, target: number, viewer: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The candidate that is the latest at-or-before `viewer` on the timeline (strict: `strict` drops
|
|
25
|
+
* the viewer's own slice). A viewer inside a storyline that declares `after` prefers, in order:
|
|
26
|
+
* a candidate in its own storyline, then one in the storylines it comes after (nearest
|
|
27
|
+
* referenced storyline first, latest slice within it), then anything else by file order — so
|
|
28
|
+
* `from`/`again` resolve against the referenced storyline's end state, not whatever view a
|
|
29
|
+
* sibling storyline left in between. Any other viewer gets plain file order.
|
|
30
|
+
*/
|
|
31
|
+
export declare function latestOnTimeline<T extends {
|
|
32
|
+
sliceIndex: number;
|
|
33
|
+
}>(model: NormalizedModel, candidates: T[], viewer: number, strict?: boolean): T | undefined;
|
|
34
|
+
/** True when the flow `source -> target` points left in the full-model render (file order) even
|
|
35
|
+
* though the composed timeline reads it forward — a storyline declared `after` another that
|
|
36
|
+
* sits lower in the file. The one case where composing the timeline would let an arrow point
|
|
37
|
+
* right-to-left, which is an error rather than a special render. */
|
|
38
|
+
export declare function composedButRenderedBackward(model: NormalizedModel, source: number, target: number): boolean;
|
|
39
|
+
/** Declaration index of the storyline a slice belongs to, or -1 for a slice outside any. */
|
|
40
|
+
export declare function storylineIndexOfSlice(model: NormalizedModel, sliceIndex: number): number;
|
|
41
|
+
/** One contiguous run of columns belonging to one storyline: a storyline whose slices are
|
|
42
|
+
* interleaved with others renders one band segment per run (GH #277). */
|
|
43
|
+
export interface StorylineRun {
|
|
44
|
+
storyline: Storyline;
|
|
45
|
+
/** Declaration index of the storyline (drives its colour). */
|
|
46
|
+
index: number;
|
|
47
|
+
fromCol: number;
|
|
48
|
+
toCol: number;
|
|
49
|
+
}
|
|
50
|
+
/** The band segments of the full-model render, in column order. */
|
|
51
|
+
export declare function storylineRuns(model: NormalizedModel): StorylineRun[];
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// The timeline a model's storylines compose (GH #277). File order is the one timeline a model
|
|
3
|
+
// has always had; a `storyline "Name" after "Other" { … }` block is read as if its slices
|
|
4
|
+
// followed the last slice of `Other` (owner ruling on Law 2: "you can't go backward in a
|
|
5
|
+
// story"). Every forward-only check (`view-from-future-event`, `reaction-from-future-view`,
|
|
6
|
+
// `arrow-backward`, `loops-to-forward`) and every `from`/`again` resolution asks THIS module
|
|
7
|
+
// which of two slices is earlier, instead of comparing `sliceIndex` directly, so the composed
|
|
8
|
+
// timeline is one definition. A model with no `after` clause gets exactly the file-order answers
|
|
9
|
+
// it always did.
|
|
10
|
+
import { normalizeName } from "./model.js";
|
|
11
|
+
/**
|
|
12
|
+
* The composed timeline as a real total order (GH #277): a deterministic topological sort of the
|
|
13
|
+
* slices in which the ONLY hard constraints are the `after` relations — every slice of the
|
|
14
|
+
* referenced storyline (all its blocks) precedes every slice of the storyline that comes after
|
|
15
|
+
* it, transitively — and file order is the tiebreak (Kahn's algorithm, always taking the ready
|
|
16
|
+
* slice with the lowest file index). `after` cycles and unknown targets contribute no constraint
|
|
17
|
+
* (validate reports them), so this never cycles. A model without `after` ranks every slice at
|
|
18
|
+
* its file index. Returns `rank[sliceIndex]`.
|
|
19
|
+
*/
|
|
20
|
+
function composedRanks(model, byKey, chain) {
|
|
21
|
+
const n = model.slices.length;
|
|
22
|
+
const preds = Array.from({ length: n }, () => new Set());
|
|
23
|
+
for (const st of model.storylines ?? []) {
|
|
24
|
+
if (st.after === undefined || (chain.get(st.name) ?? []).length === 0)
|
|
25
|
+
continue;
|
|
26
|
+
const target = byKey.get(normalizeName(st.after));
|
|
27
|
+
if (!target)
|
|
28
|
+
continue;
|
|
29
|
+
for (const dep of st.sliceIndexes)
|
|
30
|
+
for (const ref of target.sliceIndexes)
|
|
31
|
+
preds[dep].add(ref);
|
|
32
|
+
}
|
|
33
|
+
const rank = new Array(n).fill(-1);
|
|
34
|
+
const done = new Array(n).fill(false);
|
|
35
|
+
for (let next = 0; next < n; next++) {
|
|
36
|
+
let pick = -1;
|
|
37
|
+
for (let i = 0; i < n && pick < 0; i++) {
|
|
38
|
+
if (!done[i] && [...preds[i]].every((p) => done[p]))
|
|
39
|
+
pick = i;
|
|
40
|
+
}
|
|
41
|
+
if (pick < 0)
|
|
42
|
+
pick = done.indexOf(false); // unreachable: constraints are acyclic
|
|
43
|
+
done[pick] = true;
|
|
44
|
+
rank[pick] = next;
|
|
45
|
+
}
|
|
46
|
+
return rank;
|
|
47
|
+
}
|
|
48
|
+
/** A slice's position in the composed total order (see `composedRanks`). */
|
|
49
|
+
export function composedRank(model, sliceIndex) {
|
|
50
|
+
return indexOf(model).rank[sliceIndex] ?? sliceIndex;
|
|
51
|
+
}
|
|
52
|
+
const cache = new WeakMap();
|
|
53
|
+
function indexOf(model) {
|
|
54
|
+
const hit = cache.get(model);
|
|
55
|
+
if (hit)
|
|
56
|
+
return hit;
|
|
57
|
+
const storylines = model.storylines ?? [];
|
|
58
|
+
const byKey = new Map();
|
|
59
|
+
for (const st of storylines) {
|
|
60
|
+
const key = normalizeName(st.name);
|
|
61
|
+
if (!byKey.has(key))
|
|
62
|
+
byKey.set(key, st);
|
|
63
|
+
}
|
|
64
|
+
const chain = new Map();
|
|
65
|
+
for (const st of storylines) {
|
|
66
|
+
const preds = [];
|
|
67
|
+
const seen = new Set([st]);
|
|
68
|
+
let cur = st;
|
|
69
|
+
let cyclic = false;
|
|
70
|
+
while (cur && cur.after !== undefined) {
|
|
71
|
+
const next = byKey.get(normalizeName(cur.after));
|
|
72
|
+
if (!next)
|
|
73
|
+
break;
|
|
74
|
+
if (seen.has(next)) {
|
|
75
|
+
cyclic = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
seen.add(next);
|
|
79
|
+
preds.unshift(next);
|
|
80
|
+
cur = next;
|
|
81
|
+
}
|
|
82
|
+
chain.set(st.name, cyclic ? [] : preds);
|
|
83
|
+
}
|
|
84
|
+
const index = { byKey, chain, rank: composedRanks(model, byKey, chain) };
|
|
85
|
+
cache.set(model, index);
|
|
86
|
+
return index;
|
|
87
|
+
}
|
|
88
|
+
/** The storyline a slice was declared in, if any. */
|
|
89
|
+
export function storylineOfSlice(model, sliceIndex) {
|
|
90
|
+
const name = model.slices[sliceIndex]?.storyline;
|
|
91
|
+
return name === undefined ? undefined : indexOf(model).byKey.get(normalizeName(name));
|
|
92
|
+
}
|
|
93
|
+
/** Storyline lookup by (case/whitespace-insensitive) name. */
|
|
94
|
+
export function findStoryline(model, name) {
|
|
95
|
+
return indexOf(model).byKey.get(normalizeName(name));
|
|
96
|
+
}
|
|
97
|
+
/** The storylines `storyline` comes after, transitively: root first, the directly referenced one
|
|
98
|
+
* last. `[]` when it has no (valid) `after`. */
|
|
99
|
+
export function afterChain(model, storyline) {
|
|
100
|
+
return indexOf(model).chain.get(storyline.name) ?? [];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Where slice `target` sits on the timeline relative to slice `viewer`: "before" (strictly
|
|
104
|
+
* earlier), "same" (the same slice) or "after" (strictly later) — a comparison of the two slices'
|
|
105
|
+
* composed ranks (`composedRanks`), so it is a strict total order: every pair is comparable and
|
|
106
|
+
* the answer is antisymmetric.
|
|
107
|
+
*/
|
|
108
|
+
export function relationOnTimeline(model, target, viewer) {
|
|
109
|
+
if (target === viewer)
|
|
110
|
+
return "same";
|
|
111
|
+
return composedRank(model, target) < composedRank(model, viewer) ? "before" : "after";
|
|
112
|
+
}
|
|
113
|
+
/** True when `target` is strictly later than `viewer` on the timeline. */
|
|
114
|
+
export function isLaterOnTimeline(model, target, viewer) {
|
|
115
|
+
return relationOnTimeline(model, target, viewer) === "after";
|
|
116
|
+
}
|
|
117
|
+
/** True when `target` is strictly earlier than `viewer` on the timeline. */
|
|
118
|
+
export function isEarlierOnTimeline(model, target, viewer) {
|
|
119
|
+
return relationOnTimeline(model, target, viewer) === "before";
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The candidate that is the latest at-or-before `viewer` on the timeline (strict: `strict` drops
|
|
123
|
+
* the viewer's own slice). A viewer inside a storyline that declares `after` prefers, in order:
|
|
124
|
+
* a candidate in its own storyline, then one in the storylines it comes after (nearest
|
|
125
|
+
* referenced storyline first, latest slice within it), then anything else by file order — so
|
|
126
|
+
* `from`/`again` resolve against the referenced storyline's end state, not whatever view a
|
|
127
|
+
* sibling storyline left in between. Any other viewer gets plain file order.
|
|
128
|
+
*/
|
|
129
|
+
export function latestOnTimeline(model, candidates, viewer, strict = false) {
|
|
130
|
+
const visible = candidates.filter((c) => {
|
|
131
|
+
const rel = relationOnTimeline(model, c.sliceIndex, viewer);
|
|
132
|
+
return rel === "before" || (!strict && rel === "same");
|
|
133
|
+
});
|
|
134
|
+
if (visible.length === 0)
|
|
135
|
+
return undefined;
|
|
136
|
+
const own = storylineOfSlice(model, viewer);
|
|
137
|
+
const chain = own ? afterChain(model, own) : [];
|
|
138
|
+
const rank = (c) => {
|
|
139
|
+
const r = composedRank(model, c.sliceIndex);
|
|
140
|
+
if (chain.length === 0)
|
|
141
|
+
return [0, 0, r];
|
|
142
|
+
const st = storylineOfSlice(model, c.sliceIndex);
|
|
143
|
+
if (st === own)
|
|
144
|
+
return [2, 0, r];
|
|
145
|
+
const pos = st ? chain.indexOf(st) : -1;
|
|
146
|
+
if (pos >= 0)
|
|
147
|
+
return [1, pos, r];
|
|
148
|
+
return [0, 0, r];
|
|
149
|
+
};
|
|
150
|
+
return visible.reduce((best, c) => {
|
|
151
|
+
const a = rank(best);
|
|
152
|
+
const b = rank(c);
|
|
153
|
+
for (let i = 0; i < 3; i++) {
|
|
154
|
+
if (b[i] !== a[i])
|
|
155
|
+
return b[i] > a[i] ? c : best;
|
|
156
|
+
}
|
|
157
|
+
return best;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/** True when the flow `source -> target` points left in the full-model render (file order) even
|
|
161
|
+
* though the composed timeline reads it forward — a storyline declared `after` another that
|
|
162
|
+
* sits lower in the file. The one case where composing the timeline would let an arrow point
|
|
163
|
+
* right-to-left, which is an error rather than a special render. */
|
|
164
|
+
export function composedButRenderedBackward(model, source, target) {
|
|
165
|
+
return source > target && relationOnTimeline(model, source, target) === "before";
|
|
166
|
+
}
|
|
167
|
+
/** Declaration index of the storyline a slice belongs to, or -1 for a slice outside any. */
|
|
168
|
+
export function storylineIndexOfSlice(model, sliceIndex) {
|
|
169
|
+
const st = storylineOfSlice(model, sliceIndex);
|
|
170
|
+
return st ? (model.storylines ?? []).indexOf(st) : -1;
|
|
171
|
+
}
|
|
172
|
+
/** The band segments of the full-model render, in column order. */
|
|
173
|
+
export function storylineRuns(model) {
|
|
174
|
+
const runs = [];
|
|
175
|
+
const storylines = model.storylines ?? [];
|
|
176
|
+
for (let c = 0; c < model.slices.length; c++) {
|
|
177
|
+
const index = storylineIndexOfSlice(model, c);
|
|
178
|
+
if (index < 0)
|
|
179
|
+
continue;
|
|
180
|
+
const last = runs[runs.length - 1];
|
|
181
|
+
if (last && last.index === index && last.toCol === c - 1)
|
|
182
|
+
last.toCol = c;
|
|
183
|
+
else
|
|
184
|
+
runs.push({ storyline: storylines[index], index, fromCol: c, toCol: c });
|
|
185
|
+
}
|
|
186
|
+
return runs;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=storylines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storylines.js","sourceRoot":"","sources":["../../src/model/storylines.ts"],"names":[],"mappings":"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"}
|
package/dist/model/validate.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export interface Diagnostic {
|
|
|
15
15
|
/** Export-stable refs/keys (from `computeRefs()`) of every entity this diagnostic concerns —
|
|
16
16
|
* 0, 1, or more (e.g. a duplicate-name collision concerns two elements). Omitted, not `[]`,
|
|
17
17
|
* at sites with no specific entity to point at. Never a second identifier scheme — always
|
|
18
|
-
* sourced from the same `RefsResult` `em export`/`em diff` use
|
|
18
|
+
* sourced from the same `RefsResult` `em export`/`em diff` use, except that invariant-scoped
|
|
19
|
+
* rules (`command-second-intent`) also list the bare `INV-…` ids they concern. */
|
|
19
20
|
refs?: string[];
|
|
20
21
|
}
|
|
21
22
|
/** A GitHub user (`@alice`) or team (`@org/team`) — the shape CODEOWNERS accepts. */
|
|
@@ -25,6 +26,10 @@ export declare const OWNER_EMAIL_RE: RegExp;
|
|
|
25
26
|
/** Whether `owner` is usable in a CODEOWNERS line (MIL-234). */
|
|
26
27
|
export declare function isOwnerHandle(owner: string): boolean;
|
|
27
28
|
export declare function validate(model: NormalizedModel, grid: Grid, refs: RefsResult): Diagnostic[];
|
|
29
|
+
/** Minimum run length for `timeline-entity-clustered` (GH #281). A future config knob; the
|
|
30
|
+
* value is set by measurement (see the PR): no shipped example, skill em block or real model
|
|
31
|
+
* has a qualifying run even at 2, so 3 is conservative. */
|
|
32
|
+
export declare const TIMELINE_ENTITY_CLUSTER_MIN_RUN = 3;
|
|
28
33
|
/** One field on the public surface whose type does not resolve under R7 (MIL-237). */
|
|
29
34
|
export interface UnresolvedPublicField {
|
|
30
35
|
owner: {
|