Skip to content

feat(cli): add P6 backend-unblocked CLI surfaces (issue #16)#19

Merged
mrgoonie merged 2 commits into
devfrom
feat/p6-backend-unblocked-cli
May 27, 2026
Merged

feat(cli): add P6 backend-unblocked CLI surfaces (issue #16)#19
mrgoonie merged 2 commits into
devfrom
feat/p6-backend-unblocked-cli

Conversation

@mrgoonie
Copy link
Copy Markdown
Contributor

Summary

Implements the 7 CLI command surfaces unblocked by digitopvn/goclaw backend PRs:

  • PR #37 (merged, beta v3.12.0-beta.16+): traces follow, providers reconnect
  • PR #44 (merged, beta v3.12.0-beta.20+): sessions branch, sessions follow, channels writers test, activity aggregate, logs aggregate

All seven are one-shot HTTP commands — no new watch loops, no new WS streams.

Surface inventory

Command Method + path
goclaw traces follow --session-key|--agent GET /v1/traces/follow
goclaw providers reconnect <id> POST /v1/providers/{id}/reconnect
goclaw sessions branch <key> --up-to-index N POST /v1/chat/sessions/{key}/branch
goclaw sessions follow <key> GET /v1/chat/sessions/{key}/history/follow
goclaw channels writers test <id> --group-id G --user-id U POST /v1/channels/instances/{id}/writers/test
goclaw activity aggregate --group-by ... GET /v1/activity/aggregate
goclaw logs aggregate [--group-by ...] GET /v1/logs/runtime/aggregate

Notable technical decisions

  • Numeric zero preservation: up_to_index=0 and cursor=0 are valid semantic values. Request body / query is constructed directly (bypassing buildBody's int-zero skip at cmd/helpers.go:86-89) so zeros appear literally on the wire. Regression tests assert this.
  • formatLastSeen helper: type-switches RFC3339 string (activity endpoint) vs epoch-millis number (logs runtime endpoint) so neither aggregate command renders 1.76e+12 in the table view.
  • HTTP retry surfaced: internal/client/http.go auto-retries 429/5xx three times. The original "502 fail-fast" assertion in the plan was dropped because it conflicts with built-in behavior; atomic-counter + structural "not a watch loop" checks preserve intent.
  • No new top-level command: activity aggregate attaches to the pre-existing activityCmd at cmd/admin.go:133. cmd_test.go top-level inventory unchanged.
  • Path escape: every {id}/{key} substitution uses url.PathEscape; each command has a path-escape regression test.

Verification

  • go vet ./... — clean
  • go build ./... — clean
  • go test -count=1 ./... — all packages PASS
  • code-reviewer subagent: DONE, 0 Critical, 0 High, 4 Low (cosmetic)
  • All 7 surfaces require live smoke test against gateway ≥ v3.12.0-beta.20 post-merge

Backend evidence (locked)

  • PR #37 merged at commit 56e227c4
  • PR #44 merged at commit 43049d3b
  • Earliest beta tag covering both: v3.12.0-beta.20

Out of scope (explicit)

  • WS streaming for traces follow / sessions follow — deferred; one-shot polling only.
  • No SSE, no auto-reconnect loops.
  • No commands or stubs for any other backend endpoint.

Plan + reports

  • Plan: plans/260527-1412-domain-coverage-p6-backend-unblocked/plan.md
  • Scope lock: reports/scope-lock-260527-p6.md
  • Code review: reports/code-review-260527-p6.md
  • Red-team sweep: reports/sweep-red-team-diff-260527-p6.md

Closes #16

mrgoonie added 2 commits May 27, 2026 14:16
Mirrors issue #16 scope (7 command surfaces) as 4 grouped implementation
phases with TDD per the user's request. Backend evidence: digitopvn/goclaw
PR #37 + PR #44. Verified beta tag containing PR #44 is v3.12.0-beta.20.

Phases:
  1. Scope Lock — contract re-verification, drift sweep
  2. Traces Follow + Providers Reconnect (PR #37)
  3. Sessions Branch + Follow (PR #44 chat)
  4. Channels Writers Test (PR #44 channels)
  5. Activity + Logs Runtime Aggregate (PR #44 aggregation)
  6. Tests + Docs sweep with out-of-scope red-team
  7. Ship Readiness — single PR to dev

Out-of-scope list retained verbatim from issue #16: traces replay,
generic logs aggregate, WS/SSE chat history, watch loops.
Seven one-shot HTTP commands wired to backend PRs #37 and #44
(gateway tag v3.12.0-beta.20+). TDD: failing tests landed before each
implementation slice; all suites green under -count=1.

PR #37 surfaces:
- traces follow --session-key|--agent [--since --limit --include-spans
  --status --channel]: GET /v1/traces/follow
- providers reconnect <id>: POST /v1/providers/{id}/reconnect

PR #44 surfaces:
- sessions branch <key> --up-to-index N [--new-session-key --label
  --metadata k=v]: POST /v1/chat/sessions/{key}/branch.
  --up-to-index=0 preserved literally on the wire (bypasses buildBody
  int-zero skip).
- sessions follow <key> [--cursor N --limit N]: one-shot cursor-based
  history poll, NOT a watch loop. --cursor=0 preserved in raw query.
- channels writers test <id> --group-id G --user-id U: POST writers/test
  with body containing exactly two keys.
- activity aggregate --group-by {action|actor_type|entity_type|actor_id}
  [--from --to --limit + scope filters]: subcommand of existing activityCmd.
- logs aggregate [--group-by {level|source} --level --source --from]:
  ring-buffer summary, distinct from logs tail.

Shared formatLastSeen helper type-switches RFC3339-string vs epoch-millis
last_seen so neither aggregate command renders scientific notation in
table view.

All path params url.PathEscape'd; all flag validation runs before HTTP
call; central error handler honored (no direct error printing).

Docs: README + docs/codebase-summary + CHANGELOG updated.

Closes #16
@mrgoonie mrgoonie merged commit 2801486 into dev May 27, 2026
4 checks passed
@mrgoonie mrgoonie deleted the feat/p6-backend-unblocked-cli branch May 27, 2026 11:04
mrgoonie added a commit that referenced this pull request May 28, 2026
…#21)

* feat(cli): add P6 backend-unblocked CLI surfaces (issue #16) (#19)

* plan(p6): add domain coverage P6 backend-unblocked CLI plan

Mirrors issue #16 scope (7 command surfaces) as 4 grouped implementation
phases with TDD per the user's request. Backend evidence: digitopvn/goclaw
PR #37 + PR #44. Verified beta tag containing PR #44 is v3.12.0-beta.20.

Phases:
  1. Scope Lock — contract re-verification, drift sweep
  2. Traces Follow + Providers Reconnect (PR #37)
  3. Sessions Branch + Follow (PR #44 chat)
  4. Channels Writers Test (PR #44 channels)
  5. Activity + Logs Runtime Aggregate (PR #44 aggregation)
  6. Tests + Docs sweep with out-of-scope red-team
  7. Ship Readiness — single PR to dev

Out-of-scope list retained verbatim from issue #16: traces replay,
generic logs aggregate, WS/SSE chat history, watch loops.

* feat(cli): add P6 backend-unblocked CLI surfaces (issue #16)

Seven one-shot HTTP commands wired to backend PRs #37 and #44
(gateway tag v3.12.0-beta.20+). TDD: failing tests landed before each
implementation slice; all suites green under -count=1.

PR #37 surfaces:
- traces follow --session-key|--agent [--since --limit --include-spans
  --status --channel]: GET /v1/traces/follow
- providers reconnect <id>: POST /v1/providers/{id}/reconnect

PR #44 surfaces:
- sessions branch <key> --up-to-index N [--new-session-key --label
  --metadata k=v]: POST /v1/chat/sessions/{key}/branch.
  --up-to-index=0 preserved literally on the wire (bypasses buildBody
  int-zero skip).
- sessions follow <key> [--cursor N --limit N]: one-shot cursor-based
  history poll, NOT a watch loop. --cursor=0 preserved in raw query.
- channels writers test <id> --group-id G --user-id U: POST writers/test
  with body containing exactly two keys.
- activity aggregate --group-by {action|actor_type|entity_type|actor_id}
  [--from --to --limit + scope filters]: subcommand of existing activityCmd.
- logs aggregate [--group-by {level|source} --level --source --from]:
  ring-buffer summary, distinct from logs tail.

Shared formatLastSeen helper type-switches RFC3339-string vs epoch-millis
last_seen so neither aggregate command renders scientific notation in
table view.

All path params url.PathEscape'd; all flag validation runs before HTTP
call; central error handler honored (no direct error printing).

Docs: README + docs/codebase-summary + CHANGELOG updated.

Closes #16

* fix(cli): render trace details by id with validation + exit-code mapping (issue #17) (#20)

Closes #17.

Bug surface: `goclaw traces get <id>` was unreadable in TTY mode (dumped raw JSON),
silently swallowed unmarshal errors (empty `{}`), accepted any raw id including
path-traversal sequences (`..`, `/`, control chars), and collapsed every server
failure to exit code 1 regardless of HTTP status.

Fixes:
- `cmd/traces.go`
  - `tracesGetCmd.RunE`: validate id allowlist before HTTP, decode payload with
    error surfacing, render header card + span tree + events list for TTY,
    pass-through JSON for `-o json` / piped stdout.
  - `validateTraceID`: regex allowlist `^[A-Za-z0-9._-]+$` + reserved-token reject
    (`.`, `..`, empty/whitespace). Returns typed `*client.APIError{Code: INVALID_REQUEST}`
    so the central error handler maps it to exit code 4.
  - `renderTraceTable` + `buildSpanTree`: insertion-ordered span tree, orphan spans
    attach to virtual root. No relink walk, cycle-safe (cycles silently drop).
- `internal/client/http.go`
  - Retry loop bug: previously closed `resp.Body` on every iteration including the
    final attempt, then `io.ReadAll` after the loop failed with
    "read on closed response body". This collapsed typed `APIError` into an opaque
    wrapped error and lost the exit-code mapping for 5xx/429. Fixed by skipping
    the per-iteration close on the final attempt; outer `defer` handles cleanup.

Exit-code contract for `traces get`:
- 0 success / 2 permission denied / 3 not-found /
  4 malformed id (pre-HTTP) / 5 server failure / 6 rate-limit.

Tests: 10 new tests in `cmd/traces_get_test.go` lock the wire contract,
table+JSON rendering, exit-code mapping per HTTP status, and the
"malformed id makes zero HTTP calls" invariant (parameterized table).
Fixture `cmd/testdata/trace_detail_get.json` is a scrubbed stub with
`_TODO_refresh` marker; refresh against live gateway before merging to default.

Docs: CHANGELOG `[Unreleased]` Fixed entry, README `Reading a Trace by ID`
section with exit-code table, `docs/codebase-summary.md` traces bullet updated.

* docs: add planning artifacts for P6 backend-unblocked CLI and skill scaffold
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant