Feature/product platform foundation v0.62 #1

Merged
robbond merged 683 commits from feature/product-platform-foundation-v0.62 into feature/emergent-unknowns-v0.5 2026-09-09 07:58:20 +01:00
4 changed files with 114 additions and 3 deletions
Showing only changes of commit 8e941b0c7b - Show all commits
+5 -2
View File
@@ -33,10 +33,13 @@ export async function POST(request) {
);
}
// ── Invoke domain seam with provider resolution ───────────
// ── Invoke domain seam with configured model ──────────────
const result = await synthesizeCurrentUnderstanding(
{ situationGraph, findings },
{ provider: getProvider() }
{
provider: getProvider(),
modelName: process.env.OLLAMA_MODEL ?? null,
}
);
return Response.json({ success: true, currentUnderstanding: result.currentUnderstanding }, { status: 200 });
+55
View File
@@ -2845,3 +2845,58 @@ Fix synthesis route to pass `modelName: process.env.OLLAMA_MODEL`; then integrat
#### Exact first trace question
What is the minimal server-side fix to the synthesis route so it passes `modelName` to `synthesizeCurrentUnderstanding`, enabling live CU reconstruction verification?
---
## v0.50 — FOCUSED-FINDING SYNTHESIS TRIGGER (2026-08-31)
### Model-resolution repair — CLOSED
The synthesis route now supplies the configured `OLLAMA_MODEL` and the domain resolves explicit dependency / config / environment model with correct priority.
**Changes:**
- `app/api/cases/synthesis/route.js` — passes `modelName: process.env.OLLAMA_MODEL ?? null` to `synthesizeCurrentUnderstanding`
- `lib/graph/current-understanding-synthesis.js` — resolves modelName by priority: explicit dep > config dep (assertConfig) > process.env > null
- `tests/graph/current-understanding-synthesis.test.js` — two new tests confirming configured model resolution and explicit override
**Deterministic gate:**
- synthesis + route tests: 54/54 PASS
- focused integration tests: 19/19 PASS
- build: PASS (pre-existing reasoning-workspace warning unchanged)
**Direct API evidence:**
- small canonical fixture → HTTP 200
- exact persisted onboarding investigation:
- 13 nodes, 10 edges, 21 canonical Findings
- 21,301-byte request
- → HTTP 200 in 46.181s
- → coherent reconstructed CU
- → no Evidence append/repetition observed
**Static ownership:**
- one successful focused deconstruction owns one synthesis call
- no automatic duplicate synthesis
- newly derived Findings are included explicitly
- HTTP synthesis failure does not propagate into focused-deconstruction error handling
- no static lifecycle defect was proved
**Final live runtime (classification RUN-A):**
- one focused deconstruction → HTTP 200
- exactly one `/api/cases/synthesis` request → HTTP 200
- Current Understanding visibly replaced
- newly submitted evidence incorporated
- generic workspace error absent
### NEXT BOUNDED ISSUE (for future reference)
#### Name
Focused → global integration — carry synthesized understanding back through case/update path and persist across cold return.
#### Next branch
to be determined
#### Exact first trace question
How does the reconstructed Current Understanding from synthesis reach the ScenarioForm state on cold return without re-fetching or losing narrative continuity?
+10 -1
View File
@@ -213,11 +213,20 @@ export async function synthesizeCurrentUnderstanding(
throw new Error("Invalid dependency: provider must have generateReconstruction");
}
// Resolve configured model: explicit dep > config dep (assertConfig) > process.env > null
let modelName = dependencies.modelName;
if (modelName == null && dependencies.config?.OLLAMA_MODEL != null) {
modelName = dependencies.config.OLLAMA_MODEL;
}
if (modelName == null) {
modelName = process.env.OLLAMA_MODEL ?? null;
}
let rawResponse;
try {
rawResponse = await provider.generateReconstruction(
prompt,
dependencies.modelName ?? null
modelName
);
} catch (error) {
const err = new Error(error.message ?? "Synthesis provider call failed");
@@ -384,6 +384,50 @@ describe("synthesizeCurrentUnderstanding — full seam", () => {
).rejects.toThrow(/OLLAMA_BASE_URL/);
});
// ── Configured model resolution ──────────────────────────
it("default synthesis path resolves configured modelName — provider receives non-null", async () => {
const fake = makeFakeProvider();
// Stub the configured model so test does not depend on dev-machine .env.local
const savedModel = process.env.OLLAMA_MODEL;
process.env.OLLAMA_MODEL = "configured-model-v0.50";
try {
const result = await synthesizeCurrentUnderstanding(
{ situationGraph: canonicalGraph, findings: [] },
{ provider: fake }
);
expect(result.currentUnderstanding).toBe("Synthesized output");
// KEY ASSERTION: configured model must flow to provider
const receivedModel = fake.generateReconstruction.mock.calls[0][1];
expect(receivedModel).toBeDefined();
expect(receivedModel).not.toBeNull();
expect(typeof receivedModel).toBe("string");
expect(receivedModel.length).toBeGreaterThan(0);
} finally {
// Restore original env value (may be undefined)
if (savedModel == null) {
delete process.env.OLLAMA_MODEL;
} else {
process.env.OLLAMA_MODEL = savedModel;
}
}
});
it("explicit modelName dependency overrides default — provider receives injected model", async () => {
const fake = makeFakeProvider();
await synthesizeCurrentUnderstanding(
{ situationGraph: canonicalGraph, findings: [] },
{ provider: fake, modelName: "test-model-v0.50" }
);
expect(fake.generateReconstruction.mock.calls[0][1]).toBe("test-model-v0.50");
});
// ── Provider sees full graph, not just centralStatement ───
it("provider receives full canonical graph content (nodes + edges + centralStatement)", async () => {