feat(go): first slice of full Go port of sumctl (#94) - #95
Merged
Conversation
added 11 commits
September 10, 2026 17:30
First command of the full sumctl Python->Go port: read-only, no subprocess/state dependencies, so it is a clean starting slice. Differential test runs the real Python bin/sumctl release-contract and asserts byte-identical stdout against the Go implementation.
…94) Most remaining sumctl commands read/write task.json, state.json, and settings via lib/sumctl.py's atomic_json/read_json/Store. Go's encoding/json can't preserve Python dict insertion order on round-trip, so add ordjson.Object (order-preserving JSON object) with an encoder matching lib/sumctl.py's exact `json.dumps(value, indent=2, ensure_ascii=True)` output (2-space indent, empty containers stay inline, non-ASCII escaped to \uXXXX with surrogate pairs) and atomic file writes matching atomic_json (temp file in the same dir, fsync, rename, fsync directory). store.Store ports Store's __init__/init/lock/path/read/save/all methods (task ID validation, symlink refusal, flock-based exclusive locking, sorted task listing). Differential-tested: ordjson.WriteFile against the real Python atomic_json via a small reference script that loads lib/sumctl.py with importlib, for a payload covering nesting, arrays, and non-ASCII/control-character escaping. Session/registration/designated-owner methods are not yet ported; they aren't needed until doctor/status land.
Ports lib/sumctl.py's capacity_view/load_settings and the validators it depends on (capacity, worker, presets, reviewer, launch-value adapters) so `settings show` runs natively instead of shelling to Python. Validation error text is byte-identical, including Python repr()-formatted values in messages (unknown keys, invalid capacity, launch-value/adapter mismatches, preset-reference lookups) via a new pyrepr package. Native handling only kicks in for exactly `settings show` with an explicit --home; `settings set`/`preset`/anything else, or `settings show` without --home, still falls through to the Python reference unchanged (no attempt yet at replicating sum's default --home resolution for native commands). Differential-tested against the real Python bin/sumctl across seven scenarios: empty store, populated settings, invalid capacity value, unknown preset reference, unknown capacity key, a preset arg conflicting with its own resolved model flag, and occupancy counting across active/archived tasks.
Found while differential-testing preset show's error path: the
compiled entrypoint's top-level error handler used encoding/json's
compact defaults (no space after ':'/','), while lib/sumctl.py's
matching path is plain json.dumps({"error": str(exc)}) — the default
(non-indent) separators are (', ', ': '), and ensure_ascii is implied
throughout the module. A script comparing {"error": ...} byte-for-byte
would see `{"error":"x"}` from Go and `{"error": "x"}` from Python.
Adds ordjson.MarshalCompact for this shape (object/array/string/number
encoding matching MarshalIndent, just without newlines/indentation)
and switches the entrypoint to it. Updates the existing cancellation
test's expected substring and adds a differential test against the
real Python reference's stderr for an error path.
Ports lib/sumctl.py's preset_list/preset_show, reusing the settings package's already-ported load_settings/validate_preset_reference: preset_launch (the exact argv `dispatch --preset` would append, including adapter-flag construction) and preset_references (worker/ reviewer default lookups). Native handling only kicks in for exactly `preset list` or `preset show NAME` with an explicit --home; `preset set`/`delete`, anything else, or missing --home, still falls through to the Python reference unchanged. Differential-tested against the real Python bin/sumctl: list on an empty store, list with multiple presets (one referenced as the worker default), show a preset with model/reasoning/args, and show a bare preset.
Ports lib/sumctl.py's graph_config/graph_tool (go/internal/graph): locate the pinned codegraph binary (SUM_CODEGRAPH_BIN override, else <runtime>/.local/bin/codegraph derived from the reference helper's own path, matching lib/sumctl.py's RUNTIME resolution without needing the full ROOT/SUM_INSTALL_ROOT machinery), probe its --version, and print the per-harness MCP snippet (claude/cursor/opencode as JSON via ordjson, codex as TOML) plus --raw's bare-snippet-only output. `graph status`/`graph init` are unrelated (task-scoped, need a real task record) and stay on the Python reference; `graph` only goes native for the exact `config --harness NAME [--raw]` shape recognized by parseGraphConfigArgs, falling back to compat for anything else (including an invalid --harness, so Python's own argparse choice validation/usage text stays exact). Adds ordjson.QuoteString (exported single-value JSON-string encoding, reused for the codex TOML snippet's command path) and reuses the existing MarshalIndent path for the JSON-format snippets so their formatting is guaranteed to match the outer emit() convention. Differential-tested against the real Python bin/sumctl and the pinned codegraph release already staged in this installation: all four harnesses, --raw, and the tool-unavailable error path (via the compiled binary, since that error surfaces through the top-level stderr handler fixed in 46990f0).
Ports lib/sumctl.py's metadata_snippet: the static config.toml text a user merges to render sum's sum_* sidebar tokens, plus the token/state name lists and the exact enable/inbox commands to run next (command_for(store, "metadata", ...)). command_for shells out through shlex.join for reuse as a copy-pasted shell command, so this adds go/internal/shquote (Quote/Join matching Python's shlex.quote/join exactly: word/@%+=:,./- chars pass through unquoted, anything else gets single-quoted with '"'"' escaping embedded quotes) rather than hand-formatting paths that might contain spaces. `metadata` only goes native for exactly `snippet` or `snippet --raw` with an explicit --home; `enable`/`disable`/`status`/`sync`/`inbox` and anything else still shell out to the Python reference unchanged. Differential-tested against the real Python bin/sumctl: plain output, --raw, and a --home path containing spaces (exercises the shlex quoting in the emitted enable/inbox commands).
Completes the Store port (go/internal/store) with the session/ registration methods every registration-aware command needs: Designated (state.json present, dev.json absent), Owner (context.json if present), Registration/Register/Registrations (registration_key's sha256-of-machine/session/pane hash, identity mismatch detection, registered_at preserved across re-registration, the exact mcp/ sum_version/schema shape Register writes). Not wired into any CLI command yet — doctor/status/init need context() (Herdr pane env detection) ported first, which is a separate unit. registrationKey verified against the real Python registration_key() for a fixed input; the rest is covered by Go unit tests (no existing CLI surface exposes these methods' output directly to differential- test against yet).
One page crediting the projects that shaped sum, per the owner's required credit list and editorial constraints: conceptual inspiration vs. direct dependency vs. adapted code vs. historical lineage vs. evaluated-not-installed integrations are kept distinct, and the four must-find mappings (Firstmate -> agent distro, Oh My Pi -> builtins, Solo -> meta-harness, Unpeel -> MCP pane/session management) are called out explicitly at both ends of the page. README's prior one-line "License and inspiration" blurb now points here instead of duplicating a shorter version of the same credit. CONTRIBUTING's existing attribution checklist item now names this file directly. Verified every referenced GitHub repo and file path resolves via `gh api`, and fetched the plain product pages (Solo, Delta, Herdr, mise) to confirm they're live; unpeel.com blocks scraping (403) but resolves as a live domain, not a dead link.
Ports context()/session_from_env() (go/internal/store/context.go) and init()'s non-designated path (go/internal/roleinit) — installation_hint (is this checkout a linked worktree of a real sum installation?), matching_task (are we a dispatched worker's own pane?), and development_marker (.sum/dev.json), assembled into the exact role/home/task/installation_home/note shape the Python reference returns for a developer or dispatched-worker pane. The designated/coordinator branch — live pane verification via Herdr, coordinator claim/reclaim, contract state, cleanup_pending, pump, hook/metadata summaries — is NOT ported. It is sum's most safety-critical path (a bug could misfire a coordinator claim or lose a registration) and is explicitly out of scope here; `init` only goes native when the store is confirmed not designated, and falls back to the Python reference for every other shape (unrecognized --role, designated store, anything init's own argparse would need to reject). Differential-tested against the real Python bin/sumctl init in this very dev checkout (safe: the non-designated branch never writes — no store.lock, no atomic_json — same read-only observation `init` already performs here on every invocation this session), plus Go unit tests for the worker-task-matching and linked-worktree-detection logic using throwaway git fixtures.
Ports doctor(store) in full: tool() lookups for python3/node/git/gh/
herdr/quota-axi/lsof (go/internal/toolpath: SUM_<NAME>_BIN override,
then the pinned <runtime>/.local/bin/<name>, then PATH), herdr_version/
ensure_version and the herdr-context pane-get probe (go/internal/
herdrclient: a small JSON-RPC-style wrapper around the herdr binary
matching lib/sumctl.py's herdr()/run() error text for timeouts,
nonzero exit, and non-JSON output), the gh --attach probe, harness
detection over the same HARNESSES table, the Mesh-installed marker
file check, and the codegraph check via the already-ported graph.Tool.
Reuses Store.Designated/Owner/Registration for the role check.
doctor is fully read-only ("never binds") so it needed no scope
reduction like init did; every sub-probe here is observation only.
Exit code now follows value["ok"] (0 when every check passed, 1
otherwise) rather than the default always-0, matching the Python
dispatcher's special-cased handling of this one command.
Differential-tested end to end against the real Python bin/sumctl
doctor, live, in this dev checkout (byte-identical stdout AND matching
exit code) — safe, since every check here is read-only, the same
observation `doctor` already performs on every ordinary invocation.
Falls back to the Python reference for any unexpected argument (doctor
takes none). Added Go unit tests for toolpath's env-override/pinned-
local/PATH precedence.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First reviewable slice of #94: a full Go/Cobra port of
sumctlto replace the Python CLI. This is owner-authorized and separate from #38/#39/PR #90 — those correctly closed a narrower, benchmark-gated prototype as a no-go; this issue and PR do not reopen or reverse that decision, and PR #90 is untouched.Ports so far (command-for-command parity, one command's worth of Python behavior at a time):
release-contract(go/internal/contract): pure, read-only, no state/subprocess dependency — the first native command.go/internal/ordjson):lib/sumctl.py'satomic_json/read_jsonround-trip Python dicts preserving insertion order; Go'sencoding/jsoncan't do that for arbitrary maps, so this adds anObjecttype plus an encoder matchingjson.dumps(value, indent=2, ensure_ascii=True)byte-for-byte (2-space indent, empty containers stay inline, non-ASCII escaped to\uXXXXwith surrogate pairs for astral characters).store.Store(go/internal/store): portsStore.__init__/init/lock/path/read/save/all— task ID validation, symlink refusal,flock-based exclusive locking, sorted task listing.designated/owner/registration(the Herdr-session bits) are not yet ported; nothing needs them yet.settings show(go/internal/settings,go/internal/pyrepr): portscapacity_view/load_settingsand everything they validate (capacity, worker, presets, reviewer, launch-value/adapter checks). Error text is byte-identical, including Pythonrepr()-formatted values in messages — that's whatpyrepris for.preset list/preset show NAME: portspreset_list/preset_show, reusingload_settings/validate_preset_reference—preset_launch(the exact argvdispatch --presetwould append) andpreset_references(worker/reviewer default lookups).go/internal/ordjson's newMarshalCompact,cmd/sumctl-go/main.go): the compiled entrypoint's top-level error handler usedencoding/json's compact defaults ({"error":"x"});lib/sumctl.py's matching path is a plainjson.dumps({"error": str(exc)}), whose default (non-indent) separators are(', ', ': ')—{"error": "x"}. Found while differential-testingpreset show's error path; would have broken byte-for-byte parity for every native command's error output, not just this PR's.graph config --harness NAME [--raw](go/internal/graph): portsgraph_config/graph_tool— locates the pinned codegraph binary (SUM_CODEGRAPH_BINoverride, else<runtime>/.local/bin/codegraphderived from the reference helper's own path, so this doesn't need the fullROOT/SUM_INSTALL_ROOTresolution machinery), probes--version, and prints the per-harness MCP snippet (claude/cursor/opencode as JSON, codex as TOML) or, with--raw, just the bare snippet.graph status/graph initare task-scoped (need a real task record) and stay on the Python reference; an invalid--harnessalso falls back to compat so Python's own argparse choice-validation/usage text stays exact rather than being reimplemented.metadata snippet [--raw](go/internal/metadata): portsmetadata_snippet— the static config.toml text for sum'ssum_*sidebar tokens plus the exactenable/inboxcommands to run next.command_forshells that out through Python'sshlex.join, so this addsgo/internal/shquote(Quote/Join/CommandFormatchingshlex.quote/joinexactly) rather than hand-formatting a path that might contain spaces.enable/disable/status/sync/inboxand anything else stay on the Python reference.Store.designated/owner/registration/register/registrations(go/internal/store): registration-key hashing, identity-mismatch detection,registered_atpreserved across re-registration.init(below) now reusesDesignated;owner/registration/registerstill await the designated/coordinator branch this PR does not implement.init(go/internal/store/context.go,go/internal/roleinit): portscontext()/session_from_env()andinit()'s non-designated branch only — installation-hint (is this checkout a linked worktree of a real sum installation?), matching-task (is this pane a dispatched worker's own pane?), and the development marker, assembled into the exact role/home/task/installation_home/note shape. The designated/coordinator branch — live pane verification, coordinator claim/reclaim, contract state,pump, hook/metadata summaries — is deliberately not ported: it's sum's most safety-critical path, and a bug there could misfire a coordinator claim or lose a registration.initonly goes native once the store is confirmed not designated; every other shape (a designated installation, an unrecognized--role) falls back to the Python reference unchanged.ATTRIBUTIONS.md(issue docs: add a concise ATTRIBUTIONS.md and upstream-first README link #56) — a credit page for the projects that shaped sum, with the four explicit mappings the issue calls for (Firstmate → agent distro, Oh My Pi → builtins, Solo → meta-harness, Unpeel → MCP pane/session management) called out at both ends of the page. Linked from README's inspiration blurb and CONTRIBUTING's existing attribution checklist item. Not part of the Go port; included here because the boss asked for it in the same PR while this branch was already the active one.doctor(go/internal/doctor,go/internal/toolpath,go/internal/herdrclient): ports the full aggregator —tool()lookups for python3/node/git/gh/herdr/quota-axi/lsof (env override, then the pinned<runtime>/.local/bin/<name>, thenPATH),herdr --version/ensure_version, theherdr-contextpane-get probe (a small JSON-RPC-style client around theherdrbinary matchinglib/sumctl.py'sherdr()/run()error text for timeouts/nonzero-exit/non-JSON), thegh pr edit --help --attachprobe, harness detection over the sameHARNESSEStable, the Mesh-installed marker check, and codegraph (reusinggraph.Tooldirectly). ReusesStore.Designated/Owner/Registrationfor the role check.doctoris fully read-only ("never binds") so needed no scope reduction the wayinitdid. Exit code now followsvalue["ok"](0 when every check passes, 1 otherwise), matching the Python dispatcher's special case for this one command instead of the default always-0.Native handling is intentionally narrow right now: each command only takes the native path for the exact invocation shape shown above with an explicit
--home(e.g.settings set,preset delete, and everything else still shell out to the Python reference unchanged viaroot.go'scompatibilityCommandsfallback). Default--homeresolution (mirroringresolve_installation()/SUM_HOME) isn't ported yet, so native commands currently require an explicit--home.Test plan
go build ./... && go vet ./... && gofmt -l .clean insidego/.go test ./...— all packages pass, including differential tests that run the real Pythonbin/sumctland assert byte-identical stdout/exit codes against the Go binary:release-contract;settings showacross 7 scenarios (empty store, populated settings, invalid capacity value, unknown preset reference, unknown capacity key, a preset arg conflicting with its own resolved model flag, task occupancy across active/archived tasks);preset list/preset showacross 4 scenarios (empty store, multiple presets with one referenced as the worker default, a preset with model/reasoning/args, a bare preset); the error-JSON stderr format against Python's exact separators/escaping for an error path;graph configacross all 4 harnesses plus--raw(against this installation's own pinned codegraph release) and the tool-unavailable error path;metadata snippetplain,--raw, and a--homepath with spaces (exercises the shlex quoting);Store's registration-key hash checked against the real Pythonregistration_key();initanddoctorend-to-end against the real Python reference live, in this dev checkout's own real--home(safe — both are read-only paths, the same observation each already performs on every ordinary invocation here), including matching exit codes; plus Go unit tests for worker-task-matching/linked-worktree detection (roleinit) and env-override/pinned-local/PATH precedence (toolpath).MISE_ENABLE_TOOLS=go,python,node python3 .agents/skills/verify/scripts/verify_run.py --base 69bc7ea07fb37e2725309e928465992ec5586890→verify: passat candidate65a43e3913b4d706c21b4ab8ee2bbb53ff24f4c0(77 scenarios pass, 9 not-run/manual; record.artifacts/verification/20260910T190020Z-fb926836/run.json).verify: failwith 77 scenarios failing. Diagnosis found exactly one underlying test failure:tests/test_returns.py::ReturnsTest::test_concurrent_question_report_and_refresh_keep_every_obligation_with_bounded_sends, a concurrency race (5 CLI subprocesses viaThreadPoolExecutor, then an assertion readsversions(task)['requested']before the lock-protected write is guaranteed to have landed). Reproduced it failing 1/3 times in isolation with zero code changes in between on the same checkout — confirmed pre-existing and unrelated to this branch, which is 100% additive undergo/and touches nothing intests/test_returns.pyor the refresh/returns code path. Not fixed here; out of scope for Full Go/Cobra port of sumctl to replace the Python CLI (owner-directed, supersedes #38/#39 prototype scope) #94.verify: passat candidatebbbfa63c4bd67aac7586f9881921d0b153245a65(the error-fix + preset commits), clean (record.artifacts/verification/20260910T192255Z-4d25b5aa/run.json).verify: passat candidate2aecf5fcee541621b5e451f69032b1b14e7ba378(the graph config commit), clean (record.artifacts/verification/20260910T201257Z-d77b2ead/run.json).verify: passat candidatec0f459cbd7bad853f6c132375acc11b5eeef0044(the metadata snippet commit), clean (record.artifacts/verification/20260910T203430Z-31c9107b/run.json).verify: passat candidatef279121b3b9ba1ba8767608c867038b95fce437b(the Store registration foundation), clean (record.artifacts/verification/20260910T205615Z-2c37e544/run.json).verify: passat candidate544fdea04b84492b3c98b97a1c37256cc0db5710(ATTRIBUTIONS.md + the init port), clean (record.artifacts/verification/20260910T214119Z-828b6026/run.json).verify: passat candidateb437af26bb894a5fd0770db7bc59879be6b7aafc(the doctor port), clean (record.artifacts/verification/20260910T220630Z-d87480e5/run.json).No manual/live Herdr scenario was run (this is a developer checkout, not a live installation).
Review status
Independent review was not performed beyond the automated verification above — no separate reviewer pane was launched for this developer-checkout PR. Boss decides merge.
Limitations / what's not in this slice
sumctlsubcommands (release-contract,settings show,preset list,preset show,graph config,metadata snippet,init,doctor) are natively ported; everything else still runs through the Python reference.init's designated/coordinator branch (live pane verification, claim/reclaim, contract state,pump, hook/metadata summaries) is not ported and stays on the Python reference — deliberately, given how safety-critical it is.status,inbox,show,contextand most other read-only commands are the natural next targets.--homeresolution for native commands is not implemented; every native path requires--homeexplicitly.