Skip to content

Commit 5d208c3

Browse files
committed
Harden Convex phase result contracts
1 parent fc56e60 commit 5d208c3

12 files changed

Lines changed: 796 additions & 194 deletions

UPDATES.md

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,33 @@
22

33
## 0) Last Updated + Changelog
44

5-
**Last updated:** 2026-05-23
5+
**Last updated:** 2026-05-25
6+
7+
### 2026-05-25 (Convex Phase Schema Hardening + Agent Contract Alignment)
8+
**Summary:** Tightened the remaining loose Convex phase-result contracts, aligned the agent’s persisted result types to the real bridge payloads, and fixed a phase 6 report schema mismatch that previously made Convex less compatible than the runtime bridges.
9+
10+
**What changed:**
11+
1. **Shared Convex phase validators (`convex/phaseValidators.ts`, `convex/schema.ts`, `convex/integrations-mutations.ts`):**
12+
- Extracted shared phase validators so the table schema and `savePhaseResult` mutation validate against the same payload definitions.
13+
- Replaced the remaining loose `v.any()` phase surfaces with structured validators for repo dependencies/components, transformation change lists, validation comparison payloads, and report subtrees.
14+
- Updated `savePhaseResult` to take a typed `{ payload: { field, result } }` object so field/result pairs are validated together instead of accepting any blob.
15+
2. **Agent payload normalization and typing (`agent/src/bridges/python-subprocess.ts`, `agent/src/api/convex.ts`, `agent/src/orchestrator.ts`):**
16+
- Added structured TypeScript result types for persisted phase payloads and removed `unknown` from the Convex wrapper’s phase result API.
17+
- Normalized subprocess mapping outputs to preserve target context, confidence breakdowns, and canonical research-spec payloads instead of leaving snake_case/raw extractor objects to drift into persistence.
18+
- Normalized patch validation metadata (`schemaVersion`, `payloadType`) and tightened the orchestrator’s Convex handoff to use typed persisted phase results.
19+
3. **Phase 6 report contract fix (`agent/src/phases/phase6-report.ts`, `agent/src/phases/types.ts`):**
20+
- Exported a concrete `Phase6Report` type and aligned `Phase6Context.report` to it.
21+
- Fixed the persisted report contract to use the actual `diffPreview` field and full report structure (`whatChanged`, `why`, `observedImpact`, `testResults`, `recommendation`) instead of the stale `{ summary, diff, metadata }` schema.
22+
4. **Regression coverage (`agent/src/api/convex.test.ts`, `agent/src/bridges/python-subprocess.test.ts`):**
23+
- Added a Convex client regression test covering the new nested phase result payload contract.
24+
- Added subprocess bridge coverage for normalized mapping context, confidence breakdowns, canonicalized research specs, and validation metadata passthrough.
25+
26+
**Verification:**
27+
- `cd agent && bun test src/api/convex.test.ts src/bridges/python-subprocess.test.ts src/phases/phase6-report.test.ts`
28+
- `cd agent && bun run build`
29+
30+
**Notes:**
31+
- A standalone TypeScript check for `convex/*.ts` was not runnable in this checkout because `convex/_generated/server` is absent, so Convex’s generated type bindings are not available locally.
632

733
### 2026-05-23 (Paper-to-Code PDF Upload Flow)
834
**Summary:** Finished the missing PDF upload transport for the Paper-to-Code page and aligned the UI with the backend’s actual patch-generation response.
@@ -52,8 +78,8 @@
5278
- `cd agent && bun test src/bridges/python-subprocess.test.ts`
5379
- `cd agent && bun run build`
5480

55-
### 2026-05-23 (CI Failure Fixes — Ruff Format, Test Mock, Benchmark AST Revert)
56-
**Summary:** Fixed three CI failures: ruff formatting in test files, a test that failed due to incomplete mocking of the validation runner, and a benchmark regression caused by AST-affecting changes to expected benchmark files.
81+
### 2026-05-23 (CI Failure Fixes — Ruff Format, Test Mock, Benchmark AST Revert, python-multipart Dep)
82+
**Summary:** Fixed four CI/suite failures: ruff formatting in test files, a test that failed due to incomplete mocking of the validation runner, a benchmark regression caused by AST-affecting changes to expected benchmark files, and a missing `python-multipart` dependency for FastAPI file uploads.
5783

5884
**What changed:**
5985
1. **Ruff formatting (`core/tests/unit/test_patch_generator.py`, `core/tests/unit/test_cli.py`):**
@@ -62,10 +88,12 @@
6288
- `test_run_accepts_camel_case_patch_payload` now properly mocks `_check_torch_available`, `_run_training_test`, `_run_numerical_correctness`, `_run_regression_snapshot`, and `_score_diff_readability` — preventing the test from running real benchmark subprocesses and failing when torch is unavailable.
6389
3. **Benchmark AST revert (`core/benchmarks/expected/*.py`, `core/benchmarks/papers/*.py`):**
6490
- Reverted N806 variable renames (`B,T,C` -> `b,t,c`) and F401 import removals from benchmark expected/papers files that changed the AST and caused benchmark AST matching failures (score dropped from 1.0 → 0.75 for 5 of 10 cases).
91+
4. **Missing dependency (`core/pyproject.toml`):**
92+
- Added `python-multipart>=0.0.9` to the `product` extras — required by the new PDF upload endpoint (`UploadFile = File(...)`). Without it, importing the server module (and any test importing it) raises a `RuntimeError` at route definition time.
6593

6694
**Verification:**
6795
- `cd core && ruff check src/ tests/ && ruff format --check src/ tests/`
68-
- `cd core && python -m pytest tests/unit/test_validation_runner.py tests/unit/test_cli.py tests/unit/test_patch_generator.py -q`
96+
- `cd core && python -m pytest tests/ -x -q --tb=short`
6997

7098
### 2026-05-22 (Benchmark Regression + LoRA Smoke Fix)
7199
**Summary:** Restored benchmark parity after the new smoke-test scaffolding and fixed the generated LoRA runtime contract.

agent/src/api/convex.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,68 @@ describe('ConvexClientWrapper auth and approval semantics', () => {
5757
);
5858
});
5959

60+
it('wraps typed phase results in the savePhaseResult payload contract', async () => {
61+
const client = new ConvexClientWrapper('https://deployment.example');
62+
63+
mutationMock.mockResolvedValueOnce(undefined);
64+
65+
await client.savePhaseResult('integration-id', 6, {
66+
metadata: {
67+
integrationId: 'integration-id',
68+
repoUrl: '/repo',
69+
paper: 'Attention Is All You Need',
70+
algorithm: 'FlashAttention',
71+
createdAt: '2026-05-25T00:00:00.000Z',
72+
},
73+
summary: {
74+
status: 'completed',
75+
confidence: 95,
76+
changesMade: 2,
77+
filesModified: ['src/model.py'],
78+
newFiles: ['src/flash_attention.py'],
79+
},
80+
whatChanged: 'Replaced attention blocks.',
81+
why: 'Improves throughput.',
82+
observedImpact: {
83+
metricsComparison: {
84+
speedup: 1.2,
85+
numerical_correctness: {
86+
status: 'passed',
87+
},
88+
},
89+
meetsExpectations: true,
90+
},
91+
riskNotes: [],
92+
diffPreview: 'Modified: src/model.py',
93+
testResults: {
94+
unitTestsPassed: true,
95+
benchmarkResults: {
96+
speedup: 1.2,
97+
},
98+
},
99+
recommendation: {
100+
action: 'approve',
101+
confidence: 95,
102+
notes: 'Ready for integration.',
103+
},
104+
});
105+
106+
expect(mutationMock).toHaveBeenCalledWith(
107+
'integrations:savePhaseResult',
108+
expect.objectContaining({
109+
id: 'integration-id',
110+
payload: {
111+
field: 'phase6Result',
112+
result: expect.objectContaining({
113+
diffPreview: 'Modified: src/model.py',
114+
recommendation: expect.objectContaining({ action: 'approve' }),
115+
}),
116+
},
117+
authKey: 'convex-secret',
118+
}),
119+
);
120+
});
121+
60122
it('waitForApproval only resolves true on explicit approved action for phase', async () => {
61123
vi.useFakeTimers();
62124
const client = new ConvexClientWrapper('https://deployment.example');

agent/src/api/convex.ts

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
import { ConvexHttpClient } from 'convex/browser';
2+
import type {
3+
MappingResult,
4+
PatchResult,
5+
RepoAnalysisResult,
6+
ResearchSpecResult,
7+
ValidationResult,
8+
} from '../bridges/python-subprocess.js';
9+
import type { Phase6Report } from '../phases/phase6-report.js';
210
import { logger } from '../utils/logger.js';
311
import { config } from '../utils/config.js';
412

@@ -18,6 +26,22 @@ export type IntegrationStatus =
1826

1927
export type ExecutionMode = 'step_approval' | 'autonomous';
2028

29+
export type PhaseResultField =
30+
| 'phase1Result'
31+
| 'phase2Result'
32+
| 'phase3Result'
33+
| 'phase4Result'
34+
| 'phase5Result'
35+
| 'phase6Result';
36+
37+
export type PersistedPhaseResult =
38+
| RepoAnalysisResult
39+
| ResearchSpecResult
40+
| MappingResult
41+
| PatchResult
42+
| ValidationResult
43+
| Phase6Report;
44+
2145
export interface Integration {
2246
_id: string;
2347
repoUrl: string;
@@ -27,12 +51,12 @@ export interface Integration {
2751
mode: ExecutionMode;
2852
yoloMode?: boolean;
2953
currentPhase: number;
30-
phase1Result?: unknown;
31-
phase2Result?: unknown;
32-
phase3Result?: unknown;
33-
phase4Result?: unknown;
34-
phase5Result?: unknown;
35-
phase6Result?: unknown;
54+
phase1Result?: RepoAnalysisResult;
55+
phase2Result?: ResearchSpecResult;
56+
phase3Result?: MappingResult;
57+
phase4Result?: PatchResult;
58+
phase5Result?: ValidationResult;
59+
phase6Result?: Phase6Report;
3660
confidence?: number;
3761
createdAt: number;
3862
updatedAt: number;
@@ -64,6 +88,25 @@ export class ConvexClientWrapper {
6488
private client: ConvexHttpClient;
6589
private authKey: string;
6690

91+
private getPhaseResultField(phase: number): PhaseResultField {
92+
switch (phase) {
93+
case 1:
94+
return 'phase1Result';
95+
case 2:
96+
return 'phase2Result';
97+
case 3:
98+
return 'phase3Result';
99+
case 4:
100+
return 'phase4Result';
101+
case 5:
102+
return 'phase5Result';
103+
case 6:
104+
return 'phase6Result';
105+
default:
106+
throw new Error(`Unsupported phase result field for phase ${phase}`);
107+
}
108+
}
109+
67110
private getAuthArgs(): { authKey: string } {
68111
if (!this.authKey) {
69112
throw new Error(`${CONVEX_AUTH_ENV_KEY} is not configured`);
@@ -134,12 +177,14 @@ export class ConvexClientWrapper {
134177
logger.info('Updated integration status', { id, status, phase, ...details });
135178
}
136179

137-
async savePhaseResult(id: string, phase: number, result: unknown): Promise<void> {
138-
const field = `phase${phase}Result`;
180+
async savePhaseResult(id: string, phase: number, result: PersistedPhaseResult): Promise<void> {
181+
const field = this.getPhaseResultField(phase);
139182
await this.callMutation('integrations:savePhaseResult', {
140183
id,
141-
field,
142-
result,
184+
payload: {
185+
field,
186+
result,
187+
},
143188
updatedAt: Date.now(),
144189
});
145190
}

0 commit comments

Comments
 (0)