Skip to content

Commit 2f2baef

Browse files
authored
feat(growth): add synthetic Dawn research app for LangSmith (#1003)
1 parent 4ceea5e commit 2f2baef

36 files changed

Lines changed: 4447 additions & 0 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,7 @@ keys/
8080

8181
# Aborted fixture-record runs strand raw recordings under a committed path
8282
**/e2e/fixtures/.staging/
83+
84+
# Growth research generated deployment artifacts
85+
apps/growth-research/.dawn/
86+
apps/growth-research/.deployment/

apps/growth-research/.env.example

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Runtime names only. Never copy environment files into the deployment artifact.
2+
OPENAI_API_KEY=
3+
DAWN_DATABASE_URL=
4+
GROWTH_RESEARCH_TEST_DATABASE_URL=
5+
# Explicit operator-only synthetic invocation gate; blank disables model calls.
6+
GROWTH_RESEARCH_FIXTURE_MODE=
7+
# Trusted synthetic memory slot: atlas or beacon. Not an authenticated tenant ID.
8+
GROWTH_RESEARCH_FIXTURE_SLOT=
9+
# Optional cancellation probe pause; integer 0..5000, default 0.
10+
GROWTH_RESEARCH_FIXTURE_DELAY_MS=
11+
GROWTH_RESEARCH_URL=
12+
LANGSMITH_API_KEY=

apps/growth-research/README.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Growth research compatibility application
2+
3+
Private synthetic Dawn application, separate from lifecycle and the Python cockpit.
4+
Published Dawn packages are pinned to `0.8.24`; the app and deployment require Node 24.
5+
The only public graph ID is `growth_research`, pointing to the unchanged generated
6+
Dawn `/enrichment/research#agent` entry. The safe alias avoids slash/hash routing
7+
failures in the Agent Server's internal per-graph HTTP endpoints. Its registered researcher is
8+
private to coordinator delegation; staging verifies the known generated specialist
9+
entry but removes its standalone public graph key.
10+
11+
This app exercises authored plans, skills, scoped delegation, candidate memory and
12+
platform thread continuation against a fixed synthetic corpus. It is disabled by
13+
default and has no connection to Growth ingestion or campaign delivery. Configure
14+
a dedicated memory database; do not point it at Growth's canonical database.
15+
16+
Dawn 0.8.24 sets the child checkpointer to `false`; `task` accepts only `subagent`
17+
and `input`. Each delegation starts a fresh child conversation. Carry relevant
18+
context explicitly through the checkpointed parent when delegating follow-up work.
19+
20+
From the workspace root on Node 24:
21+
22+
```sh
23+
npm ci --ignore-scripts
24+
npx nx test growth-research
25+
npx nx run growth-research:check
26+
npx nx lint growth-research
27+
npx nx build growth-research
28+
```
29+
30+
The build uses the CLI resolved from this application and checks its version before
31+
execution. Dawn emits a LangSmith entry under `.dawn/build`. Packaging preserves
32+
that entry and the relative `src/` and `dawn.config.ts` layout, stages approved files
33+
under `.deployment`, and normalizes `langgraph.json` to Node 24, `dependencies: ["."]`
34+
and `env: {}`. Configure secret values in the deployment environment. Generated
35+
`.dawn/routes/*/tools.json` schemas are preserved because actual tool execution needs
36+
them; arbitrary build files and all environment files remain excluded. The artifact
37+
pins Agent Server `api_version: "0.13.4"` and contains a standalone NodeNext
38+
`tsconfig.json` for the official server's static schema extractor. It does not inherit
39+
the monorepo's compiler configuration or path aliases.
40+
41+
`deployment-package-lock.json` is the standalone runtime dependency lock. To update
42+
it after changing direct dependencies, use `deploymentManifest()` from
43+
`scripts/package-langsmith.mts` to write a temporary standalone `package.json`, run
44+
`npm install --package-lock-only --ignore-scripts --workspaces=false` there, and copy
45+
its lock to `deployment-package-lock.json`. The build rejects stale direct dependency
46+
locks. Do not copy the monorepo lock or workspace dependencies into the artifact.
47+
The workspace lock keeps this app's dependency tree nested so its testing helpers
48+
and runtime resolve Dawn 0.8.24 while lifecycle retains Dawn 0.8.21.
49+
50+
Set `GROWTH_RESEARCH_FIXTURE_MODE=synthetic-only` explicitly to permit model calls.
51+
The default blocks them. The fixed corpus contains `atlas` and `beacon`; tools accept
52+
only those identifiers and cannot fetch URLs, read arbitrary files or execute shell
53+
commands. The specialist is explicitly registered with delegation denied by default
54+
and only that specialist allowed. It can read fixtures but is denied the shared
55+
coordinator summary tool. Planning and skill instructions are authored beside the
56+
coordinator route.
57+
58+
For a local active-child cancellation probe, the operator may set
59+
`GROWTH_RESEARCH_FIXTURE_DELAY_MS` to an integer from 0 to 5000. It defaults to zero;
60+
the model cannot choose a delay. The fixture tool cooperatively observes cancellation
61+
while paused and rechecks both cancellation and fixture mode before returning data.
62+
63+
The public Dawn `seedModelImporter` bootstrap installs a process-wide bounded
64+
OpenAI model for this isolated app. Every request is gated, including cached models.
65+
It uses `gpt-4.1-mini`, a 1,024-token output cap, zero provider retries and a 20-second
66+
request timeout. Credential-free schema extraction can construct the model with a
67+
construction-only placeholder; invocation and actual HTTP fetch reject absent real
68+
credentials, so the placeholder is never sent. Route recursion is limited to 12 steps and Dawn retries to one
69+
attempt. These are compatibility-probe bounds, not a shared spending reservation or
70+
production provider selection. Provider-free tests inspect actual request bodies,
71+
verify one request on a retryable failure, and observe a stalled request timing out.
72+
73+
Candidate memory uses an explicit lazy pgvector store via `DAWN_DATABASE_URL`, with
74+
8-dimensional deterministic synthetic embeddings. Generated `remember` writes are
75+
candidates and normal `recall` excludes them. Missing database configuration fails
76+
when durable memory is accessed; there is no SQLite fallback. The eager prompt index
77+
is explicitly disabled with `indexMaxEntries: 0`; a zero-result search returns an
78+
empty list without opening a connection. This allows credential-free graph import
79+
and packaging while positive-limit recall and all writes still require the database.
80+
This index setting is necessary because Dawn 0.8.24 does not consult `memory.enabled`
81+
when a route-local memory declaration exists.
82+
83+
Run the separate, uncached integration target only against a disposable database:
84+
85+
```sh
86+
GROWTH_RESEARCH_TEST_DATABASE_URL='postgres://…' npx nx run growth-research:test-memory-integration
87+
```
88+
89+
The probe requires that variable and never falls back to a production URL. It uses
90+
fresh child processes for generated candidate writes, active recall, slot isolation
91+
and deletion, and deletes only the fixture record it created. Memory namespaces use
92+
the stable `growth-research` workspace and route plus a server-owned `GROWTH_RESEARCH_FIXTURE_SLOT` (`atlas` or
93+
`beacon`, default `atlas`). These are trusted synthetic deployment slots, not
94+
authenticated account identities. The explicit workspace remains stable across source,
95+
staging and relocated deployment directories, which the subprocess test verifies.
96+
Dawn's scope callback has no authenticated user;
97+
production tenancy still requires separate application-owned authorization. Synthetic
98+
hash embeddings do not establish semantic retrieval quality for live data.
99+
100+
Build, staging, standalone installation, and native Node graph import require no
101+
model or database credentials. Native import is only a packaging check; run server
102+
and cloud smoke checks separately to exercise the deployment boundary. The server's static
103+
schema extractor still emits a nonfatal `Unsupported type: never` diagnostic; the
104+
tested runtime operations succeeded despite it. Fast tests use the public Dawn
105+
harness and a local mock model; memory persistence is verified separately against
106+
PostgreSQL.
107+
108+
Run the fast and database suites sequentially: Dawn's local testing harness uses a
109+
shared checkpoint file, so overlapping those commands can produce a SQLite lock
110+
error. This does not change the deployed graph's LangSmith checkpoint ownership or
111+
its separate pgvector memory store.
112+
113+
Agent Server `0.13.4-node24` can acknowledge interruption before its JavaScript child
114+
stops, allowing a later result checkpoint. The generated Dawn graph cancels when a
115+
live `config.signal` is supplied; the official JS sidecar does not forward that
116+
signal. No vendor patch is included. Cancellation and protection against writes
117+
after cancellation remain failed live-use gates. The smoke client's cleanup command
118+
refuses interrupted threads; an operator must independently establish worker
119+
quiescence before deleting those records. A terminal run status alone is insufficient.
120+
Deploy the verified artifact with the official CLI `0.4.21` source archive layout
121+
and the LangSmith control-plane source-upload API. Updates should target the existing deployment ID:
122+
request its upload URL, upload only the verified `.deployment` archive, and submit
123+
the returned object path with `revision_source: "internal_source"`,
124+
`langgraph_config_path: "langgraph.json"`, and `install_command: "npm ci --ignore-scripts"`.
125+
The signed upload requires `Content-Type: application/gzip` and
126+
`X-Goog-Content-Length-Range: 0,209715200`. Configure secrets through the deployment
127+
API; never include an environment file in the archive. Re-enabling synthetic model
128+
tests requires both a provider key and the explicit fixture-mode value. Do not wire
129+
real Growth signals into this deployment until its remaining live-use gates pass.
130+
131+
The uncached platform smoke target takes positional fixture, thread and correlation
132+
identifiers. Set `GROWTH_RESEARCH_URL`, `LANGSMITH_API_KEY` when authentication is
133+
required, and the explicit fixture-mode gate in the operator environment:
134+
135+
```sh
136+
npx nx run growth-research:smoke-langsmith -- direct THREAD_UUID SMOKE_ID
137+
```
138+
139+
Other phases are `delegated`, `memory`, `continuation`, and `cleanup`. Continuation
140+
uses the same thread and smoke ID after a direct run; cleanup verifies ownership
141+
and rejects active or interrupted runs, then deletes the fixture thread and verifies
142+
absence. Interrupted fixtures require the separate operator procedure described above.
143+
144+
This application is restricted to synthetic compatibility work. It does not collect
145+
real people or companies, publish account facts, or dispatch campaigns. Live use still
146+
requires trusted scopes, source controls, budget enforcement, a durable Growth work
147+
ledger, publication validation and cross-store deletion safeguards.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { DawnConfig } from '@dawn-ai/core';
2+
import './src/runtime/model-boundary.js';
3+
import { candidateMemoryStore, syntheticEmbedder, trustedFixtureScope } from './src/runtime/memory-store.js';
4+
5+
export default {
6+
appDir: 'src/app',
7+
build: { targets: ['langsmith'] },
8+
toolOutput: { noOffloadTools: ['readFixture', 'coordinatorSummary', 'readSkill', 'writeTodos', 'recall', 'remember'] },
9+
summarization: { enabled: false },
10+
memory: {
11+
store: candidateMemoryStore,
12+
indexMaxEntries: 0,
13+
writes: 'candidate',
14+
vector: { embedder: syntheticEmbedder },
15+
resolveScope: trustedFixtureScope,
16+
episodes: { enabled: false },
17+
},
18+
} satisfies DawnConfig;

0 commit comments

Comments
 (0)