from dkdc scripts to netsky source

This post starts at eedcb25, the bootstrap commit from 2026-04-12. That commit already had a bin/ toolchain, five skills under .agents/skills/, and a repo shape that assumed CLI-first agent work. The later Rust rewrite inherited the workflow and made it explicit, typed, and durable (git show eedcb25:bin/check, git show eedcb25:bin/test-rs, git show eedcb25:.agents/skills/up/SKILL.md).

flowchart LR
    A[eedcb25: shell entrypoints] --> B[tmux and operator state]
    B --> C[rewrite]
    C --> D[netsky-cli + netsky-core]
    D --> E[agent bus + meta.db]

the proto #

The bootstrap repo was already opinionated. bin/check was an 8-line wrapper that changed to the repo root, ran bin/check-rs, ran bin/check-py, and printed “All checks passed!” (git show eedcb25:bin/check). The other entrypoints used the same shape:

  • bin/build: shell wrapper over bin/build-rs and bin/build-py (git show eedcb25:bin/build)
  • bin/test: shell wrapper over bin/test-rs and bin/test-py (git show eedcb25:bin/test)
  • bin/format: shell wrapper over bin/format-rs and bin/format-py (git show eedcb25:bin/format)
  • bin/install: shell wrapper over bin/install-rs and bin/install-py (git show eedcb25:bin/install)

A small command vocabulary, not random shell.

The Rust and Python halves were separate on purpose. bin/check-rs ran cargo fmt -- --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace (git show eedcb25:bin/check-rs). bin/check-py ran Ruff, built the Python bindings with uv run maturin develop, stopped there (git show eedcb25:bin/check-py). The repo already split “human wrapper” from “language-specific gate.”

The skill layer was just as small — five skills: up, down, notes, meta, demo (git show eedcb25:.agents/skills/up/SKILL.md, git show eedcb25:.agents/skills/down/SKILL.md, git show eedcb25:.agents/skills/notes/SKILL.md, git show eedcb25:.agents/skills/meta/SKILL.md, git show eedcb25:.agents/skills/demo/SKILL.md). up anchored UTC time, read notes, announced the next session number. down wrote notes and killed the tmux session. notes defined a strict on-disk session format. meta said self-improvement should prefer removal over addition. demo printed one line.

That is enough to see the original shape:

bootstrap repo
├── bin/check
├── bin/build
├── bin/test
├── bin/format
├── bin/install
└── .agents/skills/{up,down,notes,meta,demo}

The proto did real work because it carried three decisions:

  • CLIs were the control surface, not a GUI (eedcb25:bin/check, eedcb25:bin/test)
  • files were the primary interface, not RPC schemas (eedcb25:.agents/skills/notes/SKILL.md)
  • tmux sessions were part of the operating model (eedcb25:.agents/skills/down/SKILL.md)

what broke #

The shell spine worked until the system had to keep several agents alive, route messages, and repair itself.

The failure modes surfaced in the notes before the full rewrite settled. A permission prompt on the old channel path could wedge agent0 for hours while the watchdog reasoned about liveness and recovery (notes/2026/04/14/agentinfinity.md:7-18). A separate note records clone identity leakage through tmux environment state — exactly the bug that appears when correctness depends on shell inheritance instead of a typed runtime boundary (notes/2026/04/13/agent3.md:15-17).

The bootstrap scripts had no first-class topology: no named agent bus, no durable observability store, no one CLI surface that could say “this is a restart marker,” “this is a task row,” “this is a channel envelope.” Commands, not subsystems. Script glue composes well and draws boundaries poorly.

the rewrite #

The Rust rewrite kept the shell posture and moved the contracts into code.

The current command tree makes the subsystem split visible. netsky now has explicit surfaces for analytics, prompts, restart, doctor, channel ops, clone ops, tasks, cron, loops, and the watchdog-related flows (src/crates/netsky-cli/src/cli.rs:63-83, src/crates/netsky-cli/src/cli.rs:523-575, src/crates/netsky-cli/src/cli.rs:586-689). The shell wrappers still exist, but they are wrappers around a typed binary instead of the runtime itself.

Spawn also became a real subsystem. netsky-core renders the prompt, writes it to durable state, prepares per-agent config, allocates directories, starts the tmux session, and then runs any runtime-specific post-spawn step (src/crates/netsky-core/src/spawn.rs:90-140, src/crates/netsky-core/src/runtime/mod.rs:52-79). The old model had “start a session and hope the env is right.” The new model has one spawn path with explicit artifacts.

Communication moved from path convention to protocol. The agent source documents delivery semantics, crash replay, poison handling, and hostile-input validation for the file-backed bus (src/crates/netsky-io/src/sources/agent.rs:1-32, src/crates/netsky-io/src/sources/agent.rs:76-129, src/crates/netsky-io/src/sources/agent.rs:169-186). The path still exists on disk. The important change is that the code now treats the path as a transport, not as an informal mailbox.

Observability got the same treatment. netsky-db defines a durable meta.db with messages, ticks, sessions, workspaces, tasks, tests, watchdog events, and token usage as named tables (src/crates/netsky-db/README.md:1-10, src/crates/netsky-db/README.md:27-48, src/crates/netsky-db/README.md:58-80). netsky analytics daily reads from that store and emits JSON, HTML, and checked-in website pages (src/crates/netsky-cli/src/cmd/analytics.rs:22-53, src/crates/netsky-cli/src/cmd/analytics.rs:138-169).

The current prompt base finally names the system that the bootstrap scripts were reaching toward: S5 policy, S4 intelligence, S3 control, S2 coordination, S1 operations, plus the watchdog, agent0, clones, the ticker, and workspace isolation (src/crates/netsky-prompts/prompts/base.md:20-28, src/crates/netsky-prompts/prompts/base.md:30-49, src/crates/netsky-prompts/prompts/base.md:90-96).

diff-style snippets #

Spawning changed from shell wrappers and tmux habit into a typed runtime path:

- run a bin/ entrypoint
- open or reuse a tmux session
- rely on shell state and operator discipline
+ render prompt
+ write state under ~/.netsky/state/
+ prepare per-agent config
+ spawn runtime-specific tmux session
+ run post_spawn hook

Then: eedcb25:bin/test, eedcb25:.agents/skills/up/SKILL.md Now: src/crates/netsky-core/src/spawn.rs:90-140, src/crates/netsky-core/src/runtime/mod.rs:52-79

Comms changed from “files on disk plus operator interpretation” into a documented envelope bus:

- shared paths and tmux output
- manual interpretation of session state
+ ~/.netsky/channels/... inboxes
+ validated envelopes
+ replay and poison handling
+ netsky channel send <agent> "<text>"

Then: eedcb25:.agents/skills/notes/SKILL.md Now: src/crates/netsky-io/src/sources/agent.rs:1-32, src/crates/netsky-prompts/prompts/base.md:42-49

Observability changed from shell and notes into a queryable system record:

- repo notes
- tmux output
- ad hoc diagnosis
+ meta.db
+ record_message / record_tick / record_session
+ netsky analytics daily
+ website-facing JSON and HTML

Then: eedcb25:.agents/skills/notes/SKILL.md Now: src/crates/netsky-db/README.md:27-48, src/crates/netsky-cli/src/cmd/analytics.rs:22-53

what netsky became #

Netsky kept the bootstrap values — small CLIs, files, tmux, terse skills — and put them inside a source tree that names its own topology and records its own behavior.

The rewrite wasn’t a pivot away from scripts; it was a compression of their working parts. bin/check at eedcb25 already said “one command should run the real gate,” and the current repo keeps that idea by moving the gate logic behind one binary and one observability store (git show eedcb25:bin/check, src/crates/netsky-db/README.md:1-10, src/crates/netsky-cli/src/cli.rs:63-83). The proto found the right primitives early; the rewrite made them durable.

The shell layer didn’t disappear either. It still launches and checks the system — the source tree just implements it.