The forensics prompt and gh skill used --label "bug" / --label "type:feature"
for issue classification, polluting the label taxonomy and leaving the Type
field unset. gh issue create has no --type flag, so issue types must be set
via GraphQL mutation after creation.
Closes#2579
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(auto): align UAT artifact suffix with gsd_slice_complete output
The auto-mode files referenced UAT-RESULT as the artifact suffix,
but gsd_slice_complete writes files as S##-UAT.md. This mismatch
caused ENOENT errors during validate-milestone dispatch.
Fixes#2564
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(auto): update test and doc references from UAT-RESULT to UAT
Aligns test assertions and ADR documentation with the corrected
artifact suffix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(auto): replace separate UAT-RESULT file check with in-file verdict check
The original two-file model (UAT spec + UAT-RESULT verdict) never
worked because gsd_slice_complete only writes S##-UAT.md. The blind
string replacement made checkNeedsRunUat always return null by
resolving the same file twice. Now checks for a verdict: line inside
the UAT file content to determine if UAT has been completed.
Also deduplicates a redundant resolveSliceFile call in the verdict
gate and updates tests to verify the single-file verdict model.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server errors (500/502/503/504) are server-side failures — rotating
credentials doesn't help. Only rate_limit and quota_exhausted are
meaningfully credential-scoped. This prevents the cascading backoff
where a single 500 backs off the sole API key for 20s, causing all
subsequent retries to fail with "All credentials temporarily backed off".
Closes#2588
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SessionLockData no longer has a completedUnits field. Use sessionFile
(an actual optional field) for the same assertion coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
onCompromised was declaring lock lost when the lock file was temporarily
unreadable (NFS/CIFS latency, macOS APFS snapshot, or concurrent process
briefly holding the file). Add readExistingLockDataWithRetry (3 attempts,
200ms delay) so transient filesystem hiccups do not trigger false-positive
compromise events.
Fixes#2324
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When running GSD from a subdirectory (e.g. `cd src/ && gsd`),
ensureGsdSymlink would create a new `.gsd` symlink in the subdirectory
even though a valid `.gsd` already exists at the git root. On macOS
APFS this triggers the `.gsd 2` collision variant problem from #2205.
Add an early guard that detects when projectPath is a plain subdirectory
(not a worktree) of a git repo that already has `.gsd` at its root, and
returns the existing root .gsd target instead of creating a duplicate.
Fixes#2380
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EAGAIN (resource temporarily unavailable) is a resource exhaustion error
that cannot be recovered by retrying, yet it was missing from the infra
error set. This caused auto-mode to keep retrying on EAGAIN failures,
burning LLM budget on guaranteed failures.
Fixes#2359
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The hydrateRemoteTokensFromAuth() function used require() to load
AuthStorage from @gsd/pi-coding-agent, but the package is ESM-only
("type": "module" with only an "import" export condition). Node's
require() always throws for ESM packages, and the outer try/catch
silently swallowed the error — making hydration a no-op.
Replace require() with a static ESM import (consistent with every
other extension) and use AuthStorage.create() which resolves the
auth.json path internally via getAgentDir().
Closes#2565
Adds /^[a-z0-9][a-z0-9-]*$/ validation in formatSkillActivationBlock() so that
skill names containing quotes, braces, or other special characters are silently
filtered out before interpolation into the prompt string.
Addresses the prompt injection surface noted by @trek-e in PR review.
Updates the special-character test to verify rejection instead of passthrough.
Address review feedback:
- Update comment to clarify that the function-call-like syntax led
LLMs to infer a positional parameter name (not 'positional-looking')
- Add test documenting current behavior when skill names contain
special characters (quotes, apostrophes)
The skill activation block used positional-looking syntax
`Call Skill('name')` which caused LLMs (especially non-Anthropic
models) to pass `{name: "..."}` instead of the required
`{skill: "..."}` parameter. This triggered tool validation failures
and stuck dispatch loops in auto-mode.
Change the prompt template to `Call Skill({ skill: 'name' })` which
makes the parameter name explicit and matches the Skill tool schema.
Update all 4 affected test assertions to match the new format.
Closes#2224
writeIntegrationBranch already rejects slice branches (SLICE_BRANCH_RE) and
quick-task branches (QUICK_BRANCH_RE), but has no guard for the 8 workflow-
template branches (gsd/hotfix/*, gsd/bugfix/*, gsd/spike/*, etc.). When a
user runs `/gsd start hotfix` during an active milestone, the ephemeral
hotfix branch gets recorded as the integration target and the milestone
later merges to the wrong branch.
Add WORKFLOW_BRANCH_RE (/^gsd\/(?!M\d)[\w-]+\//) that matches all
gsd/<templateId>/<slug> branches while excluding milestone slice branches
(gsd/M001/S01). The negative lookahead ensures milestone branches starting
with 'M' followed by a digit are not affected.
Same root cause as gsd/quick/* (#1293, PR #1342).
Closes#2498
Doctor's DB-backed slice normalization already marks pending slices, but the
legacy roadmap fallback only returned done/not-done. That made future unstarted
slices look active during milestone-scoped doctor runs, producing false
missing_slice_dir errors.
Infer a doctor-local pending state for legacy slices by treating every undone
slice except the current active slice as unstarted. This keeps active-slice
missing directory checks intact while skipping false positives for future
slices, and adds a regression test for the legacy fallback path.
Closes#2518
Tests were checking `git log --oneline` for M001, but the refactor moved
milestone IDs from commit subject scopes to git trailers in the body.
Switch to `git log` (full format) so the trailer content is visible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds `/gsd auto --yolo <spec-file>` (or `-y`) which reads a spec/PRD/ADR
and creates all milestone artifacts without interactive Q&A gates. Uses
the existing showHeadlessMilestoneCreation path — no changes to
startAuto or bootstrapAutoSession internals.
Rewrites discuss-headless.md to match the full rigor of the interactive
discuss.md prompt: mandatory codebase investigation, focused research
(table stakes, domain standards, omissions), capability contract with
R### traceability, gsd_plan_milestone tool usage, roadmap preview in
chat, multi-milestone manifest tracking, and depth verification audit
trail. The only difference from interactive mode is that all decisions
are made autonomously with assumptions documented.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove GSD planning IDs (milestone/slice/task) from conventional commit
subject lines and place them in machine-parseable git trailers instead.
Skip auto-commits for lifecycle-only unit types that only touch .gsd/ files.
Resolvesgsd-build/gsd-2#2553
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/250b4775-2d82-4329-9ccc-504b857428da
The verdict gate in auto-dispatch.ts now reads the UAT file to determine
the UAT type. For mixed, human-experience, and live-runtime modes,
PARTIAL is accepted as a valid verdict (all automatable checks passed,
human-only checks documented as NEEDS-HUMAN).
The run-uat prompt is updated so that PASS is the correct verdict when
all automatable checks succeed, even if human-only checks remain. PARTIAL
is reserved for when automatable checks themselves are inconclusive.
Fixesgsd-build/gsd-2#1400
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/5a619137-0710-4934-949f-bae63945bf70
scanJournalForForensics() previously called queryJournal() which loaded
ALL journal entries from ALL daily files into memory. For long-running
projects this could be thousands of entries and megabytes of data.
Now:
- Only the last 3 daily files are fully JSON-parsed (event counts, flows)
- Older files are line-counted only (no JSON parsing) for totals
- Recent events use a rolling window of 20 (shift, not accumulate)
- Constants MAX_JOURNAL_RECENT_FILES and MAX_JOURNAL_RECENT_EVENTS
make limits explicit and tunable
Activity log scanning was already intelligent:
- nativeParseJsonlTail with 10MB byte cap
- Only last 5 files scanned
- extractTrace() distills raw JSONL into compact ExecutionTrace structs
- formatReportForPrompt has 30KB hard cap on total output
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/7e7f71ec-0d56-409b-930e-5dff1305ff2a