Skip to content

docs(hooks): document pre_spawn + add URL-injection examples#33

Open
hefgi wants to merge 3 commits into
mainfrom
docs/pre-spawn-hook
Open

docs(hooks): document pre_spawn + add URL-injection examples#33
hefgi wants to merge 3 commits into
mainfrom
docs/pre-spawn-hook

Conversation

@hefgi

@hefgi hefgi commented Jul 7, 2026

Copy link
Copy Markdown
Owner

pre_spawn has been supported by the binary since 0.2.14 but is undocumented on ecluse.ai: docs/src/hooks.md — the canonical hooks reference — lists only pre_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.local with a DATABASE_URL derived from $ECLUSE_POSTGRES_PORT, waiting for postgres, running prisma generate) either discover pre_spawn from binary strings / PR history, or try post_up first 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 read pre_spawn is undocumented on ecluse.ai. This PR closes that gap.

Commit 1: docs(hooks): document pre_spawn

Rewrites docs/src/hooks.md to five hooks:

  • Header sample and lifecycle diagram now include pre_spawn between .env.ecluse write and native service spawn — the crucial "before app boots" positioning
  • Dedicated ## pre_spawn section explaining the "before services read config" property, with an explicit contrast against post_up
  • Environment-availability table extended (worktree root, full env, native services not yet started)
  • Three canonical examples: URL injection into a frontend's .env.development.local, wait-for-postgres + migrate, and awk rewrite of .env.local with slot-derived values
  • New "which hook when" decision table so readers don't have to extrapolate from prose

Commit 2: examples: showcase pre_spawn URL injection

Adds pre_spawn to two representative examples:

  • examples/nextjs-hybrid (single-app case): writes DATABASE_URL and applies migrations in pre_spawn before Next.js boots. Moves prisma migrate out of post_up (where it races the app's first query against a missing schema) into pre_spawn. inherit_env switches to mode = "copy" for .env.local so the rewrite doesn't clobber sibling worktrees. README explains WHY.

  • examples/t3-monorepo (multi-service URL-injection case): writes NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WEB_URL, INTERNAL_API_URL alongside DATABASE_URL/REDIS_URL into .env.local before any of the three Next.js apps boot. Keeps prisma migrate in post_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" guidance

Replaces 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. Otherwise post_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 --check clean

  • cargo clippy --all-targets -- -D warnings clean

  • cargo test --bin ecluse — 446 passing (unchanged)

  • Both updated example .ecluse.toml files 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/main at b3c2b84 (tip of main post-PR #31), not from an active feature branch. Work was done in a sibling git worktree ../ecluse-docs-pre-spawn so this branch shares no state with any other agent's in-progress work.

hefgi added 3 commits July 7, 2026 16:30
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant