mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 23:37:53 +01:00
refactor: apply W1 state-root edits in W2/W3/W5-owned files; one moved-code touchfile golden for all workstreams
This commit is contained in:
1 parent
97c98d7ee5
commit
7a50eb250e
19 files changed
+142
-131
No files matched your search
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { resolveStateRoot } from '../../../lib/state-root';
|
||||
import { json, type RouteEntry } from './table';
|
||||
import { handleCookiePickerRoute } from '../cookie-picker-routes';
|
||||
import { sanitizeReplacer } from '../sanitize';
|
||||
@@ -16,7 +18,7 @@ function resolveWelcomePath(): string | null {
|
||||
const rawSlug = process.env.GSTACK_SLUG || 'unknown';
|
||||
const slug = /^[a-z0-9_-]+$/.test(rawSlug) ? rawSlug : 'unknown';
|
||||
const homeDir = process.env.HOME || process.env.USERPROFILE || '/tmp';
|
||||
const projectWelcome = `${homeDir}/.gstack/projects/${slug}/designs/welcome-page-20260331/finalized.html`;
|
||||
const projectWelcome = path.join(resolveStateRoot(), 'projects', slug, 'designs', 'welcome-page-20260331', 'finalized.html');
|
||||
if (fs.existsSync(projectWelcome)) return projectWelcome;
|
||||
// Fallback: built-in welcome page from gstack install. Reject SKILL_ROOT
|
||||
// values containing '..' for the same defense-in-depth reason.
|
||||
|
||||
@@ -586,7 +586,7 @@ Commands:
|
||||
`generate` returns `sessionFile`; `iterate` requires that existing session. `variants` returns `paths` but creates no session: regenerate with an updated brief instead.
|
||||
|
||||
**CRITICAL PATH RULE:** Design artifacts belong in `$GSTACK_STATE_ROOT/projects/$SLUG/designs/`.
|
||||
Use `bin/gstack-paths`: GSTACK_HOME → plugin storage → ~/.gstack. Keep it even if temporary; never substitute
|
||||
Use `bin/gstack-paths` (docs/state-root.md). Keep it even if temporary; never substitute
|
||||
.context/, docs/designs/ or another directory.
|
||||
These are user files, not application source.
|
||||
|
||||
@@ -669,7 +669,8 @@ Read this project's taste profile:
|
||||
```bash
|
||||
eval "$("~/.claude/skills/gstack/bin/gstack-slug" 2>/dev/null)"
|
||||
[ -n "${SLUG:-}" ] || { echo "NO_TASTE_PROFILE"; exit 0; }
|
||||
_TASTE_PROFILE=~/.gstack/projects/$SLUG/taste-profile.json
|
||||
eval "$("~/.claude/skills/gstack/bin/gstack-paths")"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
_TASTE_PROFILE="$GSTACK_STATE_ROOT/projects/$SLUG/taste-profile.json"
|
||||
if [ -f "$_TASTE_PROFILE" ]; then
|
||||
# Schema v1: { dimensions: { fonts, colors, layouts, aesthetics }, sessions: [] }
|
||||
# Each dimension has approved[] and rejected[] entries with
|
||||
|
||||
@@ -433,7 +433,7 @@ Commands:
|
||||
- `$D iterate --session /path/session.json --feedback "..." --output /path.png` — iterate
|
||||
|
||||
**CRITICAL PATH RULE:** Design artifacts belong in `$GSTACK_STATE_ROOT/projects/$SLUG/designs/`.
|
||||
Use `bin/gstack-paths`: GSTACK_HOME → plugin storage → ~/.gstack. Keep it even if temporary; never substitute
|
||||
Use `bin/gstack-paths` (docs/state-root.md). Keep it even if temporary; never substitute
|
||||
.context/, docs/designs/ or another directory.
|
||||
These are user files, not application source.
|
||||
|
||||
|
||||
@@ -769,7 +769,7 @@ Commands:
|
||||
- `$D iterate --session /path/session.json --feedback "..." --output /path.png` — iterate
|
||||
|
||||
**CRITICAL PATH RULE:** Design artifacts belong in `$GSTACK_STATE_ROOT/projects/$SLUG/designs/`.
|
||||
Use `bin/gstack-paths`: GSTACK_HOME → plugin storage → ~/.gstack. Keep it even if temporary; never substitute
|
||||
Use `bin/gstack-paths` (docs/state-root.md). Keep it even if temporary; never substitute
|
||||
.context/, docs/designs/ or another directory.
|
||||
These are user files, not application source.
|
||||
|
||||
@@ -1387,9 +1387,10 @@ Compare screenshots and observations across pages for:
|
||||
|
||||
**Project-scoped:**
|
||||
```bash
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG"
|
||||
```
|
||||
Write to: `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md`
|
||||
Write to: `<PROJECT_DIR>/{user}-{branch}-design-audit-{datetime}.md` (`PROJECT_DIR` printed above)
|
||||
|
||||
**Baseline:** Write `design-baseline.json` for regression mode (temp file then `mv`, and a per-run copy `design-baseline.<runId>.json` beside it):
|
||||
```json
|
||||
|
||||
@@ -444,7 +444,7 @@ Commands:
|
||||
- `$D iterate --session /path/session.json --feedback "..." --output /path.png` — iterate
|
||||
|
||||
**CRITICAL PATH RULE:** Design artifacts belong in `$GSTACK_STATE_ROOT/projects/$SLUG/designs/`.
|
||||
Use `bin/gstack-paths`: GSTACK_HOME → plugin storage → ~/.gstack. Keep it even if temporary; never substitute
|
||||
Use `bin/gstack-paths` (docs/state-root.md). Keep it even if temporary; never substitute
|
||||
.context/, docs/designs/ or another directory.
|
||||
These are user files, not application source.
|
||||
|
||||
@@ -556,7 +556,8 @@ Read this project's taste profile:
|
||||
```bash
|
||||
eval "$("~/.claude/skills/gstack/bin/gstack-slug" 2>/dev/null)"
|
||||
[ -n "${SLUG:-}" ] || { echo "NO_TASTE_PROFILE"; exit 0; }
|
||||
_TASTE_PROFILE=~/.gstack/projects/$SLUG/taste-profile.json
|
||||
eval "$("~/.claude/skills/gstack/bin/gstack-paths")"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
_TASTE_PROFILE="$GSTACK_STATE_ROOT/projects/$SLUG/taste-profile.json"
|
||||
if [ -f "$_TASTE_PROFILE" ]; then
|
||||
# Schema v1: { dimensions: { fonts, colors, layouts, aesthetics }, sessions: [] }
|
||||
# Each dimension has approved[] and rejected[] entries with
|
||||
|
||||
@@ -971,7 +971,7 @@ Generating visual mockups of the proposed design... (say "skip" if you don't nee
|
||||
|
||||
```bash
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
_DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/mockup-$(date +%Y%m%d)"
|
||||
mkdir -p "$_DESIGN_DIR"
|
||||
echo "DESIGN_DIR: $_DESIGN_DIR"
|
||||
|
||||
@@ -256,8 +256,9 @@ inventing duplicate counts. Preserve the Assignment, coaching, approval, and Han
|
||||
Append the helper's actual metrics to the existing analytics log (telemetry is
|
||||
best-effort and must not block approval):
|
||||
```bash
|
||||
mkdir -p ~/.gstack/analytics
|
||||
echo '{"skill":"office-hours","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","iterations":ITERATIONS,"issues_found":FOUND,"issues_fixed":FIXED,"remaining":REMAINING,"quality_score":SCORE}' >> ~/.gstack/analytics/spec-review.jsonl 2>/dev/null || true
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
mkdir -p "$GSTACK_STATE_ROOT/analytics"
|
||||
echo '{"skill":"office-hours","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","iterations":ITERATIONS,"issues_found":FOUND,"issues_fixed":FIXED,"remaining":REMAINING,"quality_score":SCORE}' >> "$GSTACK_STATE_ROOT/analytics/spec-review.jsonl" 2>/dev/null || true
|
||||
```
|
||||
Use iterations, issues_found, issues_fixed, remaining, and quality_score from the
|
||||
helper. FOUND counts finding observations across rounds; FIXED counts only
|
||||
|
||||
@@ -1221,8 +1221,9 @@ forbidden, show the actual fields as not persisted and continue without writing.
|
||||
If the reviewer fails, report that limit and continue after recording the outcome;
|
||||
if a required save fails, stop before claiming completion.
|
||||
```bash
|
||||
mkdir -p ~/.gstack/analytics || exit 1
|
||||
echo '{"skill":"plan-ceo-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","iterations":ITERATIONS,"issues_found":FOUND,"issues_fixed":FIXED,"remaining":REMAINING,"quality_score":SCORE}' >> ~/.gstack/analytics/spec-review.jsonl || exit 1
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
mkdir -p "$GSTACK_STATE_ROOT/analytics" || exit 1
|
||||
echo '{"skill":"plan-ceo-review","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","iterations":ITERATIONS,"issues_found":FOUND,"issues_fixed":FIXED,"remaining":REMAINING,"quality_score":SCORE}' >> "$GSTACK_STATE_ROOT/analytics/spec-review.jsonl" || exit 1
|
||||
```
|
||||
ITERATIONS counts actual reviewer launches. FOUND, FIXED and REMAINING count reported issues, reviewer-confirmed fixes and reported unresolved issues. Use actual counts, never estimates.
|
||||
|
||||
|
||||
@@ -688,7 +688,7 @@ Commands:
|
||||
- `$D iterate --session /path/session.json --feedback "..." --output /path.png` — iterate
|
||||
|
||||
**CRITICAL PATH RULE:** Design artifacts belong in `$GSTACK_STATE_ROOT/projects/$SLUG/designs/`.
|
||||
Use `bin/gstack-paths`: GSTACK_HOME → plugin storage → ~/.gstack. Keep it even if temporary; never substitute
|
||||
Use `bin/gstack-paths` (docs/state-root.md). Keep it even if temporary; never substitute
|
||||
.context/, docs/designs/ or another directory.
|
||||
These are user files, not application source.
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-' | tr -cd 'a-zA-Z0-9.
|
||||
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
||||
_PLAN_SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-' | tr -cd 'a-zA-Z0-9._-') || true
|
||||
_PLAN_SLUG="${_PLAN_SLUG:-$(basename "$PWD" | tr -cd 'a-zA-Z0-9._-')}"
|
||||
for PLAN_DIR in "$HOME/.gstack/projects/$_PLAN_SLUG" "$HOME/.claude/plans" "$HOME/.codex/plans" ".gstack/plans"; do
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
for PLAN_DIR in "$GSTACK_STATE_ROOT/projects/$_PLAN_SLUG" "$HOME/.claude/plans" "$HOME/.codex/plans" ".gstack/plans"; do
|
||||
[ -d "$PLAN_DIR" ] || continue
|
||||
PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | xargs grep -l "$BRANCH" 2>/dev/null | head -1)
|
||||
[ -z "$PLAN" ] && PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | xargs grep -l "$REPO" 2>/dev/null | head -1)
|
||||
|
||||
@@ -489,9 +489,10 @@ Compare screenshots and observations across pages for:
|
||||
|
||||
**Project-scoped:**
|
||||
\`\`\`bash
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG"
|
||||
\`\`\`
|
||||
Write to: \`~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md\`
|
||||
Write to: \`<PROJECT_DIR>/{user}-{branch}-design-audit-{datetime}.md\` (\`PROJECT_DIR\` printed above)
|
||||
|
||||
**Baseline:** Write \`design-baseline.json\` for regression mode (temp file then \`mv\`, and a per-run copy \`design-baseline.<runId>.json\` beside it):
|
||||
\`\`\`json
|
||||
@@ -1148,7 +1149,7 @@ Commands:
|
||||
\`generate\` returns \`sessionFile\`; \`iterate\` requires that existing session. \`variants\` returns \`paths\` but creates no session: regenerate with an updated brief instead.` : ''}
|
||||
|
||||
**CRITICAL PATH RULE:** Design artifacts belong in \`$GSTACK_STATE_ROOT/projects/$SLUG/designs/\`.
|
||||
Use \`bin/gstack-paths\`: GSTACK_HOME → plugin storage → ~/.gstack. Keep it even if temporary; never substitute
|
||||
Use \`bin/gstack-paths\` (docs/state-root.md). Keep it even if temporary; never substitute
|
||||
.context/, docs/designs/ or another directory.
|
||||
These are user files, not application source.`;
|
||||
}
|
||||
@@ -1175,7 +1176,7 @@ Generating visual mockups of the proposed design... (say "skip" if you don't nee
|
||||
|
||||
\`\`\`bash
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
_DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/mockup-$(date +%Y%m%d)"
|
||||
mkdir -p "$_DESIGN_DIR"
|
||||
echo "DESIGN_DIR: $_DESIGN_DIR"
|
||||
@@ -1382,7 +1383,8 @@ export function generateTasteProfile(ctx: TemplateContext): string {
|
||||
\`\`\`bash
|
||||
eval "$("${ctx.paths.binDir}/gstack-slug" 2>/dev/null)"
|
||||
[ -n "\${SLUG:-}" ] || { echo "NO_TASTE_PROFILE"; exit 0; }
|
||||
_TASTE_PROFILE=~/.gstack/projects/$SLUG/taste-profile.json
|
||||
eval "$("${ctx.paths.binDir}/gstack-paths")"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
_TASTE_PROFILE="$GSTACK_STATE_ROOT/projects/$SLUG/taste-profile.json"
|
||||
if [ -f "$_TASTE_PROFILE" ]; then
|
||||
# Schema v1: { dimensions: { fonts, colors, layouts, aesthetics }, sessions: [] }
|
||||
# Each dimension has approved[] and rejected[] entries with
|
||||
|
||||
@@ -201,7 +201,8 @@ BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-' | tr -cd 'a-zA-Z0-9.
|
||||
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
||||
_PLAN_SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\\([^/]*/[^/]*\\)\\.git$|\\1|;s|.*[:/]\\([^/]*/[^/]*\\)$|\\1|' | tr '/' '-' | tr -cd 'a-zA-Z0-9._-') || true
|
||||
_PLAN_SLUG="\${_PLAN_SLUG:-$(basename "$PWD" | tr -cd 'a-zA-Z0-9._-')}"
|
||||
for PLAN_DIR in "$HOME/.gstack/projects/$_PLAN_SLUG" "$HOME/.claude/plans" "$HOME/.codex/plans" ".gstack/plans"; do
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
for PLAN_DIR in "$GSTACK_STATE_ROOT/projects/$_PLAN_SLUG" "$HOME/.claude/plans" "$HOME/.codex/plans" ".gstack/plans"; do
|
||||
[ -d "$PLAN_DIR" ] || continue
|
||||
PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | xargs grep -l "$BRANCH" 2>/dev/null | head -1)
|
||||
[ -z "$PLAN" ] && PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | xargs grep -l "$REPO" 2>/dev/null | head -1)
|
||||
|
||||
@@ -114,8 +114,9 @@ inventing duplicate counts. Preserve the Assignment, coaching, approval, and Han
|
||||
Append the helper's actual metrics to the existing analytics log (telemetry is
|
||||
best-effort and must not block approval):
|
||||
\`\`\`bash
|
||||
mkdir -p ~/.gstack/analytics
|
||||
echo '{"skill":"office-hours","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","iterations":ITERATIONS,"issues_found":FOUND,"issues_fixed":FIXED,"remaining":REMAINING,"quality_score":SCORE}' >> ~/.gstack/analytics/spec-review.jsonl 2>/dev/null || true
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
mkdir -p "$GSTACK_STATE_ROOT/analytics"
|
||||
echo '{"skill":"office-hours","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","iterations":ITERATIONS,"issues_found":FOUND,"issues_fixed":FIXED,"remaining":REMAINING,"quality_score":SCORE}' >> "$GSTACK_STATE_ROOT/analytics/spec-review.jsonl" 2>/dev/null || true
|
||||
\`\`\`
|
||||
Use iterations, issues_found, issues_fixed, remaining, and quality_score from the
|
||||
helper. FOUND counts finding observations across rounds; FIXED counts only
|
||||
@@ -209,8 +210,9 @@ if a required save fails, stop before claiming completion.` : `After the loop co
|
||||
|
||||
3. Append metrics:`}
|
||||
\`\`\`bash
|
||||
mkdir -p ~/.gstack/analytics${ceo ? ' || exit 1' : ''}
|
||||
echo '{"skill":"${_ctx.skillName}","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","iterations":ITERATIONS,"issues_found":FOUND,"issues_fixed":FIXED,"remaining":REMAINING,"quality_score":SCORE}' >> ~/.gstack/analytics/spec-review.jsonl${ceo ? ' || exit 1' : ' 2>/dev/null || true'}
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
mkdir -p "$GSTACK_STATE_ROOT/analytics"${ceo ? ' || exit 1' : ''}
|
||||
echo '{"skill":"${_ctx.skillName}","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","iterations":ITERATIONS,"issues_found":FOUND,"issues_fixed":FIXED,"remaining":REMAINING,"quality_score":SCORE}' >> "$GSTACK_STATE_ROOT/analytics/spec-review.jsonl"${ceo ? ' || exit 1' : ' 2>/dev/null || true'}
|
||||
\`\`\`
|
||||
${ceo ? 'ITERATIONS counts actual reviewer launches. FOUND, FIXED and REMAINING count reported issues, reviewer-confirmed fixes and reported unresolved issues. Use actual counts, never estimates.' : 'Replace ITERATIONS, FOUND, FIXED, REMAINING, SCORE with actual values from the review.'}`;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ import { spawn, spawnSync } from 'child_process';
|
||||
import { StringDecoder } from 'node:string_decoder';
|
||||
import { createHash, randomUUID } from 'node:crypto';
|
||||
import { isPaidTestFile } from '../test/helpers/paid-test-set';
|
||||
import { resolveStateRoot } from '../lib/state-root';
|
||||
import {
|
||||
BunTestOutputClassifier,
|
||||
createShardSandbox,
|
||||
@@ -353,6 +354,10 @@ export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }>
|
||||
// pattern hit is a false positive — the point of these files is Windows
|
||||
// coverage, so auto-excluding them defeats the regression tests they carry.
|
||||
const KNOWN_WINDOWS_SAFE: Array<{ file: string; reason: string }> = [
|
||||
{
|
||||
file: 'test/state-root-parity.test.ts',
|
||||
reason: 'runs the bash twin and lib/state-root.ts over an env table with PATH empty; no shebang execution, raw-string comparison is platform-neutral',
|
||||
},
|
||||
{
|
||||
file: 'test/qa-evidence.test.ts',
|
||||
reason: 'invokes the production helper through Bun argv and exercises native Windows job cleanup, private file captures and backpressured receipt output',
|
||||
@@ -1310,7 +1315,7 @@ export function flakeLedgerPath(env: NodeJS.ProcessEnv = process.env): string {
|
||||
const slug = spawnSync('bash', ['-c', '~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null'], { stdio: 'pipe', timeout: 3000 })
|
||||
.stdout?.toString().match(/^SLUG=(.+)$/m)?.[1];
|
||||
if (slug) {
|
||||
const dir = path.join(os.homedir(), '.gstack', 'projects', slug);
|
||||
const dir = path.join(resolveStateRoot(env), 'projects', slug);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
return path.join(dir, 'flake-ledger.jsonl');
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ BRANCH=$(git branch --show-current 2>/dev/null | tr '/' '-' | tr -cd 'a-zA-Z0-9.
|
||||
REPO=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
|
||||
_PLAN_SLUG=$(git remote get-url origin 2>/dev/null | sed 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|;s|.*[:/]\([^/]*/[^/]*\)$|\1|' | tr '/' '-' | tr -cd 'a-zA-Z0-9._-') || true
|
||||
_PLAN_SLUG="${_PLAN_SLUG:-$(basename "$PWD" | tr -cd 'a-zA-Z0-9._-')}"
|
||||
for PLAN_DIR in "$HOME/.gstack/projects/$_PLAN_SLUG" "$HOME/.claude/plans" "$HOME/.codex/plans" ".gstack/plans"; do
|
||||
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
for PLAN_DIR in "$GSTACK_STATE_ROOT/projects/$_PLAN_SLUG" "$HOME/.claude/plans" "$HOME/.codex/plans" ".gstack/plans"; do
|
||||
[ -d "$PLAN_DIR" ] || continue
|
||||
PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | xargs grep -l "$BRANCH" 2>/dev/null | head -1)
|
||||
[ -z "$PLAN" ] && PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | xargs grep -l "$REPO" 2>/dev/null | head -1)
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
{
|
||||
"recordedAt": "96764e80",
|
||||
"note": "paid evals selected by touching each W1 source file, recorded on the base commit before the change",
|
||||
"modules": {
|
||||
"lib/state-root.ts": {
|
||||
"sources": [
|
||||
"browse/src/config.ts",
|
||||
"lib/cso/state.ts"
|
||||
],
|
||||
"e2e": [
|
||||
"aside-browse-basic",
|
||||
"aside-browse-flow",
|
||||
"benchmark-workflow",
|
||||
"browse-basic",
|
||||
"browse-snapshot",
|
||||
"canary-workflow",
|
||||
"carve-section-loading",
|
||||
"cso-diff-mode",
|
||||
"cso-full-audit",
|
||||
"cso-infra-scope",
|
||||
"design-review-detector-shim-dom",
|
||||
"design-review-fix",
|
||||
"diagram-authoring-quality",
|
||||
"diagram-triplet",
|
||||
"qa-b6-static",
|
||||
"qa-b7-spa",
|
||||
"qa-b8-checkout",
|
||||
"qa-fix-loop",
|
||||
"qa-only-no-fix",
|
||||
"qa-quick"
|
||||
],
|
||||
"llmJudge": [
|
||||
"browse/SKILL.md reference"
|
||||
]
|
||||
},
|
||||
"bin/gstack-state-root.sh": {
|
||||
"sources": [
|
||||
"bin/gstack-paths",
|
||||
"careful/bin/hook-extract.sh"
|
||||
],
|
||||
"e2e": [
|
||||
"investigate-owned-abort",
|
||||
"investigate-owned-completion",
|
||||
"investigate-owned-ending-error",
|
||||
"plan-design-with-ui-scope"
|
||||
],
|
||||
"llmJudge": []
|
||||
},
|
||||
"hosts/claude/hooks/hook-log.ts": {
|
||||
"sources": [
|
||||
"hosts/claude/hooks/question-log-hook.ts",
|
||||
"hosts/claude/hooks/question-preference-hook.ts",
|
||||
"hosts/claude/hooks/auq-error-fallback-hook.ts",
|
||||
"hosts/claude/hooks/timeline-stop-hook.ts",
|
||||
"hosts/claude/hooks/memorable-user-prompt-hook.ts"
|
||||
],
|
||||
"e2e": [
|
||||
"auto-decide-preserved",
|
||||
"docsync-spawned"
|
||||
],
|
||||
"llmJudge": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"workstream": "W1 state-root owners",
|
||||
"recordedAt": "96764e80",
|
||||
"sources": {
|
||||
"browse/src/config.ts": {
|
||||
"e2e": [
|
||||
"aside-browse-basic",
|
||||
"aside-browse-flow",
|
||||
"benchmark-workflow",
|
||||
"browse-basic",
|
||||
"browse-snapshot",
|
||||
"canary-workflow",
|
||||
"carve-section-loading",
|
||||
"design-review-detector-shim-dom",
|
||||
"design-review-fix",
|
||||
"diagram-authoring-quality",
|
||||
"diagram-triplet",
|
||||
"qa-b6-static",
|
||||
"qa-b7-spa",
|
||||
"qa-b8-checkout",
|
||||
"qa-fix-loop",
|
||||
"qa-only-no-fix",
|
||||
"qa-quick"
|
||||
],
|
||||
"llmJudge": [
|
||||
"browse/SKILL.md reference"
|
||||
]
|
||||
},
|
||||
"lib/cso/state.ts": {
|
||||
"e2e": [
|
||||
"cso-diff-mode",
|
||||
"cso-full-audit",
|
||||
"cso-infra-scope"
|
||||
],
|
||||
"llmJudge": []
|
||||
},
|
||||
"bin/gstack-paths": {
|
||||
"e2e": [
|
||||
"plan-design-with-ui-scope"
|
||||
],
|
||||
"llmJudge": []
|
||||
},
|
||||
"careful/bin/hook-extract.sh": {
|
||||
"e2e": [
|
||||
"investigate-owned-abort",
|
||||
"investigate-owned-completion",
|
||||
"investigate-owned-ending-error"
|
||||
],
|
||||
"llmJudge": []
|
||||
},
|
||||
"hosts/claude/hooks/question-log-hook.ts": {
|
||||
"e2e": [],
|
||||
"llmJudge": []
|
||||
},
|
||||
"hosts/claude/hooks/question-preference-hook.ts": {
|
||||
"e2e": [
|
||||
"auto-decide-preserved",
|
||||
"docsync-spawned"
|
||||
],
|
||||
"llmJudge": []
|
||||
},
|
||||
"hosts/claude/hooks/auq-error-fallback-hook.ts": {
|
||||
"e2e": [
|
||||
"docsync-spawned"
|
||||
],
|
||||
"llmJudge": []
|
||||
},
|
||||
"hosts/claude/hooks/timeline-stop-hook.ts": {
|
||||
"e2e": [],
|
||||
"llmJudge": []
|
||||
},
|
||||
"hosts/claude/hooks/memorable-user-prompt-hook.ts": {
|
||||
"e2e": [],
|
||||
"llmJudge": []
|
||||
}
|
||||
},
|
||||
"modules": {
|
||||
"lib/state-root.ts": [
|
||||
"browse/src/config.ts",
|
||||
"lib/cso/state.ts"
|
||||
],
|
||||
"bin/gstack-state-root.sh": [
|
||||
"bin/gstack-paths",
|
||||
"careful/bin/hook-extract.sh"
|
||||
],
|
||||
"hosts/claude/hooks/hook-log.ts": [
|
||||
"hosts/claude/hooks/question-log-hook.ts",
|
||||
"hosts/claude/hooks/question-preference-hook.ts",
|
||||
"hosts/claude/hooks/auq-error-fallback-hook.ts",
|
||||
"hosts/claude/hooks/timeline-stop-hook.ts",
|
||||
"hosts/claude/hooks/memorable-user-prompt-hook.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,5 @@
|
||||
{ "path": "bin/gstack-state-root.sh", "match": "*", "reason": "the bash owner of the chain" },
|
||||
{ "path": "careful/bin/check-careful.sh", "match": "_GSTACK_HOME_DIR=\"${GSTACK_HOME:-$HOME/.gstack}\"", "reason": "partial-upgrade fallback, reached only when an older hook-extract.sh lacks gstack_hook_state_root (legacy order kept on purpose)" },
|
||||
{ "path": "gstack-upgrade/SKILL.md.tmpl", "match": "elif [ -d \"$HOME/.gstack/repos/gstack/.git\" ]; then", "reason": "the relocated gstack checkout is an install location, not state" },
|
||||
{ "path": "gstack-upgrade/SKILL.md.tmpl", "match": "INSTALL_DIR=\"$HOME/.gstack/repos/gstack\"", "reason": "the relocated gstack checkout is an install location, not state" },
|
||||
{ "path": "scripts/resolvers/review.ts", "match": "*", "reason": "PENDING CROSS-OWNER (W5): delete this entry once the W1 handoff's review.ts / new-module edits are applied" },
|
||||
{ "path": "scripts/resolvers/design.ts", "match": "*", "reason": "PENDING CROSS-OWNER (W5): delete this entry once the W1 handoff's design.ts edits are applied" },
|
||||
{ "path": "scripts/test-free-shards.ts", "match": "*", "reason": "PENDING CROSS-OWNER (W2): delete this entry once the W1 handoff's test-free-shards.ts edit is applied" }
|
||||
{ "path": "gstack-upgrade/SKILL.md.tmpl", "match": "INSTALL_DIR=\"$HOME/.gstack/repos/gstack\"", "reason": "the relocated gstack checkout is an install location, not state" }
|
||||
]
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Touchfile coverage for W1's new modules: touching lib/state-root.ts,
|
||||
* bin/gstack-state-root.sh or hosts/claude/hooks/hook-log.ts must select at
|
||||
* least the paid evals their source files selected on the base commit
|
||||
* (test/fixtures/state-root-selection-golden.json, recorded before the move).
|
||||
*/
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { E2E_TOUCHFILES, LLM_JUDGE_TOUCHFILES, GLOBAL_TOUCHFILES, selectTests } from './helpers/touchfiles';
|
||||
import { resolveStateRoot } from '../lib/state-root';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const GOLDEN = 'test/fixtures/state-root-selection-golden.json';
|
||||
const golden: { modules: Record<string, { sources: string[]; e2e: string[]; llmJudge: string[] }> } =
|
||||
JSON.parse(fs.readFileSync(path.join(ROOT, GOLDEN), 'utf-8'));
|
||||
|
||||
describe('W1 touchfile superset', () => {
|
||||
test('the golden covers every new state-root module', () => {
|
||||
expect(Object.keys(golden.modules).sort()).toEqual(['bin/gstack-state-root.sh', 'hosts/claude/hooks/hook-log.ts', 'lib/state-root.ts']);
|
||||
expect(typeof resolveStateRoot).toBe('function');
|
||||
});
|
||||
|
||||
for (const [mod, rec] of Object.entries(golden.modules)) {
|
||||
test(`touching ${mod} selects a superset of ${rec.sources.join(', ')}`, () => {
|
||||
const e2e = new Set(selectTests([mod], E2E_TOUCHFILES, GLOBAL_TOUCHFILES).selected);
|
||||
const llm = new Set(selectTests([mod], LLM_JUDGE_TOUCHFILES, GLOBAL_TOUCHFILES).selected);
|
||||
const missing = [...rec.e2e.filter((t) => !e2e.has(t)), ...rec.llmJudge.filter((t) => !llm.has(t))];
|
||||
expect(missing, [
|
||||
`${mod} selects fewer paid evals than its source files did: ${missing.join(', ')}`,
|
||||
`Fix: add ${mod} to those entries in test/helpers/touchfiles-data.ts (golden: ${GOLDEN}).`,
|
||||
].join('\n')).toEqual([]);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user