Skip to content

Consolidate construct + constructd into one binary#396

Merged
edwin-zvs merged 1 commit into
mainfrom
consolidate-construct-daemon
Jun 8, 2026
Merged

Consolidate construct + constructd into one binary#396
edwin-zvs merged 1 commit into
mainfrom
consolidate-construct-daemon

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

What

Consolidates the daemon (constructd) and the TUI/CLI client (construct) into a single executable. construct runs the TUI by default and the daemon under construct daemon run. The standalone constructd binary stays as a thin back-compat aliasconstructd run and construct daemon run are equivalent and share 100% of their code path.

This consolidates the executable, not the process: the daemon is still a separate long-lived process that many clients attach to. Running construct does not embed/auto-spawn a daemon, and running it multiple times yields multiple clients against one daemon — never multiple daemons.

How

  • Daemon runtime promoted to a library (crates/daemon/src/lib.rs), exposing run, init_tracing, print_paths, DEFAULT_CONFIG_TOML. constructd's main.rs becomes a ~45-line shim over it (−466 lines).
  • construct gains a daemon subcommand (crates/cli now depends on the agentd lib). Daemon mode is dispatched before client tracing init and socket discovery, so the daemon's verbose log filter applies and it owns the socket rather than connecting to it.
  • Restart stays correct. session::request_daemon_restart re-execs by replaying std::env::args().skip(1) verbatim, so construct daemon run … re-execs construct daemon run … and constructd run re-execs constructd run. A symlink / argv[0]-multiplex approach was rejected because resolving a constructdconstruct symlink would drop the name while the replayed args still assume daemon dispatch — breaking upgrade-restart.
  • Docs + installer updated to lead with construct daemon run; design recorded in specs/0026-single-binary-daemon-and-client.md.

Verification

  • cargo build (full workspace) ✓ · cargo test -p agentd → 66 passed ✓
  • construct daemon default-config is byte-identical to constructd default-config
  • Isolated end-to-end: boot via construct daemon run, construct ping → pong, construct list shows the orchestrator ✓; same via constructd run
  • Restart self-exec(): triggered daemon.restart over IPC against a daemon started with construct daemon run → re-execs the construct binary, PID preserved, socket rebinds, construct ping works after, two listening log lines ✓

Notes

  • The unified construct binary now links both dependency sets (TUI + daemon server/tunnel); most heavy deps are already shared. We ship one file instead of two.
  • Adapters and construct-mcp are intentionally not merged — they're a separate process/plugin boundary.
  • constructd is still built and shipped, so existing installs/scripts and the atomic-rename upgrade layout keep working. Dropping it entirely (and from install.sh's BINS) is a trivial, separate follow-up if desired.

🤖 Generated with Claude Code

The daemon and client now ship as a single executable. `construct`
runs the TUI by default and the daemon under `construct daemon run`;
the standalone `constructd` binary stays as a thin back-compat alias.

- Promote the daemon's runtime to a library (`crates/daemon/src/lib.rs`)
  exposing `run`, `init_tracing`, `print_paths`, `DEFAULT_CONFIG_TOML`.
  `constructd`'s `main.rs` becomes a thin shim over it.
- Add a `daemon` subcommand to `construct` (`crates/cli`) that dispatches
  to the shared lib. Daemon mode is handled before client tracing init and
  socket discovery so the daemon's verbose log filter applies and it owns
  the socket rather than connecting to it.
- Self-restart replays argv verbatim, so `construct daemon run` re-execs
  `construct daemon run` and `constructd run` re-execs `constructd run`.
  This is why a symlink/argv[0] multiplex was rejected (it would drop the
  name on restart). Verified: restart re-execs the `construct` binary,
  preserves PID, rebinds the socket, ping works.
- Update README, architecture docs, and install.sh to lead with
  `construct daemon run`; add spec 0026.

This consolidates the executable, not the process: the daemon stays a
separate long-lived process; running `construct` does not embed a daemon,
and running it multiple times yields multiple clients against one daemon.
@edwin-zvs
edwin-zvs merged commit 4ce07e0 into main Jun 8, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the consolidate-construct-daemon branch June 8, 2026 01:15
edwin-zvs added a commit that referenced this pull request Jun 8, 2026
The daemon has had no unique role since #396/#398: `construct daemon run`
runs it and the TUI auto-starts one. `constructd` was just a thin alias,
so drop it — the daemon crate (`agentd`) is now library-only and the
daemon ships solely inside `construct`.

- crates/daemon: remove the `[[bin]] constructd` target and delete the
  `main.rs` shim; the crate is lib-only now.
- e2e harness: spawn `construct daemon run` (and copy `construct` as the
  relocatable daemon binary) instead of `constructd run`.
- scripts/test_agentd.sh, scripts/smoke.sh: launch via `construct daemon run`.
- release.yml + install.sh: drop `constructd` from BINS (and `-p agentd`
  from the release build). The release tarball KEEPS its historical
  `constructd-<target>` asset name — already-released install.sh /
  `construct upgrade` builds fetch that exact name, so renaming it would
  break the download URL.
- Docs/spec/comments updated; spec 0026 records that there is no standalone
  daemon binary and notes the one-time upgrade caveat below.

Upgrade caveat (one-time): an `install.sh` baked into an *older* binary
lists `constructd` in its `BINS` and aborts when it's absent from the new
tarball, so `construct upgrade` across this single version hop fails —
re-run the install one-liner instead. New installs and subsequent upgrades
are unaffected.

Verified: `cargo build --workspace` produces no `constructd`;
`cargo test -p agentd` (66) and the e2e restart suite (3, incl.
`restart_reloads_updated_binary`) pass with the daemon spawned as
`construct daemon run`.
@edwin-zvs edwin-zvs mentioned this pull request Jun 16, 2026
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