@mmnto/totem 2.14.0 → 2.16.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 (43) hide show
  1. package/dist/compile-manifest-mode.test.d.ts +2 -0
  2. package/dist/compile-manifest-mode.test.d.ts.map +1 -0
  3. package/dist/compile-manifest-mode.test.js +47 -0
  4. package/dist/compile-manifest-mode.test.js.map +1 -0
  5. package/dist/compile-manifest.d.ts +4 -0
  6. package/dist/compile-manifest.d.ts.map +1 -1
  7. package/dist/compile-manifest.js +10 -1
  8. package/dist/compile-manifest.js.map +1 -1
  9. package/dist/compile-manifest.test.js +37 -0
  10. package/dist/compile-manifest.test.js.map +1 -1
  11. package/dist/compiler.d.ts.map +1 -1
  12. package/dist/compiler.js +10 -2
  13. package/dist/compiler.js.map +1 -1
  14. package/dist/errors.d.ts +11 -1
  15. package/dist/errors.d.ts.map +1 -1
  16. package/dist/errors.js.map +1 -1
  17. package/dist/exporter.d.ts +64 -1
  18. package/dist/exporter.d.ts.map +1 -1
  19. package/dist/exporter.js +93 -10
  20. package/dist/exporter.js.map +1 -1
  21. package/dist/exporter.test.js +64 -1
  22. package/dist/exporter.test.js.map +1 -1
  23. package/dist/index.d.ts +6 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3 -2
  26. package/dist/index.js.map +1 -1
  27. package/dist/rule-tier.d.ts +25 -0
  28. package/dist/rule-tier.d.ts.map +1 -0
  29. package/dist/rule-tier.js +32 -0
  30. package/dist/rule-tier.js.map +1 -0
  31. package/dist/rule-tier.test.d.ts +2 -0
  32. package/dist/rule-tier.test.d.ts.map +1 -0
  33. package/dist/rule-tier.test.js +107 -0
  34. package/dist/rule-tier.test.js.map +1 -0
  35. package/dist/spine/serving-plan.d.ts +227 -0
  36. package/dist/spine/serving-plan.d.ts.map +1 -0
  37. package/dist/spine/serving-plan.js +503 -0
  38. package/dist/spine/serving-plan.js.map +1 -0
  39. package/dist/spine/serving-plan.test.d.ts +2 -0
  40. package/dist/spine/serving-plan.test.d.ts.map +1 -0
  41. package/dist/spine/serving-plan.test.js +646 -0
  42. package/dist/spine/serving-plan.test.js.map +1 -0
  43. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-tier.test.d.ts","sourceRoot":"","sources":["../src/rule-tier.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,107 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import * as coreIndex from './index.js';
3
+ import { effectiveSeverity, isBlockingRule, ruleTier } from './rule-tier.js';
4
+ const ENGINES = ['ast', 'ast-grep', 'regex', undefined];
5
+ const RULE_CLASSES = ['hard', 'advisory', undefined];
6
+ const SEVERITIES = ['error', 'warning', undefined];
7
+ /**
8
+ * Build an input with ABSENT keys left off entirely (not set to undefined), the
9
+ * shape a legacy row parsed from compiled-rules.json actually has. `engine` is
10
+ * required by the schema, so an absent engine is a legacy-row cast.
11
+ */
12
+ function makeInput(engine, ruleClass, severity) {
13
+ const input = {};
14
+ if (engine !== undefined)
15
+ input['engine'] = engine;
16
+ if (ruleClass !== undefined)
17
+ input['ruleClass'] = ruleClass;
18
+ if (severity !== undefined)
19
+ input['severity'] = severity;
20
+ return input;
21
+ }
22
+ /** Expected tier, written out per case rather than re-deriving the formula. */
23
+ function expectedTier(engine, ruleClass) {
24
+ if (ruleClass === 'hard')
25
+ return 'hard';
26
+ if (ruleClass === 'advisory')
27
+ return 'advisory';
28
+ switch (engine) {
29
+ case 'ast':
30
+ return 'hard';
31
+ case 'ast-grep':
32
+ return 'hard';
33
+ case 'regex':
34
+ return 'advisory';
35
+ default:
36
+ return 'advisory';
37
+ }
38
+ }
39
+ const TABLE = ENGINES.flatMap((engine) => RULE_CLASSES.flatMap((ruleClass) => SEVERITIES.map((severity) => {
40
+ const tier = expectedTier(engine, ruleClass);
41
+ const blocking = tier === 'hard' && severity !== 'warning';
42
+ return {
43
+ engine: engine ?? '(absent)',
44
+ ruleClass: ruleClass ?? '(absent)',
45
+ severity: severity ?? '(absent)',
46
+ input: makeInput(engine, ruleClass, severity),
47
+ tier,
48
+ blocking,
49
+ };
50
+ })));
51
+ describe('rule-tier truth table (mmnto-ai/totem#3035)', () => {
52
+ it('covers all 36 combinations', () => {
53
+ expect(TABLE).toHaveLength(36);
54
+ });
55
+ it.each(TABLE)('engine=$engine ruleClass=$ruleClass severity=$severity -> tier=$tier blocking=$blocking', ({ input, tier, blocking }) => {
56
+ expect(ruleTier(input)).toBe(tier);
57
+ expect(isBlockingRule(input)).toBe(blocking);
58
+ });
59
+ });
60
+ describe('rule-tier invariants (mmnto-ai/totem#3035)', () => {
61
+ it('ruleClass wins over the engine upward: a regex row stamped hard is hard and blocks', () => {
62
+ const rule = makeInput('regex', 'hard', 'error');
63
+ expect(ruleTier(rule)).toBe('hard');
64
+ expect(isBlockingRule(rule)).toBe(true);
65
+ });
66
+ it('ruleClass wins over the engine downward: an ast / ast-grep row stamped advisory never blocks', () => {
67
+ for (const engine of ['ast', 'ast-grep']) {
68
+ for (const severity of SEVERITIES) {
69
+ const rule = makeInput(engine, 'advisory', severity);
70
+ expect(ruleTier(rule)).toBe('advisory');
71
+ expect(isBlockingRule(rule)).toBe(false);
72
+ }
73
+ }
74
+ });
75
+ it('an absent severity blocks on a hard tier', () => {
76
+ expect(isBlockingRule(makeInput('ast', undefined, undefined))).toBe(true);
77
+ expect(isBlockingRule(makeInput('ast-grep', undefined, undefined))).toBe(true);
78
+ expect(isBlockingRule(makeInput('regex', 'hard', undefined))).toBe(true);
79
+ });
80
+ it('a warning never blocks, whatever the tier', () => {
81
+ for (const engine of ENGINES) {
82
+ for (const ruleClass of RULE_CLASSES) {
83
+ expect(isBlockingRule(makeInput(engine, ruleClass, 'warning'))).toBe(false);
84
+ }
85
+ }
86
+ });
87
+ it('a regex row (or a legacy row with no engine) never blocks unless stamped hard', () => {
88
+ for (const engine of ['regex', undefined]) {
89
+ for (const ruleClass of ['advisory', undefined]) {
90
+ for (const severity of SEVERITIES) {
91
+ expect(isBlockingRule(makeInput(engine, ruleClass, severity))).toBe(false);
92
+ }
93
+ }
94
+ }
95
+ });
96
+ it('effectiveSeverity is the stored value, or error when absent', () => {
97
+ expect(effectiveSeverity(makeInput('regex', undefined, 'warning'))).toBe('warning');
98
+ expect(effectiveSeverity(makeInput('regex', undefined, 'error'))).toBe('error');
99
+ expect(effectiveSeverity(makeInput('regex', undefined, undefined))).toBe('error');
100
+ });
101
+ it('is exported from the core index', () => {
102
+ expect(coreIndex.ruleTier).toBe(ruleTier);
103
+ expect(coreIndex.isBlockingRule).toBe(isBlockingRule);
104
+ expect(coreIndex.effectiveSeverity).toBe(effectiveSeverity);
105
+ });
106
+ });
107
+ //# sourceMappingURL=rule-tier.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-tier.test.js","sourceRoot":"","sources":["../src/rule-tier.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,227 @@
1
+ import { z } from 'zod';
2
+ import { type CompiledRule } from '../compiler-schema.js';
3
+ import { type AuthoringLedgerEntry } from './authoring-ledger.js';
4
+ import type { ParsedRuleRecord } from './rule-record.js';
5
+ /** The one instant form the writer accepts, as a refusal message names it. */
6
+ export declare const STRICT_INSTANT_FORM = "YYYY-MM-DDTHH:MM:SS(.fraction)?(Z|+HH:MM|-HH:MM)";
7
+ /**
8
+ * Epoch milliseconds of a STRICT instant (`STRICT_INSTANT_FORM`: a date-time
9
+ * with seconds and an explicit zone, every component a real calendar value), or
10
+ * `undefined` for anything else.
11
+ */
12
+ export declare function parseStrictInstant(s: string): number | undefined;
13
+ /**
14
+ * The serving writer's input: one file, so one sha256 names a run. Strict at
15
+ * every level: an unknown key anywhere is refused. Both dispositions are
16
+ * required, with no default.
17
+ */
18
+ export declare const ServingInputSchema: z.ZodObject<{
19
+ schema: z.ZodLiteral<"serving-input/1">;
20
+ /**
21
+ * The `now` for every lowered row and for the manifest, so a re-run is
22
+ * byte-identical. A STRICT instant: a zone-less date-time would read as the
23
+ * machine's local time and make the stale-floor verdict machine-dependent.
24
+ */
25
+ asOf: z.ZodEffects<z.ZodString, string, string>;
26
+ /** Free text naming the runs of record and the ruling; not machine-read. */
27
+ source: z.ZodString;
28
+ dispositions: z.ZodObject<{
29
+ legacyRow: z.ZodEnum<["archive", "remove"]>;
30
+ stamp: z.ZodEnum<["none", "advisory"]>;
31
+ }, "strict", z.ZodTypeAny, {
32
+ legacyRow: "archive" | "remove";
33
+ stamp: "advisory" | "none";
34
+ }, {
35
+ legacyRow: "archive" | "remove";
36
+ stamp: "advisory" | "none";
37
+ }>;
38
+ floor: z.ZodArray<z.ZodObject<{
39
+ legacyHash: z.ZodOptional<z.ZodString>;
40
+ records: z.ZodArray<z.ZodObject<{
41
+ path: z.ZodString;
42
+ ruleId: z.ZodString;
43
+ /**
44
+ * REQUIRED binding by content: the record's sha256 (64 hex) the
45
+ * floor was scored against, which must equal the effective
46
+ * ledger row's `record.contentHash` (mmnto-ai/totem#3045).
47
+ */
48
+ contentHash: z.ZodString;
49
+ }, "strict", z.ZodTypeAny, {
50
+ path: string;
51
+ ruleId: string;
52
+ contentHash: string;
53
+ }, {
54
+ path: string;
55
+ ruleId: string;
56
+ contentHash: string;
57
+ }>, "many">;
58
+ }, "strict", z.ZodTypeAny, {
59
+ records: {
60
+ path: string;
61
+ ruleId: string;
62
+ contentHash: string;
63
+ }[];
64
+ legacyHash?: string | undefined;
65
+ }, {
66
+ records: {
67
+ path: string;
68
+ ruleId: string;
69
+ contentHash: string;
70
+ }[];
71
+ legacyHash?: string | undefined;
72
+ }>, "many">;
73
+ archive: z.ZodArray<z.ZodObject<{
74
+ lessonHash: z.ZodString;
75
+ reason: z.ZodString;
76
+ ref: z.ZodString;
77
+ }, "strict", z.ZodTypeAny, {
78
+ lessonHash: string;
79
+ reason: string;
80
+ ref: string;
81
+ }, {
82
+ lessonHash: string;
83
+ reason: string;
84
+ ref: string;
85
+ }>, "many">;
86
+ }, "strict", z.ZodTypeAny, {
87
+ source: string;
88
+ floor: {
89
+ records: {
90
+ path: string;
91
+ ruleId: string;
92
+ contentHash: string;
93
+ }[];
94
+ legacyHash?: string | undefined;
95
+ }[];
96
+ schema: "serving-input/1";
97
+ archive: {
98
+ lessonHash: string;
99
+ reason: string;
100
+ ref: string;
101
+ }[];
102
+ asOf: string;
103
+ dispositions: {
104
+ legacyRow: "archive" | "remove";
105
+ stamp: "advisory" | "none";
106
+ };
107
+ }, {
108
+ source: string;
109
+ floor: {
110
+ records: {
111
+ path: string;
112
+ ruleId: string;
113
+ contentHash: string;
114
+ }[];
115
+ legacyHash?: string | undefined;
116
+ }[];
117
+ schema: "serving-input/1";
118
+ archive: {
119
+ lessonHash: string;
120
+ reason: string;
121
+ ref: string;
122
+ }[];
123
+ asOf: string;
124
+ dispositions: {
125
+ legacyRow: "archive" | "remove";
126
+ stamp: "advisory" | "none";
127
+ };
128
+ }>;
129
+ export type ServingInput = z.infer<typeof ServingInputSchema>;
130
+ /**
131
+ * Refuse an input whose mode is not built. `stamp: 'advisory'` waits on the
132
+ * ADR-110 reading; only `none` (unstamped) is implemented. Called by the writer
133
+ * right after the schema check and again by the planner, so neither path can
134
+ * reach a write with it.
135
+ */
136
+ export declare function assertServingInputSupported(input: ServingInput): void;
137
+ /** One floor record, ingested by the caller (`ingestRecord`): its content hash and parsed value. */
138
+ export interface ServingRecord {
139
+ path: string;
140
+ ruleId: string;
141
+ /** sha256 (64 hex) of the record file's LF image, as the intake computes it. */
142
+ contentHash: string;
143
+ parsed: ParsedRuleRecord;
144
+ }
145
+ export interface PlanServingWriteArgs {
146
+ /** The serving file's rows, as plain parsed JSON. Never mutated. */
147
+ rows: readonly CompiledRule[];
148
+ /** The authoring ledger as read (all revisions); folded here to the effective row per ruleId. */
149
+ ledger: readonly AuthoringLedgerEntry[];
150
+ /** Every record the floor names, ingested. */
151
+ records: readonly ServingRecord[];
152
+ input: ServingInput;
153
+ /** sha256 of the input file's text after CRLF to LF, carried into the report. */
154
+ inputSha256: string;
155
+ }
156
+ export type ServingRowClass = 'floor-legacy' | 'lowered' | 'archive' | 'already-archived';
157
+ /**
158
+ * `replace` is a lowered row already in the file at that ruleId whose lowering
159
+ * now differs (a new asOf, say; `changedFields` names the fields);
160
+ * `already-archived` is a floor legacy row a human archived before this run,
161
+ * left byte-untouched under `archive`; `none` is a touched row whose bytes stay.
162
+ */
163
+ export type ServingRowAction = 'archive' | 'remove' | 'insert' | 'replace' | 'already-archived' | 'none';
164
+ export interface ServingReportRow {
165
+ class: ServingRowClass;
166
+ action: ServingRowAction;
167
+ lessonHash: string;
168
+ /** For a lowered row and a floor legacy row: the floor entry's legacy hash, when it has one. */
169
+ legacyHash?: string;
170
+ /** For a lowered row: its rule id. For a floor legacy row: the record ids that serve it, joined by ', '. */
171
+ ruleId?: string;
172
+ /** For a lowered row: its record path. */
173
+ recordPath?: string;
174
+ /** For a `replace`: the top-level fields whose values differ from the row in the file. */
175
+ changedFields?: string[];
176
+ }
177
+ export interface ServingReport {
178
+ inputSha256: string;
179
+ asOf: string;
180
+ legacyRow: ServingInput['dispositions']['legacyRow'];
181
+ /** True when the planned rows serialize differently from the input rows; false means nothing to write. */
182
+ changed: boolean;
183
+ rows: ServingReportRow[];
184
+ /** Report rows per class, plus every other row of the input file counted as `carried`. */
185
+ totals: Record<ServingRowClass | 'carried', number>;
186
+ rowsBefore: number;
187
+ rowsAfter: number;
188
+ /** Floor legacy rows removed under `remove` (counted in `rowsBefore`, absent from `rowsAfter`). */
189
+ removed: number;
190
+ /** Lowered rows already present in the input file (counted in `rowsBefore`). */
191
+ loweredExisting: number;
192
+ /** Rows `isActiveCompiledRule` counts. */
193
+ active: {
194
+ before: number;
195
+ after: number;
196
+ };
197
+ /** Rows with `status: 'archived'`. */
198
+ archived: {
199
+ before: number;
200
+ after: number;
201
+ };
202
+ /** Sorted lesson hashes of the ACTIVE rows `isBlockingRule` marks (lint loads active rows only). */
203
+ blocking: {
204
+ before: string[];
205
+ after: string[];
206
+ };
207
+ }
208
+ export interface ServingPlan {
209
+ rows: CompiledRule[];
210
+ changed: boolean;
211
+ report: ServingReport;
212
+ }
213
+ /**
214
+ * Plan one serving write. PURE: no IO, no clock (`input.asOf` is the `now`).
215
+ *
216
+ * Placement. Under `archive` a floor entry's legacy row stays at its index,
217
+ * archived with a reason naming its record ids (or byte-untouched when it was
218
+ * already archived), and the lowered rows follow it in the entry's record order;
219
+ * under `remove` the lowered rows replace it at its index. An entry with no
220
+ * `legacyHash` appends its lowered rows at the end, in floor order. An entry
221
+ * whose lowered rows are already in the file (a re-run) keeps them where they
222
+ * are, so a second run with the same input is a fixpoint that changes nothing.
223
+ *
224
+ * Untouched rows are the same object references in the output.
225
+ */
226
+ export declare function planServingWrite(args: PlanServingWriteArgs): ServingPlan;
227
+ //# sourceMappingURL=serving-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serving-plan.d.ts","sourceRoot":"","sources":["../../src/spine/serving-plan.ts"],"names":[],"mappings":"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"}