mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-12 19:44:48 +01:00
Merge remote-tracking branch 'origin/garrytan/opus55-wip-D' into garrytan/opus-5-5-prompt-audit
This commit is contained in:
commit
841a766eec
36 files changed
+233
-284
No files matched your search
@@ -58,7 +58,9 @@ mkdir -p .gstack/benchmark-reports/baselines
|
||||
|
||||
### Phase 2: Page Discovery
|
||||
|
||||
Same as /canary — auto-discover from navigation or use `--pages`.
|
||||
Use `--pages` when given. Otherwise open the URL, collect its same-origin `a[href]`
|
||||
links (skip logout/delete-style URLs), and propose the homepage plus up to 5 main
|
||||
navigation targets; confirm the list with the user before measuring.
|
||||
|
||||
If `--diff` mode:
|
||||
```bash
|
||||
|
||||
@@ -245,7 +245,7 @@ If the user chooses A, copy the latest screenshots to the baselines directory an
|
||||
|
||||
## Important Rules
|
||||
|
||||
- **Speed matters.** Start monitoring within 30 seconds of invocation. Don't over-analyze before monitoring.
|
||||
- **Speed matters.** The first minutes after a deploy are the point: reach the first monitoring round as soon as the page list is settled.
|
||||
- **Alert on changes, not absolutes.** Compare against baseline, not industry standards.
|
||||
- **Screenshots are evidence.** Every alert includes a screenshot path. No exceptions.
|
||||
- **Transient tolerance.** Only alert on patterns that persist across 2+ consecutive checks.
|
||||
|
||||
+1
-5
@@ -108,10 +108,6 @@ The probe multi-signal auth logic accepts: `$CODEX_API_KEY` set, `$OPENAI_API_KE
|
||||
set, or `${CODEX_HOME:-~/.codex}/auth.json` exists. Avoids false-negatives for
|
||||
env-auth users (CI, platform engineers) that file-only checks would reject.
|
||||
|
||||
**Update the known-bad list** in `bin/gstack-codex-probe` when a new Codex CLI version
|
||||
regresses. Current entries (`0.120.0`, `0.120.1`, `0.120.2`) trace to the stdin
|
||||
deadlock fixed in #972.
|
||||
|
||||
---
|
||||
|
||||
## Step 0.6: Resolve portable roots
|
||||
@@ -190,7 +186,7 @@ mode sections.
|
||||
## Synthesis recommendation (REQUIRED) — all modes
|
||||
|
||||
Every mode ends by emitting ONE synthesis recommendation line after presenting
|
||||
Codex's verbatim output, in the canonical format the AskUserQuestion judge grades:
|
||||
Codex's verbatim output, in this format:
|
||||
|
||||
```
|
||||
Recommendation: <action> because <one-line reason that names the most actionable finding>
|
||||
|
||||
@@ -107,7 +107,7 @@ Tokens: N | Est. cost: ~$X.XX
|
||||
|
||||
3a. **Synthesis recommendation (REQUIRED).** After presenting the full
|
||||
adversarial output, emit ONE recommendation line summarizing what the user
|
||||
should do, in the canonical format the AskUserQuestion judge grades:
|
||||
should do, in this format:
|
||||
|
||||
```
|
||||
Recommendation: <action> because <one-line reason that names the most exploitable finding>
|
||||
|
||||
@@ -193,8 +193,8 @@ Session saved — run /codex again to continue this conversation.
|
||||
"Note: Claude Code disagrees on X because Y."
|
||||
|
||||
8. **Synthesis recommendation (REQUIRED).** Emit ONE recommendation line
|
||||
summarizing what the user should do based on Codex's consult output, in the
|
||||
canonical format the AskUserQuestion judge grades:
|
||||
summarizing what the user should do based on Codex's consult output, in this
|
||||
format:
|
||||
|
||||
```
|
||||
Recommendation: <action> because <one-line reason that names the most actionable insight from Codex>
|
||||
|
||||
@@ -176,7 +176,7 @@ GATE: UNVERIFIED (Codex completed and tagged nothing; read the output above)
|
||||
|
||||
5a. **Synthesis recommendation (REQUIRED).** After presenting Codex's verbatim
|
||||
output and the GATE verdict, emit ONE recommendation line summarizing what the
|
||||
user should do, in the canonical format the AskUserQuestion judge grades:
|
||||
user should do, in this format:
|
||||
|
||||
```
|
||||
Recommendation: <action> because <one-line reason that names the most actionable finding>
|
||||
|
||||
@@ -7,9 +7,7 @@ description: |
|
||||
saved state (preferring the current branch, falling back across branches) so
|
||||
you can pick up where you left off — even across Conductor workspace handoffs.
|
||||
Use when asked to "resume", "restore context", "where was I", or
|
||||
"pick up where I left off". Pair with /context-save.
|
||||
Formerly /checkpoint resume — renamed because Claude Code treats /checkpoint
|
||||
as a native rewind alias in current environments. (gstack)
|
||||
"pick up where I left off". Pair with /context-save. (gstack)
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
@@ -55,7 +53,7 @@ branch.)
|
||||
|
||||
Parse the user's input:
|
||||
|
||||
- `/context-restore` → load the most recent saved context (any branch)
|
||||
- `/context-restore` → load the most recent saved context (current branch first, then any branch)
|
||||
- `/context-restore <title-fragment-or-number>` → load a specific saved context
|
||||
- `/context-restore list` → tell the user "Use `/context-save list` — listing
|
||||
lives on the save side" and exit. No mode detection here.
|
||||
@@ -234,9 +232,9 @@ in the old bucket until the user copies them with `gstack-slug --adopt-legacy`.
|
||||
|
||||
- **Never modify code.** This skill only reads saved files and presents them.
|
||||
- **Prefer the current branch's own checkpoint, but keep all branches in the
|
||||
fallback set.** Cross-branch resume (Conductor handoff) still works when the
|
||||
current branch has no checkpoint; it just no longer lets a sibling worktree's
|
||||
newer save shadow this branch's own.
|
||||
fallback set.** Cross-branch resume (Conductor handoff) works when the
|
||||
current branch has no checkpoint, and a sibling worktree's newer save never
|
||||
shadows this branch's own.
|
||||
- **Never present another project's checkpoint as "latest".** Identity is the
|
||||
canonical remote (the root only when there is no remote); a mismatch is
|
||||
reported before any summary.
|
||||
|
||||
@@ -7,8 +7,6 @@ description: |
|
||||
so any future session can pick up without losing a beat.
|
||||
Use when asked to "save progress", "save state", "context save", or
|
||||
"save my work". Pair with /context-restore to resume later.
|
||||
Formerly /checkpoint — renamed because Claude Code treats /checkpoint as a
|
||||
native rewind alias in current environments, which was shadowing this skill.
|
||||
(gstack)
|
||||
allowed-tools:
|
||||
- Bash
|
||||
@@ -48,7 +46,7 @@ If the user provides a title after the command (e.g., `/context-save auth refact
|
||||
use it as the title. Otherwise, infer a title from the current work.
|
||||
|
||||
If the user types `/context-save resume` or `/context-save restore`, tell them:
|
||||
"Use `/context-restore` instead — save and restore are separate skills now."
|
||||
"Use `/context-restore` to resume — restoring is a separate skill."
|
||||
|
||||
---
|
||||
|
||||
@@ -184,8 +182,7 @@ modified files). Use relative paths from the repo root. Do not write `remote:` o
|
||||
`project_root:` yourself; the stamp step below adds them.
|
||||
|
||||
Then stamp the project identity. Several projects can share one checkpoints
|
||||
directory (GitLab nested groups did before #3003), so `/context-restore` needs to
|
||||
know which project wrote each file. The helper adds `remote:` (canonical origin:
|
||||
directory, so `/context-restore` needs to know which project wrote each file. The helper adds `remote:` (canonical origin:
|
||||
lowercased host, no credentials, port or `.git`) and `project_root:` to the
|
||||
frontmatter:
|
||||
|
||||
@@ -281,5 +278,4 @@ If there are no saved contexts, tell the user: "No saved contexts yet. Run
|
||||
- **Infer, don't interrogate.** Use git state and conversation context to fill in
|
||||
the file. Only use AskUserQuestion if the title genuinely cannot be inferred.
|
||||
- **This is a gstack skill, not a Claude Code built-in.** When the user types
|
||||
`/context-save`, invoke this skill via the Skill tool. The old `/checkpoint`
|
||||
name collided with Claude Code's native `/rewind` alias — the rename fixed that.
|
||||
`/context-save`, invoke this skill via the Skill tool.
|
||||
@@ -326,10 +326,9 @@ Use real token values, no placeholders; omit invented `components` entries and u
|
||||
|
||||
```markdown
|
||||
## Design System
|
||||
Always read DESIGN.md before making any visual or UI decisions.
|
||||
All font choices, colors, spacing, and aesthetic direction are defined there.
|
||||
Do not deviate without explicit user approval.
|
||||
In QA mode, flag any code that doesn't match DESIGN.md.
|
||||
Read DESIGN.md before visual or UI work: it defines the fonts, colors, spacing, and
|
||||
aesthetic direction. Ask the user before departing from it. When reviewing or QA-ing
|
||||
UI, flag code that doesn't match DESIGN.md.
|
||||
```
|
||||
|
||||
After shipping DESIGN.md, if the session produced screen-level mockups or page layouts
|
||||
|
||||
@@ -231,8 +231,9 @@ _ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||
[ -n "$_PRETEXT_VENDOR" ] && echo "VENDOR: $_PRETEXT_VENDOR" || echo "VENDOR_MISSING"
|
||||
```
|
||||
|
||||
- If `VENDOR` found: read the file and inline it in a `<script>` tag. The HTML file
|
||||
is fully self-contained with zero network dependencies.
|
||||
- If `VENDOR` found: inline the file in a `<script type="module">` and put the page's
|
||||
Pretext code after it in that same script; the bundle ends in `export {...}` and sets
|
||||
no global. The HTML file is fully self-contained with zero network dependencies.
|
||||
- If `VENDOR_MISSING`: use CDN import as fallback:
|
||||
`<script type="module">import { prepare, layout, prepareWithSegments, walkLineRanges, layoutNextLine, layoutWithLines } from 'https://esm.sh/@chenglou/pretext'</script>`
|
||||
Add a comment: `<!-- FALLBACK: vendor/pretext.js missing, using CDN -->`
|
||||
@@ -306,17 +307,20 @@ echo "SERVER: http://localhost:$_PORT/finalized.html"
|
||||
echo "PID: $_SERVER_PID"
|
||||
```
|
||||
|
||||
Remember the printed `SERVER:` URL and `PID:` value; each bash block is a fresh
|
||||
shell, so later blocks restate them literally.
|
||||
|
||||
If python3 is not available, fall back to:
|
||||
```bash
|
||||
open <path-to-finalized.html>
|
||||
```
|
||||
|
||||
Tell the user: "Live preview running at http://localhost:$_PORT/finalized.html.
|
||||
Tell the user: "Live preview running at <SERVER URL>.
|
||||
After each edit, just refresh the browser (Cmd+R) to see changes."
|
||||
|
||||
When the refinement loop ends (Step 4 exits), kill the server:
|
||||
```bash
|
||||
kill $_SERVER_PID 2>/dev/null || true
|
||||
kill <PID> 2>/dev/null || true
|
||||
```
|
||||
|
||||
---
|
||||
@@ -377,7 +381,7 @@ LOOP:
|
||||
5. Apply feedback using targeted Edit tool changes on the HTML file
|
||||
(do NOT regenerate the entire file — surgical edits only)
|
||||
|
||||
6. Brief summary of what changed (2-3 lines max)
|
||||
6. Briefly say what changed
|
||||
|
||||
7. If verification screenshots are available, re-take them to confirm the fix
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ Pretext API usage patterns. Follow them exactly.
|
||||
|
||||
**Pattern 1: Basic height computation (Simple layout, Card/grid)**
|
||||
```js
|
||||
import { prepare, layout } from './pretext-inline.js'
|
||||
// Or if inlined: const { prepare, layout } = window.Pretext
|
||||
import { prepare, layout } from '@chenglou/pretext'
|
||||
// Vanilla HTML: omit the import; this code follows the inlined bundle in the same <script type="module">
|
||||
|
||||
// 1. PREPARE — one-time, after fonts load
|
||||
await document.fonts.ready
|
||||
@@ -45,37 +45,26 @@ for (const el of elements) {
|
||||
|
||||
**Pattern 2: Shrinkwrap / tight-fit containers (Chat bubbles)**
|
||||
```js
|
||||
import { prepareWithSegments, walkLineRanges } from './pretext-inline.js'
|
||||
import { prepareWithSegments, walkLineRanges } from '@chenglou/pretext'
|
||||
|
||||
// Find the tightest width that produces the same line count
|
||||
function shrinkwrap(text, font, maxWidth, lineHeight) {
|
||||
// Tight-fit width: lay out at maxWidth, then shrink the bubble to its widest line
|
||||
function shrinkwrap(text, font, maxWidth) {
|
||||
const segs = prepareWithSegments(text, font)
|
||||
let bestWidth = maxWidth
|
||||
walkLineRanges(segs, maxWidth, (lineCount, startIdx, endIdx) => {
|
||||
// walkLineRanges calls back with progressively narrower widths
|
||||
// The first call gives us the line count at maxWidth
|
||||
// We want the narrowest width that still produces this line count
|
||||
let widest = 0
|
||||
const lineCount = walkLineRanges(segs, maxWidth, (line) => {
|
||||
widest = Math.max(widest, line.width)
|
||||
})
|
||||
// Binary search for tightest width with same line count
|
||||
const { lineCount: targetLines } = layout(prepare(text, font), maxWidth, lineHeight)
|
||||
let lo = 0, hi = maxWidth
|
||||
while (hi - lo > 1) {
|
||||
const mid = (lo + hi) / 2
|
||||
const { lineCount } = layout(prepare(text, font), mid, lineHeight)
|
||||
if (lineCount === targetLines) hi = mid
|
||||
else lo = mid
|
||||
}
|
||||
return hi
|
||||
return { width: Math.ceil(widest), lineCount }
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern 3: Text around obstacles (Editorial layout)**
|
||||
```js
|
||||
import { prepareWithSegments, layoutNextLine } from './pretext-inline.js'
|
||||
import { prepareWithSegments, layoutNextLine } from '@chenglou/pretext'
|
||||
|
||||
function layoutAroundObstacles(text, font, containerWidth, lineHeight, obstacles) {
|
||||
const segs = prepareWithSegments(text, font)
|
||||
let state = null
|
||||
let cursor = { segmentIndex: 0, graphemeIndex: 0 }
|
||||
let y = 0
|
||||
const lines = []
|
||||
|
||||
@@ -88,11 +77,11 @@ function layoutAroundObstacles(text, font, containerWidth, lineHeight, obstacles
|
||||
}
|
||||
}
|
||||
|
||||
const result = layoutNextLine(segs, state, availWidth, lineHeight)
|
||||
const result = layoutNextLine(segs, cursor, availWidth)
|
||||
if (!result) break
|
||||
|
||||
lines.push({ text: result.text, width: result.width, x: 0, y })
|
||||
state = result.state
|
||||
cursor = result.end
|
||||
y += lineHeight
|
||||
}
|
||||
|
||||
@@ -102,21 +91,21 @@ function layoutAroundObstacles(text, font, containerWidth, lineHeight, obstacles
|
||||
|
||||
**Pattern 4: Full line-by-line rendering (Complex editorial)**
|
||||
```js
|
||||
import { prepareWithSegments, layoutWithLines } from './pretext-inline.js'
|
||||
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'
|
||||
|
||||
const segs = prepareWithSegments(text, font)
|
||||
const { lines, height } = layoutWithLines(segs, containerWidth, lineHeight)
|
||||
|
||||
// lines = [{ text, width, x, y }, ...]
|
||||
// lines = [{ text, width, start, end }, ...] (no x/y: line i sits at y = i * lineHeight)
|
||||
// Use for Canvas/SVG rendering or custom DOM positioning
|
||||
for (const line of lines) {
|
||||
lines.forEach((line, i) => {
|
||||
const span = document.createElement('span')
|
||||
span.textContent = line.text
|
||||
span.style.position = 'absolute'
|
||||
span.style.left = `${line.x}px`
|
||||
span.style.top = `${line.y}px`
|
||||
span.style.left = '0px'
|
||||
span.style.top = `${i * lineHeight}px`
|
||||
container.appendChild(span)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Pretext API Reference
|
||||
@@ -134,16 +123,17 @@ layout(prepared, maxWidth, lineHeight) → { height, lineCount }
|
||||
prepareWithSegments(text, font) → handle
|
||||
Like prepare() but enables line-level APIs below.
|
||||
|
||||
layoutWithLines(segs, maxWidth, lineHeight) → { lines: [{text, width, x, y}...], height }
|
||||
Full line-by-line breakdown. For Canvas/SVG rendering.
|
||||
layoutWithLines(segs, maxWidth, lineHeight) → { lineCount, height, lines: [{text, width, start, end}...] }
|
||||
Full line-by-line breakdown (no x/y: line i is at y = i * lineHeight). For Canvas/SVG rendering.
|
||||
|
||||
walkLineRanges(segs, maxWidth, onLine) → void
|
||||
Calls onLine(lineCount, startIdx, endIdx) for each possible layout.
|
||||
Find minimum width for N lines. For tight-fit containers.
|
||||
walkLineRanges(segs, maxWidth, onLine) → lineCount
|
||||
Calls onLine({ width, start, end }) once per line at maxWidth (no text built).
|
||||
The widest line's width is the tight-fit container width. For chat bubbles.
|
||||
|
||||
layoutNextLine(segs, state, maxWidth, lineHeight) → { text, width, state } | null
|
||||
layoutNextLine(segs, cursor, maxWidth) → { text, width, start, end } | null
|
||||
Iterator. Different maxWidth per line = text around obstacles.
|
||||
Pass null as initial state. Returns null when text is exhausted.
|
||||
Start with cursor { segmentIndex: 0, graphemeIndex: 0 }; pass result.end next.
|
||||
Returns null when text is exhausted. Takes no lineHeight: advance y yourself.
|
||||
|
||||
clearCache() → void
|
||||
Clears internal measurement caches. Use when cycling many fonts.
|
||||
|
||||
@@ -31,7 +31,7 @@ triggers:
|
||||
|
||||
# /design-review: Design Audit → Fix → Verify
|
||||
|
||||
You are a senior product designer AND a frontend engineer. Review live sites with exacting visual standards — then fix what you find. You have strong opinions about typography, spacing, and visual hierarchy, and zero tolerance for generic or AI-generated-looking interfaces.
|
||||
You are a senior product designer AND a frontend engineer. Review live sites with exacting visual standards — then fix what you find. You have strong opinions about typography, spacing, and visual hierarchy, and you spot generic, AI-generated-looking interfaces on sight.
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -324,6 +324,8 @@ If the repo has a `TODOS.md`:
|
||||
|
||||
## Additional Rules (design-review specific)
|
||||
|
||||
The audit rule "Never read source code" governs the Phases 1-6 audit only; the Phase 8 fix loop reads and edits source.
|
||||
|
||||
11. **Clean working tree required.** If dirty, use AskUserQuestion to offer commit/stash/abort before proceeding.
|
||||
12. **One commit per fix.** Never bundle multiple design fixes into one commit.
|
||||
13. **Only modify tests when generating regression tests in Phase 8e.5.** Never modify CI configuration. Never modify existing tests — only create new test files.
|
||||
|
||||
@@ -122,6 +122,7 @@ ls src/ app/ pages/ components/ 2>/dev/null | head -30
|
||||
```
|
||||
|
||||
```bash
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
setopt +o nomatch 2>/dev/null || true
|
||||
ls "$GSTACK_STATE_ROOT"/projects/$SLUG/*office-hours* 2>/dev/null | head -5
|
||||
@@ -213,14 +214,14 @@ C) "Name" — one-line visual description of this direction
|
||||
|
||||
Draw on DESIGN.md, taste memory, and the user's request to make each concept distinct.
|
||||
|
||||
**Anti-convergence directive (hard requirement):** Each variant MUST use a different
|
||||
font family, color palette, and layout approach. If two variants look like siblings
|
||||
— same typographic feel, overlapping color temperature, comparable layout rhythm —
|
||||
one of them failed. Regenerate the weaker one with a deliberately different direction.
|
||||
**Anti-convergence directive:** each concept is a distinct direction. When a DESIGN.md
|
||||
exists, it decides what varies: keep its fonts and palette and vary layout and
|
||||
composition. Without one, vary font family, color palette, and layout approach. If two
|
||||
variants look like siblings, rework the weaker one with a deliberately different direction.
|
||||
|
||||
Concrete test: if someone could swap the headline text between two variants without
|
||||
noticing, they're too similar. Variants should feel like they came from three
|
||||
different design teams, not the same team at three different coffee levels.
|
||||
noticing, they're too similar. Variants should feel like separate design teams made
|
||||
them, not one team on different days.
|
||||
|
||||
### Step 3b: Concept Confirmation
|
||||
|
||||
@@ -259,10 +260,9 @@ sees what you're evolving from.
|
||||
|
||||
**Launch N Agent subagents in a single message** (parallel execution). Use the Agent
|
||||
tool with `subagent_type: "general-purpose"` and `run_in_background: false` for each
|
||||
variant (parallel foreground calls in one message still run concurrently; subagents
|
||||
default to background since Claude Code v2.1.198, and the comparison board needs every
|
||||
variant's result). Each agent is independent
|
||||
and handles its own generation, quality check, verification, and retry.
|
||||
variant (parallel foreground calls in one message still run concurrently, and the
|
||||
comparison board needs every variant's result). Each agent is independent and handles
|
||||
its own generation, quality check, verification, and retry.
|
||||
|
||||
**Important: $D path propagation.** The `$D` variable from DESIGN SETUP is a shell
|
||||
variable that agents do NOT inherit. Substitute the resolved absolute path (from the
|
||||
@@ -298,9 +298,8 @@ For the evolve path, replace step 1 with:
|
||||
{$D path} evolve --screenshot {_DESIGN_DIR}/current.png --brief "{brief}" --output /tmp/variant-{letter}.png
|
||||
```
|
||||
|
||||
**Why /tmp/ then cp?** In observed sessions, `$D generate --output ~/.gstack/...`
|
||||
failed with "The operation was aborted" while `--output /tmp/...` succeeded. This is
|
||||
a sandbox restriction. Always generate to `/tmp/` first, then `cp`.
|
||||
**Generate to `/tmp/`, then `cp`:** in sandboxed sessions `$D generate --output` under
|
||||
`~/.gstack/` can abort ("The operation was aborted"), while `/tmp/` works.
|
||||
|
||||
### Step 3d: Results
|
||||
|
||||
|
||||
@@ -78,8 +78,8 @@ any documentation. This prevents the "documentation that describes half the feat
|
||||
|
||||
## Step 1: Codebase Archaeology (Research Phase)
|
||||
|
||||
**This is the most important step.** Do not skip or rush it. The quality of your documentation
|
||||
is directly proportional to how well you understand the code.
|
||||
The quality of the documentation depends on how well you understand the code, so this
|
||||
step carries the most weight.
|
||||
|
||||
1. **Map the project structure:**
|
||||
|
||||
@@ -380,9 +380,9 @@ Fix any failures before proceeding.
|
||||
|
||||
**Redaction scan before commit.** Generated docs frequently contain example
|
||||
credentials; scan the staged doc content and block on a HIGH credential (a
|
||||
live-format secret in committed docs is a leak). Example configs belong in
|
||||
` ```example ` fences won't excuse a live-format secret, but the per-span
|
||||
placeholder filter passes obvious docs examples (e.g. `AKIAIOSFODNN7EXAMPLE`):
|
||||
live-format secret in committed docs is a leak). The per-span placeholder
|
||||
filter passes obvious docs examples (e.g. `AKIAIOSFODNN7EXAMPLE`); a
|
||||
live-format secret blocks wherever it appears, fenced or not:
|
||||
|
||||
```bash
|
||||
REDACT_VIS=$(~/.claude/skills/gstack/bin/gstack-config get redact_repo_visibility 2>/dev/null)
|
||||
|
||||
@@ -101,11 +101,11 @@ preserved them. This skill must NEVER do that.
|
||||
|
||||
**If CHANGELOG was modified in this branch**, review the entry for voice:
|
||||
|
||||
- **Sell test (Diataxis rubric):** Score each CHANGELOG entry 0-3:
|
||||
- **1 point** — answers "What changed?" (reference: names the feature/fix)
|
||||
- **1 point** — answers "Why should I care?" (explanation: user impact, pain removed)
|
||||
- **1 point** — answers "How do I use it?" (how-to: command, flag, or link to docs)
|
||||
- Entries scoring <2 need attention, not replacement. Report missing facts or user impact; polish existing wording only. A score of 3 passes all three checks.
|
||||
- **Sell test (Diataxis):** a good entry answers "What changed?" (names the
|
||||
feature/fix), "Why should I care?" (user impact, pain removed) and "How do I use
|
||||
it?" (command, flag, or link to docs). An entry that answers fewer than two of these needs
|
||||
attention, not replacement: report the missing facts or user impact, and polish
|
||||
existing wording only.
|
||||
- Lead with what the user can now **do** — not implementation details.
|
||||
- "You can now..." not "Refactored the..."
|
||||
- Flag commit-message-style entries and polish wording without removing facts.
|
||||
|
||||
@@ -127,15 +127,14 @@ Use the install type and directory detected in Step 2:
|
||||
|
||||
**For git installs** (global-git, local-git):
|
||||
|
||||
Fast-forward first (#2517) — the same policy the session-update auto-upgrade
|
||||
Fast-forward first — the same policy the session-update auto-upgrade
|
||||
uses. `--autostash` carries local edits over the pull; render-footprint dirt
|
||||
is discarded first because it is regenerable and poisons stashes (#2569):
|
||||
is discarded first because it is regenerable and poisons stashes:
|
||||
```bash
|
||||
cd "$INSTALL_DIR"
|
||||
# Discard render-footprint dirt (#2569): pre-v1.67 gbrain-enabled installs
|
||||
# ran gen:skill-docs:user IN PLACE, leaving generated SKILL.md / sections
|
||||
# files permanently modified. They are regenerable (setup re-renders to
|
||||
# ~/.gstack/render), so discarding is lossless.
|
||||
# Discard render-footprint dirt: older gbrain-enabled installs rendered
|
||||
# generated SKILL.md / sections files IN PLACE. They are regenerable (setup
|
||||
# re-renders to ~/.gstack/render), so discarding is lossless.
|
||||
git checkout -- 'SKILL.md' '*/SKILL.md' '*/sections/*.md' 2>/dev/null || true
|
||||
git fetch origin
|
||||
PRE_UPGRADE_COMMIT=$(git rev-parse HEAD)
|
||||
@@ -153,7 +152,7 @@ On `SETUP_FAILED`, STOP; keep user changes and report the recovery commit. There
|
||||
|
||||
**Fallback (ff-only refused — local commits or divergence).** `git reset
|
||||
--hard` DESTROYS things: a clean tree with unpushed local commits still loses
|
||||
those commits. Gate it (#2517):
|
||||
those commits. Gate it:
|
||||
|
||||
1. Run `git status --porcelain` and `git rev-list origin/main..HEAD --oneline`
|
||||
in `$INSTALL_DIR`.
|
||||
@@ -283,7 +282,7 @@ for how to add new migrations.
|
||||
|
||||
A browse daemon started before the upgrade keeps serving the OLD binary's code
|
||||
until it is stopped — it survives `git reset --hard` and `./setup` because the
|
||||
running process holds the old executable (#2551). Always run this step, using
|
||||
running process holds the old executable. Always run this step, using
|
||||
the install directory detected in Step 2.
|
||||
|
||||
```bash
|
||||
|
||||
+22
-17
@@ -70,7 +70,7 @@ command -v knip >/dev/null 2>&1 && echo "DEADCODE: knip"
|
||||
# Shell linting
|
||||
command -v shellcheck >/dev/null 2>&1 && ls *.sh scripts/*.sh bin/*.sh 2>/dev/null | head -1 | xargs -I{} echo "SHELL: shellcheck"
|
||||
|
||||
# GBrain presence (D6) — only report as a dimension if gbrain is actually
|
||||
# GBrain presence — only report as a dimension if gbrain is actually
|
||||
# set up; otherwise skip so machines without gbrain aren't penalized.
|
||||
if command -v gbrain >/dev/null 2>&1 && [ -f "$HOME/.gbrain/config.json" ]; then
|
||||
echo "GBRAIN: gbrain doctor --json (wrapped in timeout 5s)"
|
||||
@@ -177,7 +177,7 @@ Score each category on a 0-10 scale using this rubric:
|
||||
| Tests | 28% | All pass (exit 0) | >95% pass | >80% pass | <=80% pass |
|
||||
| Dead code | 13% | Clean (exit 0) | <5 unused exports | <20 unused | >=20 unused |
|
||||
| Shell lint | 9% | Clean (exit 0) | <5 issues | >=5 issues | N/A (skip) |
|
||||
| GBrain (D6) | 10% | doctor=ok, queue<10, pushed <24h | doctor=warnings OR queue<100 OR pushed <72h | doctor broken OR queue>=100 OR pushed >=72h | N/A (gbrain not installed) |
|
||||
| GBrain | 10% | doctor=ok, queue<10, pushed <24h | doctor=warnings OR queue<100 OR pushed <72h | doctor broken OR queue>=100 OR pushed >=72h | N/A (gbrain not installed) |
|
||||
|
||||
**Parsing tool output for counts:**
|
||||
Use the complete captured output, not the displayed tail. A zero match count cannot
|
||||
@@ -189,21 +189,27 @@ make a non-zero checker exit `CLEAN`; retain its failure and diagnostic output.
|
||||
- **knip:** Count lines reporting unused exports, files, or dependencies.
|
||||
- **shellcheck:** Count distinct findings (lines starting with "In ... line").
|
||||
|
||||
**Composite score:**
|
||||
```
|
||||
composite = (typecheck_score * 0.22) + (lint_score * 0.18) + (test_score * 0.28) + (deadcode_score * 0.13) + (shell_score * 0.09) + (gbrain_score * 0.10)
|
||||
```
|
||||
**Composite score:** compute it in code, not by hand. Pass each category's
|
||||
score, or `null` when it was skipped (tool not available — includes GBrain when
|
||||
gbrain is not installed); skipped weight is redistributed proportionally among
|
||||
the scored categories:
|
||||
|
||||
If a category is skipped (tool not available — includes GBrain when gbrain
|
||||
is not installed), redistribute its weight proportionally among the
|
||||
remaining categories.
|
||||
```bash
|
||||
bun -e '
|
||||
const w = { typecheck: 0.22, lint: 0.18, test: 0.28, deadcode: 0.13, shell: 0.09, gbrain: 0.10 };
|
||||
const s = { typecheck: <N|null>, lint: <N|null>, test: <N|null>, deadcode: <N|null>, shell: <N|null>, gbrain: <N|null> };
|
||||
const scored = Object.keys(w).filter(k => s[k] !== null);
|
||||
const total = scored.reduce((t, k) => t + w[k], 0);
|
||||
console.log(scored.length ? "COMPOSITE: " + (scored.reduce((t, k) => t + s[k] * w[k], 0) / total).toFixed(1) : "COMPOSITE: N/A");
|
||||
'
|
||||
```
|
||||
|
||||
Always report coverage: list the checked categories and the unavailable categories
|
||||
with their reasons. Label a numeric composite with skipped categories as **partial
|
||||
coverage**. If zero checks executed, report **N/A — no checks ran**, do not compute
|
||||
a numeric composite, and skip numeric history persistence and trend calculation.
|
||||
|
||||
**GBrain sub-score computation (D6):**
|
||||
**GBrain sub-score computation:**
|
||||
|
||||
```
|
||||
doctor_component: 10 if `gbrain doctor --json | jq -r .status` == "ok";
|
||||
@@ -298,9 +304,8 @@ Fields:
|
||||
- `typecheck`, `lint`, `test`, `deadcode`, `shell`, `gbrain` -- individual category scores (integer 0-10)
|
||||
- `duration_s` -- total time for all tools in seconds
|
||||
|
||||
If a category was skipped, set its value to `null`. Pre-D6 history entries
|
||||
won't have a `gbrain` field — treat them as `null` for trend comparison
|
||||
and start new tracking from the first post-D6 run.
|
||||
If a category was skipped, set its value to `null`. Treat a history entry
|
||||
with no `gbrain` field as `null` for that category.
|
||||
|
||||
---
|
||||
|
||||
@@ -358,12 +363,12 @@ Prioritize suggestions by impact (weight * score deficit):
|
||||
```
|
||||
RECOMMENDATIONS (by impact)
|
||||
============================
|
||||
1. [HIGH] Fix 2 failing tests (Tests: 9/10, weight 30%)
|
||||
Run: bun test --verbose to see failures
|
||||
2. [MED] Address 12 lint warnings (Lint: 6/10, weight 20%)
|
||||
1. [HIGH] Address 12 lint warnings (Lint: 6/10, weight 18%)
|
||||
Run: biome check . --write to auto-fix
|
||||
3. [LOW] Remove 4 unused exports (Dead code: 7/10, weight 15%)
|
||||
2. [MED] Remove 4 unused exports (Dead code: 7/10, weight 13%)
|
||||
Run: knip --fix to auto-remove
|
||||
3. [LOW] Fix 2 failing tests (Tests: 9/10, weight 28%)
|
||||
Run: bun test --verbose to see failures
|
||||
```
|
||||
|
||||
Rank by `weight * (10 - score)` descending. Only show categories below 10.
|
||||
|
||||
@@ -118,9 +118,8 @@ If any learnings come back, name which one applies to your investigation in one
|
||||
After forming your root cause hypothesis, lock edits to the affected module to prevent scope creep.
|
||||
|
||||
```bash
|
||||
# $HOME-anchored like the careful/freeze frontmatter hooks (#1871): frontmatter
|
||||
# hooks and early skill bash run before any runtime var like CLAUDE_SKILL_DIR
|
||||
# exists, so a ${CLAUDE_SKILL_DIR}-relative path silently never resolves (#2469).
|
||||
# $HOME-anchored like the careful/freeze frontmatter hooks: early skill bash runs
|
||||
# before CLAUDE_SKILL_DIR exists, so a relative path would never resolve.
|
||||
_FREEZE_SCRIPT="$HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh"
|
||||
[ -x "$_FREEZE_SCRIPT" ] && echo "FREEZE_AVAILABLE" || echo "FREEZE_UNAVAILABLE"
|
||||
```
|
||||
|
||||
+10
-6
@@ -52,7 +52,9 @@ Each item is reverted only after AskUserQuestion confirmation:
|
||||
`DebugBridgeManager.shared.start()`.
|
||||
3. Any standalone `// @Snapshotable` generator marker comments on the
|
||||
canonical app state class.
|
||||
4. Generated `StateAccessor.swift` files anywhere under the app source.
|
||||
4. The generated `DebugBridge/` package directory and `DebugBridgeGenerated/`
|
||||
(accessors and `.gstack-version`) that `gstack-ios-qa-regen` wrote under
|
||||
the app source, plus any other generated `StateAccessor.swift` files.
|
||||
5. The `gstack-ios-qa.token` file under `NSTemporaryDirectory()` on the
|
||||
device (best-effort — only works if device is connected when /ios-clean
|
||||
runs).
|
||||
@@ -67,8 +69,8 @@ Each item is reverted only after AskUserQuestion confirmation:
|
||||
|
||||
1. Glob for `import DebugBridge` across the app source.
|
||||
2. Glob for `#if DEBUG ... DebugBridgeManager` blocks.
|
||||
3. Glob for `// Auto-generated state accessor` headers in
|
||||
`StateAccessor.swift` files.
|
||||
3. Grep `StateAccessor.swift` files for the generator header
|
||||
`// AUTO-GENERATED — DO NOT EDIT. Regenerate with /ios-sync.`
|
||||
4. Parse `Package.swift` for the DebugBridge dependency entry.
|
||||
5. Show the user what's about to be removed (file list + line counts).
|
||||
AskUserQuestion: proceed, dry-run, or abort.
|
||||
@@ -79,9 +81,11 @@ For each item the user approved:
|
||||
|
||||
1. Use Edit tool to strip the import + the `#if DEBUG` block (keep the
|
||||
surrounding code intact).
|
||||
2. Use Edit tool to remove the `.package(url:...DebugBridge...)` entry
|
||||
from `Package.swift` and any `targets` referencing `"DebugBridge"`.
|
||||
3. Delete generated `StateAccessor.swift` files.
|
||||
2. Use Edit tool to remove the local `.package(path: ...DebugBridge...)`
|
||||
entry from `Package.swift` and every target dependency on a
|
||||
`DebugBridge*` product (`DebugBridgeUI`, `DebugBridgeCore`, `DebugBridgeTouch`).
|
||||
3. Delete the generated `DebugBridge/` and `DebugBridgeGenerated/`
|
||||
directories and any other generated `StateAccessor.swift` files.
|
||||
4. Run `xcodebuild -scheme <SchemeName> -destination 'platform=iOS,id=<UDID>'
|
||||
build install -configuration Release` to verify Release builds without
|
||||
the bridge. If it fails on a missing DebugBridge symbol, the removal
|
||||
|
||||
@@ -82,12 +82,14 @@ user pick the one to fix.
|
||||
|
||||
## Phase 5: Add regression test
|
||||
|
||||
Write a test in `test/fixtures/ios-fix/<bug-slug>.test.ts` that:
|
||||
Write a regression test with the project's own test runner and conventions
|
||||
(the test command in CLAUDE.md; if it has none, ask the user and record the
|
||||
answer there). The test:
|
||||
|
||||
1. Loads the pre-bug snapshot.
|
||||
2. Restores it via `POST /state/restore`.
|
||||
3. Asserts the post-fix behavior on a real device (gated
|
||||
`GSTACK_HAS_IOS_DEVICE=1`, periodic tier).
|
||||
3. Asserts the post-fix behavior on a real device, skipped when no device is
|
||||
connected.
|
||||
|
||||
Commit the snapshot fixture + test file alongside the fix.
|
||||
|
||||
@@ -96,6 +98,6 @@ Commit the snapshot fixture + test file alongside the fix.
|
||||
| Symptom | Action |
|
||||
|---|---|
|
||||
| 3 iterations, bug still present | STOP, report to user with current best hypothesis |
|
||||
| `409 schema_mismatch` on /state/restore after rebuild | Re-codegen accessors (`swift run gen-accessors`), re-snapshot |
|
||||
| `409 schema_mismatch` on /state/restore after rebuild | Regenerate accessors with `/ios-sync` (`gstack-ios-qa-regen`), re-snapshot |
|
||||
| Device disconnects mid-fix | Daemon auto-reconnects; resume from Phase 4 |
|
||||
| Build fails | Revert Swift edits; investigate compile error before re-applying fix |
|
||||
@@ -41,8 +41,7 @@ This skill regenerates the relevant artifacts in place.
|
||||
|
||||
**Templates live in upstream gstack.** The installed
|
||||
`gstack-ios-qa-regen` launcher resolves its own gstack root and copies only
|
||||
the supported bridge files from `ios-qa/templates/`. The fork's HTTP-fetch
|
||||
and wildcard-copy patterns are gone.
|
||||
the supported bridge files from `ios-qa/templates/`.
|
||||
|
||||
## Phase 1: Detect installed version
|
||||
|
||||
|
||||
+7
-15
@@ -114,7 +114,7 @@ Fence info-string options:
|
||||
|
||||
```
|
||||
```mermaid title="Auth flow" ← caption + aria-label
|
||||
```mermaid render=false ← keep it as a code block (today's behavior)
|
||||
```mermaid render=false ← keep it as a code block
|
||||
```mermaid page=landscape ← force this diagram onto a landscape page
|
||||
```mermaid page=portrait ← veto auto-landscape for this diagram
|
||||
```
|
||||
@@ -215,18 +215,11 @@ Metadata:
|
||||
--date "..." Date for cover (defaults to today)
|
||||
```
|
||||
|
||||
## When Claude should run it
|
||||
## When to run it
|
||||
|
||||
Watch for markdown-to-PDF intent. Any of these patterns → run `$P generate`:
|
||||
|
||||
- "Can you make this markdown a PDF"
|
||||
- "Export it as a PDF"
|
||||
- "Turn this letter into a PDF"
|
||||
- "I need a PDF of the essay"
|
||||
- "Print this as a PDF for me"
|
||||
|
||||
If the user has a `.md` file open and says "make it look nice", propose
|
||||
`$P generate --cover --toc` and ask before running.
|
||||
Run `$P generate` when the user wants markdown as a PDF. If the user has a `.md`
|
||||
file open and says "make it look nice", propose `$P generate --cover --toc` and ask
|
||||
before running.
|
||||
|
||||
## Debugging
|
||||
|
||||
@@ -237,9 +230,8 @@ If the user has a `.md` file open and says "make it look nice", propose
|
||||
- Diagram shows a red "failed to render" block → the parse error is printed in
|
||||
the block. If EVERY diagram fails with "diagram renderer:", the browser went
|
||||
away mid-run (Aside closed, or the fallback daemon died).
|
||||
- Fragmented text on copy-paste → highlight.js output (Phase 4). Retry with
|
||||
`--no-syntax` once that flag exists. For now, remove fenced code blocks
|
||||
and regenerate.
|
||||
- Fragmented text on copy-paste → remove fenced code blocks and regenerate
|
||||
(no flag turns code styling off).
|
||||
- Paged.js timeout → probably no headings in the markdown. Drop `--toc`.
|
||||
- "[remote image blocked]" placeholder in the output → add `--allow-network`
|
||||
(understand you're giving the markdown file permission to fetch from its
|
||||
|
||||
@@ -41,8 +41,7 @@ gbrain:
|
||||
render_as: "## Prior office-hours sessions in this repo"
|
||||
- id: builder-profile
|
||||
kind: filesystem
|
||||
glob: "~/.gstack/builder-profile.jsonl"
|
||||
tail: 1
|
||||
glob: "~/.gstack/developer-profile.json"
|
||||
render_as: "## Your builder profile snapshot"
|
||||
- id: design-doc-history
|
||||
kind: filesystem
|
||||
@@ -237,9 +236,9 @@ Use AskUserQuestion to confirm. If the user disagrees with a premise, revise und
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Alternatives Generation (MANDATORY)
|
||||
## Phase 4: Alternatives Generation
|
||||
|
||||
Produce 2-3 distinct implementation approaches. This is NOT optional.
|
||||
Produce 2-3 distinct implementation approaches.
|
||||
|
||||
For each approach:
|
||||
```
|
||||
@@ -319,7 +318,7 @@ Append one JSON line with these fields (substitute actual values from this sessi
|
||||
|
||||
The session entry is appended to `developer-profile.json`'s `sessions[]` array. A second
|
||||
session entry with `mode: "resources"` is appended via `--log-session` after resource
|
||||
selection in Phase 6 Beat 3.5.
|
||||
selection in Phase 6 (Founder Resources).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ If `$PRIOR` exists, the new doc gets a `Supersedes:` field referencing it. This
|
||||
|
||||
Write to `<PROJECT_DIR>/{user}-{branch}-design-{datetime}.md` (`PROJECT_DIR` printed by the setup block above).
|
||||
|
||||
**Repo copy (dual-write, #703 + #2000).** When the session runs inside a git
|
||||
**Repo copy (dual-write).** When the session runs inside a git
|
||||
repository, ALSO write the doc to `docs/designs/{topic-slug}.md` in the repo —
|
||||
visible, committable, team-shareable. The `~/.gstack` copy is still written
|
||||
(memory ingest and cross-session discovery depend on it); the repo copy is
|
||||
@@ -35,7 +35,7 @@ what teammates and plan reviews read. Rules:
|
||||
3. **Name the repo path** in the handoff line and any approval questions when
|
||||
the repo copy exists — that's the copy the user can open and commit.
|
||||
|
||||
**Decision-record concision (#2000).** The doc is a decision record, not a
|
||||
**Decision-record concision.** The doc is a decision record, not a
|
||||
transcript: one bullet per decision with its why; an approach the user ruled
|
||||
out DURING the session gets one line (name + rejection reason), never a
|
||||
resurrected full section that re-argues the case; omit template sections that
|
||||
@@ -313,9 +313,10 @@ Design trajectory with interpretation:
|
||||
"You started this as a side project. But you've named specific users, pushed back when challenged, and your designs keep getting sharper each time. I don't think this is a side project anymore. Have you thought about whether this could be a company?"
|
||||
This must feel earned, not broadcast. If the evidence doesn't support it, skip entirely.
|
||||
|
||||
**Builder Journey Summary** (session 5+): Auto-generate `~/.gstack/builder-journey.md`
|
||||
with a narrative arc (not a data table). The arc tells the STORY of their journey in
|
||||
second person, referencing specific things they said across sessions. Then open it:
|
||||
**Builder Journey Summary** (session 5+): Auto-generate `builder-journey.md` in the
|
||||
gstack state root (`$GSTACK_STATE_ROOT`, resolved by the block below) with a narrative
|
||||
arc (not a data table). The arc tells the STORY of their journey in second person,
|
||||
referencing specific things they said across sessions. Then open it:
|
||||
```bash
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
open "$GSTACK_STATE_ROOT/builder-journey.md"
|
||||
@@ -333,7 +334,7 @@ The data speaks. No pitch needed.
|
||||
|
||||
Full accumulated signal summary from the profile.
|
||||
|
||||
Auto-generate updated `~/.gstack/builder-journey.md` with narrative arc. Open it.
|
||||
Auto-generate updated `$GSTACK_STATE_ROOT/builder-journey.md` with narrative arc (resolve the state root and open it as in the regular tier).
|
||||
|
||||
Then proceed to Founder Resources below.
|
||||
|
||||
@@ -341,7 +342,7 @@ Then proceed to Founder Resources below.
|
||||
|
||||
### Founder Resources (all tiers)
|
||||
|
||||
**Standing opt-out check (#538) — run FIRST:**
|
||||
**Standing opt-out check — run FIRST:**
|
||||
|
||||
```bash
|
||||
~/.claude/skills/gstack/bin/gstack-config get founder_resources 2>/dev/null || echo "true"
|
||||
@@ -509,13 +510,14 @@ D) Not now — I'll run a review later
|
||||
Net: 15 minutes of structured review now against rework risk later.
|
||||
|
||||
On the user's SELECTION of A/B/C (not on invocation success), log the handoff, then invoke
|
||||
the chosen skill via the **Skill tool** (it auto-discovers the design doc):
|
||||
the chosen skill via the **Skill tool** (it auto-discovers the design doc). In both log
|
||||
commands, replace `SESSION_ID` with the value the skill-start output echoed:
|
||||
```bash
|
||||
~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type handoff --skill office-hours --outcome accepted --session-id "$_SESSION_ID" 2>/dev/null || true
|
||||
~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type handoff --skill office-hours --outcome accepted --session-id "SESSION_ID" 2>/dev/null || true
|
||||
```
|
||||
On D, log declined and stop:
|
||||
```bash
|
||||
~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type handoff --skill office-hours --outcome declined --session-id "$_SESSION_ID" 2>/dev/null || true
|
||||
~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type handoff --skill office-hours --outcome declined --session-id "SESSION_ID" 2>/dev/null || true
|
||||
```
|
||||
|
||||
The design doc at `~/.gstack/projects/` is automatically discoverable by downstream skills — they will read it during their pre-review system audit.
|
||||
|
||||
+27
-27
@@ -42,9 +42,11 @@ this skill in plain English and you interpret. Never require subcommand syntax.
|
||||
Shortcuts exist (`profile`, `vibe`, `stats`, etc.) but users don't have to
|
||||
memorize them.
|
||||
|
||||
**v1 scope (observational):** typed question registry, per-question explicit
|
||||
preferences, question logging, dual-track profile (declared + inferred),
|
||||
plain-English inspection. No skills adapt behavior based on the profile yet.
|
||||
**Scope:** typed question registry, per-question explicit preferences,
|
||||
question logging, dual-track profile (declared + inferred), plain-English
|
||||
inspection. Per-question preferences take effect: the question-preference hook
|
||||
auto-decides `never-ask` questions (see Recent auto-decisions). The profile
|
||||
itself never changes a skill's defaults.
|
||||
|
||||
Canonical reference: `docs/designs/PLAN_TUNING_V0.md`.
|
||||
|
||||
@@ -66,7 +68,7 @@ Each gate is guarded by a marker so the user is prompted at most once per choice
|
||||
`~/.gstack/developer-profile.json`'s `declared` object is empty AND
|
||||
`~/.gstack/.declared-setup-prompted` is missing → run `5-Q setup` below.
|
||||
Touch the marker after setup completes OR is declined.
|
||||
3. **Dream-cycle gate (Layer 8 / cathedral T10/T11).** If
|
||||
3. **Dream-cycle gate.** If
|
||||
`~/.gstack/projects/<slug>/distillation-proposals.json` exists AND has
|
||||
`applied_at` missing on any proposal → run `Dream cycle review` below.
|
||||
Marker: each proposal carries its own `applied_at` so re-firing this
|
||||
@@ -130,8 +132,9 @@ explicit.
|
||||
> Question tuning is off. gstack can learn which of its prompts you find
|
||||
> valuable vs noisy — so over time, gstack stops asking questions you've
|
||||
> already answered the same way. It takes about 2 minutes to set up your
|
||||
> initial profile. v1 is observational: gstack tracks your preferences
|
||||
> and shows you a profile, but doesn't silently change skill behavior yet.
|
||||
> initial profile. Questions you mark never-ask are answered with
|
||||
> gstack's recommendation (one-way doors still ask); your profile is
|
||||
> shown to you, not used to change defaults.
|
||||
> Logs stay local (`~/.gstack/projects/<slug>/question-log.jsonl`).
|
||||
>
|
||||
> RECOMMENDATION: Enable and set up your profile. Completeness: A=9/10.
|
||||
@@ -146,7 +149,7 @@ explicit.
|
||||
> (skills adapting to your steering style). Enabling logs every
|
||||
> AskUserQuestion outcome locally to
|
||||
> `~/.gstack/projects/<slug>/question-log.jsonl` — nothing leaves your
|
||||
> machine. v1 is observational only.
|
||||
> machine. Questions auto-decide only where you set never-ask.
|
||||
>
|
||||
> RECOMMENDATION: Enable and set up your profile. Completeness: A=9/10.
|
||||
>
|
||||
@@ -372,7 +375,7 @@ boil-the-ocean than 0.5 suggests", "I've gotten more careful about architecture"
|
||||
"bump detail_preference up".
|
||||
|
||||
**Always confirm before writing.** Free-form input + direct profile mutation
|
||||
is a trust boundary (Codex #15 in the design doc).
|
||||
is a trust boundary.
|
||||
|
||||
1. Parse the user's intent. Translate to `(dimension, new_value)`.
|
||||
- "more boil-the-ocean" → `scope_appetite` → pick a value 0.15 higher than
|
||||
@@ -426,9 +429,8 @@ the user decides whether declared is wrong or behavior is wrong.
|
||||
|
||||
## Stats
|
||||
|
||||
Cathedral T13 surfaces: host-aware breakdown (claude hook vs codex import
|
||||
vs agent-enriched), marked vs hash-only, auto-decided count, and dream
|
||||
cycle cost-to-date.
|
||||
Shows: host-aware breakdown (claude hook vs codex import vs agent-enriched),
|
||||
marked vs hash-only, auto-decided count, and dream cycle cost-to-date.
|
||||
|
||||
```bash
|
||||
~/.claude/skills/gstack/bin/gstack-question-preference --stats
|
||||
@@ -471,9 +473,8 @@ echo '---DISTILL---'
|
||||
|
||||
Present as a compact summary with plain-English calibration status ("5 more
|
||||
events across 2 more skills and you'll be calibrated" or "you're calibrated").
|
||||
Surface the source breakdown so the user can see capture is real (Codex
|
||||
correction — without source columns, the cathedral's "before:0 / after:>0"
|
||||
claim is invisible).
|
||||
Surface the source breakdown so the user can see which capture paths are
|
||||
actually logging.
|
||||
|
||||
---
|
||||
|
||||
@@ -510,9 +511,9 @@ Run `gstack-question-preference --write '{"question_id":"<id>","preference":
|
||||
|
||||
## Audit unmarked questions
|
||||
|
||||
Top N hash-only question_ids by frequency. These are AUQ fires the cathedral
|
||||
hook captured but cannot enforce against (no `<gstack-qid:foo>` marker in
|
||||
the skill template — D18 progressive markers). Surfacing them drives marker
|
||||
Top N hash-only question_ids by frequency. These are AUQ fires the
|
||||
preference hook captured but cannot enforce against (no `<gstack-qid:foo>`
|
||||
marker in the skill template). Surfacing them drives marker
|
||||
adoption: high-traffic unmarked questions are the next candidates to retrofit.
|
||||
|
||||
```bash
|
||||
@@ -592,20 +593,19 @@ invokes via `/plan-tune distill` / `dream`.
|
||||
~/.claude/skills/gstack/bin/gstack-distill-apply --proposal N
|
||||
```
|
||||
|
||||
4. **On decline**: skip without marking. User can re-decide later (the
|
||||
proposal stays in the file). To dismiss permanently, manually clear:
|
||||
`gstack-distill-apply --proposal N --dismiss` (not implemented in T11;
|
||||
for now, regenerate via next distill run with corrected free-text).
|
||||
4. **On decline**: skip without marking. The proposal stays in the file and
|
||||
the gate offers it again. `gstack-distill-apply` has no dismiss flag; the
|
||||
next distill run overwrites the proposals file.
|
||||
|
||||
5. **gbrain integration.** When `mcp__gbrain__*` tools are available in
|
||||
this session:
|
||||
- On `memory-nugget` apply: `mcp__gbrain__put_page` with the nugget +
|
||||
`mcp__gbrain__extract_facts` + `mcp__gbrain__add_tag` per the cathedral
|
||||
plan D9 routing. Then pass `--gbrain-published true` to the bin so
|
||||
`mcp__gbrain__extract_facts` + `mcp__gbrain__add_tag`. Then pass
|
||||
`--gbrain-published true` to the bin so
|
||||
the proposals file records the mirror.
|
||||
- When gbrain isn't configured (no MCP tools), the bin's local file
|
||||
write is the durable source-of-truth and the PreToolUse hook reads it
|
||||
via Layer 8 memory injection.
|
||||
via memory injection.
|
||||
|
||||
---
|
||||
|
||||
@@ -650,9 +650,9 @@ For background mode (e.g., the user wants to keep working):
|
||||
- **One-way doors override never-ask.** Even with a never-ask preference, the
|
||||
binary returns ASK_NORMALLY for destructive/architectural/security questions.
|
||||
Surface the safety note to the user whenever it fires.
|
||||
- **No behavior adaptation in v1.** This skill INSPECTS and CONFIGURES. No
|
||||
skills currently read the profile to change defaults. That's v2 work, gated
|
||||
on the registry proving durable.
|
||||
- **The profile never changes defaults.** This skill inspects and configures.
|
||||
Per-question preferences can auto-decide questions; the declared/inferred
|
||||
profile is advisory only.
|
||||
- **Completion status:**
|
||||
- DONE — did what the user asked (enable/inspect/set/update/disable)
|
||||
- DONE_WITH_CONCERNS — action taken but flagging something (e.g., "your
|
||||
|
||||
+5
-12
@@ -238,19 +238,12 @@ Healthy-contract commits use `test(qa): regression test for {contract}`.
|
||||
|
||||
### 8f. Self-Regulation (STOP AND EVALUATE)
|
||||
|
||||
Every 5 fixes (or after any revert), compute the WTF-likelihood:
|
||||
Every 5 fixes (or after any revert), judge the WTF-likelihood: is the loop now doing
|
||||
more harm than good? Reverts, edits to unrelated files, fixes spanning many files, a
|
||||
long run of fixes, and a queue of only Low issues all raise it.
|
||||
|
||||
```
|
||||
WTF-LIKELIHOOD:
|
||||
Start at 0%
|
||||
Each revert: +15%
|
||||
Each fix touching >3 files: +5%
|
||||
After fix 15: +1% per additional fix
|
||||
All remaining Low severity: +10%
|
||||
Touching unrelated files: +20%
|
||||
```
|
||||
|
||||
**If WTF > 20%:** STOP immediately. Show the user what you've done so far. Ask whether to continue.
|
||||
**If it is high** (two reverts or one unrelated-file edit is enough): STOP immediately.
|
||||
Show the user what you've done so far. Ask whether to continue.
|
||||
|
||||
**Hard cap: 50 fixes.** After 50 fixes, stop regardless of remaining issues.
|
||||
|
||||
|
||||
+6
-8
@@ -23,9 +23,7 @@ gbrain:
|
||||
context_queries:
|
||||
- id: prior-retros
|
||||
kind: filesystem
|
||||
# #2552: /retro writes .context/retros/*.json (repo-local; see the save
|
||||
# step below) — the old ~/.gstack/.../retros/*.md glob matched a
|
||||
# directory and extension nothing ever writes, so this query was dead.
|
||||
# /retro writes .context/retros/*.json (repo-local; see Step 13).
|
||||
glob: ".context/retros/*.json"
|
||||
sort: mtime_desc
|
||||
limit: 5
|
||||
@@ -193,7 +191,7 @@ Use the analyzed ref in the commit-count label (not always `main`). Test health
|
||||
| Test Health | N test files · M changed this period · K regression test commits |
|
||||
|
||||
Lead with user-visible features, then commit and logical-SLOC metrics; raw LOC
|
||||
is only context, not impact (PLAN_TUNING_V1.md, Workstream C).
|
||||
is only context, not impact.
|
||||
|
||||
Then show a **per-author leaderboard** immediately below, from the `AUTHOR:` lines:
|
||||
|
||||
@@ -291,7 +289,7 @@ For each contributor (including the current user), the `AUTHOR:` line carries co
|
||||
- **Praise** (1-2 specifics): cite commits and what was good, not generic praise.
|
||||
- **Opportunity for growth** (1 specific): tie an actionable suggestion to data, not criticism. Step 14 supplies examples.
|
||||
|
||||
**If only one contributor (solo repo):** Skip the team breakdown and proceed as before — the retro is personal.
|
||||
**If only one contributor (solo repo):** Skip the team breakdown — the retro is personal.
|
||||
|
||||
**Co-author credit:** `COAUTHOR:` lines carry human `Co-Authored-By:` trailers — credit those authors for the commit alongside the primary author. AI co-authors (e.g., `noreply@anthropic.com`) are counted in `AI_ASSISTED_COMMITS` instead — track "AI-assisted commits" as a separate metric, never as a team member.
|
||||
|
||||
@@ -747,13 +745,13 @@ When the user runs `/retro compare` (or `/retro compare 14d`):
|
||||
- **Praise should feel like something you'd actually say in a 1:1** — specific, earned, genuine
|
||||
- **Growth suggestions should feel like investment advice** — "this is worth your time because..." not "you failed at..."
|
||||
- Never compare teammates against each other negatively. Each person's section stands on its own.
|
||||
- Keep total output around 3000-4500 words (slightly longer to accommodate team sections)
|
||||
- Size the report to the window and the team: every section carries specific evidence, and a section with nothing to say is cut, not padded
|
||||
- Use markdown tables and code blocks for data, prose for narrative
|
||||
- Output directly to the conversation — do NOT write to filesystem (except the `.context/retros/` JSON snapshot)
|
||||
- Output the narrative directly to the conversation; the only report file is the JSON snapshot (`.context/retros/` for repo retros, `$GSTACK_STATE_ROOT/retros/` for global)
|
||||
|
||||
## Important Rules
|
||||
|
||||
- ALL narrative output goes directly to the user in the conversation. The ONLY file written is the `.context/retros/` JSON snapshot.
|
||||
- ALL narrative output goes directly to the user in the conversation. Besides the JSON snapshot, only the learnings and gbrain capture steps write anything.
|
||||
- The metrics script analyzes `origin/<default>` (not local main which may be stale); when `RETRO_REF` says otherwise, disclose it
|
||||
- Display all timestamps in the user's local timezone (do not override `TZ`)
|
||||
- If `COMMITS: 0`, say so and suggest a different window
|
||||
|
||||
@@ -35,9 +35,6 @@ checks, and deploy status commands — then persist everything to CLAUDE.md.
|
||||
|
||||
After this runs once, `/land-and-deploy` reads CLAUDE.md and skips detection entirely.
|
||||
|
||||
## User-invocable
|
||||
When the user types `/setup-deploy`, run this skill.
|
||||
|
||||
## Instructions
|
||||
|
||||
### Step 1: Check existing configuration
|
||||
@@ -108,7 +105,7 @@ If `render.yaml` detected:
|
||||
5. Set health check: the inferred URL
|
||||
|
||||
Ask the user to confirm. Render uses auto-deploy from the connected git branch — after
|
||||
merge to main, Render picks it up automatically. The "deploy wait" in /land-and-deploy
|
||||
merge to the default branch, Render picks it up automatically. The "deploy wait" in /land-and-deploy
|
||||
should poll the Render URL until it responds with the new version.
|
||||
|
||||
#### Vercel
|
||||
@@ -117,7 +114,7 @@ If vercel.json or .vercel detected:
|
||||
|
||||
1. Check for `vercel` CLI: `which vercel 2>/dev/null`
|
||||
2. If installed: `vercel ls --prod 2>/dev/null | head -3`
|
||||
3. Vercel deploys automatically on push — preview on PR, production on merge to main
|
||||
3. Vercel deploys automatically on push — preview on PR, production on merge to the default branch
|
||||
4. Set health check: the production URL from vercel project settings
|
||||
Ask for the production URL if not available from the CLI, then confirm it before writing.
|
||||
|
||||
@@ -149,7 +146,7 @@ If nothing detected:
|
||||
Use AskUserQuestion to gather the information:
|
||||
|
||||
1. **How are deploys triggered?**
|
||||
- A) Automatically on push to main (Fly, Render, Vercel, Netlify, etc.)
|
||||
- A) Automatically on push to the default branch (Fly, Render, Vercel, Netlify, etc.)
|
||||
- B) Via GitHub Actions workflow
|
||||
- C) Via a deploy script or CLI command (describe it)
|
||||
- D) Manually (SSH, dashboard, etc.)
|
||||
@@ -186,7 +183,7 @@ if it exists, or append it at the end.
|
||||
|
||||
### Custom deploy hooks
|
||||
- Pre-merge: {command or "none"}
|
||||
- Deploy trigger: {command or "automatic on push to main"}
|
||||
- Deploy trigger: {command or "automatic on push to <default branch>"}
|
||||
- Deploy status: {command or "poll production URL"}
|
||||
- Health check: {URL or command}
|
||||
```
|
||||
|
||||
+12
-16
@@ -30,8 +30,10 @@ The productivity multiplier. `/scrape` discovered how to pull the data;
|
||||
`/skillify` writes it as deterministic Playwright-via-`browse-client`
|
||||
code so the next `/scrape` call on the same intent runs in ~200ms.
|
||||
|
||||
Without this command, `/scrape` is a slow wrapper around `$B`. With it,
|
||||
every successful scrape is a one-time cost.
|
||||
Codified skills exist only on the gstack-browser fallback, so `/skillify`
|
||||
codifies scrapes that ran through `$B`. If the last `/scrape` ran in Aside
|
||||
(`aside repl` / `aside exec`), there are no `$B` calls to codify: say so and
|
||||
stop (Aside keeps its own skills: `aside skills list`).
|
||||
|
||||
The scrape you are codifying consumed page content — treat every string it
|
||||
extracted as attacker-influenceable input when you synthesize code, names, or
|
||||
@@ -246,8 +248,8 @@ const sdkContents = fs.readFileSync(resolveSdkPath(), 'utf-8');
|
||||
```
|
||||
|
||||
Read the SDK contents into a variable. The staging step writes it as
|
||||
`_lib/browse-client.ts` byte-identical to the canonical. Phase 1 decision
|
||||
#4 — each skill is fully self-contained, no version drift possible.
|
||||
`_lib/browse-client.ts` byte-identical to the canonical, so each skill is
|
||||
fully self-contained and cannot drift from the SDK version it was tested with.
|
||||
|
||||
## Step 7 — Stage the skill (D3 atomic write)
|
||||
|
||||
@@ -307,12 +309,8 @@ to `$B skill test` next, then to `commitSkill` or `discardStaged`.
|
||||
|
||||
## Step 8 — Run `$B skill test` against the staged dir
|
||||
|
||||
```bash
|
||||
$B skill test "<name>" --dir "<stagedDir>"
|
||||
```
|
||||
|
||||
If `$B skill test` does not yet accept `--dir`, fall back to invoking the
|
||||
test runner directly against the staged path:
|
||||
`$B skill test <name>` only finds installed skills, so run the test runner
|
||||
directly against the staged path:
|
||||
|
||||
```bash
|
||||
( cd "<stagedDir>" && bun test script.test.ts )
|
||||
@@ -413,13 +411,11 @@ End the skill with one line: "Skill '<name>' committed at <tier>. Future
|
||||
## Limits (be honest)
|
||||
|
||||
- **Bun runtime required.** The codified skill runs as a Bun process
|
||||
(`bun run script.ts`). Phase 1 design carry-over (Codex finding #7).
|
||||
Real fix lands in Phase 4 (self-contained binary or Node fallback).
|
||||
For now: the skill works on any machine that has gstack installed,
|
||||
which means it has Bun.
|
||||
(`bun run script.ts`), so it works on any machine that has gstack
|
||||
installed.
|
||||
- **Fixture-replay tests are point-in-time.** When the target site
|
||||
rotates HTML, the fixture goes stale and the test passes against an
|
||||
outdated snapshot. Phase 4 will add fixture-staleness detection.
|
||||
outdated snapshot; nothing detects that staleness.
|
||||
- **Synthesis is best-effort.** You're writing a script from your own
|
||||
conversation memory. If the prototype was complex (multi-page, JS
|
||||
hydration, lazy load) the codified script may need a hand-edit before
|
||||
@@ -431,7 +427,7 @@ End the skill with one line: "Skill '<name>' committed at <tier>. Future
|
||||
## What this skill does NOT do
|
||||
|
||||
- Codify match-path /scrape results (matched skills are already codified)
|
||||
- Codify mutating flows (those are /automate's job — Phase 2 P0)
|
||||
- Codify mutating flows (drive those with /qa)
|
||||
- Run skills (that's `$B skill run` — codified skills are run via /scrape's
|
||||
match path or directly)
|
||||
- Edit existing skills ($EDITOR + the skill dir is the surface — `$B skill
|
||||
|
||||
+8
-12
@@ -144,13 +144,12 @@ Ask until you can answer:
|
||||
|
||||
Do NOT proceed until scope is locked.
|
||||
|
||||
### Phase 3: Technical Interrogation (HARD requirement: read code first)
|
||||
### Phase 3: Technical Interrogation (read code first)
|
||||
|
||||
**Mandatory:** Before asking ANY Phase 3 question, you MUST read at least one
|
||||
piece of evidence from the codebase via Grep, Glob, or Read. This is the magical
|
||||
moment for the user: they see you grounded in their actual code, not generic
|
||||
checklists. Do NOT skip. Do NOT ask "what file should I look at?" first — find
|
||||
it yourself.
|
||||
Before asking any Phase 3 question, read at least one piece of evidence from the
|
||||
codebase via Grep, Glob, or Read, and cite it. This is the magical moment for the
|
||||
user: they see you grounded in their actual code, not generic checklists. Find the
|
||||
relevant file yourself rather than asking which one to look at.
|
||||
|
||||
Mapping the user's request to evidence:
|
||||
|
||||
@@ -176,9 +175,6 @@ Then ask about whichever categories apply (skip ones that clearly don't):
|
||||
- **Infrastructure** — IaC changes, secrets, cost impact
|
||||
- **Testing** — how to test at each layer, regression risk
|
||||
|
||||
Don't ask questions you can answer by reading the code. Read first, then ask
|
||||
the questions whose answers aren't in the code.
|
||||
|
||||
### Phase 4: Draft Review
|
||||
|
||||
Present a full draft issue and ask: **"Does this accurately capture what you want?
|
||||
@@ -334,7 +330,7 @@ this? Even "revert the PR" is worth stating explicitly.
|
||||
|
||||
## Issue Structure Templates
|
||||
|
||||
### Standard Issues (default; also used for `--bug`, `--feature`, `--refactor` framings)
|
||||
### Standard Issues (default; bug, feature and refactor framings adapt within it)
|
||||
|
||||
```
|
||||
## Context
|
||||
@@ -482,8 +478,8 @@ Add to the standard template:
|
||||
a `/spec` archive (frontmatter `spec_issue_number: <N>`) AND the PR delivers
|
||||
the full spec (acceptance criteria checked off per `/ship`'s existing
|
||||
plan-completion gate), `/ship` adds `Closes #<N>` to the PR body so merging
|
||||
auto-closes the source issue. Conditional — partial PRs do NOT auto-close
|
||||
(codex F4). Branch-name inference is NOT used (codex F3).
|
||||
auto-closes the source issue. Conditional — partial PRs do not auto-close, and
|
||||
the source issue is never inferred from the branch name.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -84,12 +84,6 @@ Missing/broken CLI, authentication failure, timeout, refusal, nonzero exit, inva
|
||||
|
||||
Max 3 dispatches total. If still <7 after iter 3, AskUserQuestion same options.
|
||||
|
||||
|
||||
|
||||
**Audit-sink invariant:** When the redaction gate fires, the raw spec must NOT
|
||||
be persisted anywhere downstream (no archive write, no transcript log). The
|
||||
`spec-quality-gate-secret-sink.test.ts` enforces this.
|
||||
|
||||
### Phase 5: File the Spec (+ optional --execute)
|
||||
|
||||
Produce the final spec using the structure defined below. Use `--audit` to
|
||||
@@ -148,7 +142,7 @@ is consumed by `/ship` for auto-close.
|
||||
|
||||
{{REDACT_INVOCATION_BLOCK:pre-archive:brief}}
|
||||
|
||||
**D2 — sanitized body to the archive.** If auto-redact fired, the `<body>` below
|
||||
**Sanitized body to the archive.** If auto-redact fired, the `<body>` below
|
||||
MUST be the sanitized body (`$REDACT_FILE`), not the original draft — one body for
|
||||
all sinks. The user's on-disk source draft keeps the original.
|
||||
|
||||
@@ -173,8 +167,6 @@ spec_branch: $(git branch --show-current 2>/dev/null || echo unknown)
|
||||
spec_plan_mode: ${GSTACK_PLAN_MODE:-unset}
|
||||
spec_executed: ${WILL_EXECUTE:-false}
|
||||
spec_worktree_path:
|
||||
ttfc_ms: ${TTFC_MS:-}
|
||||
tthw_ms: ${TTHW_MS:-}
|
||||
---
|
||||
|
||||
# <title>
|
||||
@@ -189,14 +181,14 @@ The PID suffix and atomic rename prevent collisions when two `/spec` invocations
|
||||
run in the same second.
|
||||
|
||||
**Sync default:** `/specs/` is auto-excluded from the artifacts-sync allowlist —
|
||||
archives stay local unless the user opts in via `--sync-archive` (privacy default
|
||||
per codex review). If `--sync-archive` is passed, append `/specs/<archive_name>`
|
||||
archives stay local unless the user opts in via `--sync-archive` (privacy default).
|
||||
If `--sync-archive` is passed, append `/specs/<archive_name>`
|
||||
to the artifacts-sync allowlist (or symlink into the synced dir, depending on
|
||||
implementation).
|
||||
|
||||
#### Spawn the agent (`--execute` path only)
|
||||
|
||||
**E2 dirty-worktree gate:**
|
||||
**Dirty-worktree gate:**
|
||||
|
||||
```bash
|
||||
DIRTY=$(git status --porcelain 2>/dev/null)
|
||||
@@ -209,7 +201,7 @@ If `$DIRTY` is non-empty, AskUserQuestion:
|
||||
- B) Stash and restore (auto-stash now, restore after spawn returns)
|
||||
- C) Cancel spawn (stop here; issue stays filed, archive stays written)
|
||||
|
||||
**E2 TOCTOU re-check (F1):** After the user answers, IMMEDIATELY re-run
|
||||
**TOCTOU re-check:** After the user answers, IMMEDIATELY re-run
|
||||
`git status --porcelain` before any worktree operation. If state diverged
|
||||
from the answer, re-prompt the AskUserQuestion. The check must happen INSIDE
|
||||
the spawn workflow, not be cached from earlier.
|
||||
@@ -222,21 +214,21 @@ git stash push -u -m "spec-execute-auto-$$" # untracked YES, ignored NO
|
||||
STASH_REF="spec-execute-auto-$$"
|
||||
```
|
||||
|
||||
F2 stash policy: `-u` includes untracked; we deliberately do NOT use `--all`
|
||||
Stash policy: `-u` includes untracked; we deliberately do NOT use `--all`
|
||||
because ignored files (build artifacts, .env caches) are usually local-by-design
|
||||
and should stay in the current worktree.
|
||||
|
||||
If C: print "Cancelled spawn. Issue filed: $ISSUE_URL, archive: $ARCHIVE_PATH."
|
||||
Exit /spec.
|
||||
|
||||
**F4 SHA pin:** Capture the exact SHA AFTER the final dirty check. Use this
|
||||
**SHA pin:** Capture the exact SHA AFTER the final dirty check. Use this
|
||||
SHA (not "HEAD") for the worktree:
|
||||
|
||||
```bash
|
||||
PIN_SHA=$(git rev-parse HEAD)
|
||||
```
|
||||
|
||||
**F5 unique branch + worktree path:** Suffix with `$$` to avoid concurrent
|
||||
**Unique branch + worktree path:** Suffix with `$$` to avoid concurrent
|
||||
collisions:
|
||||
|
||||
```bash
|
||||
@@ -245,7 +237,7 @@ SPAWN_PATH="${WORKTREE_PARENT:-../worktrees}/${SLUG_TITLE}-$$"
|
||||
mkdir -p "$(dirname "$SPAWN_PATH")"
|
||||
```
|
||||
|
||||
**D16 mandatory final-confirm gate:** AskUserQuestion: "Spawn agent now? Last
|
||||
**Final-confirm gate (required):** AskUserQuestion: "Spawn agent now? Last
|
||||
chance to revise the spec." Options: A) Spawn. B) Cancel (issue stays filed,
|
||||
archive stays written).
|
||||
|
||||
@@ -272,22 +264,9 @@ echo "Follow with: cd $SPAWN_PATH && claude --resume"
|
||||
Update archive frontmatter with `spec_worktree_path: $SPAWN_PATH` and
|
||||
`spec_executed: true` (atomic re-write).
|
||||
|
||||
**F3 stash restore safety (when B path was chosen):** Do NOT auto-restore inline
|
||||
**Stash restore safety (when B path was chosen):** Do NOT auto-restore inline
|
||||
— the spawned agent may take hours. Instead print: "Stash preserved as
|
||||
`$STASH_REF`. Restore later with `git stash list` then `git stash apply
|
||||
stash^{/$STASH_REF}`. Before restore, re-run `git status` to make sure your
|
||||
worktree is clean." Do NOT drop the stash; user owns it.
|
||||
|
||||
#### TTHW telemetry (DX11/F7)
|
||||
|
||||
Capture timestamps at three checkpoints, write to telemetry envelope at /spec
|
||||
exit:
|
||||
|
||||
- `T_PHASE1_START` — Phase 1 first AskUserQuestion or first text emit
|
||||
- `T_FIRST_CITATION` — first file/symbol reference in Phase 3 prose
|
||||
- `T_FILE_OR_SPAWN` — issue filed OR agent spawned, whichever ends Phase 5
|
||||
|
||||
Append the captured timestamps to the local analytics line that the preamble's
|
||||
end-of-skill telemetry write emits, as `ttfc_ms` (Phase 1 → first citation) and
|
||||
`tthw_ms` (Phase 1 → file/spawn) JSON fields. Surfacing the aggregates in
|
||||
`/retro` is a separate follow-up.
|
||||
@@ -650,7 +650,7 @@ do not launch the downstream skill or open a browser.`,
|
||||
'## Phase 7: Triage',
|
||||
'## Phase 8: Fix Loop',
|
||||
'8e.5. Regression Test',
|
||||
'WTF-LIKELIHOOD',
|
||||
'### 8f. Self-Regulation',
|
||||
'## Additional Rules (qa-specific)',
|
||||
'## Output Structure',
|
||||
],
|
||||
|
||||
@@ -229,7 +229,8 @@ describe('compact QA browser recipes retain native operations', () => {
|
||||
const classification = section('### 8e.', '### 8e.5.');
|
||||
for (const rule of ['passed 8c', 'native regression when available', 'disclose missing test coverage', "undo only this run's repair", 'revert its commit if already committed', 'retain the valid regression/evidence', '"deferred"', 'Never discard user changes']) expect(classification).toContain(rule);
|
||||
const regulation = section('### 8f.', '## Phase 9:');
|
||||
for (const rule of ['Every 5 fixes (or after any revert)', 'WTF > 20%', 'STOP immediately', 'Ask whether to continue', 'Hard cap: 50 fixes']) expect(regulation).toContain(rule);
|
||||
for (const rule of ['Every 5 fixes (or after any revert)', 'STOP immediately', 'Ask whether to continue', 'Hard cap: 50 fixes']) expect(regulation).toContain(rule);
|
||||
for (const signal of [/WTF-likelihood/i, /revert/i, /unrelated/i]) expect(regulation).toMatch(signal);
|
||||
expect(source).toContain('When in doubt, stop and ask');
|
||||
const rules = source.slice(source.indexOf('## Additional Rules'));
|
||||
for (const rule of ['Outside an explicitly approved browser bootstrap', 'Only create tests through authorized codification in Phase 8a.5', 'Never modify CI configuration or weaken existing tests', 'use new native test files']) expect(rules).toContain(rule);
|
||||
|
||||
@@ -1247,7 +1247,7 @@ describe('Phase 8e.5 regression test generation', () => {
|
||||
const content = fs.readFileSync(path.join(ROOT, 'qa', 'SKILL.md'), 'utf-8');
|
||||
expect(content).toContain('8e.5. Regression Test');
|
||||
expect(content).toContain('test(qa): regression test');
|
||||
expect(content).toContain('WTF-likelihood exclusion');
|
||||
expect(content).toMatch(/test-only commits do not count/i);
|
||||
});
|
||||
|
||||
test('qa/SKILL.md Rule 13 is amended for regression tests', () => {
|
||||
|
||||
@@ -200,7 +200,7 @@ describe('/spec redaction at every sink (scan-at-sink, carved: gate-and-file sec
|
||||
|
||||
describe('/spec quality gate secret-sink invariant (carved: gate-and-file section)', () => {
|
||||
test('declares "raw spec must NOT be persisted" when the scan BLOCKS', () => {
|
||||
expect(SEC_TMPL).toMatch(/raw spec must NOT[\s\S]*be persisted/i);
|
||||
expect(SEC_TMPL).toMatch(/raw spec must not\s+be\s+persisted/i);
|
||||
});
|
||||
test('BLOCK path stops before dispatch/archive/file', () => {
|
||||
expect(SEC_TMPL).toMatch(/no archive write, no transcript log, no outside\s*\n?\s*dispatch/i);
|
||||
@@ -311,7 +311,9 @@ describe('/spec plan-mode-aware Phase 5 (DX7/DX11/F1, carved: gate-and-file sect
|
||||
|
||||
describe('/spec Phase 3 hard-grep with fallback', () => {
|
||||
test('Phase 3 mandates reading evidence before asking', () => {
|
||||
expect(TMPL).toMatch(/Mandatory:[\s\S]*MUST read at least one[\s\S]*evidence/i);
|
||||
const phase3 = TMPL.slice(TMPL.indexOf('### Phase 3:'), TMPL.indexOf('### Phase 4:'));
|
||||
expect(phase3).toMatch(/before asking[\s\S]{0,40}question/i);
|
||||
expect(phase3).toMatch(/read at least one\s+piece of evidence/i);
|
||||
});
|
||||
test('project-level fallback prose for prompts with no concrete file', () => {
|
||||
expect(TMPL).toMatch(/Project-level prompt/);
|
||||
|
||||
@@ -167,7 +167,7 @@ describe('workflow judge excerpts', () => {
|
||||
expect(review).toContain('present the findings, then use AskUserQuestion ONCE:');
|
||||
expect(review).toContain('On A or per-finding approvals, make the approved edits yourself');
|
||||
expect(text).toContain('Step 9 then commits and pushes those edits');
|
||||
expect(text).toContain('Entries scoring <2 need attention, not replacement');
|
||||
expect(text).toMatch(/needs?\s+attention,?\s+not\s+replacement/i);
|
||||
expect(text).not.toContain('Flag and rewrite');
|
||||
expect(text).toContain('if VERSION is absent, use the completion date only');
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user