Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions apps/growth-research/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ Human semantic review is not replaced by model grading or string matching.

| Finding | Evidence / owning layer | Status and next verification |
| --- | --- | --- |
| Nullable tool fields become required strings | Dawn 0.8.24 compiler JSON schema conversion; observed generated submit schema and failed unknown-field submissions | Upstream core and LangChain conversion regression/fix in progress. Pilot uses the supported authored Zod schema export; a package upgrade must rerun the original extraction probe before declaring the upstream defect released. |
| Nullable tool fields become required strings | Dawn 0.8.24 compiler JSON schema conversion; observed generated submit schema and failed unknown-field submissions | Reproduced with published 0.8.26: compiler extraction of `submitCandidate` still emits required string-only profile fields. Pilot retains the supported authored Zod schema export; this upgrade does not establish an upstream fix. |
| Bound model calls bypass subclass generation hooks | Real bound-model regression in this application | Guards, request counts and JSON usage capture live at the actual provider fetch boundary; generated graph tests verify it. |
| Page capture yields empty, partial, or mostly navigation evidence | Company-only acquisition against the six documented domains | Outcomes retained. Evaluate extraction improvements separately; do not hide failures by swapping cases. |
| Baseline provider rejects billing state | Earlier live baseline synthetic calls returned a classified billing rejection | Historical finding for the retired adapter; failed calls provide no quality comparison. |
Expand All @@ -150,7 +150,11 @@ The local evaluation harness remains isolated from those production jobs.
## Synthetic compatibility deployment

Private synthetic Dawn application, separate from lifecycle and the Python cockpit.
Published Dawn packages are pinned to `0.8.24`; the app and deployment require Node 24.
Published Dawn packages are pinned to `0.8.26`; the app and deployment require Node 24.
The published 0.8.25 release contains no functional package changes; 0.8.26 adds
opt-in server CORS. The tagged core and LangChain sources contain no nullable-schema,
memory-enablement, or delegation-checkpoint fix, so the compatibility workarounds
below remain in place. Historical findings above retain their observed versions.
The only public graph ID is `growth_research`, pointing to the unchanged generated
Dawn `/enrichment/research#agent` entry. The safe alias avoids slash/hash routing
failures in the Agent Server's internal per-graph HTTP endpoints. Its registered researcher is
Expand Down Expand Up @@ -193,8 +197,13 @@ it after changing direct dependencies, use `deploymentManifest()` from
`npm install --package-lock-only --ignore-scripts --workspaces=false` there, and copy
its lock to `deployment-package-lock.json`. The build rejects stale direct dependency
locks. Do not copy the monorepo lock or workspace dependencies into the artifact.
The workspace lock keeps this app's dependency tree nested so its testing helpers
and runtime resolve Dawn 0.8.24 while lifecycle retains Dawn 0.8.21.
The workspace lock pins this app's testing helpers and runtime, together with
lifecycle, to Dawn 0.8.26.
The SDK remains nested under each app so its Zod 4 peer resolves to that app's
version, rather than the root Zod 3 package. Regenerating the stale Dawn dependency lock entries
with peer resolution enabled restores this layout, which clean `npm ci` preserves
with the repository's existing install settings. The packaging suite checks
SDK-local Zod resolution to catch regressions during later lock regeneration.

Set `GROWTH_RESEARCH_FIXTURE_MODE=synthetic-only` explicitly to permit model calls.
The default blocks them. The fixed corpus contains `atlas` and `beacon`; tools accept
Expand Down
124 changes: 62 additions & 62 deletions apps/growth-research/deployment-package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions apps/growth-research/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
"type": "module",
"engines": { "node": "24" },
"dependencies": {
"@dawn-ai/cli": "0.8.24",
"@dawn-ai/core": "0.8.24",
"@dawn-ai/langchain": "0.8.24",
"@dawn-ai/memory": "0.8.24",
"@dawn-ai/memory-pgvector": "0.8.24",
"@dawn-ai/sdk": "0.8.24",
"@dawn-ai/cli": "0.8.26",
"@dawn-ai/core": "0.8.26",
"@dawn-ai/langchain": "0.8.26",
"@dawn-ai/memory": "0.8.26",
"@dawn-ai/memory-pgvector": "0.8.26",
"@dawn-ai/sdk": "0.8.26",
"@langchain/core": "1.2.9",
"@langchain/langgraph": "1.4.14",
"@langchain/langgraph-checkpoint": "1.1.5",
Expand All @@ -20,8 +20,8 @@
"zod": "4.5.4"
},
"devDependencies": {
"@dawn-ai/evals": "0.8.24",
"@dawn-ai/testing": "0.8.24",
"@dawn-ai/workspace": "0.8.24"
"@dawn-ai/evals": "0.8.26",
"@dawn-ai/testing": "0.8.26",
"@dawn-ai/workspace": "0.8.26"
}
}
2 changes: 1 addition & 1 deletion apps/growth-research/scripts/dawn-cli.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const require = createRequire(resolve(appRoot, 'package.json'));
const cli = require.resolve('@dawn-ai/cli');
const metadata = JSON.parse(readFileSync(resolve(dirname(cli), '../package.json'), 'utf8'));
if (metadata.version !== '0.8.24') throw new Error(`Expected app-local Dawn CLI 0.8.24, resolved ${metadata.version}`);
if (metadata.version !== '0.8.26') throw new Error(`Expected app-local Dawn CLI 0.8.26, resolved ${metadata.version}`);
const result = spawnSync(process.execPath, [cli, ...process.argv.slice(2)], { cwd: appRoot, stdio: 'inherit' });
if (result.error) throw result.error;
process.exitCode = result.status ?? 1;
5 changes: 3 additions & 2 deletions apps/growth-research/test/capabilities.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { cp, mkdtemp, mkdir, rm, symlink, writeFile } from 'node:fs/promises';
import { cp, mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
import { linkFixtureDependencies } from './fixture-dependencies.js';
import { tmpdir } from 'node:os';
import { resolve, join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
Expand Down Expand Up @@ -93,7 +94,7 @@ describe('synthetic capability boundary', () => {
await cp(join(appRoot, 'src'), join(temporaryRoot, 'src'), { recursive: true });
await cp(join(appRoot, 'dawn.config.ts'), join(temporaryRoot, 'dawn.config.ts'));
await cp(join(appRoot, 'package.json'), join(temporaryRoot, 'package.json'));
await symlink(join(appRoot, 'node_modules'), join(temporaryRoot, 'node_modules'));
await linkFixtureDependencies(appRoot, temporaryRoot);
const sibling = join(temporaryRoot, 'src/app/enrichment/research/subagents/undeclared');
await mkdir(sibling, { recursive: true });
await writeFile(join(sibling, 'index.ts'), 'import {agent} from "@dawn-ai/sdk"; export default agent({model:"gpt-4.1-mini",systemPrompt:"UNDECLARED_SIBLING_EXECUTED",description:"Forbidden sibling"});');
Expand Down
27 changes: 27 additions & 0 deletions apps/growth-research/test/fixture-dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { mkdir, readFile, symlink } from 'node:fs/promises';
import { createRequire } from 'node:module';
import { dirname, join } from 'node:path';

/** Preserve application resolution in relocated fixtures with nested or hoisted installs. */
export async function linkFixtureDependencies(appRoot: string, fixtureRoot: string): Promise<void> {
const manifest = JSON.parse(await readFile(join(appRoot, 'package.json'), 'utf8'));
const require = createRequire(join(appRoot, 'package.json'));
for (const name of Object.keys({ ...manifest.dependencies, ...manifest.devDependencies })) {
let source: string | undefined;
for (const directory of require.resolve.paths(name) ?? []) {
const candidate = join(directory, name);
try {
if (JSON.parse(await readFile(join(candidate, 'package.json'), 'utf8')).name === name) {
source = candidate;
break;
}
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
}
}
if (!source) throw new Error(`Cannot resolve fixture dependency ${name}`);
const target = join(fixtureRoot, 'node_modules', name);
await mkdir(dirname(target), { recursive: true });
await symlink(source, target, 'dir');
}
}
5 changes: 3 additions & 2 deletions apps/growth-research/test/memory.integration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { execFile } from 'node:child_process';
import { cp, mkdir, mkdtemp, rm, symlink } from 'node:fs/promises';
import { cp, mkdir, mkdtemp, rm } from 'node:fs/promises';
import { linkFixtureDependencies } from './fixture-dependencies.js';
import { tmpdir } from 'node:os';
import { promisify } from 'node:util';
import { dirname, join, resolve } from 'node:path';
Expand Down Expand Up @@ -29,7 +30,7 @@ it('persists candidates across fresh processes, excludes them from active recall
for (const name of ['dawn.config.ts', 'package.json']) await cp(join(appRoot, name), join(relocated, name));
await mkdir(join(relocated, 'scripts'));
await cp(join(appRoot, 'scripts/memory-probe.mts'), join(relocated, 'scripts/memory-probe.mts'));
await symlink(join(appRoot, 'node_modules'), join(relocated, 'node_modules'));
await linkFixtureDependencies(appRoot, relocated);
const read = await probe('read', 'atlas');
expect(read.pid).not.toBe(written.pid);
expect(read.candidateIds).toContain(written.id);
Expand Down
Loading
Loading