docs(hooks): document pre_spawn + add URL-injection examples#33
Open
hefgi wants to merge 3 commits into
Open
Conversation
pre_spawn has been supported by the binary since 0.2.14 but was omitted from docs/src/hooks.md — the canonical hooks reference on ecluse.ai. A user reading the docs would see only pre_up, pre_down, post_up, and post_down and reach for post_up to inject slot-specific URLs into config files that services read at boot — which fires too late, so the service boots against stale env. This commit brings the reference up to five hooks and adds the "which hook when" guidance that would have unblocked the users who figured pre_spawn out from the binary strings: - Add pre_spawn to the header sample. - Update the lifecycle diagram to show pre_spawn between the worktree/.env.ecluse setup and the native service spawn, so readers see WHY it's the hook for pre-boot config writes. - Add a dedicated `## pre_spawn` section explaining that this is where slot-aware setup goes: writing .env.local / .dev.vars with URLs containing $ECLUSE_*_PORT, waiting for postgres, running migrations and code generation that services import at startup. Explicitly contrast against post_up (too late for boot-time reads). - Expand the environment table to include pre_spawn (worktree root, full env, native services not yet started). - Add three canonical examples: slot-specific URL injection into a frontend's .env.development.local, wait-for-postgres + migrate, and awk-rewrite of .env.local with slot-derived values (matching the pattern real users are using in their configs). - Add a "which hook when" decision table so readers don't have to extrapolate from prose.
…norepo
Zero examples in the repo demonstrated pre_spawn. The single most common
real-world pattern — inject slot-specific URLs into a .env file that a
Next.js / Vite / Cloudflare-workers frontend reads at boot — was
completely absent. Users who needed this either:
(a) discovered pre_spawn from the binary strings or PR history, or
(b) tried post_up first, saw services boot against stale env, and had
to debug from silence.
nextjs-hybrid (single-app case):
- pre_spawn writes .env.local with DATABASE_URL, waits for postgres,
applies migrations — all before Next.js boots. Moves the prisma
migrate call from post_up (where the app would query a missing
schema during the migration window) to pre_spawn.
- inherit_env switches to mode = "copy" for .env.local so the rewrite
doesn't leak across worktrees.
- README explains WHY pre_spawn is the right hook here (Prisma reads
DATABASE_URL once at boot) and WHY copy mode is required for
.env.local (the rewrite would clobber siblings under symlink).
t3-monorepo (multi-app case with cross-service URLs):
- pre_spawn writes NEXT_PUBLIC_API_URL and NEXT_PUBLIC_WEB_URL into
.env.local so all three Next.js apps (api, web, worker) read the
correct per-slot endpoints at boot. INTERNAL_API_URL is set for the
worker's server-side calls back to the api.
- post_up keeps the prisma migrate (Prisma's default retry-on-first-
query handles the brief boot-time race here).
- Same .env.local copy-mode note as nextjs-hybrid.
- README's "App config" section is rewritten: previously it showed the
URLs as if the user had to hand-write them in .env; now it correctly
identifies pre_spawn as the source and explains the interaction with
inherit_env.
Agents loading the ecluse skill previously saw pre_spawn mentioned only in passing (config-block sample, error-code reference). No guidance on when to reach for it vs. post_up. The result: agents defaulted to post_up for URL injection and slot-specific .env writes, which fires after the services have already booted with stale env. Replace the short paragraph on hooks with a five-row table (one row per lifecycle point) covering when each fires, what env is available, and what it's for. Add an explicit rule-of-thumb: "if a service reads the thing you're setting up at boot, use pre_spawn; otherwise post_up." Concrete examples in-line — NEXT_PUBLIC_API_URL, wait-for-postgres, prisma generate — so agents recognize the shapes they're likely to hit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
pre_spawnhas been supported by the binary since 0.2.14 but is undocumented on ecluse.ai:docs/src/hooks.md— the canonical hooks reference — lists onlypre_up,post_up,pre_down,post_down. Zero examples use it. Users who need slot-aware setup that must complete before their app boots (writing per-worktree.env.localwith aDATABASE_URLderived from$ECLUSE_POSTGRES_PORT, waiting for postgres, runningprisma generate) either discoverpre_spawnfrom binary strings / PR history, or trypost_upfirst and debug from silence when their app boots against stale env.This is the most-used hook in real ecluse configs I've seen — my own personal configs in
/Users/fja/Desktop/Hefgi/ai-overlays/ai-overlays-enzyme/onyx/and/Users/fja/Desktop/Hefgi/Rubbr/context-generator/both rely on it, and both carry inline warnings that readpre_spawn is undocumented on ecluse.ai. This PR closes that gap.Commit 1:
docs(hooks): document pre_spawnRewrites
docs/src/hooks.mdto five hooks:pre_spawnbetween.env.eclusewrite and native service spawn — the crucial "before app boots" positioning## pre_spawnsection explaining the "before services read config" property, with an explicit contrast againstpost_up.env.development.local, wait-for-postgres + migrate, andawkrewrite of.env.localwith slot-derived valuesCommit 2:
examples: showcase pre_spawn URL injectionAdds
pre_spawnto two representative examples:examples/nextjs-hybrid(single-app case): writesDATABASE_URLand applies migrations inpre_spawnbefore Next.js boots. Moves prisma migrate out ofpost_up(where it races the app's first query against a missing schema) intopre_spawn.inherit_envswitches tomode = "copy"for.env.localso the rewrite doesn't clobber sibling worktrees. README explains WHY.examples/t3-monorepo(multi-service URL-injection case): writesNEXT_PUBLIC_API_URL,NEXT_PUBLIC_WEB_URL,INTERNAL_API_URLalongsideDATABASE_URL/REDIS_URLinto.env.localbefore any of the three Next.js apps boot. Keeps prisma migrate inpost_up(its consumer is retry-tolerant). Rewrites the "App config" README section which previously showed the URLs as if the user had to hand-write them in.env.Commit 3:
docs(skill): "which hook when" guidanceReplaces the passing-mention paragraph in SKILL.md with a five-row table and an explicit rule of thumb: "if a service reads the thing you're setting up at boot, use
pre_spawn. Otherwisepost_up." Concrete examples in-line so agents recognize the pattern before they've hit the bug.Test plan
Docs-only change plus TOML rewrites in two examples. Verified locally:
cargo fmt --checkcleancargo clippy --all-targets -- -D warningscleancargo test --bin ecluse— 446 passing (unchanged)Both updated example
.ecluse.tomlfiles parse and validate via ecluse's own deserializer (the tests exercise this path)Verify docs render correctly on ecluse.ai after merge
Confirm the two example READMEs render correctly on GitHub
Notes
Branched from
origin/mainatb3c2b84(tip ofmainpost-PR #31), not from an active feature branch. Work was done in a sibling git worktree../ecluse-docs-pre-spawnso this branch shares no state with any other agent's in-progress work.