@rigour-labs/core 6.8.0 → 6.8.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/inference/cloud-provider.js +12 -2
- package/package.json +11 -8
- package/dist/brief/briefing.test.d.ts +0 -1
- package/dist/brief/briefing.test.js +0 -107
- 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 -136
- 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 -100
- 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 -52
- 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 -40
- 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 -792
- 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 -93
- 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/task/thread.test.d.ts +0 -1
- package/dist/task/thread.test.js +0 -133
- 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 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { appendDeepRun, readDeepRuns, summarizeDeepRuns } from './deep-runs.js';
|
|
6
|
-
let dir;
|
|
7
|
-
beforeEach(() => { dir = fs.mkdtempSync(path.join(os.tmpdir(), 'deep-runs-')); });
|
|
8
|
-
afterEach(() => { fs.rmSync(dir, { recursive: true, force: true }); });
|
|
9
|
-
describe('deep runs', () => {
|
|
10
|
-
it('records what each run spent and sums only what was reported', () => {
|
|
11
|
-
appendDeepRun(dir, { enabled: true, status: 'ok', tier: 'cloud', cost_usd: 0.25, input_tokens: 1000, output_tokens: 100,
|
|
12
|
-
router: { functions: 10, routed: 3, files_skipped: 2, already_reviewed: 1 }, tool_calls: 7, findings_count: 2 });
|
|
13
|
-
appendDeepRun(dir, { enabled: true, status: 'partial', tier: 'cloud', input_tokens: 500, output_tokens: 50 });
|
|
14
|
-
appendDeepRun(dir, { enabled: true, status: 'ok', tier: 'lite' });
|
|
15
|
-
appendDeepRun(dir, { enabled: true, status: 'error', tier: 'cloud', cost_usd: 9 }); // nothing ran: not a run
|
|
16
|
-
const runs = readDeepRuns(dir);
|
|
17
|
-
expect(runs).toHaveLength(3);
|
|
18
|
-
expect(summarizeDeepRuns(runs)).toEqual({
|
|
19
|
-
runs: 3, costUsd: 0.25, unpricedRuns: 1, inputTokens: 1500, outputTokens: 150,
|
|
20
|
-
functionsRanked: 10, functionsRouted: 3, alreadyReviewed: 1, toolCalls: 7, findingsKept: 2,
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import { appendAgentEvent, computeEffectiveness, readAgentEvents } from './effectiveness.js';
|
|
6
|
-
const review = (failures) => [
|
|
7
|
-
{ type: 'tool_call', tool: 'rigour_review' },
|
|
8
|
-
{ type: 'tool_response', tool: 'rigour_review', content: [{ type: 'text', text: JSON.stringify({ status: failures.length ? 'FAIL' : 'PASS', failures }) }] },
|
|
9
|
-
];
|
|
10
|
-
describe('computeEffectiveness', () => {
|
|
11
|
-
it('counts reviews, findings resolved by later reviews, and tool calls', () => {
|
|
12
|
-
const events = [
|
|
13
|
-
{ type: 'tool_call', tool: 'rigour_recall' },
|
|
14
|
-
...review([{ id: 'semantic-bugs', file: 'a.ts' }, { id: 'promise-safety', file: 'b.ts' }]),
|
|
15
|
-
...review([{ id: 'promise-safety', file: 'b.ts' }]),
|
|
16
|
-
...review([]),
|
|
17
|
-
];
|
|
18
|
-
expect(computeEffectiveness(events)).toMatchObject({
|
|
19
|
-
reviews: 3, failedReviews: 2, findingsReported: 2, findingsResolved: 2,
|
|
20
|
-
toolCalls: { rigour_recall: 1, rigour_review: 3 },
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
it('follows a blocked stop through to a clean stop in the same session, and counts self-reviews', () => {
|
|
24
|
-
const events = [
|
|
25
|
-
{ type: 'stop_review', session: 's1', blocked: true },
|
|
26
|
-
...review([]),
|
|
27
|
-
{ type: 'stop_review', session: 's1', blocked: false },
|
|
28
|
-
{ type: 'stop_review', session: 's2', blocked: false },
|
|
29
|
-
];
|
|
30
|
-
expect(computeEffectiveness(events)).toMatchObject({
|
|
31
|
-
stopChecks: 3, stopBlocks: 1, blockedStopsFollowedThrough: 1, stopsWithSelfReview: 1,
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
it('reads the log it writes and skips malformed lines', () => {
|
|
35
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'effectiveness-'));
|
|
36
|
-
appendAgentEvent(dir, { type: 'stop_review', blocked: true });
|
|
37
|
-
fs.appendFileSync(path.join(dir, '.rigour', 'events.jsonl'), 'not json\n');
|
|
38
|
-
expect(readAgentEvents(dir).map(e => e.type)).toEqual(['stop_review']);
|
|
39
|
-
expect(readAgentEvents(path.join(dir, 'missing'))).toEqual([]);
|
|
40
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,104 +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 { fixScope as scopeWith } from './fix-scope.js';
|
|
7
|
-
let repo;
|
|
8
|
-
let reviewed;
|
|
9
|
-
const git = (...args) => execFileSync('git', ['-C', repo, ...args], { encoding: 'utf8' }).trim();
|
|
10
|
-
const write = (rel, body) => {
|
|
11
|
-
fs.mkdirSync(path.dirname(path.join(repo, rel)), { recursive: true });
|
|
12
|
-
fs.writeFileSync(path.join(repo, rel), body);
|
|
13
|
-
};
|
|
14
|
-
const commit = (message) => {
|
|
15
|
-
git('add', '-A');
|
|
16
|
-
git('commit', '-qm', message);
|
|
17
|
-
};
|
|
18
|
-
const PR = { number: 42, state: 'OPEN', isDraft: false, author: { login: 'author' }, body: '' };
|
|
19
|
-
const user = (login, type = 'User') => ({ login, type });
|
|
20
|
-
let reviews;
|
|
21
|
-
let comments;
|
|
22
|
-
/** Real git; gh answers from `reviews` and `comments`. */
|
|
23
|
-
const exec = async (command, args, options) => {
|
|
24
|
-
if (command === 'git') {
|
|
25
|
-
try {
|
|
26
|
-
return { exitCode: 0, stdout: execFileSync('git', args, { cwd: options.cwd, encoding: 'utf8', stdio: 'pipe' }), stderr: '' };
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
return { exitCode: 1, stdout: '', stderr: String(error.message) };
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (args[0] === 'pr')
|
|
33
|
-
return { exitCode: 0, stdout: JSON.stringify(PR), stderr: '' };
|
|
34
|
-
if (args[1]?.endsWith('/reviews'))
|
|
35
|
-
return { exitCode: 0, stdout: JSON.stringify(reviews), stderr: '' };
|
|
36
|
-
if (args[1]?.endsWith('/comments'))
|
|
37
|
-
return { exitCode: 0, stdout: JSON.stringify(comments), stderr: '' };
|
|
38
|
-
return { exitCode: 1, stdout: '', stderr: 'unexpected gh call' };
|
|
39
|
-
};
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
repo = fs.mkdtempSync(path.join(os.tmpdir(), 'fix-scope-'));
|
|
42
|
-
git('init', '-q', '-b', 'main');
|
|
43
|
-
git('config', 'user.email', 't@example.com');
|
|
44
|
-
git('config', 'user.name', 't');
|
|
45
|
-
git('config', 'commit.gpgsign', 'false');
|
|
46
|
-
for (const file of ['src/job.ts', 'src/job.test.ts', 'src/util.ts', 'src/deep/thing.ts', 'src/other.ts', 'src/temp.ts', 'src/mainside.ts', 'lib/util.ts'])
|
|
47
|
-
write(file, 'export const x = 1;\n');
|
|
48
|
-
commit('base');
|
|
49
|
-
git('checkout', '-qb', 'feature');
|
|
50
|
-
write('src/job.ts', 'export const x = 2;\n');
|
|
51
|
-
commit('the change under review');
|
|
52
|
-
reviewed = git('rev-parse', 'HEAD');
|
|
53
|
-
reviews = [
|
|
54
|
-
{ id: 1, user: user('ci-bot', 'Bot'), body: 'see src/other.ts', submitted_at: '2026-10-01', commit_id: reviewed },
|
|
55
|
-
{ id: 2, user: user('author'), body: 'I will also tidy src/other.ts', submitted_at: '2026-10-02', commit_id: reviewed },
|
|
56
|
-
{ id: 3, user: user('senior'), body: 'Bound the window in `src/util.ts:4`. The scan in https://github.com/o/r/blob/abc123/src/deep/thing.ts#L3 is quadratic.', submitted_at: '2026-10-03', commit_id: reviewed },
|
|
57
|
-
{ id: 4, user: user('second'), body: '', submitted_at: '2026-10-03', commit_id: reviewed },
|
|
58
|
-
];
|
|
59
|
-
comments = [
|
|
60
|
-
{ id: 9, pull_request_review_id: 4, user: user('second'), path: 'src/job.ts', body: 'Lock before the first read.' },
|
|
61
|
-
{ id: 10, pull_request_review_id: 2, user: user('author'), path: 'src/temp.ts', body: 'my own note' },
|
|
62
|
-
];
|
|
63
|
-
});
|
|
64
|
-
afterEach(() => { fs.rmSync(repo, { recursive: true, force: true }); });
|
|
65
|
-
const fixScope = (cwd, named, run) => scopeWith(cwd, { pr: undefined, review: named.review, githubAccount: undefined }, run);
|
|
66
|
-
describe('the scope of a fix round', () => {
|
|
67
|
-
it('lists the files the round changed that no point of the review cited', async () => {
|
|
68
|
-
for (const file of ['src/job.ts', 'src/job.test.ts', 'src/util.ts', 'src/deep/thing.ts', 'src/other.ts'])
|
|
69
|
-
write(file, 'export const x = 3;\n');
|
|
70
|
-
commit('fix round');
|
|
71
|
-
write('src/temp.ts', 'export const x = 9;\n');
|
|
72
|
-
commit('try something');
|
|
73
|
-
write('src/temp.ts', 'export const x = 1;\n');
|
|
74
|
-
commit('put it back');
|
|
75
|
-
git('checkout', '-q', 'main');
|
|
76
|
-
write('src/mainside.ts', 'export const x = 4;\n');
|
|
77
|
-
commit('main moves on');
|
|
78
|
-
git('checkout', '-q', 'feature');
|
|
79
|
-
git('merge', '-q', '--no-edit', 'main');
|
|
80
|
-
const { scope, error } = await fixScope(repo, {}, exec);
|
|
81
|
-
expect(error).toBeUndefined();
|
|
82
|
-
expect(scope).toMatchObject({ pr: 42, review: { id: 4, login: 'second', commit: reviewed } });
|
|
83
|
-
expect(scope.cited).toEqual(['src/deep/thing.ts', 'src/job.ts', 'src/util.ts']);
|
|
84
|
-
expect(scope.changed).toEqual(['src/deep/thing.ts', 'src/job.test.ts', 'src/job.ts', 'src/other.ts', 'src/util.ts']);
|
|
85
|
-
expect(scope.extra).toEqual(['src/other.ts']);
|
|
86
|
-
});
|
|
87
|
-
it('takes a named review as the round, and an ambiguous file name cites nothing', async () => {
|
|
88
|
-
write('src/job.ts', 'export const x = 3;\n');
|
|
89
|
-
commit('first fix round');
|
|
90
|
-
reviews.push({ id: 5, user: user('senior'), body: 'and util.ts too', submitted_at: '2026-10-04', commit_id: git('rev-parse', 'HEAD') });
|
|
91
|
-
write('src/util.ts', 'export const x = 3;\n');
|
|
92
|
-
commit('second fix round');
|
|
93
|
-
const named = await fixScope(repo, { review: 5 }, exec);
|
|
94
|
-
expect(named.scope.cited).toEqual([]);
|
|
95
|
-
expect(named.scope.extra).toEqual(['src/util.ts']);
|
|
96
|
-
});
|
|
97
|
-
it('says why it cannot answer', async () => {
|
|
98
|
-
expect((await fixScope(repo, { review: 99 }, exec)).error).toBe('no review 99 by a person on pull request 42');
|
|
99
|
-
reviews = reviews.filter((r) => r.user.type === 'Bot');
|
|
100
|
-
expect((await fixScope(repo, {}, exec)).error).toBe('pull request 42 has no review by a person yet');
|
|
101
|
-
reviews = [{ id: 3, user: user('senior'), body: 'x', submitted_at: '2026-10-03', commit_id: 'f'.repeat(40) }];
|
|
102
|
-
expect((await fixScope(repo, {}, exec)).error).toMatch(/^the reviewed commit fffffffff is not behind HEAD/);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { isGeneratedFile, withoutGenerated } from './generated-files.js';
|
|
6
|
-
let dir;
|
|
7
|
-
beforeEach(() => { dir = fs.mkdtempSync(path.join(os.tmpdir(), 'generated-')); });
|
|
8
|
-
afterEach(() => { fs.rmSync(dir, { recursive: true, force: true }); });
|
|
9
|
-
describe('generated files', () => {
|
|
10
|
-
it('recognises generated files by name and by the marker at their top', () => {
|
|
11
|
-
fs.writeFileSync(path.join(dir, 'client.ts'), '// This file is auto-generated by @hey-api/openapi-ts\nexport const x = 1;\n');
|
|
12
|
-
fs.writeFileSync(path.join(dir, 'schema.ts'), '/* eslint-disable */\n// @generated by protoc\n');
|
|
13
|
-
fs.writeFileSync(path.join(dir, 'handwritten.ts'), "// Generates the client; edit freely.\nexport const generate = () => 'x';\n");
|
|
14
|
-
expect(isGeneratedFile(dir, 'client.ts')).toBe(true);
|
|
15
|
-
expect(isGeneratedFile(dir, 'schema.ts')).toBe(true);
|
|
16
|
-
expect(isGeneratedFile(dir, 'src/api/client.gen.ts')).toBe(true);
|
|
17
|
-
expect(isGeneratedFile(dir, 'src/__generated__/types.ts')).toBe(true);
|
|
18
|
-
expect(isGeneratedFile(dir, 'handwritten.ts')).toBe(false);
|
|
19
|
-
expect(isGeneratedFile(dir, 'src/missing.ts')).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
it('drops generated files from the change under review', () => {
|
|
22
|
-
const changed = { 'src/a.ts': new Set([1]), 'src/b.gen.ts': new Set([2]) };
|
|
23
|
-
expect(Object.keys(withoutGenerated(dir, changed))).toEqual(['src/a.ts']);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,62 +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 { diffFromGit } from './git-diff.js';
|
|
8
|
-
import { addedFiles, migrationOrderFailures } from './migration-order.js';
|
|
9
|
-
let repo;
|
|
10
|
-
const git = (...args) => execFileSync('git', ['-C', repo, ...args], { encoding: 'utf8' });
|
|
11
|
-
const write = (rel, body = 'select 1;\n') => {
|
|
12
|
-
fs.mkdirSync(path.dirname(path.join(repo, rel)), { recursive: true });
|
|
13
|
-
fs.writeFileSync(path.join(repo, rel), body);
|
|
14
|
-
};
|
|
15
|
-
const commit = (message) => { git('add', '-A'); git('commit', '-qm', message); };
|
|
16
|
-
const config = (enabled = true) => ConfigSchema.parse({ version: 1, gates: { migration_order: { enabled } } });
|
|
17
|
-
const DIR = 'supabase/migrations';
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
repo = fs.mkdtempSync(path.join(os.tmpdir(), 'migration-order-'));
|
|
20
|
-
git('init', '-q', '-b', 'main');
|
|
21
|
-
git('config', 'user.email', 't@example.com');
|
|
22
|
-
git('config', 'user.name', 't');
|
|
23
|
-
git('config', 'commit.gpgsign', 'false');
|
|
24
|
-
write(`${DIR}/20260101000000_init.sql`);
|
|
25
|
-
commit('init');
|
|
26
|
-
});
|
|
27
|
-
afterEach(() => fs.rmSync(repo, { recursive: true, force: true }));
|
|
28
|
-
describe('migrationOrderFailures', () => {
|
|
29
|
-
it('flags a branch migration dated before one main merged since', () => {
|
|
30
|
-
git('checkout', '-q', '-b', 'feature');
|
|
31
|
-
write(`${DIR}/20260107000000_feature.sql`);
|
|
32
|
-
commit('feature');
|
|
33
|
-
git('checkout', '-q', 'main');
|
|
34
|
-
write(`${DIR}/20260110000000_main.sql`);
|
|
35
|
-
commit('main moved');
|
|
36
|
-
git('checkout', '-q', 'feature');
|
|
37
|
-
const source = { mode: 'base', base: 'main' };
|
|
38
|
-
const failures = migrationOrderFailures(repo, diffFromGit(repo, source), source, config());
|
|
39
|
-
expect(failures).toEqual([expect.objectContaining({
|
|
40
|
-
id: 'migration-order', files: [`${DIR}/20260107000000_feature.sql`], line: 1,
|
|
41
|
-
})]);
|
|
42
|
-
expect(failures[0].details).toContain('20260110000000_main.sql');
|
|
43
|
-
});
|
|
44
|
-
it('passes a migration dated after the newest on the base, and uncommitted work against HEAD', () => {
|
|
45
|
-
write(`${DIR}/20260201000000_next.sql`);
|
|
46
|
-
expect(migrationOrderFailures(repo, diffFromGit(repo), { mode: 'working' }, config())).toEqual([]);
|
|
47
|
-
write(`${DIR}/20251231000000_old.sql`);
|
|
48
|
-
expect(migrationOrderFailures(repo, diffFromGit(repo), { mode: 'working' }, config()).map(f => f.files)).toEqual([[`${DIR}/20251231000000_old.sql`]]);
|
|
49
|
-
});
|
|
50
|
-
it('checks only the configured directories, and only when enabled', () => {
|
|
51
|
-
write('db/migrations/0001_old.sql');
|
|
52
|
-
write(`${DIR}/20251231000000_old.sql`);
|
|
53
|
-
expect(migrationOrderFailures(repo, diffFromGit(repo), { mode: 'working' }, config(false))).toEqual([]);
|
|
54
|
-
expect(migrationOrderFailures(repo, diffFromGit(repo), { mode: 'working' }, config()).map(f => f.files[0])).toEqual([`${DIR}/20251231000000_old.sql`]);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
describe('addedFiles', () => {
|
|
58
|
-
it('lists only files the diff creates', () => {
|
|
59
|
-
const diff = ['--- /dev/null', '+++ b/new.sql', '@@ -0,0 +1 @@', '+x', '--- a/old.sql', '+++ b/old.sql'].join('\n');
|
|
60
|
-
expect(addedFiles(diff)).toEqual(['new.sql']);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { dismissFinding, findingKey, quietSplit } from './quiet.js';
|
|
6
|
-
let dir;
|
|
7
|
-
beforeEach(() => { dir = fs.mkdtempSync(path.join(os.tmpdir(), 'quiet-')); });
|
|
8
|
-
afterEach(() => { fs.rmSync(dir, { recursive: true, force: true }); });
|
|
9
|
-
const finding = (id, details = 'd', line = 3) => ({ id, title: id, details, files: ['src/a.ts'], line });
|
|
10
|
-
describe('quiet by default', () => {
|
|
11
|
-
it('lets only findings that prove a defect speak; heuristics are advisory unless the team asks for them', () => {
|
|
12
|
-
const all = [finding('semantic-bugs'), finding('security-patterns'), finding('promise-safety'), finding('file-size'), finding('deep-analysis')];
|
|
13
|
-
const quiet = quietSplit(dir, all);
|
|
14
|
-
expect(quiet.speaking.map(f => f.id)).toEqual(['semantic-bugs', 'security-patterns', 'deep-analysis']);
|
|
15
|
-
expect(quiet.advisory.map(f => f.id)).toEqual(['promise-safety', 'file-size']);
|
|
16
|
-
expect(quietSplit(dir, all, true).speaking).toHaveLength(5);
|
|
17
|
-
});
|
|
18
|
-
it('speaks on what must be fixed, the one rule the stop hook and the push gate share: critical, or high and verified or from a security gate', () => {
|
|
19
|
-
const all = [
|
|
20
|
-
{ ...finding('frontend-secret-exposure'), severity: 'high', provenance: 'security' },
|
|
21
|
-
{ ...finding('promise-safety'), severity: 'high' },
|
|
22
|
-
{ ...finding('promise-safety', 'verified'), severity: 'high', verified: true },
|
|
23
|
-
{ ...finding('file-size'), severity: 'critical' },
|
|
24
|
-
finding('migration-order'),
|
|
25
|
-
];
|
|
26
|
-
expect(quietSplit(dir, all).speaking.map(f => f.id)).toEqual(['frontend-secret-exposure', 'promise-safety', 'file-size', 'migration-order']);
|
|
27
|
-
expect(quietSplit(dir, all).advisory.map(f => f.details)).toEqual(['d']);
|
|
28
|
-
});
|
|
29
|
-
it('never reports a dismissed finding again, even after its line moves', () => {
|
|
30
|
-
const leak = finding('security-patterns', 'Hardcoded token in `client`');
|
|
31
|
-
expect(dismissFinding(dir, findingKey(leak), 'test fixture token, never deployed')).toBe(true);
|
|
32
|
-
expect(dismissFinding(dir, findingKey(leak), 'again')).toBe(true); // idempotent
|
|
33
|
-
const moved = { ...leak, line: 40 };
|
|
34
|
-
expect(quietSplit(dir, [moved, finding('semantic-bugs')])).toMatchObject({ dismissed: 1, dismissedByGate: { 'security-patterns': 1 }, speaking: [expect.objectContaining({ id: 'semantic-bugs' })] });
|
|
35
|
-
expect(JSON.parse(fs.readFileSync(path.join(dir, '.rigour/dismissed.json'), 'utf8')).entries).toHaveLength(1);
|
|
36
|
-
expect(dismissFinding(dir, 'not-a-key', 'x')).toBe(false);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,60 +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 { findFunction } from '../deep/risk.js';
|
|
7
|
-
import { diffFromGit } from './git-diff.js';
|
|
8
|
-
import { recordReview } from './ledger.js';
|
|
9
|
-
import { buildQualityReceipt } from './receipt.js';
|
|
10
|
-
let repo;
|
|
11
|
-
const git = (...args) => execFileSync('git', ['-C', repo, ...args], { encoding: 'utf8' });
|
|
12
|
-
const write = (rel, body) => { fs.mkdirSync(path.dirname(path.join(repo, rel)), { recursive: true }); fs.writeFileSync(path.join(repo, rel), body); };
|
|
13
|
-
const review = (file, fn, reviewer = 'agent') => {
|
|
14
|
-
const found = findFunction(repo, file, fn);
|
|
15
|
-
recordReview(repo, { file, function: fn, hash: found.hash, reviewer, verdict: 'no_issue', note: 'checked' });
|
|
16
|
-
};
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
repo = fs.mkdtempSync(path.join(os.tmpdir(), 'receipt-'));
|
|
19
|
-
git('init', '-q', '-b', 'main');
|
|
20
|
-
git('config', 'user.email', 't@example.com');
|
|
21
|
-
git('config', 'user.name', 't');
|
|
22
|
-
git('config', 'commit.gpgsign', 'false');
|
|
23
|
-
write('.gitignore', '.rigour/\n');
|
|
24
|
-
write('src/pay.ts', 'export const x = 1;\n');
|
|
25
|
-
git('add', '-A');
|
|
26
|
-
git('commit', '-qm', 'init');
|
|
27
|
-
});
|
|
28
|
-
afterEach(() => fs.rmSync(repo, { recursive: true, force: true }));
|
|
29
|
-
// Risky: a data write. Low risk: a formatter. Each in its own function.
|
|
30
|
-
const RISKY = (n) => `export async function saveRefund${n}(db: any, cents: number) {\n return db.insert({ cents: cents + ${n} });\n}\n`;
|
|
31
|
-
const PLAIN = 'export function label(name: string) {\n return name.trim();\n}\n';
|
|
32
|
-
describe('buildQualityReceipt', () => {
|
|
33
|
-
it('says what was reviewed at its current code, what changed after review, what is low risk, and what is not covered', () => {
|
|
34
|
-
write('src/pay.ts', RISKY(1) + RISKY(2) + RISKY(3) + PLAIN);
|
|
35
|
-
write('config/app.yml', 'a: 1\n');
|
|
36
|
-
review('src/pay.ts', 'saveRefund1');
|
|
37
|
-
review('src/pay.ts', 'saveRefund2');
|
|
38
|
-
write('src/pay.ts', RISKY(1) + RISKY(2).replace('cents + 2', 'cents + 20') + RISKY(3) + PLAIN); // edited after its review
|
|
39
|
-
const receipt = buildQualityReceipt(repo, diffFromGit(repo));
|
|
40
|
-
expect(receipt).toMatchObject({ functions: 4, reviewed: 1, changedSinceReview: 1, lowRisk: 1, otherFiles: 1, setAside: 0, reviewers: { agent: 1 } });
|
|
41
|
-
expect(receipt.notCovered.map(g => [g.function, g.changedSinceReview])).toEqual([['saveRefund2', true], ['saveRefund3', false]]);
|
|
42
|
-
});
|
|
43
|
-
it('counts no self-reported review when independent, and says how many it set aside', () => {
|
|
44
|
-
write('src/pay.ts', RISKY(1) + RISKY(2));
|
|
45
|
-
review('src/pay.ts', 'saveRefund1');
|
|
46
|
-
const receipt = buildQualityReceipt(repo, diffFromGit(repo), { independent: true });
|
|
47
|
-
expect(receipt).toMatchObject({ reviewed: 0, setAside: 1 });
|
|
48
|
-
expect(receipt.notCovered).toHaveLength(2);
|
|
49
|
-
});
|
|
50
|
-
it('treats a function matching a verified team lesson as risky, and says which lesson', () => {
|
|
51
|
-
write('src/pay.ts', 'export function formatTotal(totalCents: number) {\n return (totalCents / 100).toFixed(2);\n}\n');
|
|
52
|
-
write('.rigour/review-lessons.json', JSON.stringify({ version: 1, lessons: [{
|
|
53
|
-
id: 'l1', text: 'Format money from integer cents, never floats.', file: 'src/pay.ts', symbols: ['totalCents'],
|
|
54
|
-
state: 'verified', evidence: [{ pr: 12, comment: 'c', author: 'reviewer' }], createdAt: '', updatedAt: '',
|
|
55
|
-
}] }));
|
|
56
|
-
const withLesson = buildQualityReceipt(repo, diffFromGit(repo));
|
|
57
|
-
expect(withLesson.notCovered).toEqual([expect.objectContaining({ function: 'formatTotal', lesson: 'Format money from integer cents, never floats.' })]);
|
|
58
|
-
expect(buildQualityReceipt(repo, diffFromGit(repo), { lessonMode: 'off' }).notCovered).toEqual([]);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,89 +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 { diffFromGit } from './git-diff.js';
|
|
7
|
-
import { acknowledgeReview } from './review-ack.js';
|
|
8
|
-
import { buildReviewTask } from './review-task.js';
|
|
9
|
-
import { exportReviewed, readLedger, REVIEWED_FILE } from './ledger.js';
|
|
10
|
-
import { reviewAckMessage } from '../hooks/stop-review.js';
|
|
11
|
-
import { writeLessons } from '../review-learning/lessons.js';
|
|
12
|
-
let repo;
|
|
13
|
-
const git = (...args) => execFileSync('git', ['-C', repo, ...args], { encoding: 'utf8' });
|
|
14
|
-
const write = (body) => fs.writeFileSync(path.join(repo, 'sync.ts'), body);
|
|
15
|
-
const BEFORE = [
|
|
16
|
-
'export async function syncOrders(db, cursor) {',
|
|
17
|
-
' const rows = await page(cursor);',
|
|
18
|
-
' return rows;',
|
|
19
|
-
'}',
|
|
20
|
-
'function label(name) {',
|
|
21
|
-
' return name;',
|
|
22
|
-
'}',
|
|
23
|
-
'',
|
|
24
|
-
].join('\n');
|
|
25
|
-
const AFTER = [
|
|
26
|
-
'export async function syncOrders(db, cursor) {',
|
|
27
|
-
' const rows = await page(cursor);',
|
|
28
|
-
" await db.from('orders').upsert(rows);",
|
|
29
|
-
' return rows;',
|
|
30
|
-
'}',
|
|
31
|
-
'function label(name) {',
|
|
32
|
-
' return name.trim();',
|
|
33
|
-
'}',
|
|
34
|
-
'',
|
|
35
|
-
].join('\n');
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
repo = fs.mkdtempSync(path.join(os.tmpdir(), 'review-task-'));
|
|
38
|
-
git('init', '-q', '-b', 'main');
|
|
39
|
-
git('config', 'user.email', 't@example.com');
|
|
40
|
-
git('config', 'user.name', 't');
|
|
41
|
-
git('config', 'commit.gpgsign', 'false');
|
|
42
|
-
write(BEFORE);
|
|
43
|
-
git('add', '-A');
|
|
44
|
-
git('commit', '-q', '-m', 'base');
|
|
45
|
-
write(AFTER);
|
|
46
|
-
});
|
|
47
|
-
afterEach(() => { fs.rmSync(repo, { recursive: true, force: true }); });
|
|
48
|
-
describe('review task and acknowledgements', () => {
|
|
49
|
-
it('asks about the risky changed function only, with questions from its signals', () => {
|
|
50
|
-
const task = buildReviewTask(repo, diffFromGit(repo));
|
|
51
|
-
expect(task.items.map(i => i.function)).toEqual(['syncOrders']);
|
|
52
|
-
expect(task.items[0].questions[0]).toMatch(/concurrent calls/);
|
|
53
|
-
expect(task.items[0].questions.at(-1)).toContain('`syncOrders`');
|
|
54
|
-
});
|
|
55
|
-
it('sends a function a verified team lesson names to review, and asks whether the change repeats it', () => {
|
|
56
|
-
write(AFTER.replace('name.trim()', 'name.toLocaleUpperCase()'));
|
|
57
|
-
writeLessons(repo, [{ id: 'l1', text: 'Locale casing breaks Turkish names.', file: 'sync.ts', symbols: ['toLocaleUpperCase'], state: 'verified', evidence: [{ pr: 7, comment: 'c1', author: 'r' }], createdAt: '', updatedAt: '' }]);
|
|
58
|
-
const task = buildReviewTask(repo, diffFromGit(repo));
|
|
59
|
-
const label = task.items.find(i => i.function === 'label');
|
|
60
|
-
expect(label?.questions[0]).toContain('Locale casing breaks Turkish names.');
|
|
61
|
-
expect(buildReviewTask(repo, diffFromGit(repo), {}, 'off').items.map(i => i.function)).toEqual(['syncOrders']);
|
|
62
|
-
});
|
|
63
|
-
it('drops an acknowledged function from the task until its code changes again', () => {
|
|
64
|
-
const ack = acknowledgeReview(repo, { file: 'sync.ts', function: 'syncOrders', verdict: 'no_issue', note: 'upsert key is the unique order id; callers retry safely' });
|
|
65
|
-
expect(ack.ok).toBe(true);
|
|
66
|
-
expect(buildReviewTask(repo, diffFromGit(repo))).toMatchObject({ items: [], alreadyReviewed: 1 });
|
|
67
|
-
write(AFTER.replace('return rows;', 'return rows.slice(1);'));
|
|
68
|
-
expect(buildReviewTask(repo, diffFromGit(repo)).items.map(i => i.function)).toEqual(['syncOrders']);
|
|
69
|
-
});
|
|
70
|
-
it('refuses an acknowledgement without a real note, a known verdict, or a real function', () => {
|
|
71
|
-
expect(acknowledgeReview(repo, { file: 'sync.ts', function: 'syncOrders', verdict: 'no_issue', note: 'ok' })).toMatchObject({ ok: false });
|
|
72
|
-
expect(acknowledgeReview(repo, { file: 'sync.ts', function: 'syncOrders', verdict: 'lgtm', note: 'looked at it carefully' })).toMatchObject({ ok: false });
|
|
73
|
-
expect(acknowledgeReview(repo, { file: 'sync.ts', function: 'nope', verdict: 'fixed', note: 'looked at it carefully' })).toMatchObject({ ok: false });
|
|
74
|
-
expect(readLedger(repo)).toEqual([]);
|
|
75
|
-
});
|
|
76
|
-
it('exports hashes and verdicts only, and a committed export clears the task too', () => {
|
|
77
|
-
acknowledgeReview(repo, { file: 'sync.ts', function: 'syncOrders', verdict: 'fixed', note: 'made the upsert key explicit' });
|
|
78
|
-
exportReviewed(repo);
|
|
79
|
-
const exported = JSON.parse(fs.readFileSync(path.join(repo, REVIEWED_FILE), 'utf8'));
|
|
80
|
-
expect(Object.keys(exported.entries[0]).sort()).toEqual(['file', 'function', 'hash', 'reviewer', 'verdict']);
|
|
81
|
-
fs.rmSync(path.join(repo, '.rigour', 'review-ledger.jsonl'));
|
|
82
|
-
expect(buildReviewTask(repo, diffFromGit(repo)).items).toEqual([]);
|
|
83
|
-
});
|
|
84
|
-
it('tells a stopping agent which functions still need review, and how', () => {
|
|
85
|
-
const message = reviewAckMessage(buildReviewTask(repo, diffFromGit(repo)).items, 1);
|
|
86
|
-
expect(message).toContain('sync.ts:1 `syncOrders`');
|
|
87
|
-
expect(message).toContain('rigour_review_ack');
|
|
88
|
-
});
|
|
89
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|