PR #1 — v0.2.0: Laying the Kernel

PR #1 — v0.2.0: Laying the Kernel

PR: feat: synlynk v0.2.0 — watch daemon, checkpoint, status command, context compaction
Merged: 2026-05-17
Tests: 46 passing


The Broader Goal at This Point

At v0.2.0, the stated goal was modest: make a solo developer with multiple AI tools feel like they have one consistent co-pilot. The product framing was a "Context Switchboard" — not a coordinator, not an orchestrator, just a reliable mechanism for keeping context.md fresh and making sure costs didn't accumulate invisibly.

The three hybrid workgroup studies had not been written yet. The OS metaphor did not exist. The vision was: fix the context tax.


What This PR Shipped

v0.2.0 was the first functional release. v0.1.0 was synlynk init and synlynk exec — a scaffolder and a wrapper. v0.2.0 added the runtime infrastructure that makes the wrapper actually useful across a session.

synlynk watch start|stop|status — the background daemon

The watch daemon polls project-docs/ on a configurable interval (default 30s) with a 3s debounce and regenerates .synlynk/context.md whenever any file changes.

The key implementation detail: the daemon uses os.fork() to detach from the current shell. It writes its PID to .synlynk/watch.pid, logs to .synlynk/watch.log, and tracks its state in .synlynk/state. The state file drives the terminal title bar update via ANSI escape sequences (\033]0;{title}\007) — a small but effective signal that the daemon is alive.

def set_state(state: str) -> None:
    icons = {"watching": "●", "active": "⚡", "stopped": "○"}
    with open(state_file, "w") as f:
        f.write(state)
    if sys.stdout.isatty():
        project = os.path.basename(os.getcwd())
        title = f"{icons.get(state, '○')} synlynk: {state}  ·  {project}"
        sys.stdout.write(f"\033]0;{title}\007")

The os.fork() approach means the daemon runs as a native process (Unix only — macOS or Linux), not a background thread inside the Python interpreter. This is the correct choice for a tool that must survive shell exits and terminal closure.

synlynk checkpoint — archiving completed work

checkpoint does three things atomically: it archives completed [x] tasks from todo.md into the user's devlog (identified via get_username(), a gh CLI + git config fallback chain), refreshes context.md, and emits a structured telemetry event.

The devlog archiving rule: entries older than 30 days are moved to devlogs/archive/YYYY-MM.md to keep the active devlog readable. This was the first implementation of the "cognitive exoskeleton" pattern — the devlog as episodic memory, automatically maintained rather than manually curated.

synlynk status [--json] — the project dashboard

The status dashboard aggregates:

  • Active [ ] tasks from todo.md
  • Last checkpoint timestamp
  • Current sentinel alerts from .synlynk/sentinel.md
  • Budget position (cumulative spend vs. configured limit)
  • Watcher state

--json emits a versioned JSON structure and exits 1 if there are active sentinel alerts or a budget breach. This exit code behavior was deliberate: it allows CI to gate on project health, not just test results.

Context compaction

Before v0.2.0, generate_context() dumped the full project-docs/ directory into context.md. This was expensive (many completed tasks, old roadmap rows, full devlog history) and increasingly noisy as the project aged.

v0.2.0 compacts aggressively:

  • Only [ ] (incomplete) tasks appear in the output — [x] tasks are stripped
  • Only "In Progress" roadmap rows appear — done and planned rows are excluded
  • Devlog is scoped to the last 7 days
  • Sentinel alerts are injected at the top of context, before everything else

This is the first implementation of a principle that will recur throughout the architecture: context should be minimal and relevant, not maximal and comprehensive. The agent reads what it needs for the current session. Historical state belongs in the ledger, not in the active context window.

The session protocol templates

synlynk init now writes CLAUDE.md and GEMINI.md with a full session protocol — a startup checklist, during-session rules, and session-end steps. The key commitments the templates enforce:

  1. Run synlynk watch status at session start — start the watcher if stopped
  2. Read .synlynk/context.md — the authoritative project state
  3. Check .synlynk/sentinel.md — surface any active alerts before doing any work
  4. Mark tasks [x] in todo.md immediately upon completion, not at session end
  5. Run synlynk checkpoint at task boundaries and at session end

These are not suggestions. They are the behavioral contract the harness enforces through the agent instruction files.

Breaking change: full TTY for exec_command

The most important implementation detail in v0.2.0 is invisible in the feature list: exec_command() switched from subprocess.run(capture_output=True) to subprocess.Popen with stdio passed directly to the subprocess.

This broke the previous behavior of extracting token counts from stdout but gained something more important: full TTY interactivity. Claude Code and Gemini CLI both rely on terminal features (cursor control, color, interactive confirmation prompts) that break when stdout is captured. Wrapping them with captured output produced a broken terminal experience. Passing stdio directly means the wrapped tool behaves exactly as if run without the wrapper.

The cost of this decision: token extraction from stdout became impossible. Cost tracking moved to a manual model — the AI agent is instructed to write its costs to costs.md after each significant operation. This is a deliberate trade: correctness over automation. An interactive tool that captures stdout to extract tokens but breaks the terminal is not useful. An interactive tool that requires manual cost entry but works is.

v0.2.0 — The Kernel Components

The Test Suite

46 pytest tests covering all new and changed behavior, with a project_dir fixture that creates a clean temporary filesystem for each test. This fixture pattern — create a real project-docs/ structure, run the CLI function against it, assert on the resulting state — became the testing foundation for all subsequent versions.

CI runs on Python 3.8, 3.10, and 3.12 via GitHub Actions, gating every push and PR. The 3.8 target is deliberate: it forces stdlib-only discipline. Any str | None union syntax (requires 3.10+) must be written as Optional[str] instead.


What This Achieved on the Path to Autonomy

v0.2.0 established the runtime model that all subsequent releases build on:

  1. Persistent background process. The watch daemon is the precursor to the v0.7.0 dispatch daemon. The architectural shape is the same: a forked process with a state file, a PID file, and a log file.

  2. Structured telemetry. log_telemetry_event() writes structured events with schema_version and type fields. Every exec and checkpoint event is recorded. This telemetry becomes the evidence base for capability routing in v0.5.0.

  3. Context as a derived artifact. The principle that context.md is computed, not written, is foundational. In v0.5.0, this shifts to SQLite queries. In v0.7.0, to HTTP context server responses. But the principle — the agent reads a snapshot computed by the harness, not a document maintained by the agent — never changes.

  4. Flatline sentinel. 3 consecutive non-zero exits with the same command trigger an alert in sentinel.md. This is the first safety mechanism against autonomous loops gone wrong — a concern that becomes structural in the dispatch and entitlements design.


Strategic Note

At this point, the goal was: reliable solo workflow. Ship the kernel and test it in real use.

The goal had not yet shifted to orchestrator. The multi-agent coordination architecture, the Trio Protocol, the state DB — none of those existed yet. synlynk was a context switchboard with a daemon and a dashboard.

But the daemon, the telemetry, the compaction logic, and the session protocol templates are all load-bearing components of the eventual orchestrator. The kernel was right. The scope was just not yet known to be much larger.


Next: PR #3 — v0.2.1: The Correctness Tax — real use found critical bugs in hours; correctness before velocity.