PR #27 — v0.4.0: The Autonomy Driver

PR #27 — v0.4.0: The Autonomy Driver

PR: feat: synlynk v0.4.0 — autonomy driver + repo migration
Status: Open (implementation complete, pending review)
Tests: 106 passing


The Broader Goal at the End of v0.3.0

At the end of v0.3.0, the goal had crystallized: workgroup orchestrator on a path to full autonomy. The OS layer model was established. The Trio Protocol (Architect→Build→Verify) was fully designed. The v1.0 architecture — SQLite WAL, HTTP context server, NATS leaf nodes — was specced.

The next question was: what does the first real autonomy primitive look like, at the v0.4.0 level, without requiring SQLite or a daemon?

The answer: the human describes a GitHub issue and the harness takes it from there — claims the board item, routes to the correct agent, and launches it with full context prepended. No manual board moves. No copy-paste of issue context. No flag lookup.


The Strategic Shift in This PR

One strategic observation shaped this PR's design: existing repos already have rich agent instruction files.

synlynk's init command writes CLAUDE.md, GEMINI.md, AGENTS.md from scratch. But the RxCC.me repo — the primary target and observatory — had been running for months with hand-crafted, deeply detailed agent instruction files. A naive synlynk init would overwrite them. Not acceptable.

This forced the design of synlynk migrate as a first-class command, not a flag. Migration is not init --force. It is semantic analysis: what sections already exist in this file? What is the harness template adding that isn't already there? What IDs need to be extracted and cached?

The migration problem also revealed that synlynk's long-term strategy requires forward compatibility with repos that predate it. The harness cannot assume a blank slate. It must be adoptable by mature repos with their own established conventions.


What This PR Shipped

synlynk migrate — intelligent adoption for existing repos

migrate is the command for repos that already have agent instruction files. It does not overwrite — it analyzes and appends.

Detection phase: reads the existing instruction files line by line, classifies sections using SECTION_SIGNALS — a dictionary mapping section headings and content patterns to template coverage categories:

SECTION_SIGNALS = {
    "live issue": "live_issues",
    "sev1": "live_issues",
    "sev2": "live_issues",
    "worktree": "worktree_policy",
    "git worktree": "worktree_policy",
    "anti-amnesia": "anti_amnesia",
    "compaction": "anti_amnesia",
    "4-doc": "four_doc",
    "four doc": "four_doc",
    "project_id": "github_projects",
    "pvt_": "github_projects",
    ...
}

A repo with "live issue" and "sev1" already documented gets live_issues marked as covered. The migrate command only appends the sections that are genuinely missing.

GH Projects ID extraction: scans existing files for PVT_ patterns (the prefix for GitHub Projects v2 node IDs) and extracts them into .synlynk/config.json automatically. A repo that had manually configured its board integration doesn't need to re-enter those IDs.

Three-option menu:

  • A (adopt + combine): Append missing sections only; skip covered content
  • B (exit): Do nothing, inspect manually
  • C (full replace): Overwrite with clean templates (last resort)

--dry-run shows the full analysis and plan without writing anything. This is the correct default mental model for a command that modifies files the user has been maintaining for months.

The synlynk start reference: migrate appends a synlynk start usage block to all instruction files, introducing the command that becomes the primary day-to-day interaction for agentic issue pickup.

synlynk start <issue-id> — autonomous issue pickup

start is the first command in synlynk that acts autonomously on the user's GitHub state. Given an issue ID, it:

  1. Reads the issue via gh api repos/{owner}/{repo}/issues/{id} — title, body, labels, assignees.

  2. Infers the agent from the agent: label on the issue (agent:claude, agent:gemini, agent:codex). No flag needed — the routing label on the issue is the dispatch signal.

  3. Auto-discovers and caches GitHub Projects v2 field IDs with a 7-day TTL in .synlynk/board-cache.json. Field IDs are the opaque strings required for updateProjectV2ItemFieldValue mutations. Discovering them requires a GraphQL query:

{
  node(id: "PJ_kwDO...") {
    ... on ProjectV2 {
      fields(first: 20) {
        nodes {
          ... on ProjectV2SingleSelectField {
            id
            name
            options { id name }
          }
        }
      }
    }
  }
}

The cache eliminates this query on every synlynk start invocation. The 7-day TTL handles board schema changes (new columns, renamed fields) without requiring manual cache invalidation.

  1. Moves the board item to "In Progress" and sets the Agent field via GraphQL mutations — no human board interaction required.

  2. Prepends the issue body to context.md — the issue becomes the top of the context window for the agent about to pick it up.

  3. Launches the agentsubprocess.Popen with the correct CLI for the inferred agent slot.

--dry-run shows the full plan (issue details, inferred agent, board mutations that would fire, context.md preview) with no writes or board moves.

Remote auto-detection

Both init and migrate now parse owner and repo from git remote get-url origin, supporting both https (https://github.com/org/repo.git) and SSH (git@github.com:org/repo.git) formats.

No --org or --repo flags required for any repo with a properly configured git remote.

Config schema extension

.synlynk/config.json gains three new fields:

{
  "owner": "nikhilsoman",
  "project_id": "PJ_kwDO...",
  "agent_slots": {
    "claude": {"cmd": ["claude"], "label": "claude"},
    "agy": {"cmd": ["agy"], "label": "agy"},
    "codex": {"cmd": ["codex"], "label": "codex"}
  }
}

agent_slots is the harness's model of the available agents and their CLI invocations. synlynk start looks up the slot for the inferred agent to determine the launch command. This is the precursor to the full agent profile in v0.5.0's capability routing system.


The Architecture Decision: Board-as-IPC

A key decision in this PR was to use GitHub Issues + Projects v2 as the inter-agent coordination layer rather than introducing any new infrastructure.

The alternatives considered:

Option Description Trade-off
A. GitHub Issues + Projects v2 Issue = unit of work + audit thread; board = coordination state No new infra; maps to existing RxCC patterns; full provenance
B. Unix domain sockets Local message passing between agent processes Fast, but no persistence, no human visibility, complex setup
C. Local handoff files Write a .synlynk/handoff/<job-id>.json on completion Simple, but no board visibility, no structured query

Option A won for one reason beyond the obvious (no new infrastructure): it maps exactly to how the RxCC workgroup already operates. Every protocol proven in RxCC — LIVE-N issues, PR discipline, board routing via GraphQL — plugs in directly. There is no migration cost for the primary user.

The board-as-IPC model persists through v0.6.0. In v0.7.0, it is augmented (not replaced) by the HTTP context server and inbox table in state.db. In v1.0, the board becomes a view over NATS subjects rather than the primary substrate. But the issue-driven attribution chain — issue created → agent claimed → PR opened → board moved → merge closes issue — stays constant at every tier.


106 Tests

The jump from 47 tests in v0.2.2 to 106 in v0.4.0 reflects the complexity of the new commands. migrate and start each require extensive mocking:

  • Filesystem state: existing CLAUDE.md with various section patterns
  • gh CLI responses: issue JSON, field ID GraphQL responses, board mutation results
  • SECTION_SIGNALS matching: every signal mapped to correct coverage category
  • --dry-run output: verified to show plan without side effects
  • Remote URL parsing: both https and SSH format variants

The test suite's fidelity requirement is high because migrate is a one-shot operation on files the user has been maintaining for months. A test that doesn't actually verify the section detection logic is not a useful test for this command.


What This Achieved on the Path to Autonomy

v0.4.0 ships the first two primitives of autonomous operation:

  1. Autonomous board management. synlynk start moves a board item from "Backlog" to "In Progress" and sets the Agent field without human board interaction. This is the harness acting on the user's GitHub state on their behalf — the first real autonomy primitive.

  2. Agent routing from labels. The agent: label on a GitHub issue is the dispatch signal. The harness reads it and routes without human selection. This is the precursor to capability-based routing: currently label-based, eventually score-based.

  3. Context prepending from issue body. The issue body becomes the top of the agent's context window. The agent has the full task specification, not a one-line description. This is the precursor to the task-packet.md artifact in the Trio Protocol.

  4. GH Projects ID auto-discovery. Field ID caching eliminates a manual lookup step that was previously required every time the board schema changed. The harness learns the board structure and remembers it.


Strategic Note

The goal at the end of v0.4.0 (pending this PR's merge):

The autonomy driver is live. The human says "start issue 42." The harness reads the issue, infers the agent, moves the board item, prepends context, and launches the agent. The human's next interaction is reviewing the PR.

The next strategic question is v0.5.0: SQLite WAL replaces flat files, capability routing replaces label-based routing, and the agentic PM hierarchy (Arc → Phase → Epic → Story → Event) replaces the flat todo.md. That design is complete — it emerged from the 2026-06-07 architecture session documented in PR #28.


Next: PR #28 — The Architecture Pivot — state DB, agent identity, workspace, and 12 arc gaps: the day synlynk designed the rest of its OS.