feat(go): continue the sumctl port — occupancy/reservations fix + status/inbox (#94) - #99
Merged
Merged
Conversation
added 17 commits
September 11, 2026 02:26
…odel (#94) Main moved since PR #95 merged: lib/sumctl.py's occupancy()/holds_slot() no longer count one slot per non-archived task. A new lib/execution_reservations.py module now tracks separate worker/ verifier attempt reservations per task (a task can hold 0, 1, or several slots at once, one per held attempt), with a validated "execution" record shape and a legacy fallback (no "execution" key: one held slot unless archived) for tasks predating the change. Ports execution_reservations.py's read path as go/internal/ reservations: the full _attempt/_occupant/_observation validation (exact same error text on a malformed record) plus Held(task). Rewrites settings.occupancy to sum held-reservation counts instead of a simple archived check, and adds capacity_view's new middle error branch (occupancy itself can now fail on a malformed record, independently of settings.json) plus its updated note text. This affects the already- merged `settings show`/`preset list` byte-for-byte. Also exports Store.RegistrationKey (was unexported) for reuse. Differential-tested against the real Python bin/sumctl across a legacy running task, a legacy archived task, a real execution record with one held worker + one released verifier + one held verifier (2 slots), and a malformed reservation (invalid id prefix) exercising the new error path.
…#94) TestDoctor_matchesThePythonReferenceInThisDevCheckout and TestInit_matchesThePythonReferenceInThisDevCheckout each run the Go and Python implementations as two separate process invocations; both embed a live now()/context() timestamp in their output, so a run that straddles a second boundary flakes on an otherwise-correct candidate. Normalize ISO timestamps to a placeholder before comparing.
Ports status(store, live=False, inbox=?) in full: the versions/brief sidecar (go/internal/versions — read_versions/legacy_versions/ versions_view/refresh_state/revision_view, including revision content- hash verification), the returns/obligations subsystem (go/internal/ returns — open_attention/open_obligations/return_route/route_key/ read_returns/notification_state/returns_view), cleanup_pending (go/internal/cleanup), and metadata_summary (added to the existing go/internal/metadata package alongside the snippet it already had). statuscmd assembles all of it into the per-task row shape plus the top-level tasks/capacity/guarantee/metadata envelope. Scope: the `--live` path is NOT ported (Snapshots' live agent fan-out across sessions/panes, attention_sweep, and pump's notification delivery are a separate, much larger, stateful undertaking). `status`/ `inbox` only go native for zero extra arguments; `--live` falls back to the Python reference unchanged. Differential-tested against the real Python bin/sumctl: both commands against an empty store, and against a fixture with a legacy running task, a legacy archived task, and a task with a real multi-reservation execution record (one held worker + one released verifier + one held verifier) to exercise the occupancy fix above end-to-end through `status`/`inbox` too.
Ports brief_command == "list": task read plus versions.View, with brief_path prepended. Now trivial thanks to the already-ported versions package. `brief regenerate`/`request`/`adopt` and anything else stay on the Python reference. Differential-tested against the real Python bin/sumctl across a legacy task (no versions.json), a real versions.json with one intact revision, and a report recorded under a superseded revision while a later revision is verification-affecting (exercises report_evidence's trickiest branch).
Ports env_show/environment_view (lib/sumctl.py) natively: read_environment's symlink/schema-mismatch checks, redact's 8 secret patterns, bounded_view's truncation metadata, and the discovery/endpoints/logs/resources/services reshaping. `env discover/record/inspect/start/stop` stay on the Python reference since they mutate. Differential-tested against the real Python bin/sumctl: absent record, a full present record (discovery with a secret-shaped command exercising redaction, endpoints, logs, resources, services with both populated and empty-dict readiness/stop), --max-chars truncation, --max-chars 0 (unbounded), and a schema-mismatch error path.
Ports release_list/release_show/verify_release (lib/sumctl.py): content_id/
sha256_file, the ~20-check manifest verification (schema, source SHA, every
manifest-listed file's hash, required files, bin/sumctl executable bit, no
.sum/.codegraph in the tree, Mesh overlay marker deep-equality plus its
dist file hashes, pinned tool symlink resolution, optional dependency
inventory + native-artifact validation), and installation_root's designated-
state-home + git-toplevel check. `release stage` stays on the Python
reference (it mutates).
A *VerifyError type marks every expected validation failure (mirroring
`raise SumError`); release_list catches only that type per release row
(ok:false + error text), matching Python's `except SumError` — an
unexpected filesystem error still propagates and fails the whole command,
as an uncaught Python exception would.
Differential-tested against the real Python bin/sumctl using a synthetic
git-repo "installation" fixture (state.json designates it, matching
installation_root's requirement, without touching the actual installation
this checkout is a worktree of): empty releases dir; a fully valid release
built file-by-file to satisfy every verify_release check plus a broken
(missing-manifest) release plus an in-progress .staging-* dir, all in one
`release list`; `release show` by full SHA and by a short prefix; and a
release show with no match, exercising the command-level `{"error": ...}`
failure path.
Ports project_list/project_show/observe_project (lib/sumctl.py): read_projects' symlink/schema checks, project_identity/parse_remote_identity/same_remote's Git-URL parsing (ssh-like and https forms, scp-style shorthand, host normalization, repo-part validation), and observe_project's live one-shot git inspection (toplevel check, remote comparison, HEAD, dirty status via `git status --porcelain --untracked-files=all`, linked worktrees) — each tolerant of git failures exactly like the Python reference (falls back to None/false rather than raising). `project enroll`/`migrate` stay on the Python reference (they mutate or clone). Differential-tested against the real Python bin/sumctl using synthetic git clones (never the real installation): an empty registry; a clean clone with a matching remote, a dirty clone, a clone with a mismatched recorded remote, and a missing clone, all in one `project list`; `project show` with active tasks (excluding an archived one and an unrelated-repository one) and an unknown-project command-level failure path.
Ports hook_status/hook_summary/pending_summary/observe_plugin (lib/sumctl.py): read_health's symlink/schema checks, pending_summary's open-obligations aggregation across all tasks (reusing go/internal/returns), hook_manifest's TOML generation (for the expected_manifest_current hash comparison), and observe_plugin's bounded live registry probe via the existing herdrclient/toolpath packages when a Herdr session context is available. Faithfully reproduces a subtle Python quirk: hook_status reads health.json a second time, unguarded, after hook_summary already caught and degraded on the first read — so a malformed health.json still fails the whole command, not just one field. `hook enable`/`disable`/`event` stay on the Python reference (they mutate or require a live session). Differential-tested against the real Python bin/sumctl with Herdr env vars blanked (both sides take the records-only ctx=None path, same as any non-Herdr invocation): no health.json, a disabled health record with one pending obligation (exercising pending_summary's count/age), an enabled record with a plugin_id but no instance identity yet (the expected_manifest_current error-to-None path), an enabled record with a mismatched manifest hash, and a malformed-schema health.json exercising the command-level failure path. The live Herdr registry sub-path (ctx available, plugin actually registered) is implemented but not exercised by these tests — verifying it needs a live Herdr session, out of scope for a differential test in this environment.
Ports `show TASK_ID` (lib/sumctl.py): the full task record plus versions_view, evidence_view, and returns_view — reusing the already-ported versions.View and returns.View, and adding a new evidenceview package for evidence_view/current_candidate. evidence_view is the closure-readiness computation: scoped evidence records tagged with candidate currency, a synthesized legacy report row when a task predates structured evidence, worker/root verification run selection, review status, and the "missing" prerequisite list (structured handoff, complete/matching PR identity, passing root verification, standardized-contract worker run + review + policy-review gates). current_candidate does one live `git rev-parse HEAD` in the task's worktree, tolerating a missing/absent worktree or git failure exactly like Python (falls back to no candidate rather than raising). Differential-tested against the real Python bin/sumctl: a minimal task with no worktree/evidence/PR/reviewer; a legacy report with no structured evidence (exercises the synthesized report row); a real git worktree with a failing root verification run (exercises the "latest root run ... was fail" missing message); and a fully standardized contract with a real worktree, handoff, worker run, root run, and an approving policy review — all current against the live HEAD — reaching prerequisites_met: true end to end.
Ports the default `context TASK_ID` shape (no --section/--role/--since, which context_view itself always resolves to the single "outline" section) from lib/sumctl.py: cursor_of/cursor_counters/state_digest (a new ordjson.MarshalSortedCompact primitive matches Python's json.dumps(sort_keys=True) exactly, sorting object keys while leaving array order alone), the outline's decisions/evidence/report/brief/returns_open/ attention_open/cleanup/notes/environment/graph/read fields, reusing versions.View, evidenceview.View, returns.View, returns.OpenAttention, and cleanup.Pending. Adds two new small packages: go/internal/notes (notes_state, the timestamped-entry parser) and environment.Outline/Stamp (environment_outline/environment_stamp, needed by the cursor and outline). --section, --role, and --since stay on the Python reference for now — a deliberate checkpoint given context_view's size (~200+ lines with 10 sections and role-scoped filtering); each is a natural follow-up commit. Also fixes a latent bug in ordjson's compact/indent/sorted encoders: a nil *ordjson.Object value (a typed nil, distinct from an untyped nil) crashed the encoder via a nil-pointer Len() call instead of encoding as JSON null. Caught by this port's first case building a container conditionally from a possibly-absent nested object. Differential-tested against the real Python bin/sumctl (read_at live-timestamped, normalized before comparing): a minimal task with no worktree/evidence/notes/environment record; a real git worktree with a current handoff and a passing root verification (exercises evidence_view's closure/verification fields end to end); and a task with open/applied decisions, a notes.md with one entry, and a stale environment record with one endpoint and one running service (exercises environment_outline's per-state counts).
Extends the native `context TASK_ID` path with `--section NAME` (repeatable, deduplicated in first-occurrence order, matching `list(dict.fromkeys(...))`): decisions, returns, handoff, evidence, and brief. Ports `paged` (the stable append-only-list pagination helper shared by several sections), `section_decisions`, the inline `returns` section body, `latest_handoff`/ `handoff_view`/`section_handoff`, `section_evidence`, and `section_brief`. `--role`, `--since`, and the paging/bounding flags (--after/--limit/ --max-chars/--revision/--kind) are not yet supported — any of those, or an unrecognized section name, falls back to the Python reference; the paging sections use the argparse defaults (after=0, limit=20, max-chars=4000) until those flags are wired up. `execution`, `environment`, and `update` sections still fall back too — they need new subsystems (graph_view, skill_references, installation_root/default_runtime) saved for a follow-up commit. Differential-tested against the real Python bin/sumctl: `decisions` + `returns` together on a task with one open and one applied question; `handoff` + `evidence` together against a real git worktree with a handoff (files, checks, next_action), a passing coordinator verification, and an approving review (three different evidence kinds in one task); `brief` on a legacy task with no versions.json; plus two fallback cases (`--role`, an unrecognized `--section` name) confirming the exact-shape-only native path still defers correctly.
The prior commit's contextview.View already wired the "notes" section (reusing notes.State), but no differential test exercised it. Add two scenarios: a present notes.md with two entries, and no notes.md at all.
Ports section_execution/graph_view/graph_summary/read_graph (new go/internal/graphview package, distinct from the existing go/internal/graph which only handles `graph config`'s MCP snippet): the task-scoped graph.json sidecar, its bounded summary (state, attempts, failures, index counts, truncated error), and the `execution` section's launch/admission/ endpoints reshaping. Fixes a real correctness bug found while adding this: `validContextSections` in root.go listed all 10 CONTEXT_SECTIONS names from the first --section commit, but contextview.View's switch only handled a subset. Requesting an unimplemented section (e.g. `--section update`) was accepted as a valid native shape and silently omitted that section's key from the output, instead of falling back to the Python reference — confirmed by hand before fixing. `validContextSections` now lists only what View's switch actually implements, with a comment warning not to add a name there before wiring its case. Added a regression test asserting `environment` and `update` still route to the Python reference exactly as before. Differential-tested against the real Python bin/sumctl: an execution section with no graph record, launch, parent, or reviewer; and one with a `ready` graph record (index counts, one successful attempt) plus a full launch, admission, parent, and reviewer.
Completes all 10 CONTEXT_SECTIONS. Ports section_environment (reusing
environment.View for the `dev` field, plus new skill_references/
return_commands — active-revision command lookup with a task-callback
command fallback, installed SKILL.md file references for all three roles
since --role isn't wired up yet) and section_update (recorded vs. active
runtime, brief active/requested, refresh_state, and default_runtime —
reusing release.VerifyRelease and release.InstallationRoot — tolerating
a non-installation state home exactly like Python's try/except, producing
an {"unavailable": ...} shape instead of failing the command).
validContextSections and their contextview.View switch cases land in this
same commit, per the lesson from the execution-section commit. The
now-obsolete `environment`/`update`-still-fall-back regression test is
replaced with real differential tests for both sections, since they're no
longer unimplemented.
Differential-tested against the real Python bin/sumctl: `environment` with
no environment.json and with a present record; `update` on a legacy task
outside any installation (exercises the try/except installation_root
"unavailable" branch, the realistic case for a lab/dev --home).
Ports context_view's --role worker|reviewer|coordinator handling: when given with no --section, ROLE_SECTIONS[role] replaces the default ["outline"] section list; section_decisions filters by role (worker→answered only, coordinator→open only); the top-level contract/authority envelope (ROLE_CONTRACT) is added; and for reviewer/coordinator, section_environment gains an artifacts field via a new artifact_references/artifact_scope port (classifies worker-supplied handoff artifact strings as checkout/ outside-checkout/unscoped, purely by string inspection, no filesystem calls). section_environment's skill_references now correctly narrows to just the given role's skills (roles=[role]) instead of always all three, matching Python's `roles = [args.role] if args.role else []`. parseContextArgs (renamed from parseContextSectionArgs) now recognizes --role alongside --section, validated against CONTEXT_ROLES; an unrecognized role or --since still falls back to the Python reference. Updated the stale fallback test that asserted --role fell back (it doesn't anymore) to cover --since and an unrecognized role instead. Differential-tested against the real Python bin/sumctl: worker role with default sections (decisions filtered to answered), coordinator role with default sections (decisions filtered to open), and reviewer role with an explicit --section environment exercising artifact_references across all three scope outcomes (a checkout-relative path, a `../`-escaping path, an absolute path, and a `~`-prefixed path) in one handoff record.
Ports context_view's cursor-based diffing: parse_cursor (the CURSOR regex, raising the same error text for a malformed token) and changes_since (new questions, status-changed questions matching the cursor's timestamp, new evidence with report/publication change flags, refresh events, new attention, notes-entries delta, and the state-digest-based changed/unchanged verdict). When --since is given and nothing changed and neither --section nor --role was also given, the command returns early with just the envelope, "changes", and a "note" — no section is rendered, exactly matching context_view's control flow (sections computed once near the top, sections stays empty when --since is given alone with no default section/role, and the early return never touches it). A malformed cursor propagates as a command-level failure with Python's exact error text, matching the established error-parity pattern from release/project. Renamed the stale fallback subtest that used to assert --since fell back (it doesn't anymore) to test --max-chars instead, since that flag is still unimplemented. Differential-tested against the real Python bin/sumctl: an unchanged cursor (fetched from a real prior read) producing the early-return note-only response; a changed cursor (a question added between reads) with no section requested, rendering just the changes block; the same changed cursor with an explicit --section, rendering both changes and the section; and a malformed cursor string exercising the command-level failure path.
Ports --after/--limit/--max-chars/--revision/--kind for `context TASK_ID`, the last unported flags on the read-only surface. Bundles the flags into a contextview.Options struct rather than growing View's parameter list further. Fixes two bugs found by differential testing against the Python reference while building this out: - section_evidence's `kinds` breakdown must count all records before --kind filtering, not just the filtered subset. - exports versions.revisionView -> RevisionView so section_brief's --revision lookup can reuse it instead of duplicating revision lookup. This completes 100% native porting of sumctl's read-only command surface.
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
Continues the full Go/Cobra port of
sumctlfrom #94, on top of merged PR #95 (8 native commands:release-contract,settings show,preset list/show,graph config,metadata snippet,init,doctor). This branch adds:go/internal/reservations):mainmoved since PR feat(go): first slice of full Go port of sumctl (#94) #95 merged — a newlib/execution_reservations.pymodule changedoccupancy()/holds_slot()from a simple non-archived-task count to per-attempt held-reservation counting (a task can now hold 0, 1, or several execution slots at once: a worker attempt plus independent verifier attempts). This silently affected the already-mergedsettings show/preset listoutput. Portsexecution_reservations.py's read path in full — the exact_attempt/_occupant/_observationvalidation (same error text on a malformed record) plusheld(task)— and rewritessettings.occupancy/capacity_viewto match, including the new middle error branch (occupancy itself can now fail independently ofsettings.json) and updated note text.doctor/initdifferential tests compare two separate process invocations that each embed a live timestamp; normalized ISO timestamps before comparing to remove a real (if rare) flake.status/inbox(non-live): portsstatus(store, live=False, inbox=?)in full — the brief/version sidecar (go/internal/versions:read_versions/legacy_versions/versions_view/refresh_state/revision_view, including revision content-hash verification), the returns/obligations subsystem (go/internal/returns:open_attention/open_obligations/return_route/route_key/read_returns/notification_state/returns_view),cleanup_pending(go/internal/cleanup), andmetadata_summary(added to the existinggo/internal/metadata).statuscmdassembles all of it into the per-task row plus the top-level envelope.brief list TASK_ID: trivial on top of the versions package — a task read plusversions.Viewwithbrief_pathprepended.brief regenerate/request/adoptstay on the Python reference.env show TASK_ID [--max-chars N](go/internal/environment): portsenv_show/environment_view—read_environment's symlink/schema-mismatch checks,redact's 8 secret-pattern regexes (GitHub/OpenAI/Slack/AWS token shapes, bearer tokens, PEM headers, generickey=/token=/secret=/password=assignments),bounded_view's truncation metadata (including itschars/truncated/redactions/text/notepolymorphic-vs-plain-string call convention), and the discovery/endpoints/logs/resources/services reshaping (services_view, including the Python falsy-empty-dictreadiness/stop→Nonequirk).env discover/record/inspect/start/stopstay on the Python reference — those observe or mutate live state.release list/release show SHA(go/internal/release): portsrelease_list/release_show/verify_release's full ~20-check manifest verification (schema, source SHA, every manifest-listed file's content hash, required files,bin/sumctl's executable bit, no.sum/.codegraphin the tree, Mesh overlay marker deep-equality plus itsdist/file hashes, pinned tool symlink resolution, optional dependency-inventory + native-artifact validation) andinstallation_root's designated-state-home + git-toplevel check. A typedVerifyErrormarks every expected validation failure (mirroringraise SumError);release_listcatches only that type per row (ok:false+ error text) and lets an unexpected filesystem error propagate and fail the whole command, matching Python'sexcept SumErrorletting other exception types through.release stagestays on the Python reference (it mutates).project list/project show NAME(go/internal/project): portsproject_list/project_show/observe_project—read_projects's symlink/schema checks,project_identity/parse_remote_identity/same_remote's Git-URL parsing (ssh-like and https forms, scp-style shorthand, host normalization, repo-part validation), andobserve_project's live one-shot git inspection (toplevel check, remote comparison, HEAD, dirty status, linked worktrees) — each tolerant of git failures exactly like Python (falls back toNone/falserather than raising).project enroll/migratestay on the Python reference (they mutate or clone).hook status(go/internal/hookstatus): portshook_status/hook_summary/pending_summary/hook_manifest/observe_plugin—read_health's symlink/schema checks,pending_summary's open-obligations aggregation across all tasks (reusinggo/internal/returns),hook_manifest's TOML generation (for theexpected_manifest_currenthash comparison), andobserve_plugin's bounded live registry probe via the existingherdrclient/toolpathpackages when a Herdr session context is available. Faithfully reproduces a subtle Python quirk:hook_statusreadshealth.jsona second time, unguarded, afterhook_summaryalready caught and degraded on the first read — so a malformedhealth.jsonstill fails the whole command, not just one field (confirmed via a differential test).hook enable/disable/eventstay on the Python reference (they mutate or require a live session). The live Herdr registry sub-path (ctx available + plugin actually registered) is implemented but not exercised by any test — verifying it needs a live Herdr session, the same category of gap asstatus --live.show TASK_ID(go/internal/evidenceview): ports the full task record plusversions_view,evidence_view, andreturns_view— reusing the already-portedversions.View/returns.Viewand addingevidence_view/current_candidatenew.evidence_viewis the closure-readiness computation: scoped evidence records tagged with candidate currency, a synthesized legacy report row when a task predates structured evidence, worker/root verification run selection, review status, and the "missing" prerequisite list (structured handoff, complete/matching PR identity, passing root verification, standardized-contract worker run + review + policy-review gates).current_candidatedoes one livegit rev-parse HEADin the task's worktree, tolerating a missing worktree or git failure exactly like Python.context TASK_ID(go/internal/contextview): ports the defaultcontext_viewpath (no flags, whichcontext_viewitself always resolves to the single "outline" section) plus--section NAME(repeatable, deduplicated in first-occurrence order, matchinglist(dict.fromkeys(...))) for all 10 of 10 CONTEXT_SECTIONS:outline,decisions,returns,handoff,evidence,brief,notes,execution,environment,update. Portscursor_of/cursor_counters/state_digest(the cursor token every read carries),paged(the stable append-only-list pagination helper several sections share),outstanding,latest_handoff/handoff_view/section_handoff,section_decisions,section_evidence,section_brief,section_execution,section_environment(skill_references,return_commands),section_update(default_runtime, reusingrelease.VerifyRelease/release.InstallationRoot), and the inlinereturnssection body — reusingversions.View,evidenceview.View,returns.View,returns.OpenAttention, andcleanup.Pending. Also ports--role worker|reviewer|coordinator: it defaults the section list toROLE_SECTIONS[role]when no--sectionis given, filterssection_decisionsby role (worker→answered-only, coordinator→open-only), adds the top-levelcontract/authorityenvelope (ROLE_CONTRACT), narrowsskill_referencesto just that role's skills, and — for reviewer/coordinator — addsenvironment.artifactsvia a newartifact_references/artifact_scopeport (classifies worker-supplied handoff artifact strings as checkout/outside-checkout/unscoped by string inspection only, no filesystem calls). Four new small packages:go/internal/notes(notes_state, the task's optional timestamped-entry notes.md),environment.Outline/Stamp(environment_outline/environment_stamp, needed by the cursor and outline),go/internal/graphview(read_graph/graph_path/graph_summary/graph_view— the task-scopedgraph.jsonsidecar, distinct from the existinggo/internal/graph, which only handlesgraph config's MCP snippet), and the role/artifact helpers above. Also ports--since CURSORcursor-based diffing:parse_cursor(theCURSORregex, raising Python's exact error text for a malformed token) andchanges_since(new questions, status-changed questions matching the cursor's timestamp, new evidence with report/publication change flags, refresh events, new attention, notes-entries delta, and the state-digest-based changed/unchanged verdict) — including the subtle early-return control flow: when--sinceis given alone (no--section/--role) and nothing changed, the command returns early with just the envelope,changes, and anote, rendering no section at all, exactly matchingcontext_view's variable-reuse structure. Also addsordjson.MarshalSortedCompact, a new encoder matching Python'sjson.dumps(sort_keys=True)exactly (sorts object keys at every level, leaves array order alone) —state_digest/environment_stamphash this text directly, so it has to be byte-identical. Along the way, fixed two real bugs: (1) a latent bug inordjsonwhere a nil*ordjson.Object(a typed nil, distinct from an untyped nil) crashed all three encoders via a nil-pointerLen()call instead of encoding as JSONnull— fixed inencodeCompact/encodeIndent/encodeSortedCompact, benefiting every port usingordjson, not justcontext; (2) a correctness bug introduced by the first--sectioncommit in this same PR:validContextSectionslisted all 10CONTEXT_SECTIONSnames beforecontextview.View's switch had cases for all of them, so requesting an unimplemented section (e.g.--section update, at the time) was silently accepted and omitted that section's key from the output entirely instead of falling back to the Python reference — fixed by restrictingvalidContextSectionsto only what's actually wired at each commit, adding each section's case and its validity entry in the same commit from then on.context TASK_ID --after/--limit/--max-chars/--revision/--kind: the last unported flags oncontext. Bundles the growing flag list into acontextview.Optionsstruct rather than continuing to growView's positional parameter list.--limit/--after/--max-charsrange validation (--limit1..CONTEXT_MAX_LIMIT=200;--after/--max-charsnon-negative) happens natively insidecontextview.View, matchingcontext_view's exact error text and code position; a malformed (non-integer) value for any of the three falls back to the Python reference so argparse's owntype=interror text applies rather than replicating it.--revisionreuses the already-portedversions.ReadVersionsplus a newly-exportedversions.RevisionView(was unexported, used only internally bybrief list) to look up a recorded brief revision's verified content.--kindfilterssection_evidence's record list before paging. Fixed a third real bug found by differential testing while building this:section_evidence'skindscount-by-kind breakdown must be computed from all evidence records, not the--kind-filtered subset — Python'sby_kindloop runs overview["records"]before filtering; the initial Go port filtered first, which would have under-counted every kind not requested. This completes native porting of 100% ofsumctl's read-only command surface — every command/flag combination that only reads state now runs natively in Go; nothing read-only still falls back to the Python reference.Scope
The
--livepath (status --live/inbox --live) is not ported:Snapshots' live agent fan-out across sessions/panes,attention_sweep, andpump's notification delivery are a separate, larger, stateful undertaking.status/inboxonly go native for zero extra arguments;--livefalls back to the Python reference unchanged.Test plan
go build ./... && go vet ./... && gofmt -l .clean insidego/.go test ./...— all packages pass, including differential tests against the real Pythonbin/sumctl:settings show/preset listre-verified against the new occupancy model: a legacy running task, a legacy archived task, a real multi-reservation execution record (1 held worker + 1 released verifier + 1 held verifier → 2 slots), and a malformed reservation (invalid id prefix) exercising the new error path.status/inboxagainst an empty store and against the same fixture set above (exercising occupancy through the full command, not justsettings show).doctor/initlive against this dev checkout's own real--home(safe: both are read-only), now with timestamp normalization.brief listacross a legacy task (no versions.json), a real versions.json with one intact revision, and a report recorded under a superseded revision while a later revision is verification-affecting (the trickiestreport_evidencebranch).env showacross: no environment record; a full present record (discovery with a secret-shaped command exercising redaction, endpoints incl. a stale one with conflicts, logs, resources, services with both populated and empty-dictreadiness/stop);--max-charstruncation;--max-chars 0unbounded; and a schema-mismatch error path.release list/release showagainst a synthetic git-repo "installation" fixture (a tempdirgit init'd with a designating.sum/state.json— never the real installation this checkout is a worktree of): empty releases dir; a fully valid release built file-by-file to satisfy everyverify_releasecheck (real hashed files, Mesh overlay marker, resolving tool symlinks) alongside a broken (missing-manifest) release and an in-progress.staging-*dir, all in onerelease list;release showby full SHA and by a short prefix; and a no-matchrelease show, exercising the command-level{"error": ...}failure path (Go's returned error text checked against Python's own stderr JSON).project list/project showagainst synthetic git clones (never the real installation): an empty registry; a clean clone with a matching remote, a dirty clone, a clone with a mismatched recorded remote, and a missing clone, all in oneproject list;project showwith active tasks (excluding an archived one and an unrelated-repository one) and an unknown-project command-level failure path.hook statuswith Herdr env vars blanked (both sides take the records-onlyctx=Nonepath): nohealth.json; a disabled record with one pending obligation (exercisingpending_summary's count/age); an enabled record with aplugin_idbut no instance identity yet (theexpected_manifest_currenterror-to-Nonepath); an enabled record with a mismatched manifest hash; and a malformed-schemahealth.jsonexercising the command-level failure path (the double-read quirk above).showacross: a minimal task (no worktree/evidence/PR/reviewer); a legacy report with no structured evidence (exercises the synthesized report row); a real git worktree with a failing root verification run (exercises the "latest root run ... was fail" missing message); and a fully standardized contract with a real worktree, handoff, worker run, root run, and an approving policy review — all current against the live HEAD — reachingprerequisites_met: trueend to end.context(bare, outline-only) across: a minimal task with no worktree/evidence/notes/environment record; a real git worktree with a current handoff and a passing root verification (exercisesevidence_view's closure/verification fields inside the outline); and a task with open/applied decisions, anotes.mdwith one entry, and a stale environment record with one endpoint and one running service (exercisesenvironment_outline's per-state counts). The liveread_attimestamp is normalized before comparing.context --section:decisions+returnstogether on a task with one open and one applied question;handoff+evidencetogether against a real git worktree with a handoff (files, checks, next_action), a passing coordinator verification, and an approving review (three evidence kinds in one task);briefon a legacy task with no versions.json;noteswith a present two-entry notes.md and with no notes.md at all;executionwith no graph record/launch/parent/reviewer and with areadygraph record (index counts, a successful attempt) plus a full launch/admission/parent/reviewer;environmentwith no environment.json and with a present record;updateon a legacy task outside any installation (exercises the try/exceptinstallation_root"unavailable" branch — the realistic case for a lab/dev--home).context --role: worker role with default sections (decisions filtered to answered-only); coordinator role with default sections (decisions filtered to open-only); reviewer role with an explicit--section environmentexercisingartifact_referencesacross all four scope outcomes (a checkout-relative path, a../-escaping path, an absolute path, and a~-prefixed path) in one handoff record.context --since: an unchanged cursor (fetched from a real prior read) producing the early-return note-only response; a changed cursor (a question added between reads) with no section requested, rendering just the changes block; the same changed cursor with an explicit--section, rendering both changes and the section; and a malformed cursor string exercising the command-level failure path.context --after/--limit/--max-chars/--revision/--kind:--after+--limitpaging thedecisionssection;--kindfiltering theevidencesection (this is the case that caught theby_kindbug above);--max-charstruncating thebriefsection's approved text;--revisionlooking up a recorded brief revision by ID against a realversions.json+revision file; an unknown--revisionID as a command-level failure;--limitout of range (0and201) and negative--after/--max-charsas command-level failures matchingcontext_view's exactSumErrortext. Plus fallback checks for a non-integer--max-chars(mirrors argparse's owntype=interror), an unrecognized--role, and an unrecognized--sectionname.MISE_ENABLE_TOOLS=go,python,node python3 .agents/skills/verify/scripts/verify_run.py --base 81dbdda96dd7c0b31696634f9f943b28981f511b→verify: pass(candidated5b2aa5c3bb8ce62da65cec03d93a0b1902d87c2, non-provisional, 87 scenarios pass, 9 not-run/manual). Record:.artifacts/verification/20260911T022710Z-e937b5fb/run.json.verify: passat candidateefeea569bfd8c59259b3f55c117a28d7e9ea470d(the brief list commit), clean. Record:.artifacts/verification/20260911T065122Z-06aa265e/run.json.verify: passat candidateebecb0979128befadc74522944438dca660cb817(the env show commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T075104Z-46e0e968/run.json.verify: passat candidate21f23905e06f719ec6b4ba90b6a84fb66965c1bd(the release list/show commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T082115Z-109d3261/run.json.verify: passat candidatefbfb1a01bea055a75b003e0a6201c5a1ad2b43cb(the project list/show commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T084841Z-235b32dc/run.json.verify: passat candidate822d13d1d75af15e9c9e6830d433dcb2d72864b0(the hook status commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T091550Z-3bb6e83f/run.json.verify: passat candidate24127e148d316af0dd09e179e610e7ad7125f732(the show commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T094139Z-5934bdf5/run.json.verify: passat candidatedae51d4661e683b410db97717b8ebb5561e19f46(the context outline commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T101436Z-8ddee53e/run.json.verify: passat candidate51de4df21f2d34f3bdca47207bf02db0dcf96bb2(the context --section commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T104124Z-1dd256e6/run.json.verify: passat candidate2a2aad2878ab7919aecb88a91aca844ce23b7287(the notes-section test fix), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T110438Z-0b839a92/run.json.verify: passat candidate265fe4733f4a2e2d8542cb0fc81a680b4bfe9b87(the execution section commit + validContextSections fix), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T112934Z-339ffa0b/run.json.verify: passat candidate9b99e8f089e916d2a1516a4f68b10d6b121d51e9(the environment/update sections commit, completing all 10 CONTEXT_SECTIONS), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T115739Z-3ba188e3/run.json.verify: passat candidate32c724cfd6f97599f437c5a64b8f26a0b9ade845(the --role support commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T122439Z-939c5b97/run.json.verify: passat candidate0091b13121eea002e655422cdf4013cfa92951c2(the --since support commit), clean, 87 scenarios pass, 9 not-run/manual. Record:.artifacts/verification/20260911T125207Z-c25f111b/run.json.02891f6(this final --after/--limit/--max-chars/--revision/--kind commit, completing the read-only surface) is NOT yet independently verified throughverify_run.py. Three consecutive attempts were killed by the OS for low memory on this shared host (free -hshowed ~9.5-9.6Gi available each time, verify's own log empty each time — consistent with transient resource pressure from other concurrent sessions, but three in a row is outside the "check memory, retry once" pattern that resolved cleanly twice earlier in this same PR, so retries were paused rather than repeated blindly).go build ./... && go vet ./... && gofmt -l . && go test ./...are all clean locally for this commit (see Test plan above) — only the full repository-level verification run is outstanding. Will retry and record the result here once it completes cleanly.origin/mainhas not moved past this branch's base since verification ran (checked each time).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 now natively ported (addingstatus,inbox,brief list,env show,release list,release show,project list,project show,hook status,show,contextto the 8 from feat(go): first slice of full Go port of sumctl (#94) #95); everything else still runs through the Python reference.status --live/inbox --live(Snapshots, attention_sweep, pump) stay on the Python reference.init's designated/coordinator branch is still not ported (unchanged from feat(go): first slice of full Go port of sumctl (#94) #95).env discover/record/inspect/start/stop,release stage,project enroll/migrate, andhook enable/disable/eventstay on the Python reference (observe, mutate, clone, or require a live session).hook status's live-registry sub-path (a Herdr session context available and a plugin actually enrolled) is implemented but untested — no live Herdr session in this environment; same category of gap asstatus --live.context TASK_IDnow natively supports the bare shape,--sectionfor all 10 sections,--role,--since, and--after/--limit/--max-chars/--revision/--kind. This was the last unported piece of the read-only surface: every read-onlysumctlcommand is now natively ported in this branch. (Full repository verification of the final commit is still pending — see Test plan / verification log above.)settings set,preset set/delete,dispatch,prepare,start,ask/answer/resolve/report,review,verify,pr reconcile/evidence,cleanup,pump,hook enable/disable/event,attention,bind,backup,project enroll/migrate,herdrbridge,graph init/status,dev prepare/list/remove,refresh request/status/adopt,release stage,update apply/rollback) — a qualitatively different risk class (store.lock()/atomic-write concurrency, not just reads) from everything ported so far. Holding for an explicit go-ahead from the boss before starting that phase, rather than proceeding autonomously as with the read-only work.