garrytan / garrytan/gstack

/ship always reports the version queue offline: Step 12 sends uppercase BUMP_LEVEL to a lowercase-only util, with stderr suppressed

Open
#2,770 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

## Summary

`/ship` Step 12 passes an **uppercase** bump level to `bin/gstack-next-version`, which
accepts only lowercase. The util exits 2 with a clear message, the skill suppresses
stderr, and the `||` fallback maps every failure to `{"offline":true}`.

An agent following the skill literally therefore gets "offline" on **every ship**, always
falls back to local arithmetic, and is told the queue is unreachable rather than that a
bad argument was sent. Workspace-aware ship — sibling detection, claimed-version
listing, collision warnings — never runs.

gstack 1.78.0.0.

## The mismatch

`ship/SKILL.md:709-711` defines the value set in uppercase:

```
- **MICRO**: <50 lines, trivial tweaks/config. **PATCH**: 50+ lines, no feature signals.
- **MINOR**: **ASK** if any feature signal ... **MAJOR**: **ASK** ...
Save as `BUMP_LEVEL`.
```

`ship/SKILL.md:715` (template: `ship/SKILL.md.tmpl:195`) passes it through verbatim:

```bash
QUEUE_JSON=$(bun run .../gstack-next-version --base --bump "$BUMP_LEVEL" --current-version "$BASE_VERSION" 2>/dev/null || echo '{"offline":true}')
```

`bin/gstack-next-version:449-452` rejects it:

```js
if (!["major", "minor", "patch", "micro"].includes(bump)) {
console.error(`Error: --bump must be major|minor|patch|micro (got ${bump})`);
process.exit(2);
}
```

## Repro

```bash
$ bun run ~/.claude/skills/gstack/bin/gstack-next-version --base master --bump MICRO --current-version 0.1.0.0
Error: --bump must be major|minor|patch|micro (got MICRO)
$ echo $?
2

$ bun run ~/.claude/skills/gstack/bin/gstack-next-version --base master --bump micro --current-version 0.1.0.0
{"version":"0.1.0.1","claimed":[],"siblings":[],"reason":"no collision; clean bump from base",...}
```

## Why it matters

`ship/SKILL.md:718` turns the swallowed exit into a **false diagnosis**:

> If `offline`/util fails: fall back to local `BUMP_LEVEL` arithmetic and print
> `⚠ workspace-aware ship offline — using local bump only`

The user is told the network/queue is unavailable. Nothing is offline; the call was
malformed. Nobody investigates a message that names an external cause.

The feature this disables exists to prevent a documented real incident —
`bin/gstack-next-version:470-479`:

> On 2026-08-12 ... `gh pr list` failed during a ship, this util reported offline, the
> bump fell back to local arithmetic, and 0.1.57.0 was allocated to a second PR while an
> open one already claimed it — both merged ... Auditing that repo's history found FOUR
> such pairs going back three weeks.

That is the same failure mode this bug reproduces on every run, not just when `gh` is
down. And `lib/version-source.ts:22-24` explains why git can't catch the result: "Two
branches cut from the same base then pick the same version, and git merges that without a
conflict because both sides set one line to identical text."

Observed cost in one session: the fallback produced a version that would have moved
`VERSION` backward when a sibling branch landed. The correct answer from the util was a
different number.

This is the shape of #2018 in your own changelog — "features returning empty results with
a green checkmark."

## Note on test coverage

The golden fixtures carry the same invocation (`test/fixtures/golden/claude-ship-SKILL.md:715`,
`codex-ship-SKILL.md:1929`, `factory-ship-SKILL.md:2364`), so the broken call is baked into
expected output and no test can catch it.

## Suggested fix

Three, ideally all:

1. **Normalize at the call site** in `ship/SKILL.md.tmpl:195` —
`--bump "$(printf '%s' "$BUMP_LEVEL" | tr 'A-Z' 'a-z')"` — or lowercase the documented
value set in Step 2. One or the other; today the doc and the util disagree.
2. **Accept case-insensitively** in `bin/gstack-next-version` (`bump.toLowerCase()` before
the `includes` check). Cheap and makes the CLI forgiving of both spellings.
3. **Stop mapping every failure to `offline`.** Drop `2>/dev/null`, or branch on the exit
code: exit 2 is a usage error the agent should surface and fix, not a queue outage.
Reporting a malformed call as "offline" is what kept this invisible.

Contributor guide

Open the contributing guide

Research direction

Start with ship/SKILL.md.tmpl:195 and bin/gstack-next-version:449-452, then reproduce the uppercase and lowercase commands from the issue. Check the generated ship/SKILL.md and golden fixtures for the invocation, and verify that valid uppercase input reaches workspace-aware version handling while malformed arguments are not reported as offline.

Written by the indexing model from the issue text.

Assessment

Tech stack
shell, typescript
Domain
cli, release, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.