@nac3/forge-cli 1.21.3 → 1.23.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 (137) hide show
  1. package/dist/chat/bridge.d.ts +43 -1
  2. package/dist/chat/bridge.d.ts.map +1 -1
  3. package/dist/chat/bridge.js +176 -31
  4. package/dist/chat/bridge.js.map +1 -1
  5. package/dist/chat/bridge_inbox.d.ts +143 -0
  6. package/dist/chat/bridge_inbox.d.ts.map +1 -0
  7. package/dist/chat/bridge_inbox.js +219 -0
  8. package/dist/chat/bridge_inbox.js.map +1 -0
  9. package/dist/chat/claude.d.ts +10 -0
  10. package/dist/chat/claude.d.ts.map +1 -1
  11. package/dist/chat/claude.js.map +1 -1
  12. package/dist/chat/panel.d.ts.map +1 -1
  13. package/dist/chat/panel.js +148 -16
  14. package/dist/chat/panel.js.map +1 -1
  15. package/dist/chat/recent_sessions_recap.d.ts.map +1 -1
  16. package/dist/chat/recent_sessions_recap.js +18 -6
  17. package/dist/chat/recent_sessions_recap.js.map +1 -1
  18. package/dist/chat/server.d.ts +1 -1
  19. package/dist/chat/server.d.ts.map +1 -1
  20. package/dist/chat/server.js +346 -53
  21. package/dist/chat/server.js.map +1 -1
  22. package/dist/chat/tools/bridge_verbs.d.ts +10 -0
  23. package/dist/chat/tools/bridge_verbs.d.ts.map +1 -1
  24. package/dist/chat/tools/bridge_verbs.js +66 -3
  25. package/dist/chat/tools/bridge_verbs.js.map +1 -1
  26. package/dist/chat/tools/nac3_invoke.d.ts.map +1 -1
  27. package/dist/chat/tools/nac3_invoke.js +20 -3
  28. package/dist/chat/tools/nac3_invoke.js.map +1 -1
  29. package/dist/chat/tools/pending.d.ts.map +1 -1
  30. package/dist/chat/tools/pending.js +27 -4
  31. package/dist/chat/tools/pending.js.map +1 -1
  32. package/dist/chat/tools/urazuke.d.ts.map +1 -1
  33. package/dist/chat/tools/urazuke.js +7 -3
  34. package/dist/chat/tools/urazuke.js.map +1 -1
  35. package/dist/chat/tools.d.ts +6 -0
  36. package/dist/chat/tools.d.ts.map +1 -1
  37. package/dist/chat/tools.js +4 -0
  38. package/dist/chat/tools.js.map +1 -1
  39. package/dist/chat/turn_control.d.ts +2 -0
  40. package/dist/chat/turn_control.d.ts.map +1 -1
  41. package/dist/chat/turn_control.js +30 -1
  42. package/dist/chat/turn_control.js.map +1 -1
  43. package/dist/commands/pending.d.ts.map +1 -1
  44. package/dist/commands/pending.js +4 -2
  45. package/dist/commands/pending.js.map +1 -1
  46. package/dist/commands/urazuke.d.ts.map +1 -1
  47. package/dist/commands/urazuke.js +206 -16
  48. package/dist/commands/urazuke.js.map +1 -1
  49. package/dist/core/cost_router.d.ts +7 -0
  50. package/dist/core/cost_router.d.ts.map +1 -1
  51. package/dist/core/cost_router.js +8 -5
  52. package/dist/core/cost_router.js.map +1 -1
  53. package/dist/core/limits_config.d.ts +37 -6
  54. package/dist/core/limits_config.d.ts.map +1 -1
  55. package/dist/core/limits_config.js +74 -8
  56. package/dist/core/limits_config.js.map +1 -1
  57. package/dist/core/whats_new.d.ts.map +1 -1
  58. package/dist/core/whats_new.js +12 -0
  59. package/dist/core/whats_new.js.map +1 -1
  60. package/dist/nac3/danger_zone.d.ts +39 -0
  61. package/dist/nac3/danger_zone.d.ts.map +1 -0
  62. package/dist/nac3/danger_zone.js +31 -0
  63. package/dist/nac3/danger_zone.js.map +1 -0
  64. package/dist/nac3/internal_manifest.d.ts.map +1 -1
  65. package/dist/nac3/internal_manifest.js +33 -0
  66. package/dist/nac3/internal_manifest.js.map +1 -1
  67. package/dist/pending/store.d.ts +7 -0
  68. package/dist/pending/store.d.ts.map +1 -1
  69. package/dist/pending/store.js +41 -0
  70. package/dist/pending/store.js.map +1 -1
  71. package/dist/urazuke/catalog.d.ts +1 -1
  72. package/dist/urazuke/catalog.d.ts.map +1 -1
  73. package/dist/urazuke/catalog.js.map +1 -1
  74. package/dist/urazuke/current.d.ts +2 -2
  75. package/dist/urazuke/current.d.ts.map +1 -1
  76. package/dist/urazuke/current.js +32 -12
  77. package/dist/urazuke/current.js.map +1 -1
  78. package/dist/urazuke/gate.d.ts +45 -7
  79. package/dist/urazuke/gate.d.ts.map +1 -1
  80. package/dist/urazuke/gate.js +190 -20
  81. package/dist/urazuke/gate.js.map +1 -1
  82. package/dist/urazuke/gates_data.d.ts +170 -0
  83. package/dist/urazuke/gates_data.d.ts.map +1 -1
  84. package/dist/urazuke/gates_data.js +98 -5
  85. package/dist/urazuke/gates_data.js.map +1 -1
  86. package/dist/urazuke/gates_ledger.d.ts +75 -0
  87. package/dist/urazuke/gates_ledger.d.ts.map +1 -0
  88. package/dist/urazuke/gates_ledger.js +183 -0
  89. package/dist/urazuke/gates_ledger.js.map +1 -0
  90. package/dist/urazuke/gates_pull.d.ts +97 -0
  91. package/dist/urazuke/gates_pull.d.ts.map +1 -0
  92. package/dist/urazuke/gates_pull.js +303 -0
  93. package/dist/urazuke/gates_pull.js.map +1 -0
  94. package/dist/urazuke/heuristics.d.ts +108 -0
  95. package/dist/urazuke/heuristics.d.ts.map +1 -0
  96. package/dist/urazuke/heuristics.js +248 -0
  97. package/dist/urazuke/heuristics.js.map +1 -0
  98. package/dist/urazuke/manual_cases.d.ts +31 -0
  99. package/dist/urazuke/manual_cases.d.ts.map +1 -0
  100. package/dist/urazuke/manual_cases.js +87 -0
  101. package/dist/urazuke/manual_cases.js.map +1 -0
  102. package/dist/urazuke/project_config.d.ts +25 -0
  103. package/dist/urazuke/project_config.d.ts.map +1 -0
  104. package/dist/urazuke/project_config.js +65 -0
  105. package/dist/urazuke/project_config.js.map +1 -0
  106. package/dist/urazuke/quality.d.ts +0 -8
  107. package/dist/urazuke/quality.d.ts.map +1 -1
  108. package/dist/urazuke/quality.js +4 -29
  109. package/dist/urazuke/quality.js.map +1 -1
  110. package/dist/urazuke/retro.d.ts.map +1 -1
  111. package/dist/urazuke/retro.js +4 -1
  112. package/dist/urazuke/retro.js.map +1 -1
  113. package/dist/urazuke/scoreboard.d.ts +85 -0
  114. package/dist/urazuke/scoreboard.d.ts.map +1 -0
  115. package/dist/urazuke/scoreboard.js +260 -0
  116. package/dist/urazuke/scoreboard.js.map +1 -0
  117. package/dist/urazuke/section.d.ts +2 -2
  118. package/dist/urazuke/section.d.ts.map +1 -1
  119. package/dist/urazuke/section.js +31 -6
  120. package/dist/urazuke/section.js.map +1 -1
  121. package/dist/version.d.ts +1 -1
  122. package/dist/version.js +1 -1
  123. package/dist/workflow/orchestrator.d.ts +9 -0
  124. package/dist/workflow/orchestrator.d.ts.map +1 -1
  125. package/dist/workflow/orchestrator.js +17 -16
  126. package/dist/workflow/orchestrator.js.map +1 -1
  127. package/package.json +1 -1
  128. package/src/i18n/catalogs/ar.json +558 -550
  129. package/src/i18n/catalogs/de.json +558 -550
  130. package/src/i18n/catalogs/en.json +558 -550
  131. package/src/i18n/catalogs/es.json +558 -550
  132. package/src/i18n/catalogs/fr.json +558 -550
  133. package/src/i18n/catalogs/hi.json +558 -550
  134. package/src/i18n/catalogs/it.json +558 -550
  135. package/src/i18n/catalogs/ja.json +558 -550
  136. package/src/i18n/catalogs/pt.json +558 -550
  137. package/src/i18n/catalogs/zh.json +558 -550
@@ -0,0 +1,248 @@
1
+ /**
2
+ * THE FORTY-FIVE RULES, RESOLVED AGAINST THE DEFINITION IN FORCE
3
+ * (Annex H, layers 1 to 3 -- pieces 1, 2, 3 reaching the evaluator).
4
+ *
5
+ * Everything in here is READ from the gate definition the project is being
6
+ * measured by: the rule ids, the group they belong to, the instrument, the
7
+ * severity, which rules a machine may not judge (`human_red`), the
8
+ * thresholds and which of them the method declares. Nothing is a constant
9
+ * of Forge. That is the whole point: on 2026-10-27 this tool was measuring
10
+ * two thresholds it had invented (`max_complexity=10` standing in for
11
+ * CQ-04's undeclared `paths_per_function`, and a `dup_block_lines=8` with
12
+ * no rule id at all), which is the one thing the annex forbids outright.
13
+ *
14
+ * THE SHAPE OF THE PROJECT'S OWN DECLARATIONS is published too, and the
15
+ * field names are the method's, not ours: frontend, real_users, switches,
16
+ * switched_off, thresholds, tools (`project_config.fields`). They live in
17
+ * <root>/.forge/project-settings.json. They are the VALUES; the published
18
+ * `project_config` is the SCHEMA, and the two must not share a field --
19
+ * before this slice `resolveGates` wrote the values over the schema, so a
20
+ * consumer asking the definition for the field names got a project's
21
+ * answers instead.
22
+ *
23
+ * WHAT THIS MODULE REFUSES TO GUESS. The evaluator `heuristics` asks that
24
+ * every active rule have "an instrument, a severity and a checker". The
25
+ * data publishes the first two per rule. The third it does not: 29 of the
26
+ * 45 rules say `checker_declared_by: adapter`, and the adapter table is a
27
+ * POINTER -- `adapters.annex_columns.note` says the text of every cell
28
+ * lives only in the annex, and the pointer covers 6 rules of 45. The other
29
+ * 16 say `owner`, and the field name the owner would write it in is not
30
+ * published either (`adapters.measured_by.illustrative: true`, with the
31
+ * warning that an installation taking those names as defaults "is
32
+ * inventing layer 2"). So the checker resolves to `not_published` and the
33
+ * gate reports it as something it COULD NOT VERIFY. Not green, not red:
34
+ * named. Inventing the cell here would be the same defect this file was
35
+ * written to delete.
36
+ *
37
+ * ASCII-only.
38
+ */
39
+ import fs from 'node:fs/promises';
40
+ import path from 'node:path';
41
+ import { readManualCases } from './manual_cases.js';
42
+ export * from './scoreboard.js';
43
+ export function projectSettingsPath(root) {
44
+ return path.join(root, '.forge', 'project-settings.json');
45
+ }
46
+ export async function readProjectSettings(root) {
47
+ try {
48
+ const parsed = JSON.parse(await fs.readFile(projectSettingsPath(root), 'utf8'));
49
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
50
+ return parsed;
51
+ }
52
+ catch { /* no declarations: that is a gap the report names, not an error */ }
53
+ return undefined;
54
+ }
55
+ function isRec(v) {
56
+ return v != null && typeof v === 'object' && !Array.isArray(v);
57
+ }
58
+ const RECORD_FIELDS = ['rule', 'reason', 'authorized_by', 'review_date'];
59
+ /** A switch-off record, or the list of what is missing from it. The method
60
+ * is explicit that an incomplete record is not a lenient switch-off: it is
61
+ * red, and it makes the whole ruleset invalid (D-046, D-047). */
62
+ export function switchOffProblems(entry, index) {
63
+ if (!isRec(entry)) {
64
+ return { problems: ['switched_off[' + index + '] no es un registro: ' + JSON.stringify(entry ?? null)] };
65
+ }
66
+ const rule = typeof entry['rule'] === 'string' ? entry['rule'] : undefined;
67
+ const problems = [];
68
+ for (const f of RECORD_FIELDS) {
69
+ const v = entry[f];
70
+ if (typeof v !== 'string' || v.trim() === '') {
71
+ problems.push('apagada sin un registro completo: ' + (rule ?? 'switched_off[' + index + ']')
72
+ + ' no trae ' + f);
73
+ }
74
+ }
75
+ return { ...(rule ? { rule } : {}), problems };
76
+ }
77
+ function severityOf(rule, realUsers, declaredThresholdIds) {
78
+ const base = String(rule['initial_severity'] ?? 'records');
79
+ const esc = rule['escalates_to'];
80
+ /* CQ-18 escalates to `blocks` when the product has users outside the
81
+ * organisation, and the method says the escalation "requires a
82
+ * declaration by the owner, through the same mechanism as any threshold"
83
+ * (D-048). So real_users alone does not escalate it: without the owner's
84
+ * declaration it stays where the catalogue put it, and the report says
85
+ * the escalation is pending a declaration. */
86
+ if (isRec(esc) && realUsers && declaredThresholdIds.has(String(rule['id']))) {
87
+ return { severity: String(esc['severity'] ?? base), source: 'escalated' };
88
+ }
89
+ return { severity: base, source: 'catalog' };
90
+ }
91
+ function thresholdsOf(adapters, settings) {
92
+ const declared = isRec(adapters) && Array.isArray(adapters['thresholds'])
93
+ ? adapters['thresholds']
94
+ : [];
95
+ const own = settings?.thresholds ?? {};
96
+ return declared.filter(isRec).map((t) => {
97
+ const id = String(t['id']);
98
+ const rule = String(t['rule'] ?? '');
99
+ if (Object.prototype.hasOwnProperty.call(own, id) && own[id] != null) {
100
+ return { id, rule, value: own[id], declared: true, source: 'project' };
101
+ }
102
+ if (t['declared'] === true && t['value'] != null) {
103
+ return { id, rule, value: t['value'], declared: true, source: 'annex' };
104
+ }
105
+ /* Not declared anywhere: reported as undeclared. No approximating, no
106
+ * silent inheriting and no inventing (adapters.undeclared_threshold). */
107
+ return { id, rule, value: null, declared: false, source: 'undeclared' };
108
+ });
109
+ }
110
+ export async function resolveRuleset(opts) {
111
+ const settings = opts.settings ?? await readProjectSettings(opts.projectRoot);
112
+ const h = isRec(opts.gates.heuristics) ? opts.gates.heuristics : {};
113
+ const publishedRules = Array.isArray(h['rules']) ? h['rules'].filter(isRec) : [];
114
+ const scoreboard = isRec(h['scoreboard']) ? h['scoreboard'] : {};
115
+ const outsideNamed = new Set((Array.isArray(scoreboard['human_outside_the_percentage'])
116
+ ? scoreboard['human_outside_the_percentage']
117
+ : []).filter((x) => typeof x === 'string' && /^[A-Z]{2}-\d\d$/.test(x)));
118
+ const realUsers = settings?.real_users === true;
119
+ /* ---- the switch-off list, and whether it is a valid ruleset ---- */
120
+ const problems = [];
121
+ const invalidating = [];
122
+ const offRecords = new Map();
123
+ const offIds = new Set();
124
+ const rawOff = Array.isArray(settings?.switched_off) ? settings?.switched_off : [];
125
+ rawOff.forEach((entry, i) => {
126
+ const r = switchOffProblems(entry, i);
127
+ if (r.rule)
128
+ offIds.add(r.rule);
129
+ if (r.problems.length) {
130
+ problems.push(...r.problems);
131
+ invalidating.push(...r.problems);
132
+ return;
133
+ }
134
+ offRecords.set(r.rule, entry);
135
+ });
136
+ /* ---- the group switches ---- */
137
+ const groupOff = [];
138
+ for (const [group, sw] of Object.entries(settings?.switches ?? {})) {
139
+ if (sw === true)
140
+ continue;
141
+ if (!isRec(sw)) {
142
+ const p = 'switches.' + group + ' no es ni true ni un registro de apagado';
143
+ problems.push(p);
144
+ invalidating.push(p);
145
+ continue;
146
+ }
147
+ const missing = ['reason', 'authorized_by', 'review_date']
148
+ .filter((f) => typeof sw[f] !== 'string' || String(sw[f]).trim() === '');
149
+ if (missing.length) {
150
+ const p = 'el grupo ' + group + ' esta apagado sin un registro completo: falta ' + missing.join(', ');
151
+ problems.push(p);
152
+ invalidating.push(p);
153
+ continue;
154
+ }
155
+ groupOff.push(group);
156
+ }
157
+ const thresholds = thresholdsOf(opts.gates.adapters, settings);
158
+ /* The escalation of a severity travels "through the same mechanism as any
159
+ * threshold" (D-048), so the owner declares it as a `thresholds` entry
160
+ * keyed by the rule id. The method publishes no threshold id for it, which
161
+ * is why this is the rule id and not one of the five published ones. */
162
+ const ownerDeclared = new Set(Object.keys(settings?.thresholds ?? {}));
163
+ const couldNotVerify = [];
164
+ const rules = publishedRules.map((raw) => {
165
+ const id = String(raw['id']);
166
+ const group = String(raw['group'] ?? '');
167
+ const instrument = Array.isArray(raw['instrument']) ? raw['instrument'].map(String) : [];
168
+ const humanRed = raw['human_red'] === true;
169
+ const { severity, source } = severityOf(raw, realUsers, ownerDeclared);
170
+ const appliesIf = typeof raw['applies_if'] === 'string' ? raw['applies_if'] : undefined;
171
+ let state = 'active';
172
+ let why;
173
+ if (groupOff.includes(group)) {
174
+ state = 'group_off';
175
+ why = 'the group ' + group + ' is switched off, and a switched-off group is never read as fulfilled';
176
+ }
177
+ else if (offRecords.has(id)) {
178
+ const rec = offRecords.get(id);
179
+ state = 'switched_off';
180
+ why = 'switched off by ' + rec.authorized_by + ', review ' + rec.review_date + ': ' + rec.reason;
181
+ }
182
+ else if (appliesIf === 'real_users' && !realUsers) {
183
+ state = 'out_of_scope';
184
+ why = 'applies_if real_users, and this project declares real_users=false: that is scope, not an exception';
185
+ }
186
+ /* `checker_declared_by` says WHO declares what measures the rule; the
187
+ * data does not carry the cell itself (see the header). */
188
+ const checker = 'not_published';
189
+ const outside = humanRed || instrument.includes('manual_case') || outsideNamed.has(id);
190
+ return {
191
+ id, group,
192
+ family: String(raw['family'] ?? ''),
193
+ instrument,
194
+ severity, severity_source: source,
195
+ state,
196
+ human_red: humanRed,
197
+ in_percentage: state === 'active' && !outside,
198
+ passing: false,
199
+ measured: false,
200
+ checker,
201
+ ...(offRecords.has(id) ? { record: offRecords.get(id) } : {}),
202
+ ...(why ? { why } : {}),
203
+ };
204
+ });
205
+ for (const r of publishedRules) {
206
+ if (isRec(r['escalates_to']) && realUsers && !ownerDeclared.has(String(r['id']))) {
207
+ couldNotVerify.push('la regla ' + String(r['id']) + ' escala su severidad cuando el producto tiene'
208
+ + ' usuarios fuera de la organizacion, y la escalada necesita que el dueno la declare por el mismo'
209
+ + ' mecanismo que cualquier umbral -- la definicion en vigor no publica ningun id de umbral para'
210
+ + ' ella, asi que esta instalacion no distingue una escalada pendiente de una declinada.');
211
+ }
212
+ }
213
+ const activeNeedingChecker = rules.filter((r) => r.state === 'active').length;
214
+ if (activeNeedingChecker > 0) {
215
+ couldNotVerify.push('con que se mide cada regla: la definicion en vigor no publica la celda del'
216
+ + ' adaptador (adapters.annex_columns es un puntero al anexo y cubre 6 reglas de 45) ni publica el'
217
+ + ' nombre del campo donde el dueno la declararia (adapters.measured_by.illustrative=true). '
218
+ + activeNeedingChecker + ' reglas activas no tienen entonces NINGUN medidor alcanzable, y esta'
219
+ + ' instalacion lo reporta en lugar de inventar uno.');
220
+ }
221
+ if (!settings) {
222
+ problems.push('este proyecto no declara nada: falta .forge/project-settings.json, asi que ningun grupo'
223
+ + ' esta declarado activo y no hay umbral, adaptador ni version de herramienta en el registro');
224
+ }
225
+ else {
226
+ for (const g of ['code_quality', 'usability']) {
227
+ if (!Object.prototype.hasOwnProperty.call(settings.switches ?? {}, g)) {
228
+ problems.push('el grupo ' + g + ' no esta declarado en switches: el metodo pide una entrada por grupo');
229
+ }
230
+ }
231
+ }
232
+ /* A ruleset is INVALID only when something was switched off without its
233
+ * record (D-046/D-047). A missing declaration is a red verdict with the
234
+ * figures still emitted; an invalid ruleset emits no figures at all. */
235
+ return {
236
+ catalog_version: String(opts.gates.catalog_version ?? opts.gates.version ?? 'unknown'),
237
+ adapter: settings?.frontend ?? null,
238
+ tools: settings?.tools ?? {},
239
+ real_users: realUsers,
240
+ rules,
241
+ thresholds,
242
+ group_off: groupOff,
243
+ validity: { ok: invalidating.length === 0, problems, invalidating },
244
+ could_not_verify: couldNotVerify,
245
+ manual_cases: await readManualCases(opts.projectRoot),
246
+ };
247
+ }
248
+ //# sourceMappingURL=heuristics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heuristics.js","sourceRoot":"","sources":["../../src/urazuke/heuristics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export type ManualVerdict = 'passed' | 'failed';
2
+ export interface ManualCase {
3
+ /** The rule id the verdict is about (CQ-12, UX-22, ...). */
4
+ rule: string;
5
+ verdict: ManualVerdict;
6
+ /** The person who judged it. Never a tool, never "the agent". */
7
+ judged_by: string;
8
+ /** ISO date (YYYY-MM-DD) or a full ISO timestamp. */
9
+ at: string;
10
+ /** The profile the case was run under, when the project has profiles.
11
+ * `manual_cases_per_profile` needs one run per affected profile; this
12
+ * field is what will let that evaluator be measured without a migration. */
13
+ profile?: string;
14
+ note?: string;
15
+ }
16
+ export interface ManualCaseLedger {
17
+ version: 1;
18
+ cases: ManualCase[];
19
+ }
20
+ export declare function manualCasesPath(root: string): string;
21
+ export declare function readManualCases(root: string): Promise<ManualCaseLedger>;
22
+ /** A stored entry only counts when it still carries the four things that
23
+ * make it a verdict. A hand-edited ledger that lost a name loses the
24
+ * verdict with it -- it does not inherit a green. */
25
+ export declare function isVerdict(c: unknown): c is ManualCase;
26
+ export declare function recordManualCase(root: string, c: ManualCase): Promise<ManualCaseLedger>;
27
+ /** The verdict in force for a rule: the LAST recorded one. Undefined means
28
+ * nobody signed, which is neither a pass nor a fail and must never be
29
+ * rendered as either. */
30
+ export declare function verdictFor(led: ManualCaseLedger, rule: string, profile?: string): ManualCase | undefined;
31
+ //# sourceMappingURL=manual_cases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual_cases.d.ts","sourceRoot":"","sources":["../../src/urazuke/manual_cases.ts"],"names":[],"mappings":"AA6BA,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,CAAC;IACvB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,EAAE,EAAE,MAAM,CAAC;IACX;;iFAE6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAQ7E;AAED;;sDAEsD;AACtD,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,UAAU,CAOrD;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyB7F;AAED;;0BAE0B;AAC1B,wBAAgB,UAAU,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAGxG"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * THE HUMAN VERDICT ON RECORD (Annex H, pieza 5).
3
+ *
4
+ * Four of the forty-five rules carry `human_red: true` in the published
5
+ * data -- CQ-12, CQ-14, UX-22 and UX-27 as of catalog 2026.10.6 -- and the
6
+ * method's `manual_case` instrument says what such a verdict IS: "passed or
7
+ * failed, with a name and a date", judged by a person. The list is read
8
+ * from the definition in force, never from this file: a rule that gains
9
+ * `human_red` in a later catalogue is picked up without an install.
10
+ *
11
+ * Why a ledger and not a boolean. Before this module the `owner` state
12
+ * existed in the gate and signed nothing: a step with nothing mechanical to
13
+ * look at came out as "the owner verifies it", and the close gate let it
14
+ * through (`orchestrator.ts` treated only `red` and `unmeasured` as
15
+ * blocking). So the strongest claim in the method -- a person looked at
16
+ * this and said yes -- was the one claim with no evidence behind it. The
17
+ * ledger makes it a record with a name and a date, or it makes it absent.
18
+ *
19
+ * What is refused on write. A verdict with an empty `judged_by` or an empty
20
+ * `at` is not a verdict with a missing field, it is not a verdict: an
21
+ * anonymous or undated signature is exactly the "green without having
22
+ * looked" this whole slice exists to remove. `recordManualCase` throws
23
+ * instead of storing it.
24
+ *
25
+ * ASCII-only.
26
+ */
27
+ import fs from 'node:fs/promises';
28
+ import path from 'node:path';
29
+ export function manualCasesPath(root) {
30
+ return path.join(root, '.forge', 'urazuke-manual-cases.json');
31
+ }
32
+ export async function readManualCases(root) {
33
+ try {
34
+ const parsed = JSON.parse(await fs.readFile(manualCasesPath(root), 'utf8'));
35
+ if (parsed && parsed.version === 1 && Array.isArray(parsed.cases)) {
36
+ return { version: 1, cases: parsed.cases.filter(isVerdict) };
37
+ }
38
+ }
39
+ catch { /* no ledger yet: that is "nobody signed", not an error */ }
40
+ return { version: 1, cases: [] };
41
+ }
42
+ /** A stored entry only counts when it still carries the four things that
43
+ * make it a verdict. A hand-edited ledger that lost a name loses the
44
+ * verdict with it -- it does not inherit a green. */
45
+ export function isVerdict(c) {
46
+ if (c == null || typeof c !== 'object')
47
+ return false;
48
+ const r = c;
49
+ return typeof r['rule'] === 'string' && String(r['rule']).trim() !== ''
50
+ && (r['verdict'] === 'passed' || r['verdict'] === 'failed')
51
+ && typeof r['judged_by'] === 'string' && String(r['judged_by']).trim() !== ''
52
+ && typeof r['at'] === 'string' && String(r['at']).trim() !== '';
53
+ }
54
+ export async function recordManualCase(root, c) {
55
+ if (!c || typeof c.rule !== 'string' || c.rule.trim() === '') {
56
+ throw new Error('a manual case needs the rule it judges');
57
+ }
58
+ if (c.verdict !== 'passed' && c.verdict !== 'failed') {
59
+ throw new Error('verdict has to be passed or failed: ' + String(c.verdict));
60
+ }
61
+ if (typeof c.judged_by !== 'string' || c.judged_by.trim() === '') {
62
+ throw new Error('judged_by is empty: an anonymous verdict is not a verdict (rule ' + c.rule + ')');
63
+ }
64
+ if (typeof c.at !== 'string' || c.at.trim() === '') {
65
+ throw new Error('at is empty: an undated verdict is not a verdict (rule ' + c.rule + ')');
66
+ }
67
+ const led = await readManualCases(root);
68
+ /* Append-only by design: the previous verdict stays visible, and the
69
+ * latest one for a (rule, profile) pair is the one that counts. The
70
+ * history is how a reviewer sees a red that turned green and when. */
71
+ led.cases.push({
72
+ rule: c.rule, verdict: c.verdict, judged_by: c.judged_by, at: c.at,
73
+ ...(c.profile ? { profile: c.profile } : {}),
74
+ ...(c.note ? { note: c.note } : {}),
75
+ });
76
+ await fs.mkdir(path.dirname(manualCasesPath(root)), { recursive: true });
77
+ await fs.writeFile(manualCasesPath(root), JSON.stringify(led, null, 2) + '\n', 'utf8');
78
+ return led;
79
+ }
80
+ /** The verdict in force for a rule: the LAST recorded one. Undefined means
81
+ * nobody signed, which is neither a pass nor a fail and must never be
82
+ * rendered as either. */
83
+ export function verdictFor(led, rule, profile) {
84
+ const matching = led.cases.filter((c) => c.rule === rule && (profile == null || c.profile === profile));
85
+ return matching.length ? matching[matching.length - 1] : undefined;
86
+ }
87
+ //# sourceMappingURL=manual_cases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual_cases.js","sourceRoot":"","sources":["../../src/urazuke/manual_cases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * PROJECT CONFIGURATION: validation and semantics.
3
+ *
4
+ * A project's .forge/project-config.json can declare switches to
5
+ * turn off rules of the method. Each off-switch requires a 4-field
6
+ * audit record (rule, reason, authorized_by, review_date), and
7
+ * an evaluator can only read a rule as off if the record is complete.
8
+ *
9
+ * ASCII-only.
10
+ */
11
+ export interface SwitchOffRecord {
12
+ rule: string;
13
+ reason: string;
14
+ authorized_by: string;
15
+ review_date: string;
16
+ }
17
+ export interface ValidationResult {
18
+ ok: boolean;
19
+ invalid: string[];
20
+ problems: string[];
21
+ }
22
+ export declare function isSwitchOffValid(record: unknown): record is SwitchOffRecord;
23
+ export declare function validateSwitchOffRecord(records: unknown): ValidationResult;
24
+ export declare function isRuleSwitchedOff(rule: string, switched_off: string[] | undefined, switch_off_record: unknown): boolean;
25
+ //# sourceMappingURL=project_config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project_config.d.ts","sourceRoot":"","sources":["../../src/urazuke/project_config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAQD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,eAAe,CAM3E;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAkC1E;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EAAE,GAAG,SAAS,EAClC,iBAAiB,EAAE,OAAO,GACzB,OAAO,CAQT"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * PROJECT CONFIGURATION: validation and semantics.
3
+ *
4
+ * A project's .forge/project-config.json can declare switches to
5
+ * turn off rules of the method. Each off-switch requires a 4-field
6
+ * audit record (rule, reason, authorized_by, review_date), and
7
+ * an evaluator can only read a rule as off if the record is complete.
8
+ *
9
+ * ASCII-only.
10
+ */
11
+ const REQUIRED_FIELDS = ['rule', 'reason', 'authorized_by', 'review_date'];
12
+ function isRecord(v) {
13
+ return v != null && typeof v === 'object' && !Array.isArray(v);
14
+ }
15
+ export function isSwitchOffValid(record) {
16
+ if (!isRecord(record))
17
+ return false;
18
+ for (const field of REQUIRED_FIELDS) {
19
+ if (typeof record[field] !== 'string')
20
+ return false;
21
+ }
22
+ return true;
23
+ }
24
+ export function validateSwitchOffRecord(records) {
25
+ const invalid = [];
26
+ const problems = [];
27
+ const input = Array.isArray(records) ? records : [];
28
+ for (const rec of input) {
29
+ if (!isRecord(rec)) {
30
+ /* Reading `.rule` off this is what used to throw: a null left behind
31
+ * by a hand-edited config crashed the validator instead of being
32
+ * reported as the invalid entry it is. */
33
+ invalid.push('?');
34
+ problems.push('record is not an object: ' + JSON.stringify(rec ?? null));
35
+ continue;
36
+ }
37
+ const rule = rec.rule;
38
+ if (typeof rule !== 'string') {
39
+ invalid.push('?');
40
+ problems.push('rule is not a string');
41
+ continue;
42
+ }
43
+ for (const field of REQUIRED_FIELDS) {
44
+ if (typeof rec[field] !== 'string') {
45
+ invalid.push(rule);
46
+ problems.push(`rule ${rule}: ${field} is missing or not a string`);
47
+ break;
48
+ }
49
+ }
50
+ }
51
+ return {
52
+ ok: invalid.length === 0,
53
+ invalid,
54
+ problems,
55
+ };
56
+ }
57
+ export function isRuleSwitchedOff(rule, switched_off, switch_off_record) {
58
+ const list = switched_off ?? [];
59
+ if (!list.includes(rule))
60
+ return false;
61
+ const records = Array.isArray(switch_off_record) ? switch_off_record : [];
62
+ const recordForRule = records.find((r) => isRecord(r) && r.rule === rule);
63
+ return isSwitchOffValid(recordForRule);
64
+ }
65
+ //# sourceMappingURL=project_config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project_config.js","sourceRoot":"","sources":["../../src/urazuke/project_config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAeH,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;AAE3E,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,CAAC,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAe;IAC9C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;IACtD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAEpD,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB;;sDAE0C;YAC1C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC,2BAA2B,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC;YACzE,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACtB,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YACtC,SAAS;QACX,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,QAAQ,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK,KAAK,6BAA6B,CAAC,CAAC;gBACnE,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC;QACxB,OAAO;QACP,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,YAAkC,EAClC,iBAA0B;IAE1B,MAAM,IAAI,GAAG,YAAY,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAEvC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAE1E,OAAO,gBAAgB,CAAC,aAAa,CAAC,CAAC;AACzC,CAAC"}
@@ -2,9 +2,6 @@ import { type StackAdapter } from './stacks.js';
2
2
  export interface QualityLimits {
3
3
  max_file_lines: number;
4
4
  max_function_lines: number;
5
- max_complexity: number;
6
- /** Lines in a duplicated block to count it. */
7
- dup_block_lines: number;
8
5
  }
9
6
  export declare const DEFAULT_LIMITS: QualityLimits;
10
7
  export interface Violation {
@@ -20,8 +17,6 @@ export interface NativeMeasure {
20
17
  violations: Violation[];
21
18
  max_file_lines: number;
22
19
  max_function_lines: number;
23
- max_complexity: number;
24
- duplicated_blocks: number;
25
20
  }
26
21
  interface FnSpan {
27
22
  name: string;
@@ -39,14 +34,12 @@ export interface Ratchet {
39
34
  coverage_pct?: number;
40
35
  mutation_score?: number;
41
36
  violations?: number;
42
- duplicated_blocks?: number;
43
37
  updated_at: number;
44
38
  history: Array<{
45
39
  at: number;
46
40
  coverage_pct?: number;
47
41
  mutation_score?: number;
48
42
  violations: number;
49
- duplicated_blocks: number;
50
43
  }>;
51
44
  }
52
45
  export declare function ratchetPath(root: string): string;
@@ -63,7 +56,6 @@ export declare function applyRatchet(prev: Ratchet, m: {
63
56
  coverage_pct?: number;
64
57
  mutation_score?: number;
65
58
  violations: number;
66
- duplicated_blocks: number;
67
59
  }, now?: number): RatchetVerdict;
68
60
  export interface QualityCheckResult {
69
61
  stack: StackAdapter;
@@ -1 +1 @@
1
- {"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../src/urazuke/quality.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAmD,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEjG,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,cAAc,EAAE,aAAuG,CAAC;AAErI,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,YAAY,GAAG,gBAAgB,GAAG,YAAY,GAAG,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAEhJ,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAwBD,UAAU,MAAM;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AAEjF;;oBAEoB;AACpB,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAiCxE;AAMD,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,EAAE,MAAM,GAAE,aAA8B,GAAG,OAAO,CAAC,aAAa,CAAC,CAqCjI;AAID,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,CAAC,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/H;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAA8D;AAE/G,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMhE;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;AAED,MAAM,WAAW,cAAc;IAAG,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE;AAEjF;4CAC4C;AAC5C,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,SAAa,GAAG,cAAc,CAoBlL;AAID,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,cAAc,CAAC;IACxB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC,oDAAoD;IACpD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA+B/G;AAED;0EAC0E;AAC1E,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAyClF"}
1
+ {"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../src/urazuke/quality.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@
18
18
  import fs from 'node:fs/promises';
19
19
  import path from 'node:path';
20
20
  import { detectStack, readCoveragePct, readMutationScore } from './stacks.js';
21
- export const DEFAULT_LIMITS = { max_file_lines: 300, max_function_lines: 40, max_complexity: 10, dup_block_lines: 8 };
21
+ export const DEFAULT_LIMITS = { max_file_lines: 300, max_function_lines: 40 };
22
22
  /* ---------------- scanning ---------------- */
23
23
  async function walk(root, a) {
24
24
  const out = [];
@@ -121,27 +121,9 @@ export async function measureNative(root, a, limits = DEFAULT_LIMITS) {
121
121
  maxCx = Math.max(maxCx, fn.complexity);
122
122
  if (len > limits.max_function_lines)
123
123
  violations.push({ kind: 'function_lines', where: rel + ':' + fn.start + ' ' + fn.name, value: len, limit: limits.max_function_lines });
124
- if (fn.complexity > limits.max_complexity)
125
- violations.push({ kind: 'complexity', where: rel + ':' + fn.start + ' ' + fn.name, value: fn.complexity, limit: limits.max_complexity });
126
- }
127
- const norm = code.map(normalise).filter((l) => l.length > 12);
128
- for (let i = 0; i + limits.dup_block_lines <= norm.length; i++) {
129
- const key = norm.slice(i, i + limits.dup_block_lines).join('\n');
130
- const arr = blocks.get(key) ?? [];
131
- arr.push(rel + ':' + (i + 1));
132
- blocks.set(key, arr);
133
- }
134
- }
135
- let dup = 0;
136
- for (const [, where] of blocks) {
137
- const distinctFiles = new Set(where.map((w) => w.split(':')[0]));
138
- if (where.length > 1 && (distinctFiles.size > 1 || where.length > 2)) {
139
- dup += 1;
140
- if (violations.filter((v) => v.kind === 'duplication').length < 50)
141
- violations.push({ kind: 'duplication', where: where.slice(0, 3).join(' = '), value: where.length, limit: 1 });
142
124
  }
143
125
  }
144
- return { files: files.length, lines, functions, violations, max_file_lines: maxFile, max_function_lines: maxFn, max_complexity: maxCx, duplicated_blocks: dup };
126
+ return { files: files.length, lines, functions, violations, max_file_lines: maxFile, max_function_lines: maxFn };
145
127
  }
146
128
  export function ratchetPath(root) { return path.join(root, '.forge', 'quality-ratchet.json'); }
147
129
  export async function readRatchet(root) {
@@ -167,16 +149,13 @@ export function applyRatchet(prev, m, now = Date.now()) {
167
149
  reasons.push('mutation score dropped ' + prev.mutation_score + ' -> ' + m.mutation_score);
168
150
  if (prev.violations != null && m.violations > prev.violations)
169
151
  reasons.push('violations rose ' + prev.violations + ' -> ' + m.violations);
170
- if (prev.duplicated_blocks != null && m.duplicated_blocks > prev.duplicated_blocks)
171
- reasons.push('duplicated blocks rose ' + prev.duplicated_blocks + ' -> ' + m.duplicated_blocks);
172
152
  const ok = reasons.length === 0;
173
153
  const next = {
174
154
  version: 1, updated_at: now,
175
155
  coverage_pct: ok ? Math.max(prev.coverage_pct ?? -1, m.coverage_pct ?? -1) : prev.coverage_pct,
176
156
  mutation_score: ok ? Math.max(prev.mutation_score ?? -1, m.mutation_score ?? -1) : prev.mutation_score,
177
157
  violations: ok ? Math.min(prev.violations ?? Infinity, m.violations) : prev.violations,
178
- duplicated_blocks: ok ? Math.min(prev.duplicated_blocks ?? Infinity, m.duplicated_blocks) : prev.duplicated_blocks,
179
- history: [...prev.history.slice(-199), { at: now, ...(m.coverage_pct != null ? { coverage_pct: m.coverage_pct } : {}), ...(m.mutation_score != null ? { mutation_score: m.mutation_score } : {}), violations: m.violations, duplicated_blocks: m.duplicated_blocks }],
158
+ history: [...prev.history.slice(-199), { at: now, ...(m.coverage_pct != null ? { coverage_pct: m.coverage_pct } : {}), ...(m.mutation_score != null ? { mutation_score: m.mutation_score } : {}), violations: m.violations }],
180
159
  };
181
160
  if (next.coverage_pct === -1)
182
161
  delete next.coverage_pct;
@@ -184,8 +163,6 @@ export function applyRatchet(prev, m, now = Date.now()) {
184
163
  delete next.mutation_score;
185
164
  if (next.violations === Infinity)
186
165
  delete next.violations;
187
- if (next.duplicated_blocks === Infinity)
188
- delete next.duplicated_blocks;
189
166
  return { ok, reasons, next };
190
167
  }
191
168
  export async function runQualityCheck(root, opts = {}) {
@@ -215,7 +192,7 @@ export async function runQualityCheck(root, opts = {}) {
215
192
  mutation_score = (await readMutationScore(root, stack, r.output))?.score;
216
193
  }
217
194
  const prev = await readRatchet(root);
218
- const ratchet = applyRatchet(prev, { ...(coverage_pct != null ? { coverage_pct } : {}), ...(mutation_score != null ? { mutation_score } : {}), violations: native.violations.length, duplicated_blocks: native.duplicated_blocks });
195
+ const ratchet = applyRatchet(prev, { ...(coverage_pct != null ? { coverage_pct } : {}), ...(mutation_score != null ? { mutation_score } : {}), violations: native.violations.length });
219
196
  const lintOk = lint.every((l) => l.exit_code === 0);
220
197
  const ok = lintOk && ratchet.ok;
221
198
  if (ok && opts.persist !== false)
@@ -247,8 +224,6 @@ export function renderCodeQuality(r, prevBody) {
247
224
  '|---|---|---|---|',
248
225
  '| Lineas por archivo (max) | ' + r.limits.max_file_lines + ' | ' + r.native.max_file_lines + ' | ' + (by('file_lines').length ? by('file_lines').length + ' archivos pasan el limite' : 'ok') + ' |',
249
226
  '| Lineas por funcion (max) | ' + r.limits.max_function_lines + ' | ' + r.native.max_function_lines + ' | ' + (by('function_lines').length ? by('function_lines').length + ' funciones pasan el limite' : 'ok') + ' |',
250
- '| Complejidad por funcion (max) | ' + r.limits.max_complexity + ' | ' + r.native.max_complexity + ' | ' + (by('complexity').length ? by('complexity').length + ' funciones pasan el limite' : 'ok') + ' |',
251
- '| Bloques duplicados (' + r.limits.dup_block_lines + ' lineas) | 0 | ' + r.native.duplicated_blocks + ' | ' + (r.native.duplicated_blocks ? 'hay duplicacion' : 'ok') + ' |',
252
227
  '| Cobertura de lineas (%) | piso ' + (r.ratchet.next.coverage_pct ?? '-') + ' | ' + (r.coverage_pct ?? '-') + ' | ' + (r.coverage_pct != null ? 'trinquete' : 'sin medir') + ' |',
253
228
  '| Puntaje de mutacion (%) | piso ' + (r.ratchet.next.mutation_score ?? '-') + ' | ' + (r.mutation_score ?? '-') + ' | ' + (r.mutation_score != null ? 'trinquete' : 'sin medir') + ' |',
254
229
  '| Lint / formato | 0 fallos | ' + r.lint.filter((l) => l.exit_code !== 0).length + ' fallos | ' + (r.lint.every((l) => l.exit_code === 0) ? 'ok' : 'rojo') + ' |',