T-B7GN-sdf-locate-sdlc-cli
State: closed/done · Impact: high · Complexity: medium
Every shell-out from SDF to the sdlc CLI hardcodes
bun run solutions/ontological/cli/sdlc.ts … with the project root as the
working directory. That only works when the registered project is this
monorepo. For any other checkout the file does not exist and the spawn
fails. SDF needs one seam that finds the sdlc checkout the same way the
bash launcher does and invokes it with --project-root <path>, so the
watch probe, the dashboard spawn, and the setup action in T-1D8N all
work against a foreign project.
| Location | Role today |
|---|---|
solutions/ontological/desktop/src-tauri/src/backend.rs#sdlc | Spawns bun run solutions/ontological/cli/sdlc.ts dashboard … with current_dir(root). |
solutions/ontological/desktop/src-tauri/src/actions/watch_probe.rs#probe_argv | Builds the same hardcoded argv for orchestrate watch --dry-run --output json. |
solutions/ontological/desktop/src-tauri/src/actions/spawn.rs | The Spawner trait and RecordingSpawner test double; runs an Invocation from a cwd. |
solutions/ontological/cli/sdlc | The bash launcher. Resolves the checkout in order: SDLC_HOME, a checkout above $PWD, the link at <config>/sdlc/home, then its own siblings. |
packages/rust/fleet/src/lib.rs#standalone | The Rust fleet SDK’s standalone("sdlc").config() gives the same <config>/sdlc directory the launcher reads home from. |
solutions/ontological/cli/registry_adapter.ts | Every registry op accepts --project-root <path>; dashboard start accepts it too. |
Proposed
Section titled “Proposed”A new module actions/sdlc_cli.rs with two pure functions and one
resolver:
resolve_checkout() -> Result<PathBuf, String>:SDLC_HOMEwhen it is a checkout, else thehomefile underFleet::standalone("sdlc").config(), else a checkout above SDF’s own cwd. A checkout is a directory containingsolutions/ontological/cli/sdlc.ts. The error names every location it tried.invocation(checkout, verb_args, project_root) -> Invocation: programbun, argsrun <checkout>/solutions/ontological/cli/sdlc.ts <verb…> --project-root <project_root>.- Callers run the invocation with
cwd = checkout, never the project, so Bun resolves the checkout’snode_modules.
backend.rs#sdlc is rewritten onto it and watch_probe.rs#probe_argv is
deleted, its callers going through the shared builder instead. The spawn sets
no environment: the launcher’s announce line already goes to stderr, so JSON
stdout is clean without silencing anything.
Approach
Section titled “Approach”- Write
actions/sdlc_cli.rswithis_checkout,resolve_checkout,invocation, and arun(verb_args, project_root, spawner)helper that resolves and spawns in one call. The resolver takes its layers as aLayersparameter so unit tests construct them directly against a tempdir checkout and never touch process environment. - Delete
watch_probe::probe_argvand routewatch_probe::probethroughsdlc_cli::run. ItsRecordingSpawnertests assert the literal argv, including--project-root, rather than comparing against a helper. - Rewrite
backend::sdlcandbackend::ensureto resolve the checkout and rundashboard startwith--project-rootpointing at the checkout itself (the dashboard server scopes per request; its own root is only its fallback). Delete the dev-checkout walkT-FW4Zleft there. - Add a
SdlcCliUnavailablevariant toAppErrorso the action surface can report “no sdlc checkout found” distinctly from an op failure. - Document the resolution order in the SDF README under Commands.
| Area | Note |
|---|---|
solutions/ontological/desktop/src-tauri/src/actions/sdlc_cli.rs | Resolver, argv builder, run helper, unit tests. |
solutions/ontological/desktop/src-tauri/src/actions/mod.rs | Declare the module. |
solutions/ontological/desktop/src-tauri/src/actions/watch_probe.rs | Build argv through sdlc_cli; tests assert --project-root. |
solutions/ontological/desktop/src-tauri/src/backend.rs | sdlc and ensure go through the resolver. |
solutions/ontological/desktop/src-tauri/src/schema/mod.rs | Add the SdlcCliUnavailable error variant. |
solutions/ontological/desktop/README.md | Document how SDF finds the CLI. |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
grep -rn "solutions/ontological/cli/sdlc.ts" solutions/ontological/desktop/src-tauri/srcmatches onlyactions/sdlc_cli.rs. - AC-2: With
SDLC_HOMEpointing at a fake checkout in a tempdir,resolve_checkout()returns it; withSDLC_HOMEunset and ahomefile under the fleet config tier, it returns that path; with neither and no checkout above cwd, it returns an error naming all three locations tried. - AC-3:
watch_probe::probeagainst a registered project at/tmp/xproduces argv ending inorchestrate watch --dry-run --output json --project-root /tmp/x, asserted withRecordingSpawner. - AC-4: From a registered project that is not this monorepo,
orchestrate_watch_probe(project_id)returns a verdict instead of a spawn error (manual check, noted in the PR). - AC-5:
moon run sdf:checkandmoon run sdf:testpass.
Out of scope
Section titled “Out of scope”- Bundling the CLI or a Bun runtime into the desktop app; SDF still requires
bunon PATH and an sdlc checkout on the machine. - Any new action (
T-1D8N). gitandghspawns incontrol_plane/andsolutions/ontological/desktop/src-tauri/src/actions/refresh_leases.rs, which do not involve the sdlc CLI.
Dependencies
Section titled “Dependencies”T-FW4Zremoves the ambient root thatbackend::ensurereads today; this task replaces what it leaves behind.
Discovery context
Section titled “Discovery context”Found while surveying the SDLC setup action for SDF (2026-09-20): the
hardcoded argv in backend.rs and watch_probe.rs was the one blocker to
running any sdlc verb against a project that is not this repo.
Depends on
Section titled “Depends on”T-FW4Z