mandrel 2.62.0 → 2.63.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 (31) hide show
  1. package/.agents/docs/configuration.md +0 -1
  2. package/.agents/schemas/baselines/audit-baselines-envelope.schema.json +1 -1
  3. package/.agents/schemas/baselines/baseline-envelope.schema.json +2 -19
  4. package/.agents/schemas/baselines/bundle-size.schema.json +0 -24
  5. package/.agents/schemas/baselines/coverage.schema.json +0 -26
  6. package/.agents/schemas/baselines/crap.schema.json +0 -28
  7. package/.agents/schemas/baselines/duplication.schema.json +0 -38
  8. package/.agents/schemas/baselines/maintainability.schema.json +0 -45
  9. package/.agents/schemas/baselines/mutation.schema.json +0 -28
  10. package/.agents/scripts/check-dead-exports.js +1 -1
  11. package/.agents/scripts/lib/audit-baselines/headroom.js +1 -1
  12. package/.agents/scripts/lib/audit-baselines/kinds.js +12 -7
  13. package/.agents/scripts/lib/audit-baselines/rollup.js +41 -0
  14. package/.agents/scripts/lib/audit-baselines/staleness.js +14 -3
  15. package/.agents/scripts/lib/audit-baselines/surface-entry.js +1 -1
  16. package/.agents/scripts/lib/baselines/envelope.js +5 -50
  17. package/.agents/scripts/lib/baselines/merge-envelopes.js +16 -79
  18. package/.agents/scripts/lib/baselines/reader.js +7 -7
  19. package/.agents/scripts/lib/baselines/refresh-service.js +2 -7
  20. package/.agents/scripts/lib/baselines/writer.js +16 -45
  21. package/.agents/scripts/lib/coverage-baseline.js +0 -2
  22. package/.agents/scripts/lib/cyclomatic-ceiling.js +6 -18
  23. package/.agents/scripts/lib/git/sync-from-base.js +1 -1
  24. package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +20 -27
  25. package/.agents/scripts/merge-baseline.js +13 -11
  26. package/.agents/workflows/audit-baselines.md +2 -1
  27. package/docs/CHANGELOG.md +11 -0
  28. package/lib/cli/registry.js +1 -1
  29. package/lib/migrations/index.js +2 -0
  30. package/lib/migrations/steps/2.63.0-baseline-merge-queue-shape.js +96 -0
  31. package/package.json +1 -1
@@ -0,0 +1,96 @@
1
+ // lib/migrations/steps/2.63.0-baseline-merge-queue-shape.js
2
+ /**
3
+ * Strip `generatedAt` and `rollup` from the committed row-set baselines. Both
4
+ * were rewritten on every refresh, so two branches refreshing disjoint rows
5
+ * always conflicted on GitHub, which never runs the custom merge driver.
6
+ * Readers now derive the rollup from `rows`. Files are identified by their
7
+ * `$schema` tail; any other baseline is left byte-identical.
8
+ */
9
+
10
+ import nodeFs from 'node:fs';
11
+ import path from 'node:path';
12
+
13
+ const BASELINES_DIR = 'baselines';
14
+
15
+ /** `$schema` tail → the keys the new shape no longer carries. */
16
+ const RETIRED_KEYS_BY_SCHEMA = Object.freeze({
17
+ 'coverage.schema.json': ['generatedAt', 'rollup'],
18
+ 'crap.schema.json': ['generatedAt', 'rollup'],
19
+ 'maintainability.schema.json': ['generatedAt', 'rollup'],
20
+ 'duplication.schema.json': ['generatedAt', 'rollup'],
21
+ 'mutation.schema.json': ['generatedAt', 'rollup'],
22
+ 'bundle-size.schema.json': ['generatedAt', 'rollup'],
23
+ 'cyclomatic.schema.json': ['generatedAt', 'rollup'],
24
+ 'dead-exports.schema.json': ['generatedAt'],
25
+ });
26
+
27
+ /**
28
+ * @param {unknown} envelope
29
+ * @returns {string[]}
30
+ */
31
+ function retiredKeysPresent(envelope) {
32
+ if (!envelope || typeof envelope !== 'object' || Array.isArray(envelope)) {
33
+ return [];
34
+ }
35
+ const schema = envelope.$schema;
36
+ if (typeof schema !== 'string') return [];
37
+ const keys = RETIRED_KEYS_BY_SCHEMA[schema.split('/').pop()] ?? [];
38
+ return keys.filter((key) => Object.hasOwn(envelope, key));
39
+ }
40
+
41
+ /**
42
+ * @param {{ projectRoot?: string }} ctx
43
+ * @param {typeof nodeFs} fsImpl
44
+ * @returns {Array<{ file: string, envelope: object, keys: string[] }>}
45
+ */
46
+ function pendingBaselines(ctx, fsImpl) {
47
+ const dir = path.join(ctx?.projectRoot ?? process.cwd(), BASELINES_DIR);
48
+ let names;
49
+ try {
50
+ names = fsImpl.readdirSync(dir);
51
+ } catch {
52
+ return [];
53
+ }
54
+ const out = [];
55
+ for (const name of [...names].sort()) {
56
+ if (!String(name).endsWith('.json')) continue;
57
+ const file = path.join(dir, String(name));
58
+ let envelope;
59
+ try {
60
+ envelope = JSON.parse(fsImpl.readFileSync(file, 'utf8'));
61
+ } catch {
62
+ continue;
63
+ }
64
+ const keys = retiredKeysPresent(envelope);
65
+ if (keys.length > 0) out.push({ file, envelope, keys });
66
+ }
67
+ return out;
68
+ }
69
+
70
+ export const baselineMergeQueueShape = {
71
+ version: '2.63.0',
72
+ description:
73
+ 'strip generatedAt and rollup from committed row-set baselines so ' +
74
+ 'disjoint refreshes merge textually on GitHub, where the custom merge ' +
75
+ 'driver never runs (Story #5400)',
76
+ /**
77
+ * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
78
+ * @param {typeof nodeFs} [fsImpl]
79
+ * @returns {boolean}
80
+ */
81
+ detect(ctx, fsImpl = ctx?.fs ?? nodeFs) {
82
+ return pendingBaselines(ctx, fsImpl).length > 0;
83
+ },
84
+ /**
85
+ * @param {{ projectRoot?: string, fs?: typeof nodeFs }} [ctx]
86
+ * @param {typeof nodeFs} [fsImpl]
87
+ * @returns {void}
88
+ */
89
+ apply(ctx, fsImpl = ctx?.fs ?? nodeFs) {
90
+ for (const { file, envelope, keys } of pendingBaselines(ctx, fsImpl)) {
91
+ const next = { ...envelope };
92
+ for (const key of keys) delete next[key];
93
+ fsImpl.writeFileSync(file, `${JSON.stringify(next, null, 2)}\n`);
94
+ }
95
+ },
96
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mandrel",
3
- "version": "2.62.0",
3
+ "version": "2.63.0",
4
4
  "description": "Claude Code-first opinionated workflow framework: instructions, skills, rules, and SDLC workflows that govern AI coding assistants.",
5
5
  "files": [
6
6
  ".agents/",