diff --git a/.github/workflows/sdd-gates.yml b/.github/workflows/sdd-gates.yml index 13dcca3..8cce534 100644 --- a/.github/workflows/sdd-gates.yml +++ b/.github/workflows/sdd-gates.yml @@ -196,6 +196,38 @@ jobs: grep -q '' openspec/project.md grep -q '' openspec/project.md + # C2 upgrade smoke test. install.sh had a greenfield gate and a consumer gate; + # upgrade.sh had neither, which is why a `realpath` call that aborts on any + # destination whose parent does not exist yet survived in it long after the same + # defect was fixed in install.sh. Regressing one directory is enough to reproduce + # the real case: every pre-1.15.0 install lacks openspec/changes/_template/specs/. + - name: Upgrade smoke test (blocking) + timeout-minutes: 4 + run: | + set -euo pipefail + VER=$(grep -E '^version:' sdd-kit/MANIFEST.yaml | head -1 | sed 's/.*"\(.*\)".*/\1/') + U="${{ runner.temp }}/upgrade" + rm -rf "$U"; mkdir -p "$U" + cp -R sdd-kit "$U/" + cd "$U" + git init -q . + git -c user.email=ci@example.com -c user.name=CI commit -q --allow-empty -m "initial commit" + git checkout -q -b work # --apply refuses the default branch without --force + bash sdd-kit/install.sh --profile APP --chat-lang en --docs-lang en --code-lang en >/dev/null + + # Regress to the pre-1.15.0 shape: this directory is what --apply must recreate. + rm -rf openspec/changes/_template/specs + + bash sdd-kit/upgrade.sh --from 1.0.0 --to "$VER" --profile APP --dry-run >/dev/null + RPT="openspec/changes/upgrade-sdd-v${VER}/UPGRADE_REPORT.md" + sed 's/- \[ \] Upgrade approved/- [x] Upgrade approved/' "$RPT" > "$RPT.tmp" + mv "$RPT.tmp" "$RPT" + bash sdd-kit/upgrade.sh --from 1.0.0 --to "$VER" --profile APP --apply + + # Exit code alone is not enough — assert the path that used to abort. + test -f openspec/changes/_template/specs/example-capability/spec.md + echo "OK: upgrade recreated a destination whose parent did not exist" + # D4 — report-only: verify.sh orquestra verify-infra.sh, que marca FAIL # para CLIs de conhecimento (GitNexus/Graphify) ausentes no runner e muta # openspec/infra.md. O resultado é reportado sem bloquear o merge; a diff --git a/README.md b/README.md index 1622b52..b29c252 100644 --- a/README.md +++ b/README.md @@ -141,21 +141,6 @@ We **compose** OpenSpec; we don't replace it. | [`sdd-kit/README.md`](./sdd-kit/README.md) | EN | Kit scenarios & commands | | [`doc/avaliacoes/2026-07-26-sdd-discovery-positioning.md`](./doc/avaliacoes/2026-07-26-sdd-discovery-positioning.md) | EN | Market / SEO / backlog decisions | -## [MANUAL ACTION REQUIRED] — GitHub rename, About & topics - -Settings are not in git. After merge, the operator should: - -1. **Rename repo** (Settings → General → Repository name): `gitnexus-graphify-openspec` → **`byebyevibe`** -2. Update local remotes: `git remote set-url origin git@github.com:pvilarim/byebyevibe.git` (or HTTPS) -3. Set **About** and **Topics** (below) -4. Optional **Homepage**: `https://pedrocodeart.netlify.app/` - -**About:** - -> ByeByeVibe — Spec-Driven Development (SDD) install kit from vibe coding to shippable AI engineering. Control plane (OpenSpec + graphs + gates) for Cursor & Claude Code. Not a Next.js starter. - -**Topics:** `vibe-coding` · `spec-driven-development` · `context-engineering` · `agentic-coding` · `claude-code` · `cursor` - ## Maintainer Pedro Vilarim — [LinkedIn](https://www.linkedin.com/in/pedrovilarim/) · [Portfolio](https://pedrocodeart.netlify.app/) diff --git a/doc/byebyevibe-guide.md b/doc/byebyevibe-guide.md index 5136df6..b8829f0 100644 --- a/doc/byebyevibe-guide.md +++ b/doc/byebyevibe-guide.md @@ -2,7 +2,7 @@ **GitNexus + Graphify + OpenSpec, integrated in Cursor and VS Code + Claude Code** -> **Canonical install guide (v1.15.0)** — use in any Git repository, manually or via an AI agent. Payloads in `sdd-kit/`; procedure in this document. +> **Canonical install guide (v1.15.1)** — use in any Git repository, manually or via an AI agent. Payloads in `sdd-kit/`; procedure in this document. ## How to use this document @@ -19,7 +19,7 @@ | **SDD metrics (G4)** | `bash scripts/sdd-metrics.sh` — §2.17 (mode C; no DevLake) | - **`AGENTS.md` pattern:** aligned with [agents.md](https://agents.md/) + TLC workshop (Context Engineering, on-demand loading). -- **Guide version:** 1.15.0 — see [Guide changelog](#guide-changelog). +- **Guide version:** 1.15.1 — see [Guide changelog](#guide-changelog). - **Versioned payload:** `sdd-kit/MANIFEST.yaml` — see §1.6 and `sdd-kit/README.md`. - **Does not replace** `openspec/project.md` (project constitution) or specs in `openspec/specs/`. @@ -3107,6 +3107,16 @@ bash scripts/verify-task-patterns.sh # Pattern: paths exist; DOCS_SPECS withou ## Guide changelog +### 1.15.1 (2026-08-14) + +**Change `fix-upgrade-realpath`** — repairs `sdd-kit/upgrade.sh`, which could not deliver the 1.15.0 payload at all. The C2 upgrade path had no gate of its own: `install.sh` gained a greenfield smoke test in 1.14.0 and a consumer smoke test in 1.15.0, while `upgrade.sh` kept a `realpath` call whose equivalent defect had already been fixed in `install.sh` one release earlier. + +- **`--apply` no longer aborts on a destination whose parent does not exist** — the traversal guard used plain `realpath --no-symlinks`, which requires every leading path component to already exist. Any MANIFEST entry creating a new directory killed the run mid-apply, leaving the repository half-upgraded. This is **not** macOS-specific: it reproduces on GNU coreutils. Concretely, upgrading any pre-1.15.0 install to 1.15.0 aborted on `openspec/changes/_template/specs/`, a directory none of them have. The call now uses `-m` with the same `posixpath.normpath` fallback `install.sh` has carried since 1.14.0, probed once. `..` is still resolved and traversal is still blocked. +- **BSD/macOS realpath covered by the same change** — `--no-symlinks` is a GNU long option that macOS realpath does not accept at all, so one fix repairs both failures. This was recorded as out of scope in 1.15.0 and pulled forward, because the upgrade tool is exactly what the existing installed base needs. +- **New blocking gate: C2 upgrade smoke test** — `sdd-gates` now installs, regresses one directory to the pre-1.15.0 shape, and runs a full dry-run to approve to `--apply` cycle, asserting the exact path that used to abort. The defect survived precisely because no gate ever exercised `upgrade.sh`. + +**Housekeeping** — the stale `[MANUAL ACTION REQUIRED]` block is removed from `README.md`: the repository rename it asked for was completed long ago. + ### 1.15.0 (2026-08-13) **Change `fix-consumer-install`** — repairs the consumer install path end to end. One root cause under all seven defects: **the hub validated itself.** Every hub/consumer branch was a directory-shaped guess, so a consumer holding a full copy of `sdd-kit/` read as the hub and skipped the checks written for it; and every "cannot run" state reported success instead of failing. A 1.14.0 install completed, printed `Done.`, and left the operator without a guide, without a constitution, and with a validation that could not pass. diff --git a/sdd-kit/MANIFEST.yaml b/sdd-kit/MANIFEST.yaml index 8d80097..0841ac8 100644 --- a/sdd-kit/MANIFEST.yaml +++ b/sdd-kit/MANIFEST.yaml @@ -11,8 +11,8 @@ # The `gate:` field is documentary metadata only — the suggested verification # command for manual or CI use. It MUST NOT be executed via `eval` or any # equivalent by install.sh, upgrade.sh, or any other script. -version: "1.15.0" -guide_version: "1.15.0" +version: "1.15.1" +guide_version: "1.15.1" min_openspec: "1.3.1" profiles: [APP, DOCS_SPECS, HYBRID] @@ -110,7 +110,7 @@ files: - path: .github/workflows/sdd-gates.yml source: templates/.github/workflows/sdd-gates.yml - sha256: "7326bf67327d42aed6407a93b776dd9bb0b239736b86cf4d78adc114fedc93bb" + sha256: "58f8516995c07457a51abacd1c54456afc699c62657da5638cf562858402dcc7" merge: COPY profiles: [APP, DOCS_SPECS, HYBRID] gate: "test -f .github/workflows/sdd-gates.yml" @@ -303,7 +303,7 @@ files: # openspec/ specs and development history — an operator guide is neither. - path: doc/byebyevibe-guide.md source: templates/doc/byebyevibe-guide.md - sha256: "28a9dfa3a9cf6fcdf5b0570bd0df7163e85d917967aa7ff54c12eacfc90d1ea2" + sha256: "6044ae92297c3b4cd7ed597a19398fbb2f27b29b36ede2746885271b6f6de25a" merge: COPY profiles: [APP, DOCS_SPECS, HYBRID] gate: "test -s doc/byebyevibe-guide.md && grep -q 'Guide version' doc/byebyevibe-guide.md" diff --git a/sdd-kit/README.md b/sdd-kit/README.md index 3767f6c..8541708 100644 --- a/sdd-kit/README.md +++ b/sdd-kit/README.md @@ -1,4 +1,4 @@ -# ByeByeVibe — sdd-kit v1.15.0 +# ByeByeVibe — sdd-kit v1.15.1 **ByeByeVibe** is the public name of this project. This folder (`sdd-kit/`) is the versioned **install payload** — commands and paths stay `sdd-kit/*`. diff --git a/sdd-kit/templates/.github/workflows/sdd-gates.yml b/sdd-kit/templates/.github/workflows/sdd-gates.yml index 13dcca3..8cce534 100644 --- a/sdd-kit/templates/.github/workflows/sdd-gates.yml +++ b/sdd-kit/templates/.github/workflows/sdd-gates.yml @@ -196,6 +196,38 @@ jobs: grep -q '' openspec/project.md grep -q '' openspec/project.md + # C2 upgrade smoke test. install.sh had a greenfield gate and a consumer gate; + # upgrade.sh had neither, which is why a `realpath` call that aborts on any + # destination whose parent does not exist yet survived in it long after the same + # defect was fixed in install.sh. Regressing one directory is enough to reproduce + # the real case: every pre-1.15.0 install lacks openspec/changes/_template/specs/. + - name: Upgrade smoke test (blocking) + timeout-minutes: 4 + run: | + set -euo pipefail + VER=$(grep -E '^version:' sdd-kit/MANIFEST.yaml | head -1 | sed 's/.*"\(.*\)".*/\1/') + U="${{ runner.temp }}/upgrade" + rm -rf "$U"; mkdir -p "$U" + cp -R sdd-kit "$U/" + cd "$U" + git init -q . + git -c user.email=ci@example.com -c user.name=CI commit -q --allow-empty -m "initial commit" + git checkout -q -b work # --apply refuses the default branch without --force + bash sdd-kit/install.sh --profile APP --chat-lang en --docs-lang en --code-lang en >/dev/null + + # Regress to the pre-1.15.0 shape: this directory is what --apply must recreate. + rm -rf openspec/changes/_template/specs + + bash sdd-kit/upgrade.sh --from 1.0.0 --to "$VER" --profile APP --dry-run >/dev/null + RPT="openspec/changes/upgrade-sdd-v${VER}/UPGRADE_REPORT.md" + sed 's/- \[ \] Upgrade approved/- [x] Upgrade approved/' "$RPT" > "$RPT.tmp" + mv "$RPT.tmp" "$RPT" + bash sdd-kit/upgrade.sh --from 1.0.0 --to "$VER" --profile APP --apply + + # Exit code alone is not enough — assert the path that used to abort. + test -f openspec/changes/_template/specs/example-capability/spec.md + echo "OK: upgrade recreated a destination whose parent did not exist" + # D4 — report-only: verify.sh orquestra verify-infra.sh, que marca FAIL # para CLIs de conhecimento (GitNexus/Graphify) ausentes no runner e muta # openspec/infra.md. O resultado é reportado sem bloquear o merge; a diff --git a/sdd-kit/templates/doc/byebyevibe-guide.md b/sdd-kit/templates/doc/byebyevibe-guide.md index 5136df6..b8829f0 100644 --- a/sdd-kit/templates/doc/byebyevibe-guide.md +++ b/sdd-kit/templates/doc/byebyevibe-guide.md @@ -2,7 +2,7 @@ **GitNexus + Graphify + OpenSpec, integrated in Cursor and VS Code + Claude Code** -> **Canonical install guide (v1.15.0)** — use in any Git repository, manually or via an AI agent. Payloads in `sdd-kit/`; procedure in this document. +> **Canonical install guide (v1.15.1)** — use in any Git repository, manually or via an AI agent. Payloads in `sdd-kit/`; procedure in this document. ## How to use this document @@ -19,7 +19,7 @@ | **SDD metrics (G4)** | `bash scripts/sdd-metrics.sh` — §2.17 (mode C; no DevLake) | - **`AGENTS.md` pattern:** aligned with [agents.md](https://agents.md/) + TLC workshop (Context Engineering, on-demand loading). -- **Guide version:** 1.15.0 — see [Guide changelog](#guide-changelog). +- **Guide version:** 1.15.1 — see [Guide changelog](#guide-changelog). - **Versioned payload:** `sdd-kit/MANIFEST.yaml` — see §1.6 and `sdd-kit/README.md`. - **Does not replace** `openspec/project.md` (project constitution) or specs in `openspec/specs/`. @@ -3107,6 +3107,16 @@ bash scripts/verify-task-patterns.sh # Pattern: paths exist; DOCS_SPECS withou ## Guide changelog +### 1.15.1 (2026-08-14) + +**Change `fix-upgrade-realpath`** — repairs `sdd-kit/upgrade.sh`, which could not deliver the 1.15.0 payload at all. The C2 upgrade path had no gate of its own: `install.sh` gained a greenfield smoke test in 1.14.0 and a consumer smoke test in 1.15.0, while `upgrade.sh` kept a `realpath` call whose equivalent defect had already been fixed in `install.sh` one release earlier. + +- **`--apply` no longer aborts on a destination whose parent does not exist** — the traversal guard used plain `realpath --no-symlinks`, which requires every leading path component to already exist. Any MANIFEST entry creating a new directory killed the run mid-apply, leaving the repository half-upgraded. This is **not** macOS-specific: it reproduces on GNU coreutils. Concretely, upgrading any pre-1.15.0 install to 1.15.0 aborted on `openspec/changes/_template/specs/`, a directory none of them have. The call now uses `-m` with the same `posixpath.normpath` fallback `install.sh` has carried since 1.14.0, probed once. `..` is still resolved and traversal is still blocked. +- **BSD/macOS realpath covered by the same change** — `--no-symlinks` is a GNU long option that macOS realpath does not accept at all, so one fix repairs both failures. This was recorded as out of scope in 1.15.0 and pulled forward, because the upgrade tool is exactly what the existing installed base needs. +- **New blocking gate: C2 upgrade smoke test** — `sdd-gates` now installs, regresses one directory to the pre-1.15.0 shape, and runs a full dry-run to approve to `--apply` cycle, asserting the exact path that used to abort. The defect survived precisely because no gate ever exercised `upgrade.sh`. + +**Housekeeping** — the stale `[MANUAL ACTION REQUIRED]` block is removed from `README.md`: the repository rename it asked for was completed long ago. + ### 1.15.0 (2026-08-13) **Change `fix-consumer-install`** — repairs the consumer install path end to end. One root cause under all seven defects: **the hub validated itself.** Every hub/consumer branch was a directory-shaped guess, so a consumer holding a full copy of `sdd-kit/` read as the hub and skipped the checks written for it; and every "cannot run" state reported success instead of failing. A 1.14.0 install completed, printed `Done.`, and left the operator without a guide, without a constitution, and with a validation that could not pass. diff --git a/sdd-kit/upgrade.sh b/sdd-kit/upgrade.sh index 8a362ad..6c07c6f 100755 --- a/sdd-kit/upgrade.sh +++ b/sdd-kit/upgrade.sh @@ -26,6 +26,14 @@ _sha256() { echo "" fi } +# Probe GNU `realpath -m` once; the macOS fallback lives at the call site (design D6, +# mirroring install.sh). BSD/macOS realpath has neither -m nor --no-symlinks. +if realpath -m --no-symlinks / >/dev/null 2>&1; then + REALPATH_M=true +else + REALPATH_M=false +fi + FROM_VER="" TO_VER="" DRY_RUN=false @@ -303,7 +311,19 @@ PY [[ -n "$dest" ]] || continue [[ "$merge" == "COPY" ]] || continue [[ -f "$KIT_DIR/$src" ]] || continue - dest_path="$(realpath --no-symlinks "$REPO_ROOT/$dest")" + # -m: canonicalise a destination whose parent does not exist YET. Plain `realpath` + # requires every leading component to exist, so --apply aborted on any entry that + # creates a new directory — e.g. openspec/changes/_template/specs/ when upgrading a + # pre-1.15.0 install, which made the upgrade to 1.15.0 impossible. `--no-symlinks` + # is additionally a GNU long option that BSD/macOS realpath does not have at all, + # so the fallback covers both failures. `..` is still resolved, so the prefix check + # below keeps catching escapes. Same form as install.sh (design D6). + if $REALPATH_M; then + dest_path="$(realpath -m --no-symlinks "$REPO_ROOT/$dest")" + else + # $SDD_PYTHON unquoted by convention: "py -3" is two words + dest_path="$($SDD_PYTHON -c 'import posixpath,sys; print(posixpath.normpath(sys.argv[1]))' "$REPO_ROOT/$dest")" + fi [[ "$dest_path" == "$REPO_ROOT"/* ]] || { echo "ERROR: path traversal blocked: $dest" >&2 exit 1