Conversation
Adds resolveComputeServiceId (services.ts) — resolve by name, or the sole compute service in the project when name is omitted — and four lifecycle subcommands wired into the existing `compute` group: start/stop/suspend (mutating, via rawRequest + handleApproval, gated for future-proofing) and status (plain read via request).
feat(nextactions): send Insta-Hints and render Next: affordance
There was a problem hiding this comment.
2 issues found across 10 files
Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/util.ts">
<violation number="1" location="src/util.ts:40">
P3: `NextAction.args` is typed as `Record<string, unknown>`, so property accesses like `a.branch` and `a.type` in the `OP_COMMAND` handlers are completely unchecked. A call-site typo (e.g., `{brnach: 'main'}` instead of `{branch: 'main'}`) would silently produce a wrong placeholder command rather than a compile error. Consider using a per-op discriminated union or at least documenting the expected shape per op so callers get editor feedback on valid property names.</violation>
</file>
<file name="src/commands/compute.ts">
<violation number="1" location="src/commands/compute.ts:72">
P3: `insta compute status` without `[service]` prints the opaque service ID even when the sole compute service has a name. Derive the name from the already-fetched `services` list so status output stays readable and consistent with lifecycle commands.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| return false | ||
| } | ||
|
|
||
| export type NextAction = { op: string; reason: string; args?: Record<string, unknown>; gated?: boolean } |
There was a problem hiding this comment.
P3: NextAction.args is typed as Record<string, unknown>, so property accesses like a.branch and a.type in the OP_COMMAND handlers are completely unchecked. A call-site typo (e.g., {brnach: 'main'} instead of {branch: 'main'}) would silently produce a wrong placeholder command rather than a compile error. Consider using a per-op discriminated union or at least documenting the expected shape per op so callers get editor feedback on valid property names.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/util.ts, line 40:
<comment>`NextAction.args` is typed as `Record<string, unknown>`, so property accesses like `a.branch` and `a.type` in the `OP_COMMAND` handlers are completely unchecked. A call-site typo (e.g., `{brnach: 'main'}` instead of `{branch: 'main'}`) would silently produce a wrong placeholder command rather than a compile error. Consider using a per-op discriminated union or at least documenting the expected shape per op so callers get editor feedback on valid property names.</comment>
<file context>
@@ -37,6 +37,34 @@ export function handleApproval(res: { status: number; body: any }): boolean {
return false
}
+export type NextAction = { op: string; reason: string; args?: Record<string, unknown>; gated?: boolean }
+
+// Neutral op → an `insta` command string. Unknown ops fall back to reason-only (no crash).
</file context>
| const id = resolveComputeServiceId(services, serviceName) | ||
| const r = await api.request('GET', `/projects/${p.projectId}/services/${id}/state`) | ||
| if (opts.json) return printJson(r) | ||
| info(`compute ${serviceName ?? id}: desired=${r.desiredState} live=${r.state}`) |
There was a problem hiding this comment.
P3: insta compute status without [service] prints the opaque service ID even when the sole compute service has a name. Derive the name from the already-fetched services list so status output stays readable and consistent with lifecycle commands.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/compute.ts, line 72:
<comment>`insta compute status` without `[service]` prints the opaque service ID even when the sole compute service has a name. Derive the name from the already-fetched `services` list so status output stays readable and consistent with lifecycle commands.</comment>
<file context>
@@ -41,3 +42,32 @@ function printDomain(r: any, json?: boolean): void {
+ const id = resolveComputeServiceId(services, serviceName)
+ const r = await api.request('GET', `/projects/${p.projectId}/services/${id}/state`)
+ if (opts.json) return printJson(r)
+ info(`compute ${serviceName ?? id}: desired=${r.desiredState} live=${r.state}`)
+}
</file context>
| info(`compute ${serviceName ?? id}: desired=${r.desiredState} live=${r.state}`) | |
| info(`compute ${services.find((s: { id: string; name: string }) => s.id === id)?.name ?? id}: desired=${r.desiredState} live=${r.state}`) |
jwfing
left a comment
There was a problem hiding this comment.
Review — compute lifecycle controls + "Next:" action hints
Summary: Adds insta compute start|stop|suspend|status [service] and server-driven "Next:" hints after key operations; the diff is tight, well-scoped, and the pure logic is properly unit-tested — no blocking issues found.
Requirements context
No matching spec/plan found — this repo has no docs/superpowers/ (nor docs/specs/) directory, and no DEVELOPMENT.md/CONTRIBUTING.md. Assessed against the PR description (cubic summary) and the existing code conventions in src/ alone.
Critical
(none)
Suggestion
Functionality — state-field casing diverges between the two read paths. src/commands/compute.ts:58 reads res.body.service?.desired_state (snake) + res.body.state from POST /services/{id}/{verb}, while src/commands/compute.ts:72 reads r.desiredState (camel) + r.state from GET /services/{id}/state. These are two different endpoints so different shapes are plausible — and the nested service object already uses snake_case elsewhere (s.machine_count, services.ts:62), which makes desired_state reasonable. But nothing (no test, no spec) pins either shape, so a single wrong field name would silently print desired=undefined with no failure. Recommend confirming both field names against the platform API contract, or adding a light smoke assertion.
Security — secrets.set hint interpolates the value directly. src/util.ts:46 renders insta secrets set ${a.name} ${a.value}. If the control-plane ever populates args.value with a real secret, it would be echoed to stdout (and into shell history / CI logs when copied). Low blast radius since the value is server-controlled, but secrets should never be printed — suggest always emitting a <value> placeholder regardless of args.
Software engineering — status vs. lifecycle label inconsistency. computeStatus prints serviceName ?? id (compute.ts:72), so when the name is omitted it shows the raw service id, whereas lifecycle prints the resolved res.body.service?.name ?? id (compute.ts:58). Minor UX inconsistency; consider surfacing the resolved name in status too.
Information
- Testing (positive). Pure helpers are well covered —
resolveComputeServiceIdhas 5 cases including the ambiguity/none/not-found branches (test/services.test.ts:46-65) andnextActionsLineshas 5 including unknown-op fallback and empty input (test/util.test.ts:25-56). Leaving the I/O command wrappers (lifecycle/computeStatus) untested matches the existing repo convention (servicesAdd,deployare untested too), so this is consistent, not a gap. - Functionality.
Insta-Hints: 1(src/api.ts:59) is now sent on every request, including the unauthenticated/auth/refresh. Benign —renderNextActions(undefined)no-ops when a response carries nonextActions, so existing output is unchanged — just noting the header is global rather than per-command. - Performance. No concerns. Each lifecycle/status call does one extra
GET /servicesto resolve the id, matching the existing pattern inservicesRemove/scale/upgrade. No N+1, no unbounded loops, no blocking work on a hot path. - Security. Aside from the
secrets.setnote above: no auth/authorization checks were changed, no new dependencies, and mutating calls still route throughrawRequest+handleApproval, so approval gating is preserved (compute.ts:56).
Verdict
approved (informational — zero Critical findings; the three Suggestions are non-blocking). Posted as a COMMENT; explicit GitHub approval remains a separate human action.
Fermionic-Lyu
left a comment
There was a problem hiding this comment.
LGTM, Approved. (Relaying John-bot's approved verdict — approved with the maintainer account, since John-bot can't approve its own PR.)
Summary by cubic
Add compute lifecycle controls and show "Next:" action hints after key operations to guide follow-ups. Sends
Insta-Hintsto the API to receive suggestions and updates compute command help.New Features
insta compute start|stop|suspend|status [service]. Resolves the target compute by name or the sole compute service; prints desired vs live state; supports--json; mutating calls honor approvals.project create,services add,deploy, andbranch create. Maps ops likeservice.add,deploy,secrets.set,metrics,logs, andapprovals.approve; unknown ops fall back to reason-only. Enabled by adding theInsta-Hints: 1header.Bug Fixes
computetarget so the suggested commands are runnable.Written for commit 80da15e. Summary will update on new commits.