@lcv-ideas-software/cross-review 4.0.1 → 4.0.3
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 +111 -0
- package/README.md +6 -0
- package/SECURITY.md +1 -1
- package/dist/scripts/runtime-default-smoke.js.map +1 -1
- package/dist/scripts/smoke.js +30 -6
- package/dist/scripts/smoke.js.map +1 -1
- package/dist/src/core/caller-tokens.js +1 -1
- package/dist/src/core/caller-tokens.js.map +1 -1
- package/dist/src/core/config.d.ts +1 -1
- package/dist/src/core/config.js +3 -3
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/orchestrator.d.ts +1 -1
- package/dist/src/core/orchestrator.js +7 -7
- package/dist/src/core/orchestrator.js.map +1 -1
- package/dist/src/core/relator-lottery.js.map +1 -1
- package/dist/src/core/session-store.d.ts +1 -1
- package/dist/src/core/session-store.js +2 -2
- package/dist/src/core/session-store.js.map +1 -1
- package/dist/src/mcp/server.js +2 -2
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/observability/logger.d.ts +1 -1
- package/dist/src/observability/logger.js.map +1 -1
- package/dist/src/peers/anthropic.js.map +1 -1
- package/dist/src/peers/base.js.map +1 -1
- package/dist/src/peers/deepseek.js.map +1 -1
- package/dist/src/peers/gemini.js.map +1 -1
- package/dist/src/peers/grok.js +1 -1
- package/dist/src/peers/grok.js.map +1 -1
- package/dist/src/peers/model-selection.js +6 -6
- package/dist/src/peers/model-selection.js.map +1 -1
- package/dist/src/peers/openai.js +1 -1
- package/dist/src/peers/openai.js.map +1 -1
- package/dist/src/peers/perplexity.js +0 -4
- package/dist/src/peers/perplexity.js.map +1 -1
- package/docs/architecture.md +6 -2
- package/docs/model-selection.md +20 -11
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,117 @@ standard `v00.00.00`; npm package versions remain SemVer.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [v04.00.03] — 2026-05-15
|
|
11
|
+
|
|
12
|
+
**Patch — biome integration to satisfy the 4-gate quality directive
|
|
13
|
+
(operator 2026-05-15: eslint + biome + prettier + cross-review).** The
|
|
14
|
+
repo had eslint + prettier covering the static gates but lacked biome.
|
|
15
|
+
This release adds biome at parity with the 8 other workspace apps that
|
|
16
|
+
already use it (admin-app, astrologo-frontend, calculadora-app,
|
|
17
|
+
mainsite-frontend, mainsite-worker, mtasts-motor, oraculo-financeiro,
|
|
18
|
+
sponsor-motor).
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `@biomejs/biome` (^2.4.0) devDep + `biome.json` config matching the
|
|
23
|
+
prettier conventions already in use (lineWidth 100, indent space 2,
|
|
24
|
+
double quotes, trailing commas all, semicolons always). Linter
|
|
25
|
+
enabled with `recommended` rules.
|
|
26
|
+
- `npm run biome` (check-only) + `npm run biome:write` (with --write
|
|
27
|
+
auto-fix) scripts scoped to `src/` and `scripts/`.
|
|
28
|
+
- `npm run check` aggregate script that runs all 4 statics:
|
|
29
|
+
`format:check && lint && biome && typecheck`. Single-command gate
|
|
30
|
+
for local + CI use.
|
|
31
|
+
|
|
32
|
+
### Updated
|
|
33
|
+
|
|
34
|
+
- `.github/workflows/ci.yml` adds an explicit `Biome (lint + format)`
|
|
35
|
+
step between `Lint (eslint)` and `Typecheck` for granular per-gate
|
|
36
|
+
visibility in CI logs.
|
|
37
|
+
- `.github/workflows/publish.yml` adds a `Pre-publish gate (format +
|
|
38
|
+
lint + biome + typecheck)` step calling `npm run check` before the
|
|
39
|
+
existing `npm test` verify step. Defense-in-depth: the publish
|
|
40
|
+
runner re-verifies the 4 statics independent of the CI workflow.
|
|
41
|
+
|
|
42
|
+
### Fixed (biome --write --unsafe applied)
|
|
43
|
+
|
|
44
|
+
- `scripts/smoke.ts`: 5 `lint/style/useTemplate` (prefer template
|
|
45
|
+
literals) + 1 `lint/correctness/noEmptyCharacterClassInRegex`
|
|
46
|
+
(the regex `[^]*?` flagged as negated-empty class → replaced with
|
|
47
|
+
`[\s\S]*?` which is semantically identical and lint-clean).
|
|
48
|
+
- `src/peers/perplexity.ts`: 4 `lint/complexity/noUselessSwitchCase`
|
|
49
|
+
(collapsed cases that fell through to the same return).
|
|
50
|
+
- `src/core/caller-tokens.ts`: 1 `lint/complexity/useOptionalChain`.
|
|
51
|
+
- `src/observability/logger.ts`: 1
|
|
52
|
+
`lint/correctness/noUnusedPrivateClassMembers`.
|
|
53
|
+
- 15 files received import-reorder auto-fixes (mostly grouping
|
|
54
|
+
`import type` vs `import` and sort order). Zero behavioral
|
|
55
|
+
change — typecheck + smoke + npm test all green post-fix.
|
|
56
|
+
|
|
57
|
+
## [v04.00.02] — 2026-05-15
|
|
58
|
+
|
|
59
|
+
**Patch — Codex second-pass audit close-out (6 findings).** v4.0.1 closed 8
|
|
60
|
+
findings from the first Codex parecer; this v4.0.2 closes 6 additional
|
|
61
|
+
items the second parecer flagged. None affects runtime semantics.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- **AUDIT-1 (MEDIUM) — lockfile version drift.** v4.0.1 bumped
|
|
66
|
+
`package.json` to `4.0.1` but the lockfile root `.version` and
|
|
67
|
+
`.packages[""].version` stayed at `4.0.0` (the `npm install` ran
|
|
68
|
+
during v4.0.0 captured deps, then the version bump didn't trigger a
|
|
69
|
+
re-resolve). Full reinstall this release brings the lockfile back to
|
|
70
|
+
the current `package.json` version. **Plus: new anti-drift smoke
|
|
71
|
+
marker `package_version_consistency_test`** asserts the four
|
|
72
|
+
equalities `pkg.name === pkg-lock.name`, `pkg-lock.name ===
|
|
73
|
+
"@lcv-ideas-software/cross-review"`, `pkg.version === pkg-lock.version`,
|
|
74
|
+
`pkg.version === pkg-lock.packages[""].version`. Any future version
|
|
75
|
+
bump that forgets the `npm install` re-resolve fails smoke loudly
|
|
76
|
+
instead of slipping through to publish.
|
|
77
|
+
- **AUDIT-2 (MEDIUM) — model-selection wording aligned with no-fallback
|
|
78
|
+
policy.** Both `docs/model-selection.md` and `src/peers/model-selection.ts`
|
|
79
|
+
were still describing "automatic model selection", "priority list",
|
|
80
|
+
and "documented fallback" — terms from the pre-v3.7.2 multi-model era.
|
|
81
|
+
The runtime has used canonical pins (one model per peer, no auto-chain)
|
|
82
|
+
since v3.7.2 (operator directive "sem fallback é sem fallback").
|
|
83
|
+
Updated docs + the `selected/candidates/reason` messages in
|
|
84
|
+
`selectFromCandidates`, `overrideSelection`, and the no-API-key
|
|
85
|
+
path to use canonical-pin language consistently. The `confidence`
|
|
86
|
+
classification logic is unchanged.
|
|
87
|
+
- **AUDIT-3 (LOW) — SECURITY.md stub wording.** The doc said
|
|
88
|
+
`CROSS_REVIEW_STUB=1` alone is "ignored"; the code at
|
|
89
|
+
`src/peers/registry.ts:36-44` actually throws an explicit error
|
|
90
|
+
referencing the missing confirmation flag. Updated wording to
|
|
91
|
+
"rejected fail-fast" to match runtime behavior.
|
|
92
|
+
- **AUDIT-4 (LOW) — README MCP tools list completed.** The README
|
|
93
|
+
Section listing the available tools had 22 entries; the runtime
|
|
94
|
+
exposes 28. Added the 6 missing tools:
|
|
95
|
+
`session_evidence_checklist_update`,
|
|
96
|
+
`session_evidence_judge_pass`,
|
|
97
|
+
`session_evidence_judge_consensus_pass`,
|
|
98
|
+
`session_judgment_precision_report`,
|
|
99
|
+
`contest_verdict`,
|
|
100
|
+
`regenerate_caller_tokens`.
|
|
101
|
+
- **AUDIT-5 (LOW) — `docs/architecture.md` rename event.** The "Stable
|
|
102
|
+
Rename" section said the rename to `cross-review` happened at
|
|
103
|
+
`2.1.0`; the actual event is v4.0.0 on 2026-05-15 (this rename ship).
|
|
104
|
+
Rewrote the section to record v4.0.0 as the rename event and note
|
|
105
|
+
that prior names live only in dated changelog and memory.
|
|
106
|
+
- **AUDIT-6 (MEDIUM) — StepSecurity post-rename detections triaged.**
|
|
107
|
+
Two new org-level suppression rules created:
|
|
108
|
+
(a) `Source-Code-Overwritten` for `*/dist/*` in
|
|
109
|
+
`.github/workflows/publish.yml` under the `Pre-publish gate (test +
|
|
110
|
+
metadata)` job for repo `cross-review` (the prior cross-review-v2
|
|
111
|
+
rules no longer match after rename); (b)
|
|
112
|
+
`Action-Uses-Commit-From-Non-Default-Branch` for
|
|
113
|
+
`github/codeql-action*` (GitHub's release-branch model — tagged
|
|
114
|
+
versions cut from `releases/vN` not from default branch are
|
|
115
|
+
intended/audited by GitHub Security; rejecting would force less
|
|
116
|
+
secure tag-based usage). Plus 20 existing detections (19
|
|
117
|
+
Source-Code-Overwritten dist/\* + 1 codeql-action SHA) suppressed
|
|
118
|
+
per-detection via `update_detection_status` with rule-id citations
|
|
119
|
+
in the suppress reason.
|
|
120
|
+
|
|
10
121
|
## [v04.00.01] — 2026-05-15
|
|
11
122
|
|
|
12
123
|
**Patch — close-out of post-v4.0.0 audit (eight surfaces left stale by the
|
package/README.md
CHANGED
|
@@ -193,7 +193,13 @@ these environment variables before running real sessions (example):
|
|
|
193
193
|
- `session_report`
|
|
194
194
|
- `session_check_convergence`
|
|
195
195
|
- `session_attach_evidence`
|
|
196
|
+
- `session_evidence_checklist_update`
|
|
197
|
+
- `session_evidence_judge_pass`
|
|
198
|
+
- `session_evidence_judge_consensus_pass`
|
|
199
|
+
- `session_judgment_precision_report`
|
|
200
|
+
- `contest_verdict`
|
|
196
201
|
- `escalate_to_operator`
|
|
202
|
+
- `regenerate_caller_tokens`
|
|
197
203
|
- `session_sweep`
|
|
198
204
|
- `session_finalize`
|
|
199
205
|
|
package/SECURITY.md
CHANGED
|
@@ -37,7 +37,7 @@ This repository employs:
|
|
|
37
37
|
|
|
38
38
|
- **Multi-host concurrency.** Running two MCP host instances of `cross-review` against the same `CROSS_REVIEW_DATA_DIR` is **not supported**. The per-session lock has TTL + PID-liveness fallbacks that close the common cases but leave a narrow TOCTOU window when two hosts contend for the same session. If you need multi-host operation, point each instance at a distinct `CROSS_REVIEW_DATA_DIR` (introduced in v2.0; tilde expansion since v2.4.0) or share one host across all clients.
|
|
39
39
|
- **API keys in memory.** `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `DEEPSEEK_API_KEY` are loaded into `AppConfig.api_keys` at boot. The persistence layer redacts secrets via [`redact()`](./src/security/redact.ts) before any meta.json/event log write, but the in-memory object is not opaque — do not log `config` directly. Stack traces from SDK errors are passed through `safeErrorMessage()` which redacts known key shapes.
|
|
40
|
-
- **Stub adapters.** `CROSS_REVIEW_STUB=1` alone is **
|
|
40
|
+
- **Stub adapters.** `CROSS_REVIEW_STUB=1` alone is **rejected fail-fast** since v2.4.0 — boot throws an explicit error referencing the missing confirmation flag, rather than silently demoting to real adapters. To deliberately activate stubs, set BOTH `CROSS_REVIEW_STUB=1` AND one of `NODE_ENV=test` OR `CROSS_REVIEW_STUB_CONFIRMED=1`. The double-confirmation prevents a stray dotenv variable from invalidating a cross-review used as a pre-commit gate.
|
|
41
41
|
- **Dashboard HTTP.** The dashboard binds only to `127.0.0.1`. There is no authentication or rate-limit; same-machine processes can read all session metadata, costs and report markdown. Do not expose the dashboard port over a network without an authenticating reverse proxy.
|
|
42
42
|
- **Untrusted callers.** The MCP `tools/list` schemas enforce per-field caps (`maxLength`, `pattern`) since v2.4.0 to defend against memory-exhaustion attempts via oversized `task`/`draft`/`prompt`. The trust boundary still assumes a cooperative caller — do not expose the stdio transport over a network socket without an authenticating proxy.
|
|
43
43
|
- **Untrusted peers.** Peer streaming responses are capped at 16 MiB per call (`STREAM_TEXT_MAX_BYTES` since v2.4.0). The structured `<cross_review_status>` payload is rejected as malformed when it exceeds 64 KiB before `JSON.parse` runs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-default-smoke.js","sourceRoot":"","sources":["../../scripts/runtime-default-smoke.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,+DAA+D;AAC/D,mEAAmE;AACnE,wEAAwE;AACxE,wEAAwE;AACxE,sEAAsE;AACtE,0DAA0D;AAC1D,EAAE;AACF,gBAAgB;AAChB,0EAA0E;AAC1E,0EAA0E;AAC1E,wDAAwD;AACxD,yEAAyE;AACzE,4DAA4D;AAC5D,oEAAoE;AACpE,0EAA0E;AAC1E,2BAA2B;AAC3B,uDAAuD;AACvD,EAAE;AACF,qEAAqE;AACrE,yDAAyD;AACzD,oCAAoC;AACpC,8CAA8C;AAC9C,oCAAoC;AACpC,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-default-smoke.js","sourceRoot":"","sources":["../../scripts/runtime-default-smoke.ts"],"names":[],"mappings":"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"}
|
package/dist/scripts/smoke.js
CHANGED
|
@@ -3842,26 +3842,26 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
|
|
|
3842
3842
|
assert.equal(stripPerplexityThinkingBlock(cleanJson), cleanJson, "clean JSON without <think> blocks must be returned unchanged");
|
|
3843
3843
|
// Scenario B: single `<think>` block followed by JSON — strip block,
|
|
3844
3844
|
// trim whitespace, return JSON.
|
|
3845
|
-
const singleThink =
|
|
3845
|
+
const singleThink = `<think>\nLet me reason about this carefully.\nThe evidence shows...\n</think>\n\n${cleanJson}`;
|
|
3846
3846
|
assert.equal(stripPerplexityThinkingBlock(singleThink), cleanJson, "single <think> block must be stripped; trailing JSON preserved");
|
|
3847
3847
|
// Scenario C: multiple `<think>` blocks (rare but legal) — every
|
|
3848
3848
|
// occurrence must be removed.
|
|
3849
|
-
const multipleThinks =
|
|
3849
|
+
const multipleThinks = `<think>first thought</think>\nintermediate text\n<think>second thought</think>\n${cleanJson}`;
|
|
3850
3850
|
const strippedMultiple = stripPerplexityThinkingBlock(multipleThinks);
|
|
3851
3851
|
assert.ok(!/<think/i.test(strippedMultiple), "multiple <think> blocks must all be stripped");
|
|
3852
3852
|
assert.ok(strippedMultiple.includes(cleanJson), "multi-think strip must preserve trailing JSON payload");
|
|
3853
3853
|
// Scenario D: `<think>` block spanning multiple lines with arbitrary
|
|
3854
3854
|
// whitespace and nested-looking content (no actual nesting since
|
|
3855
3855
|
// Perplexity never emits nested reasoning blocks).
|
|
3856
|
-
const multilineThink =
|
|
3856
|
+
const multilineThink = `<think>\n Line 1\n Line 2 with <b>html</b>\n Line 3\n</think>\n${cleanJson}`;
|
|
3857
3857
|
assert.equal(stripPerplexityThinkingBlock(multilineThink), cleanJson, "multi-line <think> with arbitrary indentation must strip cleanly");
|
|
3858
3858
|
// Scenario E: `<think>` with attribute-like content (`<think foo="bar">`)
|
|
3859
3859
|
// — regex must allow attribute fragment before close `>`.
|
|
3860
|
-
const attributedThink =
|
|
3860
|
+
const attributedThink = `<think foo="bar">reasoning</think>\n${cleanJson}`;
|
|
3861
3861
|
assert.equal(stripPerplexityThinkingBlock(attributedThink), cleanJson, "<think> tag with attribute fragment must still strip");
|
|
3862
3862
|
// Scenario F: empty `<think></think>` (degenerate case) — strip + trim
|
|
3863
3863
|
// still yields the trailing JSON.
|
|
3864
|
-
const emptyThink =
|
|
3864
|
+
const emptyThink = `<think></think>\n${cleanJson}`;
|
|
3865
3865
|
assert.equal(stripPerplexityThinkingBlock(emptyThink), cleanJson, "empty <think></think> must strip cleanly");
|
|
3866
3866
|
// Scenario G: only `<think>` block, no trailing JSON — output is empty
|
|
3867
3867
|
// string after trim (caller's downstream parser then triggers
|
|
@@ -6015,7 +6015,7 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
|
|
|
6015
6015
|
// 5 sites must use `.max(PEERS.length)`.
|
|
6016
6016
|
const maxPeersLen = serverSrcA.match(/\.max\(PEERS\.length\)/g) ?? [];
|
|
6017
6017
|
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*[
|
|
6018
|
+
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
6019
|
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
6020
|
// AUDIT-4.
|
|
6021
6021
|
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");
|
|
@@ -6120,6 +6120,30 @@ assert.equal(Object.hasOwn(metrics.decision_quality, "undefined"), false);
|
|
|
6120
6120
|
assert.ok(/pruneCorruptSessions\(minAgeMs: number\): \{ scanned: number; removed: number; kept: number \}/.test(storeSrcB1), "v3.7.5 / B1: store.pruneCorruptSessions must declare the {scanned, removed, kept} shape");
|
|
6121
6121
|
console.log("[smoke] session_sweep_prune_corrupt_test: PASS");
|
|
6122
6122
|
}
|
|
6123
|
+
// v4.0.2 (AUDIT-1, Codex audit close-out 2026-05-15): anti-drift check
|
|
6124
|
+
// that `package.json.version` === `package-lock.json.version` ===
|
|
6125
|
+
// `package-lock.json.packages[""].version`. The v4.0.1 ship bumped
|
|
6126
|
+
// package.json without re-running `npm install`, so the lockfile root
|
|
6127
|
+
// versions stayed at `4.0.0` while package.json said `4.0.1`. Codex's
|
|
6128
|
+
// audit caught it. This marker pins the consistency invariant so any
|
|
6129
|
+
// future bump that forgets to regenerate the lockfile fails smoke
|
|
6130
|
+
// loudly instead of slipping through to publish.
|
|
6131
|
+
{
|
|
6132
|
+
const pjPath = path.join(process.cwd(), "package.json");
|
|
6133
|
+
const plPath = path.join(process.cwd(), "package-lock.json");
|
|
6134
|
+
const pj = JSON.parse(fs.readFileSync(pjPath, "utf8"));
|
|
6135
|
+
const pl = JSON.parse(fs.readFileSync(plPath, "utf8"));
|
|
6136
|
+
const pjVer = pj.version;
|
|
6137
|
+
const plRootVer = pl.version;
|
|
6138
|
+
const plPackagesRootVer = pl.packages?.[""]?.version;
|
|
6139
|
+
assert.equal(plRootVer, pjVer, `v4.0.2 / AUDIT-1: package-lock.json root .version (${plRootVer}) must equal package.json .version (${pjVer}); run \`npm install\` after bumping the version.`);
|
|
6140
|
+
assert.equal(plPackagesRootVer, pjVer, `v4.0.2 / AUDIT-1: package-lock.json packages[""].version (${plPackagesRootVer}) must equal package.json .version (${pjVer}); run \`npm install\` after bumping the version.`);
|
|
6141
|
+
// Also verify package.json `name` matches the new canonical name; if
|
|
6142
|
+
// the rename ever needs to happen again, this fails fast.
|
|
6143
|
+
assert.equal(pj.name, "@lcv-ideas-software/cross-review", `v4.0.2 / AUDIT-1: package.json .name must be "@lcv-ideas-software/cross-review" (post-v4.0.0 rename); got "${pj.name}".`);
|
|
6144
|
+
assert.equal(pl.name, "@lcv-ideas-software/cross-review", `v4.0.2 / AUDIT-1: package-lock.json .name must be "@lcv-ideas-software/cross-review"; got "${pl.name}".`);
|
|
6145
|
+
console.log("[smoke] package_version_consistency_test: PASS");
|
|
6146
|
+
}
|
|
6123
6147
|
// v2.6.1 NOTE: smoke coverage for `peer.fallback.budget_blocked` and
|
|
6124
6148
|
// `peer.moderation_recovery.budget_blocked` is intentionally NOT
|
|
6125
6149
|
// included. These two gates use the same arithmetic shape as preflight
|