Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-04-22
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Why

- Guardex currently documents OpenSpec tiers on `gx branch start`, but the start script still treated tier selection as a no-op and always behaved like the full scaffold path once auto-bootstrap was enabled.
- Codex launches also had no lightweight task-size gate, so tiny asks paid the full OMX/T3 setup cost even when the repo contract says those asks should stay caveman-only.
- Active session records did not preserve the routing decision, which made takeover prompts and the Active Agents surface blind to whether a sandbox was intentionally lightweight or OMX-backed.

## What Changes

- Wire real `T0`/`T1`/`T2`/`T3` behavior into `scripts/agent-branch-start.sh` and its template so auto-bootstrapped branch starts create the right OpenSpec footprint for the requested tier.
- Add a task-size decider to `scripts/codex-agent.sh` and its template so explicit lightweight asks route to caveman with `T1`, standard behavior changes route to OMX with `T2`, and orchestration-heavy asks escalate to `T3`.
- Persist `taskMode`, `openspecTier`, and `taskRoutingReason` in the active-session record schema and cover the new behavior with focused branch-start, sandbox, and session-state tests.

## Impact

- Affected surfaces are the Guardex branch bootstrap path, Codex launcher flow, and the VS Code Active Agents session metadata readers.
- Main risk is routing drift between the branch-start script, the Codex launcher, and the session schema; the targeted regression suite now exercises those surfaces together to keep them aligned.
- Existing default `gx branch start` behavior remains intact when `GUARDEX_OPENSPEC_AUTO_INIT=false`; tiered scaffolding only materializes on the same auto-bootstrap path Guardex already uses when OpenSpec initialization is enabled.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## ADDED Requirements

### Requirement: Guardex branch start honors OpenSpec tiers
`gx branch start` SHALL apply the requested OpenSpec tier instead of always creating the full T3 scaffold.

#### Scenario: T1 branch start creates a notes-only change workspace
- **WHEN** an operator runs `gx branch start --tier T1 ...`
- **THEN** Guardex creates the agent branch/worktree
- **AND** it initializes `openspec/changes/<change>/notes.md` plus `.openspec.yaml`
- **AND** it does not create `proposal.md`, `tasks.md`, or an `openspec/plan/<plan>/` workspace.

#### Scenario: T2 branch start skips the plan workspace
- **WHEN** an operator runs `gx branch start --tier T2 ...`
- **THEN** Guardex creates the full change workspace with `proposal.md`, `tasks.md`, and `specs/.../spec.md`
- **AND** it does not create an `openspec/plan/<plan>/` workspace.

#### Scenario: T3 branch start keeps the full scaffold
- **WHEN** an operator runs `gx branch start --tier T3 ...`
- **THEN** Guardex creates both the full change workspace and the plan workspace.

### Requirement: Codex launcher auto-routes task size into mode plus tier
The Codex launcher SHALL classify the requested task before starting the sandbox and choose the lightweight or OMX lane accordingly.

#### Scenario: explicit lightweight task routes to caveman and T1
- **WHEN** `scripts/codex-agent.sh` launches a task whose text starts with `quick:`, `simple:`, `tiny:`, `minor:`, `small:`, `just:`, or `only:`
- **THEN** it reports a `caveman` task mode
- **AND** it starts the sandbox with OpenSpec tier `T1`
- **AND** the launched Codex process receives the selected mode/tier in its environment.

#### Scenario: non-trivial task routes to OMX-backed tiers
- **WHEN** `scripts/codex-agent.sh` launches a broader behavior/refactor/workflow task without a lightweight prefix
- **THEN** it reports an `omx` task mode
- **AND** it selects `T2` by default
- **AND** it upgrades to `T3` for clearly plan-heavy or orchestration-heavy requests.

### Requirement: active session records capture the routing decision
The active session record written for Codex sandboxes SHALL preserve the selected task mode and OpenSpec tier.

#### Scenario: active session record stores mode plus tier
- **WHEN** `scripts/agent-session-state.js start ...` is called with task-routing metadata
- **THEN** the written `.omx/state/active-sessions/*.json` record includes the selected task mode, OpenSpec tier, and routing reason.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Definition of Done

This change is complete only when **all** of the following are true:

- Every checkbox below is checked.
- The agent branch reaches `MERGED` state on `origin` and the PR URL + state are recorded in the completion handoff.
- If any step blocks (test failure, conflict, ambiguous result), append a `BLOCKED:` line under section 4 explaining the blocker and **STOP**. Do not tick remaining cleanup boxes; do not silently skip the cleanup pipeline.

## Handoff

- Handoff: change=`agent-codex-codex-session-task-mode-decider-2026-04-22-12-16`; branch=`agent/codex/codex-session-task-mode-decider-2026-04-22-12-16`; scope=`scripts/agent-branch-start.sh, scripts/codex-agent.sh, scripts/agent-session-state.js, templates/scripts/agent-branch-start.sh, templates/scripts/codex-agent.sh, vscode/guardex-active-agents/session-schema.js, templates/vscode/guardex-active-agents/session-schema.js, test/branch.test.js, test/sandbox.test.js, test/vscode-active-agents-session-state.test.js, openspec/changes/agent-codex-codex-session-task-mode-decider-2026-04-22-12-16/*`; action=`finish the tier-aware branch-start and task-routing lane, verify the focused coverage, then run the mandatory PR merge + cleanup flow`.
- Copy prompt: Continue `agent-codex-codex-session-task-mode-decider-2026-04-22-12-16` on branch `agent/codex/codex-session-task-mode-decider-2026-04-22-12-16`. Work inside the existing sandbox, review `openspec/changes/agent-codex-codex-session-task-mode-decider-2026-04-22-12-16/tasks.md`, continue from the current state instead of creating a new sandbox, and when the work is done run `gx branch finish --branch agent/codex/codex-session-task-mode-decider-2026-04-22-12-16 --base main --via-pr --wait-for-merge --cleanup`.

## 1. Specification

- [x] 1.1 Finalize proposal scope and acceptance criteria for `agent-codex-codex-session-task-mode-decider-2026-04-22-12-16`.
- [x] 1.2 Define normative requirements in `specs/codex-session-task-routing/spec.md`.

## 2. Implementation

- [x] 2.1 Wire real `T0/T1/T2/T3` behavior into `scripts/agent-branch-start.sh` and `templates/scripts/agent-branch-start.sh`.
- [x] 2.2 Add a Codex-side task-mode decider in `scripts/codex-agent.sh` and `templates/scripts/codex-agent.sh` so simple asks route to caveman/T1 and broader asks route to OMX/T2-or-T3.
- [x] 2.3 Persist the selected task mode/tier in the active-session record surface.
- [x] 2.4 Add/update focused regression coverage for branch-start tier scaffolding, codex-agent routing, and session metadata.

## 3. Verification

- [x] 3.1 Run targeted project verification commands. `node --test test/branch.test.js test/sandbox.test.js test/vscode-active-agents-session-state.test.js` passed after aligning the new tiered branch-start tests with the existing `GUARDEX_OPENSPEC_AUTO_INIT=true` bootstrap path; `git diff --check` also passed cleanly.
- [x] 3.2 Run `openspec validate agent-codex-codex-session-task-mode-decider-2026-04-22-12-16 --type change --strict`. Result: `Change 'agent-codex-codex-session-task-mode-decider-2026-04-22-12-16' is valid`.
- [x] 3.3 Run `openspec validate --specs`. Result: command completed successfully and reported `No items found to validate.`

## 4. Cleanup (mandatory; run before claiming completion)

- [ ] 4.1 Run the cleanup pipeline: `gx branch finish --branch agent/codex/codex-session-task-mode-decider-2026-04-22-12-16 --base main --via-pr --wait-for-merge --cleanup`. This handles commit -> push -> PR create -> merge wait -> worktree prune in one invocation.
- [ ] 4.2 Record the PR URL and final merge state (`MERGED`) in the completion handoff.
- [ ] 4.3 Confirm the sandbox worktree is gone (`git worktree list` no longer shows the agent path; `git branch -a` shows no surviving local/remote refs for the branch).
60 changes: 52 additions & 8 deletions scripts/agent-branch-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
OPENSPEC_MASTERPLAN_LABEL_RAW="${GUARDEX_OPENSPEC_MASTERPLAN_LABEL-masterplan}"
OPENSPEC_TIER_RAW="${GUARDEX_OPENSPEC_TIER:-T3}"
PRINT_NAME_ONLY=0
POSITIONAL_ARGS=()

Expand Down Expand Up @@ -54,8 +55,7 @@ while [[ $# -gt 0 ]]; do
shift
;;
--tier)
# Accepted for CLAUDE.md compatibility; scaffold size is not yet wired
# through this script. Consume the value so callers can pass it.
OPENSPEC_TIER_RAW="${2:-$OPENSPEC_TIER_RAW}"
shift 2
;;
--in-place|--allow-in-place)
Expand Down Expand Up @@ -246,11 +246,45 @@ normalize_bool() {

OPENSPEC_AUTO_INIT="$(normalize_bool "$OPENSPEC_AUTO_INIT_RAW" "1")"

normalize_tier() {
local raw="${1:-}"
local fallback="${2:-T3}"
local upper
upper="$(printf '%s' "$raw" | tr '[:lower:]' '[:upper:]')"
case "$upper" in
T0|T1|T2|T3) printf '%s' "$upper" ;;
'') printf '%s' "$fallback" ;;
*) return 1 ;;
esac
}

if ! OPENSPEC_TIER="$(normalize_tier "$OPENSPEC_TIER_RAW" "T3")"; then
echo "[agent-branch-start] Unsupported OpenSpec tier: ${OPENSPEC_TIER_RAW}" >&2
exit 1
fi

OPENSPEC_SKIP_CHANGE=0
OPENSPEC_SKIP_PLAN=0
OPENSPEC_MINIMAL=0
case "$OPENSPEC_TIER" in
T0)
OPENSPEC_SKIP_CHANGE=1
OPENSPEC_SKIP_PLAN=1
;;
T1)
OPENSPEC_SKIP_PLAN=1
OPENSPEC_MINIMAL=1
;;
T2)
OPENSPEC_SKIP_PLAN=1
;;
esac

resolve_openspec_masterplan_label() {
local raw="${OPENSPEC_MASTERPLAN_LABEL_RAW:-}"
local label

if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ -z "$raw" ]]; then
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]] || [[ -z "$raw" ]]; then
printf ''
return 0
fi
Expand Down Expand Up @@ -404,7 +438,7 @@ initialize_openspec_plan_workspace() {
local worktree="$2"
local plan_slug="$3"

if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
return 0
fi

Expand All @@ -430,14 +464,15 @@ initialize_openspec_change_workspace() {
local change_slug="$3"
local capability_slug="$4"

if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
return 0
fi

local init_output=""
if ! init_output="$(
cd "$worktree"
run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
GUARDEX_OPENSPEC_MINIMAL="$OPENSPEC_MINIMAL" \
run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
)"; then
printf '%s\n' "$init_output" >&2
echo "[agent-branch-start] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
Expand Down Expand Up @@ -599,8 +634,17 @@ fi

echo "[agent-branch-start] Created branch: ${branch_name}"
echo "[agent-branch-start] Worktree: ${worktree_path}"
echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_slug}"
echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
echo "[agent-branch-start] OpenSpec tier: ${OPENSPEC_TIER}"
if [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
echo "[agent-branch-start] OpenSpec change: skipped by tier ${OPENSPEC_TIER}"
else
echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_slug}"
fi
if [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
echo "[agent-branch-start] OpenSpec plan: skipped by tier ${OPENSPEC_TIER}"
else
echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
fi
echo "[agent-branch-start] Next steps:"
echo " cd \"${worktree_path}\""
echo " gx locks claim --branch \"${branch_name}\" <file...>"
Expand Down
5 changes: 4 additions & 1 deletion scripts/agent-session-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const sessionSchema = resolveSessionSchemaModule();
function usage() {
return (
'Usage:\n' +
' node scripts/agent-session-state.js start --repo <path> --branch <name> --task <task> --agent <agent> --worktree <path> --pid <pid> --cli <name>\n' +
' node scripts/agent-session-state.js start --repo <path> --branch <name> --task <task> --agent <agent> --worktree <path> --pid <pid> --cli <name> [--task-mode <caveman|omx>] [--openspec-tier <T0|T1|T2|T3>] [--routing-reason <text>]\n' +
' node scripts/agent-session-state.js stop --repo <path> --branch <name>\n'
);
}
Expand Down Expand Up @@ -65,6 +65,9 @@ function writeSessionRecord(options) {
worktreePath: requireOption(options, 'worktree'),
pid: requireOption(options, 'pid'),
cliName: requireOption(options, 'cli'),
taskMode: options['task-mode'],
openspecTier: options['openspec-tier'],
taskRoutingReason: options['routing-reason'],
});

const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
Expand Down
Loading