@lcv-ideas-software/cross-review 4.0.2 → 4.0.4

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/CHANGELOG.md CHANGED
@@ -7,6 +7,86 @@ standard `v00.00.00`; npm package versions remain SemVer.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [v04.00.04] — 2026-05-15
11
+
12
+ **Patch — restore prettier coverage of `src/` and `scripts/` (close audit
13
+ finding on v4.0.3 hard-gate gap).** The v4.0.3 ship added biome but also
14
+ moved `src/**/*.ts`, `src/**/*.js`, `scripts/**/*.ts`, `scripts/**/*.js`
15
+ into `.prettierignore` to dodge a biome↔prettier disagreement on the
16
+ dynamic-import call-style. Net effect: prettier ran against zero JS/TS
17
+ under `src/` and `scripts/`, silently turning one of the four hard-gate
18
+ checks into a no-op there. v4.0.4 restores full prettier coverage and
19
+ keeps both formatters green simultaneously.
20
+
21
+ ### Changed
22
+
23
+ - `.prettierignore` no longer excludes `src/**/*.ts`, `src/**/*.js`,
24
+ `scripts/**/*.ts`, `scripts/**/*.js`. Prettier and biome now both
25
+ check the full JS/TS surface.
26
+ - `scripts/smoke.ts` — the 7 dynamic-import sites that triggered the
27
+ biome↔prettier wrap disagreement were rewritten from the
28
+ destructure-from-call form to a 2-statement form (`const mod = await
29
+ import("..."); const { A, B, C } = mod;`). Functionally identical;
30
+ static type inference preserved because the import argument remains a
31
+ string literal in 6 of 7 sites and a template literal in 1.
32
+
33
+ ### Why a 2-statement refactor instead of a config tweak
34
+
35
+ Biome 2.x and Prettier 3.x disagree on where to wrap when
36
+ `const { ... } = await import("...")` exceeds `lineWidth`/`printWidth`:
37
+ prettier breaks after `=`, biome breaks inside the call parens. Neither
38
+ tool exposes a per-rule config knob for this specific case. Aligning
39
+ `lineWidth` (already 100 in both) doesn't help because the disagreement
40
+ is about which axis to break on, not the threshold. Refactoring to a
41
+ form short enough to keep on one line each removes the disagreement at
42
+ the source — durable across future biome/prettier releases without
43
+ relying on tool-internal heuristics matching.
44
+
45
+ **Patch — biome integration to satisfy the 4-gate quality directive
46
+ (operator 2026-05-15: eslint + biome + prettier + cross-review).** The
47
+ repo had eslint + prettier covering the static gates but lacked biome.
48
+ This release adds biome at parity with the 8 other workspace apps that
49
+ already use it (admin-app, astrologo-frontend, calculadora-app,
50
+ mainsite-frontend, mainsite-worker, mtasts-motor, oraculo-financeiro,
51
+ sponsor-motor).
52
+
53
+ ### Added
54
+
55
+ - `@biomejs/biome` (^2.4.0) devDep + `biome.json` config matching the
56
+ prettier conventions already in use (lineWidth 100, indent space 2,
57
+ double quotes, trailing commas all, semicolons always). Linter
58
+ enabled with `recommended` rules.
59
+ - `npm run biome` (check-only) + `npm run biome:write` (with --write
60
+ auto-fix) scripts scoped to `src/` and `scripts/`.
61
+ - `npm run check` aggregate script that runs all 4 statics:
62
+ `format:check && lint && biome && typecheck`. Single-command gate
63
+ for local + CI use.
64
+
65
+ ### Updated
66
+
67
+ - `.github/workflows/ci.yml` adds an explicit `Biome (lint + format)`
68
+ step between `Lint (eslint)` and `Typecheck` for granular per-gate
69
+ visibility in CI logs.
70
+ - `.github/workflows/publish.yml` adds a `Pre-publish gate (format +
71
+ lint + biome + typecheck)` step calling `npm run check` before the
72
+ existing `npm test` verify step. Defense-in-depth: the publish
73
+ runner re-verifies the 4 statics independent of the CI workflow.
74
+
75
+ ### Fixed (biome --write --unsafe applied)
76
+
77
+ - `scripts/smoke.ts`: 5 `lint/style/useTemplate` (prefer template
78
+ literals) + 1 `lint/correctness/noEmptyCharacterClassInRegex`
79
+ (the regex `[^]*?` flagged as negated-empty class → replaced with
80
+ `[\s\S]*?` which is semantically identical and lint-clean).
81
+ - `src/peers/perplexity.ts`: 4 `lint/complexity/noUselessSwitchCase`
82
+ (collapsed cases that fell through to the same return).
83
+ - `src/core/caller-tokens.ts`: 1 `lint/complexity/useOptionalChain`.
84
+ - `src/observability/logger.ts`: 1
85
+ `lint/correctness/noUnusedPrivateClassMembers`.
86
+ - 15 files received import-reorder auto-fixes (mostly grouping
87
+ `import type` vs `import` and sort order). Zero behavioral
88
+ change — typecheck + smoke + npm test all green post-fix.
89
+
10
90
  ## [v04.00.02] — 2026-05-15
11
91
 
12
92
  **Patch — Codex second-pass audit close-out (6 findings).** v4.0.1 closed 8
package/README.md CHANGED
@@ -21,7 +21,7 @@ npm install -g @lcv-ideas-software/cross-review
21
21
  npm install -g @lcv-ideas-software/cross-review --registry=https://npm.pkg.github.com
22
22
  ```
23
23
 
24
- **Status.** Stable. Current release: **v04.00.00** (npm package `4.0.0`). See
24
+ **Status.** Stable. Current release: **v04.00.04** (npm package `4.0.4`). See
25
25
  [CHANGELOG.md](./CHANGELOG.md) for the release history.
26
26
 
27
27
  > **Project renamed 2026-05-15.** This project was previously published as
@@ -36,6 +36,7 @@ The version history at a glance:
36
36
 
37
37
  | Release | Scope |
38
38
  |---|---|
39
+ | **`v04.00.04`** | **Patch — restore prettier coverage of `src/` and `scripts/` (close audit on v4.0.3 hard-gate gap).** v4.0.3 added biome but also moved `src/**/*.ts`, `src/**/*.js`, `scripts/**/*.ts`, `scripts/**/*.js` into `.prettierignore` to dodge a biome↔prettier disagreement on dynamic-import call-style. Net effect: prettier ran against zero JS/TS under `src/`/`scripts/`, silently turning one of the four hard-gate checks into a no-op there. v4.0.4 restores full coverage and resolves the disagreement at the source — the 7 `scripts/smoke.ts` dynamic-import sites that triggered the wrap conflict were rewritten from destructure-from-call form to a 2-statement form (`const mod = await import("..."); const { A, B, C } = mod;`). Functionally identical; static type inference preserved. Both formatters now check the full JS/TS surface and pass simultaneously. |
39
40
  | **`v04.00.00`** | **Major — project renamed to `cross-review`** (drops the `-v2` suffix after the companion `cross-review-v1` project was discontinued and archived 2026-05-15). Breaking: npm package `@lcv-ideas-software/cross-review-v2` → `@lcv-ideas-software/cross-review` (old name stays on npm at `3.7.5` for historical installs); binaries `cross-review-v2` / `cross-review-v2-dashboard` → `cross-review` / `cross-review-dashboard`; env-var prefix `CROSS_REVIEW_V2_*` → `CROSS_REVIEW_*` across all config knobs that previously carried the `V2` infix (e.g. `CROSS_REVIEW_DATA_DIR`, `CROSS_REVIEW_DISABLE_CACHE_ANTHROPIC`); API-key env vars unchanged; per-host identity env vars (`CROSS_REVIEW_CALLER_TOKEN`, `CROSS_REVIEW_REQUIRE_TOKEN`) unchanged. GitHub repo URL: `LCV-Ideas-Software/cross-review-v2` → `LCV-Ideas-Software/cross-review` (auto-redirected). GitHub Pages: `cross-review-v2.lcv.dev` → `cross-review.lcv.dev`. MCP server key in host configs: operators who declared `cross-review-v2` rename to `cross-review`; after reload, MCP tool prefix becomes `mcp__cross-review__*`. Data dir migration is manual: operators copy `${HOME}/.cross-review/data_v2/*` into the new default `${HOME}/.cross-review/data/` (or set `CROSS_REVIEW_DATA_DIR` to the legacy path) — the v4.0.0 runtime reads only `CROSS_REVIEW_DATA_DIR` and does not fall back to the `_v2` suffix automatically. Preserved when copied: persisted session data, `config.json`, `host-tokens.json`, `cache_manifest.json`, archived/corrupt session dirs. Wire shape of all MCP tools, event types, convergence semantics is unchanged; all capabilities, peers, models, security defenses carry over from v3.7.5 verbatim. 504 source/script/doc text substitutions across 26 files. |
40
41
  | **`v03.07.05`** | **Patch — logs+sessions study 2026-05-15 close-out (4 surgical fixes from 244-session/429-round corpus).** **A1** — `session_doctor` classified cancelled sessions as `stale` (22 of 244 false positives); doctor now treats any terminal outcome (`aborted`/`converged`/`max-rounds`) as NOT-stale regardless of the persisted `convergence_health.state`. Source-layer state untouched (backward-compat with existing sessions). **A2** — `lockCallerPeerSelection` emitted false-positive `session.caller_peer_selection_ignored` events when callers passed a panel identical to the enabled set (13 of 106 recent events); the lock now accepts an optional `enabledPeers` snapshot in its context and short-circuits the emit when the caller-supplied list set-equals the enabled set (sorted comparison). **A3** — per-provider cache disable env vars (`CROSS_REVIEW_DISABLE_CACHE_ANTHROPIC|OPENAI|GEMINI|DEEPSEEK|GROK|PERPLEXITY`; provider names match v2.21.0 `_CACHE_TTL_*` convention; same parsing as `peer_enabled`); Anthropic default flipped to disabled based on empirical 0.3% hit-rate ($1.18 wasted to save $0.0035 over 244 sessions). Global `CROSS_REVIEW_DISABLE_CACHE` kill-switch unchanged; per-provider is an additive layer. Anthropic adapter `buildSystemBlock` + short-prefix warning gated on the per-provider flag; central `config.json` `cache` block accepts the new disable keys. **B1** — `session_sweep` gains opt-in `prune_corrupt: boolean.default(false)` + `corrupt_min_age_days: number.int.default(30)` to clean `<data_dir>/corrupt_sessions/` (no prior automated cleanup; 1 stale entry from 2026-05-08 v2.25.1 redact bug still on disk at study time). New `store.pruneCorruptSessions(minAgeMs)` returns `{scanned, removed, kept}`. Response shape stays `SessionMeta[]` when `prune_corrupt: false` (default); wraps to `{ swept, pruned_corrupt }` when true. **Patch bump** (3.7.4 → 3.7.5). |
41
42
  | **`v03.07.04`** | **Patch — Codex v3.7.3 parecer close-out + two cross-review-gate root-cause fixes** (APROVADO-COM-RESSALVAS; 2 parecer findings + 2 operator-directed fixes; no public-surface or tool-schema change). **`model_match` `-latest`-alias false positive (operator-directed)** — `BasePeerAdapter.modelMatches()` matched the reported model with `reported === requested` or `reported.startsWith(`${requested}-`)`. That works for a base id resolving to a dated id (`gpt-5.5` → `gpt-5.5-2026-04-23`) but FAILS for a `-latest` alias: xAI returns `grok-4-0709` for the pinned `grok-4-latest`, which does not start with the literal `grok-4-latest-`. Every grok response was flagged `model_match: false` → `status` forced `null` → `silent_model_downgrade` rejection → format-recovery skipped, so grok was dead-on-arrival in every cross-review session and no panel including grok could reach unanimity. Fix: `modelMatches` strips a `-latest` suffix to the family stem and matches the reported id against it (`grok-4-latest` → `grok-4` → `grok-4-0709` matches); a genuine cross-family downgrade (`grok-3-*`) is still flagged. New smoke marker `model_match_latest_alias_test`. **`detectFabricatedEvidence` false positive (operator-directed)** — the detector validated operational assertions (`npm run build`, `index <hash>..<hash>`, `cargo test`, …) against the `provenanceCorpus` (attached evidence) ONLY; the prior draft was lumped into `narrativeCorpus` and never consulted for assertions. The documented process REQUIRES embedding the verbatim diff + raw gate output in `initial_draft`, so when R1 didn't converge and a relator generated an R2 revision, the relator faithfully PRESERVING that embedded evidence was flagged as "fabricating" it → `lead_fabrication_repeated` abort (misread as "perplexity keeps fabricating"; in fact it hit any relator and was a detector self-contradiction). Fix: a **three-tier corpus** — `FabricationDetectionCorpus` gains a `priorDraftCorpus` field; operational assertions are flagged only when **net-new** vs `{provenanceCorpus ∪ priorDraftCorpus}` (symmetric with the hex-token check). Preserved evidence is not fabrication; the task `narrativeCorpus` stays excluded so the v2.24.0 eee886d3 protection holds exactly. Signature unchanged; interface gains one field. **AUDIT-1 (MEDIUM)** — `scripts/runtime-smoke.ts` injected cost rate cards for only 4 peers (codex/claude/gemini/deepseek), but the public MCP path strips a caller's `peers` list (the v3.3.0 `lockCallerPeerSelection` lock), so every round runs the full 6-peer panel; grok + perplexity had no rate cards → `missingFinancialControlVars` tripped → the round finalized `outcome=max-rounds`/`financial_controls_missing` while runtime-smoke still printed `ok: true` with no assert. Fix: inject grok + perplexity rate cards (+ `CROSS_REVIEW_PERPLEXITY_DISABLE_SEARCH` and per-size request-fee defaults), and add explicit `assert` calls on every async flow's durable terminal `outcome` (review round + unanimity flow → `converged`, cancellation flow → `aborted`) placed before the `ok: true` print so a non-converging round fails the smoke loudly. **AUDIT-2 (LOW)** — `src/core/convergence.ts` comment imprecision: the skip was framed only as "the user declared no fallback models", but `fallback_exhausted` is in the skippable set and arises AFTER a declared fallback chain is drained; both comment blocks now split the skip into its two paths (no fallback declared → retry-same exhausted → skip; fallback declared, tried, and drained → also skip). Comment-only, zero logic change. New smoke marker `runtime_smoke_outcome_assert_test` + 2 new `relator_evidence_provenance_lock_test` cases source-pin the fixes. **Patch bump** (3.7.3 → 3.7.4). |
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-default-smoke.js","sourceRoot":"","sources":["../../scripts/runtime-default-smoke.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime-default-smoke.js","sourceRoot":"","sources":["../../scripts/runtime-default-smoke.ts"],"names":[],"mappings":"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"}
@@ -1165,7 +1165,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
1165
1165
  // (c) does NOT scan the accumulated buffer per delta — the contract is
1166
1166
  // `append measures only delta`.
1167
1167
  {
1168
- const { StreamBuffer, StreamBufferOverflowError, STREAM_TEXT_MAX_BYTES } = await import("../src/peers/base.js");
1168
+ const baseMod = await import("../src/peers/base.js");
1169
+ const { StreamBuffer, StreamBufferOverflowError, STREAM_TEXT_MAX_BYTES } = baseMod;
1169
1170
  const buffer = new StreamBuffer("smoke-peer");
1170
1171
  buffer.append("hello world");
1171
1172
  assert.equal(buffer.text(), "hello world");
@@ -2530,7 +2531,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
2530
2531
  // Chamada explícita com caller=claude e lead_peer=claude DEVE lançar
2531
2532
  // CallerCannotBeLeadPeerError. Sem fallback silencioso pra sorteio.
2532
2533
  {
2533
- const { assertLeadPeerNotCaller, CallerCannotBeLeadPeerError } = await import("../src/core/relator-lottery.js");
2534
+ const lotteryMod1 = await import("../src/core/relator-lottery.js");
2535
+ const { assertLeadPeerNotCaller, CallerCannotBeLeadPeerError } = lotteryMod1;
2534
2536
  let threw = false;
2535
2537
  try {
2536
2538
  assertLeadPeerNotCaller("claude", "claude");
@@ -2598,7 +2600,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
2598
2600
  // (não PEERS global). Sem isso, caller=claude com peers=["codex","gemini"]
2599
2601
  // poderia atribuir deepseek (não-participante) como lead_peer.
2600
2602
  {
2601
- const { assignRelator, resolveLeadPeer, LeadPeerNotInSessionError } = await import("../src/core/relator-lottery.js");
2603
+ const lotteryMod2 = await import("../src/core/relator-lottery.js");
2604
+ const { assignRelator, resolveLeadPeer, LeadPeerNotInSessionError } = lotteryMod2;
2602
2605
  // (1) Subset com 2 peers + caller=claude → assigned ∈ subset.
2603
2606
  for (let i = 0; i < 50; i++) {
2604
2607
  const a = assignRelator("claude", ["codex", "gemini"]);
@@ -3677,7 +3680,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3677
3680
  // none/low/medium/high). Other Grok models (per xAI docs) reject the
3678
3681
  // param OR auto-apply reasoning internally, so we omit it.
3679
3682
  {
3680
- const { modelAcceptsReasoningEffort, GROK_REASONING_EFFORT_MODELS } = await import("../src/peers/grok.js");
3683
+ const grokMod = await import("../src/peers/grok.js");
3684
+ const { modelAcceptsReasoningEffort, GROK_REASONING_EFFORT_MODELS } = grokMod;
3681
3685
  // Allowlist contract: grok-4.20-multi-agent + grok-4.3.
3682
3686
  assert.equal(modelAcceptsReasoningEffort("grok-4.20-multi-agent"), true);
3683
3687
  assert.equal(modelAcceptsReasoningEffort("grok-4.3"), true);
@@ -3842,26 +3846,26 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
3842
3846
  assert.equal(stripPerplexityThinkingBlock(cleanJson), cleanJson, "clean JSON without <think> blocks must be returned unchanged");
3843
3847
  // Scenario B: single `<think>` block followed by JSON — strip block,
3844
3848
  // trim whitespace, return JSON.
3845
- const singleThink = "<think>\nLet me reason about this carefully.\nThe evidence shows...\n</think>\n\n" + cleanJson;
3849
+ const singleThink = `<think>\nLet me reason about this carefully.\nThe evidence shows...\n</think>\n\n${cleanJson}`;
3846
3850
  assert.equal(stripPerplexityThinkingBlock(singleThink), cleanJson, "single <think> block must be stripped; trailing JSON preserved");
3847
3851
  // Scenario C: multiple `<think>` blocks (rare but legal) — every
3848
3852
  // occurrence must be removed.
3849
- const multipleThinks = "<think>first thought</think>\nintermediate text\n<think>second thought</think>\n" + cleanJson;
3853
+ const multipleThinks = `<think>first thought</think>\nintermediate text\n<think>second thought</think>\n${cleanJson}`;
3850
3854
  const strippedMultiple = stripPerplexityThinkingBlock(multipleThinks);
3851
3855
  assert.ok(!/<think/i.test(strippedMultiple), "multiple <think> blocks must all be stripped");
3852
3856
  assert.ok(strippedMultiple.includes(cleanJson), "multi-think strip must preserve trailing JSON payload");
3853
3857
  // Scenario D: `<think>` block spanning multiple lines with arbitrary
3854
3858
  // whitespace and nested-looking content (no actual nesting since
3855
3859
  // Perplexity never emits nested reasoning blocks).
3856
- const multilineThink = "<think>\n Line 1\n Line 2 with <b>html</b>\n Line 3\n</think>\n" + cleanJson;
3860
+ const multilineThink = `<think>\n Line 1\n Line 2 with <b>html</b>\n Line 3\n</think>\n${cleanJson}`;
3857
3861
  assert.equal(stripPerplexityThinkingBlock(multilineThink), cleanJson, "multi-line <think> with arbitrary indentation must strip cleanly");
3858
3862
  // Scenario E: `<think>` with attribute-like content (`<think foo="bar">`)
3859
3863
  // — regex must allow attribute fragment before close `>`.
3860
- const attributedThink = '<think foo="bar">reasoning</think>\n' + cleanJson;
3864
+ const attributedThink = `<think foo="bar">reasoning</think>\n${cleanJson}`;
3861
3865
  assert.equal(stripPerplexityThinkingBlock(attributedThink), cleanJson, "<think> tag with attribute fragment must still strip");
3862
3866
  // Scenario F: empty `<think></think>` (degenerate case) — strip + trim
3863
3867
  // still yields the trailing JSON.
3864
- const emptyThink = "<think></think>\n" + cleanJson;
3868
+ const emptyThink = `<think></think>\n${cleanJson}`;
3865
3869
  assert.equal(stripPerplexityThinkingBlock(emptyThink), cleanJson, "empty <think></think> must strip cleanly");
3866
3870
  // Scenario G: only `<think>` block, no trailing JSON — output is empty
3867
3871
  // string after trim (caller's downstream parser then triggers
@@ -4771,7 +4775,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
4771
4775
  assert.ok(!/CROSS_REVIEW_EVIDENCE_JUDGE_MAX_ITEMS_PER_PASS\s*\?\?\s*"8"/.test(configSrc), 'v2.18.5 / P1.4: legacy `?? "8"` default is gone (would silently double the worst-case judge call budget)');
4772
4776
  // (4) Behavioral: loadConfig() with env unset returns max_items_per_pass = 4.
4773
4777
  delete process.env.CROSS_REVIEW_EVIDENCE_JUDGE_MAX_ITEMS_PER_PASS;
4774
- const { loadConfig: loadConfigFresh } = await import(`../src/core/config.js?max_items_4=${Date.now()}`);
4778
+ const freshConfigMod = await import(`../src/core/config.js?max_items_4=${Date.now()}`);
4779
+ const { loadConfig: loadConfigFresh } = freshConfigMod;
4775
4780
  const cfg4 = loadConfigFresh();
4776
4781
  assert.equal(cfg4.evidence_judge_autowire.max_items_per_pass, 4, `v2.18.5 / P1.4: loadConfig() with env unset returns max_items_per_pass=4 (got ${cfg4.evidence_judge_autowire.max_items_per_pass})`);
4777
4782
  console.log("[smoke] max_items_per_pass_default_anti_drift_test: PASS");
@@ -4855,7 +4860,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
4855
4860
  // the new prompt caching surface. Pure-function tests (no API keys
4856
4861
  // required); they pin the structural invariants the runtime depends on.
4857
4862
  {
4858
- const { buildPromptParts, hashStablePrefix, assertHashInvariant, pairScopedCacheKey } = await import("../src/core/prompt-parts.js");
4863
+ const promptPartsMod = await import("../src/core/prompt-parts.js");
4864
+ const { buildPromptParts, hashStablePrefix, assertHashInvariant, pairScopedCacheKey } = promptPartsMod;
4859
4865
  const baseInput = {
4860
4866
  cacheSchemaVersion: "v1",
4861
4867
  systemRole: "You are a peer reviewer.",
@@ -4903,7 +4909,8 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
4903
4909
  console.log("[smoke] cache_rates_no_runtime_import_test: PASS");
4904
4910
  // (4) cache_manifest_atomic_write_test — write + multiple appends
4905
4911
  // preserve every entry.
4906
- const { writeCacheManifest, appendCacheManifestEntry, readCacheManifest } = await import("../src/core/cache-manifest.js");
4912
+ const cacheManifestMod = await import("../src/core/cache-manifest.js");
4913
+ const { writeCacheManifest, appendCacheManifestEntry, readCacheManifest } = cacheManifestMod;
4907
4914
  const manifestSession = "550e8400-e29b-41d4-a716-446655440099";
4908
4915
  const manifestData = {
4909
4916
  session_id: manifestSession,
@@ -6015,7 +6022,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
6015
6022
  // 5 sites must use `.max(PEERS.length)`.
6016
6023
  const maxPeersLen = serverSrcA.match(/\.max\(PEERS\.length\)/g) ?? [];
6017
6024
  assert.ok(maxPeersLen.length >= 5, `v3.7.0 / AUDIT-3: expected >=5 .max(PEERS.length) sites (4 peers panels + judge_peers), found ${maxPeersLen.length}`);
6018
- assert.ok(!/\.min\(1\)\s*\n?\s*[^]*?\.max\(5\)\s*\n?\s*\.default\(\[\.\.\.PEERS\]/.test(serverSrcA), "v3.7.0 / AUDIT-3: the peers schema must NOT keep the stale `.max(5)` against a 6-element PEERS default");
6025
+ assert.ok(!/\.min\(1\)\s*\n?\s*[\s\S]*?\.max\(5\)\s*\n?\s*\.default\(\[\.\.\.PEERS\]/.test(serverSrcA), "v3.7.0 / AUDIT-3: the peers schema must NOT keep the stale `.max(5)` against a 6-element PEERS default");
6019
6026
  assert.ok(!/judge_peers: z\.array\(PeerSchema\)\.min\(2\)\.max\(5\)/.test(serverSrcA), "v3.7.0 / AUDIT-3: judge_peers must NOT keep the stale `.max(5)`");
6020
6027
  // AUDIT-4.
6021
6028
  assert.ok(/PEERS\.filter\(\(peer\) => runtime\.config\.peer_enabled\[peer\]\)/.test(serverSrcA), "v3.7.0 / AUDIT-4: server_info.financial_controls must compute readiness over the enabled peer subset");