@rigour-labs/core 6.7.10 → 6.8.1-rc.1
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/dist/brief/briefing.d.ts +59 -0
- package/dist/brief/briefing.js +126 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/inference/cloud-provider.js +12 -2
- package/dist/review/backtest-init.d.ts +8 -5
- package/dist/review/backtest-init.js +27 -9
- package/dist/review/backtest-last.js +3 -1
- package/dist/review/reviewer/adapters.d.ts +4 -0
- package/dist/review/reviewer/adapters.js +23 -0
- package/dist/review/reviewer/inputs.d.ts +4 -1
- package/dist/review/reviewer/inputs.js +36 -2
- package/dist/review/reviewer/record.d.ts +5 -0
- package/dist/review/reviewer/record.js +3 -3
- package/dist/review/reviewer/verdict.d.ts +14 -0
- package/dist/review/reviewer/verdict.js +46 -6
- package/dist/review/reviewer.d.ts +2 -0
- package/dist/review/reviewer.js +24 -5
- package/dist/review-learning/repo-rules.d.ts +14 -2
- package/dist/review-learning/repo-rules.js +59 -9
- package/dist/task/thread.d.ts +47 -0
- package/dist/task/thread.js +234 -0
- package/dist/templates/universal-config.js +4 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.js +7 -0
- package/package.json +11 -8
- package/dist/context/automatic-index-cache.test.d.ts +0 -1
- package/dist/context/automatic-index-cache.test.js +0 -30
- package/dist/context/automatic-index.test.d.ts +0 -1
- package/dist/context/automatic-index.test.js +0 -45
- package/dist/context/cache-engine.test.d.ts +0 -1
- package/dist/context/cache-engine.test.js +0 -99
- package/dist/context/dependency-graph.test.d.ts +0 -1
- package/dist/context/dependency-graph.test.js +0 -13
- package/dist/context/index-status.test.d.ts +0 -1
- package/dist/context/index-status.test.js +0 -26
- package/dist/context.test.d.ts +0 -1
- package/dist/context.test.js +0 -228
- package/dist/deep/agent-review.test.d.ts +0 -1
- package/dist/deep/agent-review.test.js +0 -53
- package/dist/deep/code-context.test.d.ts +0 -1
- package/dist/deep/code-context.test.js +0 -48
- package/dist/deep/code-pass.test.d.ts +0 -1
- package/dist/deep/code-pass.test.js +0 -112
- package/dist/deep/code-review-prompt.test.d.ts +0 -1
- package/dist/deep/code-review-prompt.test.js +0 -20
- package/dist/deep/code-verifier.test.d.ts +0 -1
- package/dist/deep/code-verifier.test.js +0 -54
- package/dist/deep/diff-tests/calls.test.d.ts +0 -1
- package/dist/deep/diff-tests/calls.test.js +0 -33
- package/dist/deep/diff-tests/run.test.d.ts +0 -1
- package/dist/deep/diff-tests/run.test.js +0 -58
- package/dist/deep/fact-extractor.test.d.ts +0 -1
- package/dist/deep/fact-extractor.test.js +0 -581
- package/dist/deep/parse-findings.test.d.ts +0 -1
- package/dist/deep/parse-findings.test.js +0 -25
- package/dist/deep/pr-review.test.d.ts +0 -1
- package/dist/deep/pr-review.test.js +0 -80
- package/dist/deep/prompts.test.d.ts +0 -1
- package/dist/deep/prompts.test.js +0 -235
- package/dist/deep/reference-pack.test.d.ts +0 -1
- package/dist/deep/reference-pack.test.js +0 -46
- package/dist/deep/related-changes.test.d.ts +0 -1
- package/dist/deep/related-changes.test.js +0 -33
- package/dist/deep/review-context-export.test.d.ts +0 -1
- package/dist/deep/review-context-export.test.js +0 -32
- package/dist/deep/review-tools.test.d.ts +0 -1
- package/dist/deep/review-tools.test.js +0 -39
- package/dist/deep/risk.test.d.ts +0 -1
- package/dist/deep/risk.test.js +0 -101
- package/dist/deep/verifier.test.d.ts +0 -1
- package/dist/deep/verifier.test.js +0 -635
- package/dist/discovery.test.d.ts +0 -1
- package/dist/discovery.test.js +0 -93
- package/dist/environment.test.d.ts +0 -1
- package/dist/environment.test.js +0 -94
- package/dist/firewall/firewall.test.d.ts +0 -1
- package/dist/firewall/firewall.test.js +0 -117
- package/dist/firewall/trust-boundaries.test.d.ts +0 -1
- package/dist/firewall/trust-boundaries.test.js +0 -64
- package/dist/firewall/trusted-control.test.d.ts +0 -1
- package/dist/firewall/trusted-control.test.js +0 -188
- package/dist/gates/agent-team.test.d.ts +0 -1
- package/dist/gates/agent-team.test.js +0 -113
- package/dist/gates/ast.test.d.ts +0 -1
- package/dist/gates/ast.test.js +0 -112
- package/dist/gates/checkpoint.test.d.ts +0 -1
- package/dist/gates/checkpoint.test.js +0 -105
- package/dist/gates/content.test.d.ts +0 -1
- package/dist/gates/content.test.js +0 -73
- package/dist/gates/coverage.test.d.ts +0 -1
- package/dist/gates/coverage.test.js +0 -53
- package/dist/gates/dedupe-failures.test.d.ts +0 -1
- package/dist/gates/dedupe-failures.test.js +0 -12
- package/dist/gates/deep-analysis.test.d.ts +0 -1
- package/dist/gates/deep-analysis.test.js +0 -86
- package/dist/gates/deep-intent.test.d.ts +0 -1
- package/dist/gates/deep-intent.test.js +0 -51
- package/dist/gates/deep-timeout.test.d.ts +0 -1
- package/dist/gates/deep-timeout.test.js +0 -10
- package/dist/gates/deprecated-apis.test.d.ts +0 -1
- package/dist/gates/deprecated-apis.test.js +0 -318
- package/dist/gates/deprecated-dependencies.test.d.ts +0 -1
- package/dist/gates/deprecated-dependencies.test.js +0 -55
- package/dist/gates/frontend-secret-exposure.test.d.ts +0 -1
- package/dist/gates/frontend-secret-exposure.test.js +0 -148
- package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.js +0 -27
- package/dist/gates/hallucinated-imports/js-resolver-types.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports/js-resolver-types.test.js +0 -14
- package/dist/gates/hallucinated-imports-sveltekit.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports-sveltekit.test.js +0 -132
- package/dist/gates/hallucinated-imports.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports.test.js +0 -1206
- package/dist/gates/js-style-context.test.d.ts +0 -1
- package/dist/gates/js-style-context.test.js +0 -35
- package/dist/gates/logic-drift.test.d.ts +0 -1
- package/dist/gates/logic-drift.test.js +0 -52
- package/dist/gates/phantom-apis.test.d.ts +0 -1
- package/dist/gates/phantom-apis.test.js +0 -396
- package/dist/gates/promise-safety.test.d.ts +0 -1
- package/dist/gates/promise-safety.test.js +0 -34
- package/dist/gates/runner.test.d.ts +0 -1
- package/dist/gates/runner.test.js +0 -77
- package/dist/gates/scoped-gates.test.d.ts +0 -1
- package/dist/gates/scoped-gates.test.js +0 -52
- package/dist/gates/security-patterns-owasp.test.d.ts +0 -1
- package/dist/gates/security-patterns-owasp.test.js +0 -186
- package/dist/gates/security-patterns.test.d.ts +0 -1
- package/dist/gates/security-patterns.test.js +0 -194
- package/dist/gates/semantic-bugs.test.d.ts +0 -1
- package/dist/gates/semantic-bugs.test.js +0 -76
- package/dist/gates/side-effect-analysis.test.d.ts +0 -1
- package/dist/gates/side-effect-analysis.test.js +0 -162
- package/dist/gates/style-drift.test.d.ts +0 -1
- package/dist/gates/style-drift.test.js +0 -26
- package/dist/gates/test-quality.test.d.ts +0 -1
- package/dist/gates/test-quality.test.js +0 -325
- package/dist/gates/trusted-reviews.test.d.ts +0 -1
- package/dist/gates/trusted-reviews.test.js +0 -16
- package/dist/gates/unindexed-reads/queries.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/queries.test.js +0 -54
- package/dist/gates/unindexed-reads/schema.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/schema.test.js +0 -81
- package/dist/gates/unindexed-reads/unindexed-reads.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/unindexed-reads.test.js +0 -90
- package/dist/hooks/checker.test.d.ts +0 -1
- package/dist/hooks/checker.test.js +0 -159
- package/dist/hooks/dlp-confidence.test.d.ts +0 -1
- package/dist/hooks/dlp-confidence.test.js +0 -51
- package/dist/hooks/dlp-feedback.test.d.ts +0 -1
- package/dist/hooks/dlp-feedback.test.js +0 -131
- package/dist/hooks/input-validator.test.d.ts +0 -1
- package/dist/hooks/input-validator.test.js +0 -329
- package/dist/hooks/templates.test.d.ts +0 -1
- package/dist/hooks/templates.test.js +0 -27
- package/dist/inference/brain-placeholder.test.d.ts +0 -1
- package/dist/inference/brain-placeholder.test.js +0 -28
- package/dist/inference/cloud-provider.test.d.ts +0 -1
- package/dist/inference/cloud-provider.test.js +0 -139
- package/dist/inference/executable.test.d.ts +0 -1
- package/dist/inference/executable.test.js +0 -41
- package/dist/inference/http-download.test.d.ts +0 -1
- package/dist/inference/http-download.test.js +0 -109
- package/dist/inference/llama-engine-checksum.test.d.ts +0 -1
- package/dist/inference/llama-engine-checksum.test.js +0 -27
- package/dist/inference/llama-engine.test.d.ts +0 -1
- package/dist/inference/llama-engine.test.js +0 -51
- package/dist/inference/llama-process.test.d.ts +0 -1
- package/dist/inference/llama-process.test.js +0 -61
- package/dist/inference/local-model.test.d.ts +0 -1
- package/dist/inference/local-model.test.js +0 -23
- package/dist/inference/model-download.test.d.ts +0 -1
- package/dist/inference/model-download.test.js +0 -125
- package/dist/inference/model-manager.test.d.ts +0 -1
- package/dist/inference/model-manager.test.js +0 -24
- package/dist/inference/types.test.d.ts +0 -1
- package/dist/inference/types.test.js +0 -19
- package/dist/memory/recall.test.d.ts +0 -1
- package/dist/memory/recall.test.js +0 -36
- package/dist/pattern-index/indexer.test.d.ts +0 -6
- package/dist/pattern-index/indexer.test.js +0 -197
- package/dist/pattern-index/matcher.test.d.ts +0 -6
- package/dist/pattern-index/matcher.test.js +0 -238
- package/dist/pattern-index/pattern-reuse.test.d.ts +0 -1
- package/dist/pattern-index/pattern-reuse.test.js +0 -76
- package/dist/pattern-index/semantic-runtime.test.d.ts +0 -1
- package/dist/pattern-index/semantic-runtime.test.js +0 -31
- package/dist/pattern-index/staleness.test.d.ts +0 -6
- package/dist/pattern-index/staleness.test.js +0 -211
- package/dist/review/agent-fixes.test.d.ts +0 -1
- package/dist/review/agent-fixes.test.js +0 -32
- package/dist/review/backtest-init.test.d.ts +0 -1
- package/dist/review/backtest-init.test.js +0 -98
- package/dist/review/backtest-judges.test.d.ts +0 -1
- package/dist/review/backtest-judges.test.js +0 -32
- package/dist/review/backtest-last.test.d.ts +0 -1
- package/dist/review/backtest-last.test.js +0 -109
- package/dist/review/backtest.test.d.ts +0 -1
- package/dist/review/backtest.test.js +0 -183
- package/dist/review/baseline.test.d.ts +0 -1
- package/dist/review/baseline.test.js +0 -22
- package/dist/review/branch-checks.test.d.ts +0 -1
- package/dist/review/branch-checks.test.js +0 -48
- package/dist/review/check-outcomes.test.d.ts +0 -1
- package/dist/review/check-outcomes.test.js +0 -56
- package/dist/review/code-patterns.test.d.ts +0 -1
- package/dist/review/code-patterns.test.js +0 -142
- package/dist/review/dead-code.test.d.ts +0 -1
- package/dist/review/dead-code.test.js +0 -154
- package/dist/review/deep-runs.test.d.ts +0 -1
- package/dist/review/deep-runs.test.js +0 -23
- package/dist/review/effectiveness.test.d.ts +0 -1
- package/dist/review/effectiveness.test.js +0 -42
- package/dist/review/fix-scope.test.d.ts +0 -1
- package/dist/review/fix-scope.test.js +0 -104
- package/dist/review/generated-files.test.d.ts +0 -1
- package/dist/review/generated-files.test.js +0 -25
- package/dist/review/migration-order.test.d.ts +0 -1
- package/dist/review/migration-order.test.js +0 -62
- package/dist/review/quiet.test.d.ts +0 -1
- package/dist/review/quiet.test.js +0 -38
- package/dist/review/receipt.test.d.ts +0 -1
- package/dist/review/receipt.test.js +0 -60
- package/dist/review/review-task.test.d.ts +0 -1
- package/dist/review/review-task.test.js +0 -89
- package/dist/review/review.test.d.ts +0 -1
- package/dist/review/review.test.js +0 -203
- package/dist/review/reviewer/adapters.test.d.ts +0 -1
- package/dist/review/reviewer/adapters.test.js +0 -60
- package/dist/review/reviewer/api-judge.test.d.ts +0 -1
- package/dist/review/reviewer/api-judge.test.js +0 -108
- package/dist/review/reviewer/background.test.d.ts +0 -1
- package/dist/review/reviewer/background.test.js +0 -118
- package/dist/review/reviewer/context.test.d.ts +0 -1
- package/dist/review/reviewer/context.test.js +0 -45
- package/dist/review/reviewer/exec.test.d.ts +0 -1
- package/dist/review/reviewer/exec.test.js +0 -59
- package/dist/review/reviewer/inputs.test.d.ts +0 -1
- package/dist/review/reviewer/inputs.test.js +0 -26
- package/dist/review/reviewer/panel.test.d.ts +0 -1
- package/dist/review/reviewer/panel.test.js +0 -110
- package/dist/review/reviewer/record.test.d.ts +0 -1
- package/dist/review/reviewer/record.test.js +0 -32
- package/dist/review/reviewer/rule-writer.test.d.ts +0 -1
- package/dist/review/reviewer/rule-writer.test.js +0 -52
- package/dist/review/reviewer/settings.test.d.ts +0 -1
- package/dist/review/reviewer/settings.test.js +0 -57
- package/dist/review/reviewer/usage.test.d.ts +0 -1
- package/dist/review/reviewer/usage.test.js +0 -14
- package/dist/review/reviewer.test.d.ts +0 -1
- package/dist/review/reviewer.test.js +0 -705
- package/dist/review/stories.test.d.ts +0 -1
- package/dist/review/stories.test.js +0 -58
- package/dist/review/toolchain.test.d.ts +0 -1
- package/dist/review/toolchain.test.js +0 -108
- package/dist/review/typed/redundancy.test.d.ts +0 -1
- package/dist/review/typed/redundancy.test.js +0 -315
- package/dist/review/typed/schema-nullability.test.d.ts +0 -1
- package/dist/review/typed/schema-nullability.test.js +0 -63
- package/dist/review-learning/human-edits.test.d.ts +0 -1
- package/dist/review-learning/human-edits.test.js +0 -47
- package/dist/review-learning/repo-rules.test.d.ts +0 -1
- package/dist/review-learning/repo-rules.test.js +0 -55
- package/dist/review-learning/review-learning.test.d.ts +0 -1
- package/dist/review-learning/review-learning.test.js +0 -249
- package/dist/safety.test.d.ts +0 -1
- package/dist/safety.test.js +0 -42
- package/dist/semantic/benchmark.test.d.ts +0 -1
- package/dist/semantic/benchmark.test.js +0 -22
- package/dist/semantic/intent/intent.test.d.ts +0 -1
- package/dist/semantic/intent/intent.test.js +0 -46
- package/dist/semantic/learn/learn.test.d.ts +0 -1
- package/dist/semantic/learn/learn.test.js +0 -67
- package/dist/semantic/origins.test.d.ts +0 -1
- package/dist/semantic/origins.test.js +0 -77
- package/dist/semantic/project-facts.test.d.ts +0 -1
- package/dist/semantic/project-facts.test.js +0 -45
- package/dist/semantic/sites/call-sites.test.d.ts +0 -1
- package/dist/semantic/sites/call-sites.test.js +0 -46
- package/dist/services/adaptive-thresholds.test.d.ts +0 -1
- package/dist/services/adaptive-thresholds.test.js +0 -53
- package/dist/services/agent-history.test.d.ts +0 -1
- package/dist/services/agent-history.test.js +0 -69
- package/dist/services/context-scope-summary.test.d.ts +0 -1
- package/dist/services/context-scope-summary.test.js +0 -17
- package/dist/services/context-telemetry-service.test.d.ts +0 -1
- package/dist/services/context-telemetry-service.test.js +0 -182
- package/dist/services/cursor-usage-sync.test.d.ts +0 -1
- package/dist/services/cursor-usage-sync.test.js +0 -173
- package/dist/services/engineering-knowledge-graph.test.d.ts +0 -1
- package/dist/services/engineering-knowledge-graph.test.js +0 -76
- package/dist/services/model-pricing.test.d.ts +0 -1
- package/dist/services/model-pricing.test.js +0 -44
- package/dist/services/observed-savings.test.d.ts +0 -1
- package/dist/services/observed-savings.test.js +0 -37
- package/dist/services/score-history.test.d.ts +0 -1
- package/dist/services/score-history.test.js +0 -61
- package/dist/smoke.test.d.ts +0 -1
- package/dist/smoke.test.js +0 -17
- package/dist/storage/cache-cleanup.test.d.ts +0 -1
- package/dist/storage/cache-cleanup.test.js +0 -54
- package/dist/storage/context-telemetry.test.d.ts +0 -1
- package/dist/storage/context-telemetry.test.js +0 -80
- package/dist/storage/db.test.d.ts +0 -1
- package/dist/storage/db.test.js +0 -46
- package/dist/storage/fix-lessons.test.d.ts +0 -1
- package/dist/storage/fix-lessons.test.js +0 -61
- package/dist/storage/lessons.test.d.ts +0 -1
- package/dist/storage/lessons.test.js +0 -81
- package/dist/storage/local-encryption.test.d.ts +0 -1
- package/dist/storage/local-encryption.test.js +0 -34
- package/dist/storage/local-memory.test.d.ts +0 -1
- package/dist/storage/local-memory.test.js +0 -55
- package/dist/storage/share-memory.test.d.ts +0 -1
- package/dist/storage/share-memory.test.js +0 -34
- package/dist/storage/team-diagnostics.test.d.ts +0 -1
- package/dist/storage/team-diagnostics.test.js +0 -51
- package/dist/storage/team-scope.test.d.ts +0 -1
- package/dist/storage/team-scope.test.js +0 -22
- package/dist/storage/team-store.test.d.ts +0 -1
- package/dist/storage/team-store.test.js +0 -11
- package/dist/storage/team-sync-scope.test.d.ts +0 -1
- package/dist/storage/team-sync-scope.test.js +0 -100
- package/dist/storage/team-vector-store.test.d.ts +0 -1
- package/dist/storage/team-vector-store.test.js +0 -56
- package/dist/storage/telemetry-scope.test.d.ts +0 -1
- package/dist/storage/telemetry-scope.test.js +0 -38
- package/dist/telemetry/telemetry.test.d.ts +0 -1
- package/dist/telemetry/telemetry.test.js +0 -64
- package/dist/types/index.test.d.ts +0 -1
- package/dist/types/index.test.js +0 -33
- package/dist/utils/command-line.test.d.ts +0 -1
- package/dist/utils/command-line.test.js +0 -8
- package/dist/utils/diff-removed.test.d.ts +0 -1
- package/dist/utils/diff-removed.test.js +0 -29
- package/dist/utils/diff.test.d.ts +0 -1
- package/dist/utils/diff.test.js +0 -38
- package/dist/utils/glob-paths.test.d.ts +0 -1
- package/dist/utils/glob-paths.test.js +0 -40
- package/dist/utils/profile.test.d.ts +0 -1
- package/dist/utils/profile.test.js +0 -68
- package/dist/utils/scanner.test.d.ts +0 -1
- package/dist/utils/scanner.test.js +0 -48
- package/dist/utils/scope.test.d.ts +0 -1
- package/dist/utils/scope.test.js +0 -38
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { humanReviews } from './inputs.js';
|
|
3
|
-
const pr = { number: 7, state: 'open', draft: false, author: 'author', body: '' };
|
|
4
|
-
const reviews = [
|
|
5
|
-
{ id: 1, user: { login: 'senior', type: 'User' }, body: 'Keep a separate case for a visitor with no account.', state: 'CHANGES_REQUESTED', submitted_at: '2026-09-25T18:09:11Z', commit_id: 'a0a0a0a0a0aa' },
|
|
6
|
-
{ id: 2, user: { login: 'senior', type: 'User' }, body: '', state: 'APPROVED', submitted_at: '2026-09-28T15:12:53Z', commit_id: 'b1b1b1b1b1bb' },
|
|
7
|
-
{ id: 3, user: { login: 'author', type: 'User' }, body: '', state: 'APPROVED', submitted_at: '2026-09-28T16:00:00Z', commit_id: 'b1b1b1b1b1bb' },
|
|
8
|
-
];
|
|
9
|
-
const gh = async (args) => ({ exitCode: 0, stdout: JSON.stringify(args[1].endsWith('/reviews') ? reviews : []), stderr: '' });
|
|
10
|
-
describe('the human reviews a judge reads', () => {
|
|
11
|
-
it('keeps an approval without words: it settles the points its author raised before it', async () => {
|
|
12
|
-
const { reviews: read } = await humanReviews(gh, pr, undefined);
|
|
13
|
-
expect(read.markdown).toContain('## Review by senior, 2026-09-25T18:09:11Z, CHANGES_REQUESTED (on a0a0a0a0a)');
|
|
14
|
-
expect(read.markdown).toContain('## Review by senior, 2026-09-28T15:12:53Z, APPROVED (on b1b1b1b1b)\n\n(approved without comment: every point senior raised before this is settled)');
|
|
15
|
-
expect(read.approvals).toEqual([{ login: 'senior', at: '2026-09-28T15:12:53Z', commit: 'b1b1b1b1b1bb' }]); // the author's own approval is not a review
|
|
16
|
-
expect(read.count).toBe(1); // reviews with points: the judge must answer those, and an approval has none
|
|
17
|
-
expect(read.label).toBe('senior, 2026-09-28T15:12:53Z (1 review)');
|
|
18
|
-
});
|
|
19
|
-
it('hides the approval like any review from the reviewed moment on', async () => {
|
|
20
|
-
const { reviews: read } = await humanReviews(gh, pr, '2026-09-28T15:12:53Z');
|
|
21
|
-
expect(read.approvals).toEqual([]);
|
|
22
|
-
expect(read.markdown).not.toContain('APPROVED');
|
|
23
|
-
const later = await humanReviews(gh, pr, '2026-09-28T15:12:54.000Z');
|
|
24
|
-
expect(later.reviews.approvals).toHaveLength(1);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { clusterItems, similarity, statusOf } from './consensus.js';
|
|
3
|
-
import { applyPanel, parseAnswers, runPanel } from './panel.js';
|
|
4
|
-
let n = 0;
|
|
5
|
-
const item = (file, line, cls, issue, consequence = 'wrong rows') => ({ id: `i${++n}`, kind: 'finding', class: cls, file, line, issue, consequence });
|
|
6
|
-
/** Evidence counts only as a real file:line: here, any src/q.ts line. */
|
|
7
|
-
const evidenced = (text) => /src\/q\.ts:\d+/.test(text);
|
|
8
|
-
const members = (judges, items) => clusterItems(judges, items).map(c => c.members.map(m => `${m.judge}:${m.item.issue}`));
|
|
9
|
-
describe('matching findings across judges', () => {
|
|
10
|
-
it('needs the same file, and shared wording unless it is the same line and class', () => {
|
|
11
|
-
const a = item('src/a.ts', 10, 'correctness', 'window not bounded at both ends', 'scans every past event');
|
|
12
|
-
expect(similarity(a, { ...a, file: 'src/b.ts' })).toBe(0);
|
|
13
|
-
expect(similarity(a, item('src/a.ts', 11, 'correctness', 'dedupe key changes when the user edits', 'duplicate emails go out'))).toBe(0);
|
|
14
|
-
expect(similarity(a, item('src/a.ts', 10, 'correctness', 'something else entirely', 'duplicate emails go out'))).toBeGreaterThanOrEqual(0.5);
|
|
15
|
-
});
|
|
16
|
-
it('keeps two different findings on adjacent lines apart, even listed in a misleading order (first-fit by line would cross them)', () => {
|
|
17
|
-
const judgeA = [item('src/q.ts', 10, 'production-cost', 'time window not bounded at both ends'), item('src/q.ts', 11, 'correctness', 'dedupe key changes when the user edits')];
|
|
18
|
-
const judgeB = [item('src/q.ts', 10, 'correctness', 'dedupe key built from updated_at changes when the user edits'), item('src/q.ts', 11, 'production-cost', 'read window has no upper bound at either end')];
|
|
19
|
-
expect(members(['a', 'b'], [judgeA, judgeB])).toEqual([
|
|
20
|
-
['a:time window not bounded at both ends', 'b:read window has no upper bound at either end'],
|
|
21
|
-
['a:dedupe key changes when the user edits', 'b:dedupe key built from updated_at changes when the user edits'],
|
|
22
|
-
]);
|
|
23
|
-
});
|
|
24
|
-
it('matches one bug two judges cite at different lines', () => {
|
|
25
|
-
const judgeA = [item('src/q.ts', 10, 'correctness', 'lock is checked after the first read of rows')];
|
|
26
|
-
const judgeB = [item('src/q.ts', 40, 'correctness', 'the first read of rows happens before the lock is checked')];
|
|
27
|
-
expect(members(['a', 'b'], [judgeA, judgeB])).toHaveLength(1);
|
|
28
|
-
});
|
|
29
|
-
it('puts what one judge split under the one item another judge wrote', () => {
|
|
30
|
-
const judgeA = [item('src/q.ts', 10, 'production-cost', 'both reads, rows and cards, are unbounded')];
|
|
31
|
-
const judgeB = [item('src/q.ts', 10, 'production-cost', 'rows read is unbounded'), item('src/q.ts', 14, 'production-cost', 'cards read is unbounded')];
|
|
32
|
-
expect(members(['a', 'b'], [judgeA, judgeB])).toEqual([['a:both reads, rows and cards, are unbounded', 'b:rows read is unbounded', 'b:cards read is unbounded']]);
|
|
33
|
-
});
|
|
34
|
-
it('never merges one judge\'s own two findings, however close', () => {
|
|
35
|
-
const judgeA = [item('src/x.ts', 10, 'correctness', 'session.user may be null when the token expired'), item('src/x.ts', 12, 'correctness', 'session.expires compared as a string, not a date')];
|
|
36
|
-
expect(members(['a', 'b'], [judgeA, []])).toEqual([['a:session.user may be null when the token expired'], ['a:session.expires compared as a string, not a date']]);
|
|
37
|
-
});
|
|
38
|
-
it('groups three judges, and is the same every run', () => {
|
|
39
|
-
const items = [
|
|
40
|
-
[item('src/q.ts', 10, 'correctness', 'lock checked after the read')],
|
|
41
|
-
[item('src/q.ts', 11, 'correctness', 'the read happens before the lock is checked'), item('src/r.ts', 3, 'dead-code', 'export unused')],
|
|
42
|
-
[item('src/q.ts', 10, 'correctness', 'lock checked only after the read')],
|
|
43
|
-
];
|
|
44
|
-
const once = members(['a', 'b', 'c'], items);
|
|
45
|
-
expect(once).toEqual(members(['a', 'b', 'c'], items));
|
|
46
|
-
expect(once.map(c => c.length)).toEqual([3, 1]);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe('deciding a finding', () => {
|
|
50
|
-
it('confirms on a strict majority, drops on evidence against with nobody unsure, else disputes', () => {
|
|
51
|
-
expect(statusOf({ a: 'raised', b: 'raised' }, 2)).toBe('confirmed');
|
|
52
|
-
expect(statusOf({ a: 'raised', b: 'confirm' }, 2)).toBe('confirmed');
|
|
53
|
-
expect(statusOf({ a: 'raised', b: 'refute' }, 2)).toBe('dropped');
|
|
54
|
-
expect(statusOf({ a: 'raised', b: 'unsure' }, 2)).toBe('disputed');
|
|
55
|
-
expect(statusOf({ a: 'raised', b: 'raised' }, 3)).toBe('confirmed');
|
|
56
|
-
expect(statusOf({ a: 'raised', b: 'confirm', c: 'refute' }, 3)).toBe('confirmed');
|
|
57
|
-
expect(statusOf({ a: 'raised', b: 'refute', c: 'refute' }, 3)).toBe('dropped');
|
|
58
|
-
expect(statusOf({ a: 'raised', b: 'refute', c: 'unsure' }, 3)).toBe('disputed');
|
|
59
|
-
});
|
|
60
|
-
it('asks each judge once about what it did not raise, needs file:line evidence, and caps what it asks', async () => {
|
|
61
|
-
const asked = {};
|
|
62
|
-
const lone = item('src/q.ts', 10, 'correctness', 'lock checked after the read');
|
|
63
|
-
const refuted = item('src/q.ts', 30, 'correctness', 'the cursor skips the last page');
|
|
64
|
-
const bare = item('src/q.ts', 50, 'correctness', 'an index is missing on created_at');
|
|
65
|
-
const overCap = item('src/z.ts', 1, 'correctness', 'retries forever on 429');
|
|
66
|
-
const answers = { b: [{ id: lone.id, call: 'confirm', evidence: 'src/q.ts:12 reads before lock()' }, { id: refuted.id, call: 'refute', evidence: 'src/q.ts:33 the loop runs once more' }, { id: bare.id, call: 'confirm' }] };
|
|
67
|
-
const decided = await runPanel({
|
|
68
|
-
judges: ['a', 'b'], items: [[lone, refuted, bare, overCap], []], previousDisputed: [], touched: new Set(), maxItems: 3, evidenced,
|
|
69
|
-
ask: async (judge, items) => { asked[judge] = items.map(i => i.issue); return answers[judge] ?? []; },
|
|
70
|
-
});
|
|
71
|
-
expect(asked).toEqual({ b: ['lock checked after the read', 'the cursor skips the last page', 'an index is missing on created_at'] });
|
|
72
|
-
const status = Object.fromEntries(decided.map(d => [d.item.issue, d.status]));
|
|
73
|
-
expect(status).toEqual({ 'lock checked after the read': 'confirmed', 'the cursor skips the last page': 'dropped', 'an index is missing on created_at': 'disputed', 'retries forever on 429': 'disputed' });
|
|
74
|
-
expect(decided.find(d => d.item.id === overCap.id)?.note).toContain('panel_max_items');
|
|
75
|
-
expect(decided.find(d => d.item.id === lone.id)).toMatchObject({ judges: ['a'], calls: { a: 'raised', b: 'confirm' }, cross: [{ by: 'b', call: 'confirm', evidence: 'src/q.ts:12 reads before lock()' }] });
|
|
76
|
-
});
|
|
77
|
-
it('does not ask again about a finding disputed before on a file nothing has touched since', async () => {
|
|
78
|
-
const again = item('src/q.ts', 10, 'correctness', 'lock checked after the read');
|
|
79
|
-
const asked = [];
|
|
80
|
-
const ask = async (_judge, items) => { asked.push(...items.map(i => i.id)); return []; };
|
|
81
|
-
const untouched = await runPanel({ judges: ['a', 'b'], items: [[again], []], previousDisputed: [{ ...again, id: 'old' }], touched: new Set(), maxItems: 20, evidenced, ask });
|
|
82
|
-
expect(untouched[0]).toMatchObject({ status: 'disputed', note: 'disputed before; its file is unchanged since' });
|
|
83
|
-
expect(asked).toEqual([]);
|
|
84
|
-
await runPanel({ judges: ['a', 'b'], items: [[again], []], previousDisputed: [{ ...again, id: 'old' }], touched: new Set(['src/q.ts']), maxItems: 20, evidenced, ask });
|
|
85
|
-
expect(asked).toEqual([again.id]);
|
|
86
|
-
});
|
|
87
|
-
it('a judge that fails to answer leaves the findings disputed, never confirmed', async () => {
|
|
88
|
-
const lone = item('src/q.ts', 10, 'correctness', 'lock checked after the read');
|
|
89
|
-
const decided = await runPanel({ judges: ['a', 'b'], items: [[lone], []], previousDisputed: [], touched: new Set(), maxItems: 20, evidenced, ask: async () => { throw new Error('timed out'); } });
|
|
90
|
-
expect(decided[0]).toMatchObject({ status: 'disputed', calls: { a: 'raised', b: 'unsure' } });
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
describe('the panel in the accounting', () => {
|
|
94
|
-
it('blocks only on what it confirmed, and lists the rest', () => {
|
|
95
|
-
const confirmed = item('src/q.ts', 10, 'correctness', 'lock checked after the read');
|
|
96
|
-
const disputed = item('src/q.ts', 30, 'correctness', 'cursor skips a page');
|
|
97
|
-
const human = { ...item('', 0, 'prior point', 'a human point'), kind: 'prior' };
|
|
98
|
-
const result = applyPanel({ open: [confirmed, disputed, human] }, new Set([confirmed.id, disputed.id]), [
|
|
99
|
-
{ item: confirmed, judges: ['a', 'b'], calls: { a: 'raised', b: 'raised' }, status: 'confirmed' },
|
|
100
|
-
{ item: disputed, judges: ['a'], calls: { a: 'raised', b: 'unsure' }, status: 'disputed' },
|
|
101
|
-
]);
|
|
102
|
-
expect(result.open.map(i => i.issue)).toEqual(['a human point', 'lock checked after the read']);
|
|
103
|
-
expect(result.open[1].reviewer).toBe('a+b');
|
|
104
|
-
expect(result.disputed.map(i => i.issue)).toEqual(['cursor skips a page']);
|
|
105
|
-
});
|
|
106
|
-
it('reads the answers out of a reply with prose around them, and nothing from a reply without them', () => {
|
|
107
|
-
expect(parseAnswers('Checked.\n```json\n{"answers":[{"id":"x","call":"refute","evidence":"a.ts:3"},{"id":"y","call":"maybe"}]}\n```')).toEqual([{ id: 'x', call: 'refute', evidence: 'a.ts:3' }]);
|
|
108
|
-
expect(parseAnswers('I could not decide.')).toEqual([]);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { buildRecord, recordIntact, recordLines } from './record.js';
|
|
3
|
-
const item = (over) => ({ id: 'i1', kind: 'finding', class: 'correctness', file: 'src/job.ts', line: 2, issue: 'returns before the lock', quote: 'return 1;', ...over });
|
|
4
|
-
const input = () => ({
|
|
5
|
-
head: 'abcdef0123456789', base: '0123456789abcdef', scope: 'full',
|
|
6
|
-
verdict: { prior_points: [], redundant: [], reads: [], scans: [], merge_impact: [], findings: [], carried: [], resolved_previous: [],
|
|
7
|
-
rules: [{ id: 'r1', status: 'followed' }, { id: 'r2', status: 'broken' }, { id: 'r3', status: 'not-applicable' }], lessons: [{ lesson: 'bound the window', applies: true }, { lesson: 'keyset', applies: false }] },
|
|
8
|
-
accounted: { open: [item({}), item({ id: 'p1', kind: 'prior', class: 'prior point', issue: 'take the lock' })], advisory: [item({ id: 's1', issue: 'could log the id' })], unverified: [item({ id: 'u1' })], notes: [item({ id: 'n1' }), item({ id: 'n2' })], resolved: [{ item: item({ id: 'old' }), evidence: 'fixed' }], answerInReply: [], disputed: [], dismissed: [item({ id: 'd1' })] },
|
|
9
|
-
judges: [{ reviewer: 'claude', version: '2.1.0', model: 'opus', cost_usd: 1.25, turns: 19 }],
|
|
10
|
-
lessonsServed: 4, humanReviews: 2, at: '2026-10-08T00:00:00Z',
|
|
11
|
-
});
|
|
12
|
-
describe('the review record', () => {
|
|
13
|
-
it('counts what was verified, what was reported and what people decided, and hashes it', () => {
|
|
14
|
-
const record = buildRecord(input());
|
|
15
|
-
expect(record.verified).toMatchObject({ rules: { served: 3, followed: 1, broken: 1, not_applicable: 1 }, lessons: { served: 4, applied: 1 }, prior_points: { open: 1, resolved: 1, answer_in_reply: 0 }, unverified: 1, notes: 2, disputed: 0 });
|
|
16
|
-
expect(record.verified.blocking).toHaveLength(2);
|
|
17
|
-
expect(record.verified.should_fix).toHaveLength(1);
|
|
18
|
-
expect(record).toMatchObject({ reported: { human_reviews: 2 }, people: { dismissed: 1 }, judges: [{ reviewer: 'claude', turns: 19 }] });
|
|
19
|
-
expect(record.integrity).toMatch(/^[0-9a-f]{64}$/);
|
|
20
|
-
expect(buildRecord(input()).integrity).toBe(record.integrity); // the same review hashes the same
|
|
21
|
-
expect(recordIntact(record)).toBe(true);
|
|
22
|
-
expect(recordIntact({ ...record, verified: { ...record.verified, blocking: [] } })).toBe(false); // a block removed after the fact shows
|
|
23
|
-
});
|
|
24
|
-
it('renders for a pull request: blocks in full, a few should-fixes, the counts, the judges and the hash', () => {
|
|
25
|
-
const lines = recordLines(buildRecord(input()), 1);
|
|
26
|
-
expect(lines[0]).toBe('**Review record** · 2 blocking · 1 should-fix · rules 1 followed, 1 broken, 1 not applicable of 3 · lessons 1 of 4 apply · prior points 1 open, 1 resolved');
|
|
27
|
-
expect(lines).toContain('- **Blocking** `src/job.ts:2` returns before the lock');
|
|
28
|
-
expect(lines).toContain('- Should fix: `src/job.ts:2` could log the id');
|
|
29
|
-
expect(lines.at(-2)).toBe('Also seen, never blocking: 2 working notes, 1 unverified, 1 dismissed.');
|
|
30
|
-
expect(lines.at(-1)).toMatch(/^Judged by claude 2\.1\.0 \(opus\) \$1\.25 on `abcdef012` against `012345678` \(full\); 2 human review\(s\) seen\. Integrity `[0-9a-f]{16}`\.$/);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'child_process';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
6
|
-
import { ConfigSchema } from '../../types/index.js';
|
|
7
|
-
import { ruleWriterFor } from './rule-writer.js';
|
|
8
|
-
let repo;
|
|
9
|
-
let bin;
|
|
10
|
-
const originalPath = process.env.PATH;
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
repo = fs.mkdtempSync(path.join(os.tmpdir(), 'rule-writer-'));
|
|
13
|
-
execFileSync('git', ['-C', repo, 'init', '-q', '-b', 'main']);
|
|
14
|
-
bin = fs.mkdtempSync(path.join(os.tmpdir(), 'rule-writer-bin-'));
|
|
15
|
-
fs.writeFileSync(path.join(bin, process.platform === 'win32' ? 'claude.cmd' : 'claude'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
16
|
-
process.env.PATH = [bin, originalPath ?? ''].join(path.delimiter);
|
|
17
|
-
});
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
process.env.PATH = originalPath;
|
|
20
|
-
for (const dir of [repo, bin])
|
|
21
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
22
|
-
});
|
|
23
|
-
/** Answers git for real, the fake claude with a priced reply, and any other copy of claude as not installed. */
|
|
24
|
-
function fake(calls) {
|
|
25
|
-
return async (command, args, options) => {
|
|
26
|
-
if (command === 'git')
|
|
27
|
-
return { exitCode: 0, stdout: execFileSync('git', args, { cwd: options.cwd, encoding: 'utf8' }), stderr: '' };
|
|
28
|
-
if (!command.startsWith(bin))
|
|
29
|
-
return { exitCode: 127, stdout: '', stderr: 'not found' };
|
|
30
|
-
if (args[0] === '--version')
|
|
31
|
-
return { exitCode: 0, stdout: '2.1.0 (Claude Code)\n', stderr: '' };
|
|
32
|
-
calls.push({ args, unset: options.unset });
|
|
33
|
-
return { exitCode: 0, stdout: JSON.stringify({ result: '{"rules":[]}', total_cost_usd: 0.6 }), stderr: '' };
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
describe('the rule writer', () => {
|
|
37
|
-
it('runs the team reviewer CLI isolated like a judge, counts its cost, and stops at the daily cap', async () => {
|
|
38
|
-
const calls = [];
|
|
39
|
-
const config = ConfigSchema.parse({ version: 1, review: { reviewer: { reviewers: ['claude'], max_usd_per_day: 1 } } });
|
|
40
|
-
const write = (await ruleWriterFor(repo, config, fake(calls)));
|
|
41
|
-
expect(await write('first')).toBe('{"rules":[]}');
|
|
42
|
-
expect(await write('second')).toBe('{"rules":[]}'); // $0.60 spent: under the $1 cap
|
|
43
|
-
expect(await write('third')).toBeUndefined(); // $1.20: the cap is reached, no call made
|
|
44
|
-
expect(calls).toHaveLength(2);
|
|
45
|
-
expect(calls[0].args).toEqual(expect.arrayContaining(['--strict-mcp-config', '--disallowedTools', 'Edit', 'Write']));
|
|
46
|
-
expect(calls[0].unset).toContain('RIGOUR_API_KEY');
|
|
47
|
-
});
|
|
48
|
-
it('is not available when no reviewer CLI is installed', async () => {
|
|
49
|
-
const config = ConfigSchema.parse({ version: 1, review: { reviewer: { reviewers: ['codex'] } } });
|
|
50
|
-
expect(await ruleWriterFor(repo, config, fake([]))).toBeUndefined();
|
|
51
|
-
});
|
|
52
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { ConfigSchema } from '../../types/index.js';
|
|
3
|
-
import { resolveReviewer } from './settings.js';
|
|
4
|
-
const team = (reviewer = {}) => ConfigSchema.parse({ version: 1, review: { reviewer } });
|
|
5
|
-
describe('the reviewer settings a run uses', () => {
|
|
6
|
-
it('is one judge, no panel, until someone asks', () => {
|
|
7
|
-
expect(resolveReviewer(team(), {}, undefined, {})).toMatchObject({ enabled: false, mode: 'single', panel: false, judges: 2, escalate: 'always', source: { mode: 'team', panel: 'team' }, refused: [] });
|
|
8
|
-
});
|
|
9
|
-
it('takes the nearest layer: a flag, then the environment, then the person, then the team', () => {
|
|
10
|
-
const user = { enabled: true, mode: 'full', panel: true, judges: 3 };
|
|
11
|
-
expect(resolveReviewer(team({ mode: 'single' }), {}, user, {})).toMatchObject({ enabled: true, mode: 'full', panel: true, judges: 3, source: { mode: 'user', panel: 'user' } });
|
|
12
|
-
expect(resolveReviewer(team(), {}, user, { RIGOUR_REVIEWER_PANEL: 'off', RIGOUR_REVIEWER_MODE: 'cross' })).toMatchObject({ mode: 'cross', panel: false, source: { mode: 'env', panel: 'env' } });
|
|
13
|
-
expect(resolveReviewer(team(), { mode: 'single', panel: false }, user, { RIGOUR_REVIEWER_MODE: 'full' })).toMatchObject({ mode: 'single', source: { mode: 'flag' } });
|
|
14
|
-
});
|
|
15
|
-
it('turns a panel into a full review, since it needs two vendors', () => {
|
|
16
|
-
expect(resolveReviewer(team({ mode: 'single' }), { panel: true }, undefined, {})).toMatchObject({ mode: 'full', panel: true, source: { mode: 'flag', panel: 'flag' } });
|
|
17
|
-
});
|
|
18
|
-
it('keeps the team floor, and says what it refused', () => {
|
|
19
|
-
const floor = team({ mode: 'full', panel: 'required', mode_required: true });
|
|
20
|
-
const resolved = resolveReviewer(floor, { mode: 'single', panel: false }, { escalate: 'risk' }, {});
|
|
21
|
-
expect(resolved).toMatchObject({ mode: 'full', panel: true, escalate: 'always', required: { mode: true, panel: true } });
|
|
22
|
-
expect(resolved.refused).toEqual([
|
|
23
|
-
'panel off (flag) refused: rigour.yml sets review.reviewer.panel: required',
|
|
24
|
-
'mode single (flag) refused: rigour.yml sets review.reviewer.mode_required with mode full',
|
|
25
|
-
'escalate risk (user) refused: rigour.yml requires every review to be full',
|
|
26
|
-
]);
|
|
27
|
-
});
|
|
28
|
-
it('reports an environment value it does not understand instead of ignoring it quietly', () => {
|
|
29
|
-
expect(resolveReviewer(team(), {}, undefined, { RIGOUR_REVIEWER_MODE: 'triple', RIGOUR_REVIEWER_PANEL: 'maybe' }).refused).toEqual([
|
|
30
|
-
'RIGOUR_REVIEWER_MODE=triple ignored: use single, cross or full',
|
|
31
|
-
'RIGOUR_REVIEWER_PANEL=maybe ignored: use on or off',
|
|
32
|
-
]);
|
|
33
|
-
});
|
|
34
|
-
it('lets a nearer choice of one judge turn a panel off, unless the team requires it, and says when it refuses', () => {
|
|
35
|
-
expect(resolveReviewer(team({ mode: 'full', panel: 'on' }), { mode: 'single' }, undefined, {})).toMatchObject({ mode: 'single', panel: false, refused: [] });
|
|
36
|
-
expect(resolveReviewer(team(), { mode: 'single' }, { panel: true }, {})).toMatchObject({ mode: 'single', panel: false }); // the flag is nearer than your setting
|
|
37
|
-
const user = resolveReviewer(team(), {}, { mode: 'single', panel: true }, {});
|
|
38
|
-
expect(user).toMatchObject({ mode: 'full', panel: true });
|
|
39
|
-
expect(user.refused).toEqual(['mode single (user) refused: the panel (user) needs judges from two vendors']);
|
|
40
|
-
expect(resolveReviewer(team({ mode: 'full', panel: 'required' }), { mode: 'single' }, undefined, {}).refused).toEqual(['mode single (flag) refused: rigour.yml sets review.reviewer.panel: required']);
|
|
41
|
-
});
|
|
42
|
-
it('under a team floor a person cannot turn reviews off or lower the judges', () => {
|
|
43
|
-
const floor = team({ enabled: true, mode: 'full', judges: 3, mode_required: true });
|
|
44
|
-
const resolved = resolveReviewer(floor, {}, { enabled: false, judges: 2 }, {});
|
|
45
|
-
expect(resolved).toMatchObject({ enabled: true, judges: 3 });
|
|
46
|
-
expect(resolved.refused).toEqual(['reviews off (user) refused: rigour.yml requires the reviewer', 'judges 2 (user) refused: rigour.yml requires 3']);
|
|
47
|
-
expect(resolveReviewer(team({ enabled: true }), {}, { enabled: false, judges: 3 }, {})).toMatchObject({ enabled: false, judges: 3 }); // no floor: your choice
|
|
48
|
-
});
|
|
49
|
-
it('takes the lower daily cap: a person can spend less than the team allows, never more', () => {
|
|
50
|
-
expect(resolveReviewer(team({ max_runs_per_day: 20, max_usd_per_day: 5 }), {}, { max_runs_per_day: 6 }, {})).toMatchObject({ max_runs_per_day: 6, max_usd_per_day: 5, refused: [] });
|
|
51
|
-
const higher = resolveReviewer(team({ max_runs_per_day: 20 }), {}, { max_runs_per_day: 50 }, {});
|
|
52
|
-
expect(higher.max_runs_per_day).toBe(20);
|
|
53
|
-
expect(higher.refused).toEqual(['max_runs_per_day 50 (user) refused: rigour.yml caps it at 20']);
|
|
54
|
-
expect(resolveReviewer(team(), {}, { max_usd_per_day: 2 }, {})).toMatchObject({ max_usd_per_day: 2 }); // no team cap: yours
|
|
55
|
-
expect(resolveReviewer(team(), {}, undefined, {}).max_runs_per_day).toBeUndefined(); // unset by default
|
|
56
|
-
});
|
|
57
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { reviewerUsage } from './usage.js';
|
|
3
|
-
const item = { id: 'abcdef0123', kind: 'finding', class: 'correctness', file: 'src/secret-path.ts', line: 2, issue: 'private text' };
|
|
4
|
-
describe('reviewer usage telemetry', () => {
|
|
5
|
-
it('reports counts and buckets only: never file names, finding text or ids', () => {
|
|
6
|
-
const result = { outcome: 'findings', items: [item], unverified: [], resolved: [], answerInReply: [], notes: [], advisory: [], disputed: [item, item], dropped: [], dismissed: [], reviewers: ['claude', 'codex', 'cursor'], scope: 'delta', cached: false, costUsd: 0.7, runs: 5,
|
|
7
|
-
mode: { asked: 'panel', ran: 'panel', source: 'team', escalation: '2 risky changed function(s)' } };
|
|
8
|
-
const usage = reviewerUsage(result, 'push');
|
|
9
|
-
expect(usage).toEqual({ outcome: 'findings', trigger: 'push', scope: 'delta', asked: 'panel', ran: 'panel', source: 'team', degraded: false, escalation: 'all-judges', refused: 0, judges: 3, cached: false, confirmed: 1, disputed: 2, dropped: 0, notes: 0, dismissed: 0, runs: 5, cost_bucket: '$0.50-2' });
|
|
10
|
-
const text = JSON.stringify(usage);
|
|
11
|
-
for (const leak of ['secret-path', 'private text', 'abcdef0123'])
|
|
12
|
-
expect(text).not.toContain(leak);
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|