Skip to content

plan: align display across plan-family commands; surface CreatedBy#331

Merged
daniel-de-vera merged 2 commits into
mainfrom
plan-display-consistency
May 7, 2026
Merged

plan: align display across plan-family commands; surface CreatedBy#331
daniel-de-vera merged 2 commits into
mainfrom
plan-display-consistency

Conversation

@daniel-de-vera
Copy link
Copy Markdown
Contributor

@daniel-de-vera daniel-de-vera commented May 7, 2026

Summary

Audit + alignment of the plan-family commands' rendered output, on top of #326. Five small things that had drifted apart:

  • plan tag get now embeds the full plan body (Inputs / Steps / Outputs in the arrow form) instead of the old thin metadata blurb (ID, Steps count, prompt first line, Created). The tag header gains a Selection Hint: line so plan tag list and plan tag get agree on what summarises a tag.
  • Refactor: to embed the plan view from plantag without a circular import (plan → plantag exists for tag-application), the plan-detail printer moved into planshared as PrintPlanDetails alongside a PrintPlanBody variant that skips the top-level header for embedded use. plan/printers.go collapses to a thin wrapper.
  • Created By surfaced (new PlanStatus.CreatedBy from the latest go-sdk). Renders in the plan header for plan get / compile / create / recompile / run, and as Plan Created By: in plan tag get's tag header. Resolution by ActorType: user → email, api_key"api key " + masked id, system"system".
  • plan action get Outputs table drops REQUIRED / DEFAULT columns — they're input concepts that always rendered as false/empty for outputs. Review feedback on Add plan action list/get commands #325 that hadn't landed.
  • planrunnergroup cleanup: migrate inline time.Parse + timeago.NoMax to utils.TimeAgo, matching the cleanup we'd already done in plantag and planexec.

Examples

plan get (with CreatedBy)

ID:               4t88gfw8g4c7y
Selection Hint:   Runs a k6 load test against location.hotrod-devmesh:8081/locations through a sandbox; pick when validating that location-svc changes hold up under load with a p95 latency gate. Required param: sandbox. Optional: p95_ms (default 500), vus (default 10), duration (default 30s).
Cluster:          from sandbox param "sandbox"
Created:          Wed, 06 May 2026 15:06:19 -03 (19 hours ago)
Created By:       daniel@signadot.com
Executions:       4

Inputs:
  sandbox    (required)
  p95_ms     ← 500   (default)
  vus        ← 10    (default)
  duration   ← 30s   (default)

Steps:
  build_options
    action: eval (tjb9bx9zgdh5w)   (revision 1)
    inputs:
      duration     ← params.duration   (ref)
      expression   ← "--vus " + string(vus) + " --duration " + duration + " --env P95_MS=" + string(p…   (set in plan)
      p95_ms       ← params.p95_ms     (ref)
      vus          ← params.vus        (ref)

  load_test
    action: k6 (bzjkwqyg7497q)   (revision 4)
    image: grafana/k6:1.7.1
    inputs:
      options    ← steps.build_options.outputs.result   (ref)
      script     ← import http from 'k6/http';… (23 lines)   (set in plan)

Outputs:
  exit_code   ← steps.load_test.outputs.exit_code
  summary     ← steps.load_test.outputs.summary

plan tag get (before / after)

Before — thin embedded summary, no selection hint:

Name:      hotrod-location-latency-k6
Plan ID:   4t88gfw8g4c7y
Created:   Wed, 06 May 2026 15:06:24 -03 (18 hours ago)
Updated:   Wed, 06 May 2026 15:06:24 -03 (18 hours ago)

Plan:
  ID:        4t88gfw8g4c7y
  Steps:     2
  Created:   Wed, 06 May 2026 15:06:19 -03 (18 hours ago)

After — full embedded plan body, hint at top, Plan Created By: for unambiguity against the tag-level Created/Updated:

Name:              hotrod-location-latency-k6
Plan ID:           n2414qgqqp2vu
Selection Hint:    Runs a k6 load test against location.hotrod-devmesh:8081/locations through a sandbox; pick when validating that location-svc changes hold up under load with a p95 latency gate. Required param: sandbox. Optional: p95_ms (default 500), vus (default 10), duration (default 30s).
Plan Created By:   api key cahsmm-uJP...
Created:           Wed, 06 May 2026 15:06:24 -03 (19 hours ago)
Updated:           Wed, 06 May 2026 15:06:24 -03 (19 hours ago)

Inputs:
  sandbox    (required)
  p95_ms     ← 500   (default)
  vus        ← 10    (default)
  duration   ← 30s   (default)

Steps:
  build_options
    action: eval (tjb9bx9zgdh5w)   (revision 1)
    inputs:
      duration     ← params.duration   (ref)
      ...

Outputs:
  exit_code   ← steps.load_test.outputs.exit_code
  summary     ← steps.load_test.outputs.summary

History:
PLAN ID         TAGGED          UNTAGGED
n2414qgqqp2vu   3 minutes ago   (current)
4t88gfw8g4c7y   19 hours ago    3 minutes ago

plan action get Outputs table

Before (REQUIRED / DEFAULT always empty for outputs):

Outputs:
NAME        REQUIRED   DEFAULT   SCHEMA
exit_code   false                integer
summary     false                object

After:

Outputs:
NAME        SCHEMA
exit_code   integer
summary     object

Files touched

  • internal/command/planshared/plan.go — new file: PrintPlanDetails, PrintPlanBody, plan rendering moved in from plan/printers.go
  • internal/command/planshared/render.goFormatCreatedBy helper
  • internal/command/plan/printers.go — collapsed to a thin wrapper around planshared.PrintPlanDetails
  • internal/command/plantag/printers.go — embed PrintPlanBody in tag get, add Selection Hint and Plan Created By to the tag header
  • internal/command/planaction/printers.go — separate row type for action outputs (drops REQUIRED/DEFAULT)
  • internal/command/planrunnergroup/printers.go — migrate to utils.TimeAgo
  • go.mod / go.sum — bump go-sdk to the tip of main with PlanStatus.CreatedBy

Test plan

  • signadot plan get <id> shows Created By: (system / user email / api key) when populated
  • signadot plan tag get <name> shows the full embedded Inputs/Steps/Outputs and Plan Created By:
  • signadot plan action get <name> Outputs table has just NAME / SCHEMA
  • signadot planrunnergroup list CREATED column renders compactly ("X days ago")
  • signadot plan tag list SELECTION HINT column unchanged

The plan-family commands had drifted from each other in several
small ways since the previous PR. Audit + fixes:

- plan tag get rendered the embedded plan as a thin metadata blurb
  (ID, Steps count, prompt first line, Created) — the shape we
  replaced in plan get during the previous PR. It now embeds the
  full plan body (Inputs / Steps / Outputs sections) using the
  same arrow form plan get uses. Plan-level metadata is omitted:
  ID and SelectionHint already appear in the tag header above; the
  rest is one signadot plan get away. The tag header also gains a
  Selection Hint: line so plan tag list and plan tag get agree on
  what summarises a tag.

  To make this shareable without a circular import (plan imports
  plantag for tag-application after compile/create), the plan-detail
  printer moved into the planshared package as PrintPlanDetails
  alongside a PrintPlanBody variant that skips the top-level header
  for embedded use. plan/printers.go collapses to a thin wrapper.

- Surface PlanStatus.CreatedBy (added in the latest go-sdk drop)
  as a Created By: row in the plan header — used by plan get,
  compile, create, recompile, run, and the embedded view in plan
  tag get (where it renders as Plan Created By: to disambiguate
  against the tag-level Created/Updated fields). Resolution by
  ActorType: user → email, api_key → "api key" + masked id,
  system → "system".

- plan action get's Outputs table no longer carries REQUIRED /
  DEFAULT columns. Those are input concepts that always rendered
  as false / empty for outputs (review feedback on PR #325 that
  hadn't landed). Separate row type with just NAME / SCHEMA.

- planrunnergroup/printers.go: migrate inline time.Parse +
  timeago.NoMax to utils.TimeAgo, matching the cleanup we'd already
  done in plantag and planexec. Drops the time and timeago imports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@davixcky davixcky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

However one question, in some scenarios where the number of steps is larger, the plan get can be useless, can we have an option to by default hide the steps?

Empty-string values previously collapsed to the same shape as
nil/missing values: name (set in plan) with no detail. That hides
the distinction between "value not provided" and "deliberately
empty," which matters when an action interprets the empty string
specifically — e.g. run-image treats image:"" as "run on the host
runner without a container," a meaningful authoring choice the
reader needs to see.

Now an empty string renders as the literal "" (with quotes):

    image    ← ""   (set in plan)

and an unset/nil value still renders as no-detail:

    image    (set in plan)

Same disambiguation applies wherever FormatValue is used (plan
get's plan-level Inputs, plan x get's resolved inputs, plan get's
declared param defaults, output rows). Scalars still pass through
unchanged; objects and arrays continue to round-trip through
json.Marshal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@daniel-de-vera
Copy link
Copy Markdown
Contributor Author

However one question, in some scenarios where the number of steps is larger, the plan get can be useless, can we have an option to by default hide the steps?

I think it’s fine as it is, as we are not rendeing action bodies, even for larger plans it remains fairly readable (as shown below). That said, I’d also like to hear @foxish and @scott-cotton’s thoughts.

$ signadot plan get rfy1vrt9m9p70
ID:               rfy1vrt9m9p70
Selection Hint:   Hotrod-istio health/dispatch/load checks; image is a plan param so callers can pin a runc image or "" to run on the host runner.
Cluster:          pattern "signadot-staging"
Created:          Thu, 07 May 2026 10:38:35 -03 (32 seconds ago)

Inputs:
  frontend_url   ← http://frontend.hotrod-istio.svc:8080   (default)
  location_url   ← http://location.hotrod-istio.svc:8081   (default)
  runner_image   ← ""   (default)

Steps:
  frontend_health
    action: run-image (vjpm3jvtwkxu2)   (revision 2)
    inputs:
      image    ← params.runner_image   (ref)
      script   ← STATUS=$(curl -s -o "$TMPDIR/body.html" -w '%{http_code}' --max-time 10 "$(cat .… (3 lines)   (set in plan)
      url      ← params.frontend_url   (ref)
    outputs:
      result   schema: object

  location_health
    action: run-image (vjpm3jvtwkxu2)   (revision 2)
    inputs:
      image    ← params.runner_image   (ref)
      script   ← URL="$(cat ./context/url)/locations"… (7 lines)   (set in plan)
      url      ← params.location_url   (ref)
    outputs:
      result   schema: object

  dispatch_ride
    action: run-image (vjpm3jvtwkxu2)   (revision 2)
    inputs:
      frontend_ok    ← steps.frontend_health.outputs.result   (ref)
      frontend_url   ← params.frontend_url   (ref)
      image          ← params.runner_image   (ref)
      script         ← FRONTEND="$(cat ./context/frontend_url)"… (9 lines)   (set in plan)
    outputs:
      result   schema: object

  load_test
    action: k6 (0fk4dk2myr0h2)   (revision 2)
    inputs:
      frontend_ok   ← steps.frontend_health.outputs.result   (ref)
      script        ← import http from 'k6/http';… (32 lines)   (set in plan)
      target_url    ← params.frontend_url   (ref)

  verify_all
    action: check (0t57r22fxdvjb)   (revision 12)
    inputs:
      dispatch     ← steps.dispatch_ride.outputs.result   (ref)
      expression   ← frontend.healthy == true && location.healthy == true && location.locationCount >…   (set in plan)
      frontend     ← steps.frontend_health.outputs.result   (ref)
      location     ← steps.location_health.outputs.result   (ref)
      name         ← hotrod-all-checks   (set in plan)

Outputs:
  check      ← steps.verify_all.outputs.result
  dispatch   ← steps.dispatch_ride.outputs.result
  frontend   ← steps.frontend_health.outputs.result
  location   ← steps.location_health.outputs.result

@daniel-de-vera daniel-de-vera merged commit 429e525 into main May 7, 2026
@daniel-de-vera daniel-de-vera deleted the plan-display-consistency branch May 7, 2026 15:28
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.

2 participants