feat: apply external gate verdicts#474
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds an “external gate verdict apply” surface to the Go-native spacedock status runner so that externally captured gate decisions (approve/revise/reject) can be applied via the existing guarded mutation path, and records that decision in entity frontmatter (gate-id, gate-verdict) with accompanying docs/schema updates.
Changes:
- Add
status --apply-gate --gate <id> --entity <ref> --verdict approve|revise|rejectargument parsing and dispatch routing. - Implement
runApplyGateto validate the entity is currently at agate: truestage, compute the target stage transition, and writestatus,gate-id, andgate-verdictviarunSet. - Extend schema + docs + FO skill surfaces, and add focused unit tests for apply-gate behavior.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| skills/fo-write-core/SKILL.md | Documents FO write authority including the new apply-gate mutation surface. |
| skills/fo-status-viewer/SKILL.md | Extends the FO-facing status invocation docs to mention --apply-gate. |
| internal/status/parse.go | Adds argument parsing for --apply-gate. |
| internal/status/native_runner.go | Wires apply-gate parsing and dispatch, with incompatibility checks for some read/mutate flags. |
| internal/status/apply_gate.go | Implements the apply-gate mutation and output/JSON emission. |
| internal/status/apply_gate_test.go | Adds tests covering approve advance, reject routing to feedback-to, and refusal on non-gate stages. |
| docs/site/reference/frontmatter-contract.md | Documents gate-id / gate-verdict as distinct from terminal verdict. |
| docs/site/reference/command-reference.md | Adds --apply-gate to the command reference entry for spacedock status. |
| docs/schema/entity.mdschema.yml | Adds gate-id and gate-verdict to the entity schema’s optional canonical fields. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func parseApplyGateArgs(args []string) (*applyGateRequest, error) { | ||
| if !contains(args, "--apply-gate") { | ||
| return nil, nil | ||
| } | ||
| gateID, err := parseSingleArg(args, "--gate", "gate-id") |
There was a problem hiding this comment.
Fixed in da0b09c. --gate, --entity, and --verdict now fail fast when used without --apply-gate, with coverage in TestApplyGateRejectsGateFlagsWithoutApplyGate so partial invocations cannot fall through to read output.
| case quiet: | ||
| fmt.Fprintf(stdout, "apply-gate slug=%s gate=%s verdict=%s status=%s->%s\n", | ||
| resolved.slug, req.gateID, req.verdict, currentStatus, nextStatus) | ||
| default: | ||
| fmt.Fprintf(stdout, "apply-gate slug=%s gate=%s verdict=%s status=%s->%s\n", | ||
| resolved.slug, req.gateID, req.verdict, currentStatus, nextStatus) | ||
| } |
There was a problem hiding this comment.
Not changing this one. In this repo, mutation --quiet means a single machine-readable success line, not silence; json_quiet_test.go already pins that behavior for --set --quiet and --archive --quiet. Keeping apply-gate aligned with that mutation contract is intentional.
a33d7e9 to
36428f3
Compare
7c97341 to
5dee228
Compare
Journey cost delta
|
Motivation
Spacedock gates are increasingly decided outside the FO's immediate text loop: gate UI, chat approval recovery, handoff packets, and future harness integrations can all capture a human verdict before the workflow state is updated. Without a first-class apply seam, agents have to translate those verdicts into ad-hoc
status --setmutations, which loses gate identity, bypasses stage semantics, and makes recovery paths harder to audit.This PR gives Spacedock a small, guarded adoption point for externally captured gate decisions. Callers provide the gate id, entity ref, and verdict; Spacedock resolves the entity, verifies it is actually parked at a declared gate stage, applies the workflow's own approve/revise/reject routing rules, and records the gate decision separately from terminal completion verdicts. That keeps external gate surfaces useful without teaching each harness to mutate frontmatter by hand.
Architecture fit
This is a Spacedock-core seam, not a Helm-specific integration. In the pillar map terms, external surfaces such as a gate UI, chat recovery path, or future harness capture the call at the resolution station;
spacedock status --apply-gateis the core ingress that turns that captured resolution into durable workflow record. The command intentionally accepts only generic workflow terms (gate id,entity ref,verdict) and then applies the workflow README's declared stage/gate semantics.That keeps the five-pillar boundary clean: P2/Malleable UI can present evidence and capture the call without owning frontmatter mutation rules; P0/Spacedock core owns validation, stage movement, and the decision record; P3/Self-improvement can later read that record without depending on which UI or harness captured it.
Summary
spacedock status --apply-gate --gate <id> --entity <ref> --verdict approve|revise|rejectfor externally captured gate decisions.runSetmutation path: approve advances to the next declared stage; revise/reject route tofeedback-towhen present; non-gate stages fail closed.gate-idandgate-verdictas separate entity fields, and document the schema, command reference, and FO write skill surface.origin/mainand update local host-integration tests for current Codex/agentsview CLI output shapes.--gate/--entity/--verdictflags now require--apply-gate, action conflicts fail fast, and JSON emitsgate_idinstead of ambiguousgate.Test Plan
go test ./internal/status -run ApplyGate -count=1go test ./internal/statusgo test ./internal/status -racego test ./internal/cli -run 'NativeRunner|RunDefaultsToNativeRunner|status|Status' -count=1go test ./internal/cli -run TestCodexPluginInstallIsHostNative -count=1go test ./skills/integration -run TestSurveyCodexPresenceThroughSync -count=1go test ./...go test ./... -raceReview
feedback-tobefore lookup/write--apply-gatecombined with another action such as--reador--archive--apply-gategate_idin JSON output--quietaligned with existing single-line machine-summary contractgate-idis generated by the surface/harness that captures the decision, not bystatus --apply-gatefo-status-viewerand intofo-write-coreYou've hit your session limit · resets 4pm (Asia/Taipei)