Skip to content

Commit 89e6c49

Browse files
NagyViktNagyViktclaude
authored
fix(gx): make OpenSpec scaffolding on-by-default and stop the silent-failure log (#549)
`gx branch start` claimed in its end-of-run log that the OpenSpec change workspace was created at openspec/changes/<slug>, but the directory was never materialized unless the user happened to have GUARDEX_OPENSPEC_AUTO_INIT=true in their environment. Reproduced three times this session: PRs #546, #547, #548 all required manual scaffold creation despite the log claim. Root cause: templates/scripts/agent-branch-start.sh line 12 defaulted the env raw to "false", which short-circuited initialize_openspec_*_workspace at line 655 / 629; but the trailing log lines 901-911 only checked $OPENSPEC_SKIP_CHANGE (tier-driven), not $OPENSPEC_AUTO_INIT, so the log lied. This change: - Flips the default to "true" so scaffolding happens by default, matching the project's own CLAUDE.md OpenSpec-first contract and the tier table that describes T2/T3 as creating "full change workspace". - Rewrites the end-of-run log block so each branch distinguishes three states: auto-init disabled, tier-skipped, scaffolded-at-path. The log can no longer claim a workspace exists when the helper exited early. Verified in-session: with the fix applied, a default-invocation `bash scripts/agent-branch-start.sh --tier T2 ...` (no env var) materializes proposal.md, tasks.md, .openspec.yaml, and specs/<n>/spec.md under the new worktree. Negative path with GUARDEX_OPENSPEC_AUTO_INIT=false prints "OpenSpec change: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)". Only templates/scripts/agent-branch-start.sh is edited; scripts/agent-branch-start.sh is a symlink to it (PR #548) and inherits the fix transparently. OpenSpec change at openspec/changes/agent-claude-fix-openspec-scaffold-silent-failure-2026-05-11-12-12/. Co-authored-by: NagyVikt <nagy.viktordp@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ac82f98 commit 89e6c49

4 files changed

Lines changed: 135 additions & 3 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Proposal: enable OpenSpec scaffolding by default in `gx branch start` and stop the silent-failure log
2+
3+
## Problem
4+
5+
`gx branch start` claims to scaffold the OpenSpec change workspace in its end-of-run log:
6+
7+
```
8+
[agent-branch-start] OpenSpec change: openspec/changes/<slug>
9+
```
10+
11+
But the directory is not created. Reproduced three times this session (PRs #546, #547, #548) — every agent worktree I started had to be manually populated with `proposal.md`/`tasks.md`/`spec.md` despite the log claiming the scaffold was in place.
12+
13+
### Root cause
14+
15+
Two coupled bugs in `templates/scripts/agent-branch-start.sh`:
16+
17+
1. **Default disables scaffolding.** Line 12 reads `OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-false}"`. The default is `false`, so the helper `initialize_openspec_change_workspace()` (line 649) hits its guard at line 655 (`if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then return 0; fi`) and exits without creating anything. The same gate disables `initialize_openspec_plan_workspace()` at line 629.
18+
19+
2. **Log lines do not check the guard.** Lines 901–911 print "OpenSpec change: openspec/changes/<slug>" only checking `$OPENSPEC_SKIP_CHANGE` (the tier-driven skip), not `$OPENSPEC_AUTO_INIT`. So when AUTO_INIT is 0, the function returns early without printing anything, but the final log still announces the scaffold path as if it had been created.
20+
21+
This contradicts the project's own contract in `CLAUDE.md` (the "Workflow (OpenSpec-first)" section: *"For every repo change (feature, fix, refactor, chore, test, config, docs), create/update an OpenSpec change in `openspec/changes/**` before editing code."*) and contradicts the tier table that describes T2/T3 as creating "full change workspace (`proposal.md`, `tasks.md`, `specs/.../spec.md`)".
22+
23+
## Approach
24+
25+
Two small changes, both in `templates/scripts/agent-branch-start.sh` (the runtime-canonical copy; `scripts/agent-branch-start.sh` is a symlink to it as of PR #548).
26+
27+
1. **Flip the default** at line 12 from `:-false` to `:-true`. Scaffolding is on by default. The downstream `run_guardex_cli internal run-shell changeInit ...` path works correctly — proven in-session by setting `GUARDEX_OPENSPEC_AUTO_INIT=true` once and seeing `proposal.md`, `tasks.md`, `.openspec.yaml`, and `specs/<name>/spec.md` materialize at the worktree's `openspec/changes/<slug>/` path.
28+
29+
2. **Make the end-of-run log honest** at lines 901–911. Each branch of the log now checks `$OPENSPEC_AUTO_INIT` first; if 0, the log says `skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)`; if 1, it falls through to the existing tier-skip / scaffolded-path branches.
30+
31+
### Out of scope
32+
33+
- Tier policy (T0 skips, T1 minimal, T2/T3 full) — unchanged.
34+
- The `initialize_openspec_change_workspace()` function body — unchanged.
35+
- The scaffolding pipeline downstream (`run-shell changeInit`) — unchanged.
36+
- `scripts/agent-branch-start.sh` — already a symlink to the file we edit (PR #548).
37+
38+
## Rationale
39+
40+
- The OpenSpec contract in `CLAUDE.md` requires a change workspace for every PR. The current default forces every contributor to either set `GUARDEX_OPENSPEC_AUTO_INIT=true` in their environment or manually scaffold by hand — neither is documented. The default should match the contract.
41+
- The downstream pipeline works as soon as the flag is on — verified empirically before committing this fix.
42+
- A misleading log is worse than no log; the new log accurately reports the three real states (auto-init disabled, tier-skipped, scaffolded).
43+
44+
## Compatibility
45+
46+
- Anyone currently relying on the `false` default for some reason (none observed in this repo) can restore it explicitly with `export GUARDEX_OPENSPEC_AUTO_INIT=false`. The negative-path log line clearly tells them what state they're in.
47+
- T0 tier (`gx branch start --tier T0 ...`) still creates no scaffold — `$OPENSPEC_SKIP_CHANGE=1` short-circuits the function regardless of `$OPENSPEC_AUTO_INIT`.
48+
- No CLI surface change. No new flag. No new env var.
49+
50+
## Risks
51+
52+
- Existing CI or fast-iteration scripts that called `bash scripts/agent-branch-start.sh ...` without setting the env var will now create `openspec/changes/<slug>/` directories that they previously didn't. The directories are scoped to the new agent worktree (under `.omc/agent-worktrees/.../openspec/changes/<slug>/`), so they don't pollute the primary checkout. The blast radius is limited to "extra files in throwaway worktrees that get pruned anyway."
53+
- `git status` inside fresh agent worktrees will show one extra directory tracked by the change. This is the intended behavior — those files are the change docs the contract requires.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Spec Delta: gitguardex-agent-lifecycle
2+
3+
## ADDED Requirements
4+
5+
### Requirement: `gx branch start` MUST scaffold the OpenSpec change workspace by default
6+
7+
When `gx branch start` is invoked without an explicit `GUARDEX_OPENSPEC_AUTO_INIT` setting, and the resolved tier is `T1`, `T2`, or `T3`, the script MUST create the change workspace at `<worktree>/openspec/changes/<slug>/` with the tier-appropriate scaffold (`.openspec.yaml`, `proposal.md`, `tasks.md`, and for `T2`/`T3` also `specs/<capability>/spec.md`). The user MUST NOT have to set an environment variable for the workspace to materialize.
8+
9+
#### Scenario: Default invocation creates the change workspace
10+
11+
- **GIVEN** a fresh agent worktree being created via `gx branch start --tier T2 "<task>" "<agent>"`
12+
- **AND** `GUARDEX_OPENSPEC_AUTO_INIT` is not set in the environment
13+
- **WHEN** the script completes successfully
14+
- **THEN** `<worktree>/openspec/changes/<slug>/proposal.md` exists
15+
- **AND** `<worktree>/openspec/changes/<slug>/tasks.md` exists
16+
- **AND** `<worktree>/openspec/changes/<slug>/.openspec.yaml` exists
17+
- **AND** `<worktree>/openspec/changes/<slug>/specs/<capability>/spec.md` exists
18+
19+
#### Scenario: `--tier T0` still skips scaffolding regardless of the default
20+
21+
- **GIVEN** `gx branch start --tier T0 "<task>" "<agent>"`
22+
- **WHEN** the script completes
23+
- **THEN** no `openspec/changes/<slug>/` directory is created
24+
- **AND** the end-of-run log says `OpenSpec change: skipped by tier T0`
25+
26+
#### Scenario: Explicit opt-out via env var
27+
28+
- **GIVEN** `GUARDEX_OPENSPEC_AUTO_INIT=false` is exported in the environment
29+
- **WHEN** `gx branch start --tier T2 ...` runs
30+
- **THEN** no `openspec/changes/<slug>/` directory is created
31+
- **AND** the end-of-run log says `OpenSpec change: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)`
32+
33+
### Requirement: `gx branch start` end-of-run log MUST accurately reflect scaffold state
34+
35+
The trailing log block in `templates/scripts/agent-branch-start.sh` MUST distinguish three cases for both the change and plan workspaces: `auto-init disabled`, `tier-skipped`, and `created at <path>`. The log MUST NOT claim a workspace was created when the corresponding `initialize_openspec_*_workspace` helper exited early.
36+
37+
#### Scenario: Log honesty when auto-init is disabled
38+
39+
- **GIVEN** `GUARDEX_OPENSPEC_AUTO_INIT=false` is set
40+
- **WHEN** `gx branch start --tier T2` runs
41+
- **THEN** the end-of-run log does NOT contain `OpenSpec change: openspec/changes/<slug>`
42+
- **AND** the log contains `OpenSpec change: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)`
43+
- **AND** the log contains `OpenSpec plan: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)`
44+
45+
#### Scenario: Log honesty when tier skips the change workspace
46+
47+
- **GIVEN** auto-init is enabled (default) and the tier is `T0`
48+
- **WHEN** `gx branch start --tier T0 ...` runs
49+
- **THEN** the log contains `OpenSpec change: skipped by tier T0`
50+
- **AND** the log contains `OpenSpec plan: skipped by tier T0`
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Tasks
2+
3+
## 1. Spec
4+
5+
- [x] 1.1 Capture problem + approach in `proposal.md`.
6+
- [x] 1.2 Add ADDED requirements + scenarios to `specs/gitguardex-agent-lifecycle/spec.md`.
7+
8+
## 2. Implementation
9+
10+
- [x] 2.1 `templates/scripts/agent-branch-start.sh`: flip `OPENSPEC_AUTO_INIT_RAW` default from `false` to `true` (line 12).
11+
- [x] 2.2 `templates/scripts/agent-branch-start.sh`: rewrite end-of-run OpenSpec log block (lines 901–911) so each branch checks `$OPENSPEC_AUTO_INIT` first and prints `skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)` when the flag is off.
12+
13+
## 3. Verification
14+
15+
- [x] 3.1 `bash -n` clean on the edited script.
16+
- [x] 3.2 Positive probe: `bash scripts/agent-branch-start.sh --tier T2 --no-transfer "<task>" "<agent>"` without setting the env var creates `openspec/changes/<slug>/{proposal.md,tasks.md,.openspec.yaml,specs/<name>/spec.md}` in the new worktree.
17+
- [x] 3.3 Symlink check: confirm `scripts/agent-branch-start.sh` (symlink → `templates/...`) sees the fix transparently.
18+
- [x] 3.4 Negative probe: `GUARDEX_OPENSPEC_AUTO_INIT=false bash scripts/agent-branch-start.sh ...` exits without creating the scaffold and logs `OpenSpec change: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)`.
19+
20+
## 4. Cleanup
21+
22+
- [ ] 4.1 Commit on `agent/claude/fix-openspec-scaffold-silent-failure-2026-05-11-12-12`.
23+
- [ ] 4.2 Push and open PR against `main`.
24+
- [ ] 4.3 PR merged (record URL + MERGED state).
25+
- [ ] 4.4 Sandbox worktree pruned via `gx branch finish --cleanup`.

templates/scripts/agent-branch-start.sh

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ WORKTREE_ROOT_REL=""
99
WORKTREE_ROOT_EXPLICIT=0
1010
NODE_BIN="${GUARDEX_NODE_BIN:-node}"
1111
CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
12-
OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-false}"
12+
OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-true}"
1313
OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
1414
OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
1515
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
@@ -899,12 +899,16 @@ fi
899899
echo "[agent-branch-start] Created branch: ${branch_name}"
900900
echo "[agent-branch-start] Worktree: ${worktree_path}"
901901
echo "[agent-branch-start] OpenSpec tier: ${OPENSPEC_TIER}"
902-
if [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
902+
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
903+
echo "[agent-branch-start] OpenSpec change: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)"
904+
elif [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
903905
echo "[agent-branch-start] OpenSpec change: skipped by tier ${OPENSPEC_TIER}"
904906
else
905907
echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_slug}"
906908
fi
907-
if [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
909+
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
910+
echo "[agent-branch-start] OpenSpec plan: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)"
911+
elif [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
908912
echo "[agent-branch-start] OpenSpec plan: skipped by tier ${OPENSPEC_TIER}"
909913
else
910914
echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"

0 commit comments

Comments
 (0)