fix(skills): land-and-deploy squash readback, next-version paths, embed-flags quoting

Three template one-liners: land-and-deploy reads the squash-merge result
from the merge commit instead of the stale branch tip; review/landing-report
/land-and-deploy templates call bin/gstack-next-version via its installed
path instead of a bare repo-relative one; setup-gbrain quotes
GBRAIN_EMBED_FLAGS so zsh word-splitting stops silently dropping
voyage-code-3 flags. Regenerated output included.

Contributed by @stormeoio (PR #2011), @rjmurillo (PR #1820) and
@trevorhstandridge (PR #1817).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan 2026-08-14 20:20:35 -07:00
parent 0295b74b24
commit 55c75d69f8
No known key found for this signature in database
GPG Key ID: C1F69E85C74EFE1D
9 changed files with 206 additions and 98 deletions

View File

@ -1229,7 +1229,7 @@ BASE_VERSION=$(git show origin/$BASE_BRANCH:VERSION 2>/dev/null | tr -d '\r\n[:s
# We don't need the exact original level — we just need "a level" that passes to the util.
# If the minor digit advanced, call it minor; patch digit, patch; etc. If base > branch, skip (not ours to land).
# For simplicity: use "patch" as a conservative default; util handles collision-past regardless of input level.
QUEUE_JSON=$(bun run bin/gstack-next-version \
QUEUE_JSON=$(bun run ~/.claude/skills/gstack/bin/gstack-next-version \
--base "$BASE_BRANCH" \
--bump patch \
--current-version "$BASE_VERSION" 2>/dev/null || echo '{"offline":true}')
@ -1531,6 +1531,18 @@ Capture merge SHA:
gh pr view --json mergeCommit -q .mergeCommit.oid
```
Squash/rebase merge readback guard:
- Do **not** prove success by requiring the PR head SHA to be an ancestor of the base branch. GitHub squash and rebase merges deliberately create a new commit, so `git merge-base --is-ancestor <head_sha> origin/<base>` can fail even when the PR is merged.
- Once GitHub reports `state == "MERGED"` with a non-null `mergeCommit.oid`, treat that as authoritative. Record the merge SHA and continue.
- If local cleanup or readback is needed, fetch the base branch and compare/sync against the merge commit, not the old PR branch commit:
```bash
BASE=$(gh pr view --json baseRefName -q .baseRefName)
MERGE_SHA=$(gh pr view --json mergeCommit -q .mergeCommit.oid)
git fetch origin "$BASE"
git diff --quiet "$MERGE_SHA" origin/"$BASE" || git log --oneline --decorate -1 "$MERGE_SHA" origin/"$BASE"
```
- If the worktree is clean and only needs to stop looking diverged after a squash merge, prefer a named local branch at the merge commit, for example `git switch -c "codex/post-merge-pr-$PR_NUMBER" "$MERGE_SHA"`. Avoid detached HEAD in Codex Desktop worktrees because git action workers often expect `git symbolic-ref --short HEAD` to return a branch. Do not force-push or reset a user's branch unless they explicitly ask.
Worktree cleanup — non-destructive, candidate-based:
```bash
git worktree list --porcelain

View File

@ -341,7 +341,7 @@ BASE_VERSION=$(git show origin/$BASE_BRANCH:VERSION 2>/dev/null | tr -d '\r\n[:s
# We don't need the exact original level — we just need "a level" that passes to the util.
# If the minor digit advanced, call it minor; patch digit, patch; etc. If base > branch, skip (not ours to land).
# For simplicity: use "patch" as a conservative default; util handles collision-past regardless of input level.
QUEUE_JSON=$(bun run bin/gstack-next-version \
QUEUE_JSON=$(bun run ~/.claude/skills/gstack/bin/gstack-next-version \
--base "$BASE_BRANCH" \
--bump patch \
--current-version "$BASE_VERSION" 2>/dev/null || echo '{"offline":true}')
@ -643,6 +643,18 @@ Capture merge SHA:
gh pr view --json mergeCommit -q .mergeCommit.oid
```
Squash/rebase merge readback guard:
- Do **not** prove success by requiring the PR head SHA to be an ancestor of the base branch. GitHub squash and rebase merges deliberately create a new commit, so `git merge-base --is-ancestor <head_sha> origin/<base>` can fail even when the PR is merged.
- Once GitHub reports `state == "MERGED"` with a non-null `mergeCommit.oid`, treat that as authoritative. Record the merge SHA and continue.
- If local cleanup or readback is needed, fetch the base branch and compare/sync against the merge commit, not the old PR branch commit:
```bash
BASE=$(gh pr view --json baseRefName -q .baseRefName)
MERGE_SHA=$(gh pr view --json mergeCommit -q .mergeCommit.oid)
git fetch origin "$BASE"
git diff --quiet "$MERGE_SHA" origin/"$BASE" || git log --oneline --decorate -1 "$MERGE_SHA" origin/"$BASE"
```
- If the worktree is clean and only needs to stop looking diverged after a squash merge, prefer a named local branch at the merge commit, for example `git switch -c "codex/post-merge-pr-$PR_NUMBER" "$MERGE_SHA"`. Avoid detached HEAD in Codex Desktop worktrees because git action workers often expect `git symbolic-ref --short HEAD` to return a branch. Do not force-push or reset a user's branch unless they explicitly ask.
Worktree cleanup — non-destructive, candidate-based:
```bash
git worktree list --porcelain

View File

@ -848,7 +848,7 @@ they'd claim for micro/patch/minor/major. Cheap (same gh call cached by bun).
```bash
for LEVEL in micro patch minor major; do
bun run bin/gstack-next-version \
bun run ~/.claude/skills/gstack/bin/gstack-next-version \
--base "$BASE_BRANCH" \
--bump "$LEVEL" \
--current-version "$BASE_VERSION" \

View File

@ -67,7 +67,7 @@ they'd claim for micro/patch/minor/major. Cheap (same gh call cached by bun).
```bash
for LEVEL in micro patch minor major; do
bun run bin/gstack-next-version \
bun run ~/.claude/skills/gstack/bin/gstack-next-version \
--base "$BASE_BRANCH" \
--bump "$LEVEL" \
--current-version "$BASE_VERSION" \

View File

@ -1141,7 +1141,7 @@ Check whether this PR's claimed VERSION still points at a free slot in the queue
BRANCH_VERSION=$(git show HEAD:VERSION 2>/dev/null | tr -d '\r\n[:space:]' || echo "")
BASE_BRANCH=$(gh pr view --json baseRefName -q .baseRefName 2>/dev/null || echo main)
BASE_VERSION=$(git show origin/$BASE_BRANCH:VERSION 2>/dev/null | tr -d '\r\n[:space:]' || echo "")
QUEUE_JSON=$(bun run bin/gstack-next-version \
QUEUE_JSON=$(bun run ~/.claude/skills/gstack/bin/gstack-next-version \
--base "$BASE_BRANCH" \
--bump patch \
--current-version "$BASE_VERSION" 2>/dev/null || echo '{"offline":true}')

View File

@ -89,7 +89,7 @@ Check whether this PR's claimed VERSION still points at a free slot in the queue
BRANCH_VERSION=$(git show HEAD:VERSION 2>/dev/null | tr -d '\r\n[:space:]' || echo "")
BASE_BRANCH=$(gh pr view --json baseRefName -q .baseRefName 2>/dev/null || echo main)
BASE_VERSION=$(git show origin/$BASE_BRANCH:VERSION 2>/dev/null | tr -d '\r\n[:space:]' || echo "")
QUEUE_JSON=$(bun run bin/gstack-next-version \
QUEUE_JSON=$(bun run ~/.claude/skills/gstack/bin/gstack-next-version \
--base "$BASE_BRANCH" \
--bump patch \
--current-version "$BASE_VERSION" 2>/dev/null || echo '{"offline":true}')

View File

@ -896,16 +896,22 @@ mv "$HOME/.gbrain/config.json" "$BACKUP"
# gstack default: voyage-code-3 (1024d) when VOYAGE_API_KEY is set — best for
# code retrieval. Without the key, fall back to gbrain's own auto-selected
# embedding provider chain (OpenAI 1536d when OPENAI_API_KEY is present, etc.).
GBRAIN_EMBED_FLAGS=""
if [ -n "${VOYAGE_API_KEY:-}" ]; then
GBRAIN_EMBED_FLAGS="--embedding-model voyage:voyage-code-3 --embedding-dimensions 1024"
fi
if ! gbrain init --pglite --json $GBRAIN_EMBED_FLAGS; then
# Restore on failure
mv "$BACKUP" "$HOME/.gbrain/config.json"
echo "gbrain init failed. Your previous config was restored at $HOME/.gbrain/config.json." >&2
echo "PGLite directory at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` if needed before retrying." >&2
exit 1
if ! gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024; then
# Restore on failure
mv "$BACKUP" "$HOME/.gbrain/config.json"
echo "gbrain init failed. Your previous config was restored at $HOME/.gbrain/config.json." >&2
echo "PGLite directory at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` if needed before retrying." >&2
exit 1
fi
else
if ! gbrain init --pglite --json; then
# Restore on failure
mv "$BACKUP" "$HOME/.gbrain/config.json"
echo "gbrain init failed. Your previous config was restored at $HOME/.gbrain/config.json." >&2
echo "PGLite directory at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` if needed before retrying." >&2
exit 1
fi
fi
echo "Switched to local PGLite. Previous config saved at $BACKUP — review before deleting."
```
@ -1110,11 +1116,11 @@ Then follow the same secret-read + verify + init flow as Path 1.
# gstack default: voyage-code-3 (1024d) when VOYAGE_API_KEY is set — code
# retrieval beats general-purpose embeddings on real code queries (validated
# A/B). Without the key, gbrain auto-selects (OpenAI 1536d when available).
GBRAIN_EMBED_FLAGS=""
if [ -n "${VOYAGE_API_KEY:-}" ]; then
GBRAIN_EMBED_FLAGS="--embedding-model voyage:voyage-code-3 --embedding-dimensions 1024"
gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024
else
gbrain init --pglite --json
fi
gbrain init --pglite --json $GBRAIN_EMBED_FLAGS
```
Done. No network, no secrets (beyond Voyage embedding API calls during sync, if
@ -1202,14 +1208,18 @@ fi
# VOYAGE_API_KEY is set. It wins the A/B over voyage-4-large and OpenAI
# text-embedding-3-large on this codebase's symbol queries. Falls back to
# gbrain's auto-selected provider when the key isn't present.
GBRAIN_EMBED_FLAGS=""
if [ -n "${VOYAGE_API_KEY:-}" ]; then
GBRAIN_EMBED_FLAGS="--embedding-model voyage:voyage-code-3 --embedding-dimensions 1024"
fi
if ! gbrain init --pglite --json $GBRAIN_EMBED_FLAGS; then
if [ -n "${BACKUP:-}" ] && [ -f "$BACKUP" ]; then mv "$BACKUP" "$HOME/.gbrain/config.json"; fi
echo "gbrain init failed. Existing config (if any) was restored. PGLite at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` to reset." >&2
echo "Continuing setup without local code search; you can re-run /setup-gbrain to retry." >&2
if ! gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024; then
if [ -n "${BACKUP:-}" ] && [ -f "$BACKUP" ]; then mv "$BACKUP" "$HOME/.gbrain/config.json"; fi
echo "gbrain init failed. Existing config (if any) was restored. PGLite at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` to reset." >&2
echo "Continuing setup without local code search; you can re-run /setup-gbrain to retry." >&2
fi
else
if ! gbrain init --pglite --json; then
if [ -n "${BACKUP:-}" ] && [ -f "$BACKUP" ]; then mv "$BACKUP" "$HOME/.gbrain/config.json"; fi
echo "gbrain init failed. Existing config (if any) was restored. PGLite at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` to reset." >&2
echo "Continuing setup without local code search; you can re-run /setup-gbrain to retry." >&2
fi
fi
```

View File

@ -134,16 +134,22 @@ mv "$HOME/.gbrain/config.json" "$BACKUP"
# gstack default: voyage-code-3 (1024d) when VOYAGE_API_KEY is set — best for
# code retrieval. Without the key, fall back to gbrain's own auto-selected
# embedding provider chain (OpenAI 1536d when OPENAI_API_KEY is present, etc.).
GBRAIN_EMBED_FLAGS=""
if [ -n "${VOYAGE_API_KEY:-}" ]; then
GBRAIN_EMBED_FLAGS="--embedding-model voyage:voyage-code-3 --embedding-dimensions 1024"
fi
if ! gbrain init --pglite --json $GBRAIN_EMBED_FLAGS; then
# Restore on failure
mv "$BACKUP" "$HOME/.gbrain/config.json"
echo "gbrain init failed. Your previous config was restored at $HOME/.gbrain/config.json." >&2
echo "PGLite directory at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` if needed before retrying." >&2
exit 1
if ! gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024; then
# Restore on failure
mv "$BACKUP" "$HOME/.gbrain/config.json"
echo "gbrain init failed. Your previous config was restored at $HOME/.gbrain/config.json." >&2
echo "PGLite directory at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` if needed before retrying." >&2
exit 1
fi
else
if ! gbrain init --pglite --json; then
# Restore on failure
mv "$BACKUP" "$HOME/.gbrain/config.json"
echo "gbrain init failed. Your previous config was restored at $HOME/.gbrain/config.json." >&2
echo "PGLite directory at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` if needed before retrying." >&2
exit 1
fi
fi
echo "Switched to local PGLite. Previous config saved at $BACKUP — review before deleting."
```
@ -348,11 +354,11 @@ Then follow the same secret-read + verify + init flow as Path 1.
# gstack default: voyage-code-3 (1024d) when VOYAGE_API_KEY is set — code
# retrieval beats general-purpose embeddings on real code queries (validated
# A/B). Without the key, gbrain auto-selects (OpenAI 1536d when available).
GBRAIN_EMBED_FLAGS=""
if [ -n "${VOYAGE_API_KEY:-}" ]; then
GBRAIN_EMBED_FLAGS="--embedding-model voyage:voyage-code-3 --embedding-dimensions 1024"
gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024
else
gbrain init --pglite --json
fi
gbrain init --pglite --json $GBRAIN_EMBED_FLAGS
```
Done. No network, no secrets (beyond Voyage embedding API calls during sync, if
@ -440,14 +446,18 @@ fi
# VOYAGE_API_KEY is set. It wins the A/B over voyage-4-large and OpenAI
# text-embedding-3-large on this codebase's symbol queries. Falls back to
# gbrain's auto-selected provider when the key isn't present.
GBRAIN_EMBED_FLAGS=""
if [ -n "${VOYAGE_API_KEY:-}" ]; then
GBRAIN_EMBED_FLAGS="--embedding-model voyage:voyage-code-3 --embedding-dimensions 1024"
fi
if ! gbrain init --pglite --json $GBRAIN_EMBED_FLAGS; then
if [ -n "${BACKUP:-}" ] && [ -f "$BACKUP" ]; then mv "$BACKUP" "$HOME/.gbrain/config.json"; fi
echo "gbrain init failed. Existing config (if any) was restored. PGLite at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` to reset." >&2
echo "Continuing setup without local code search; you can re-run /setup-gbrain to retry." >&2
if ! gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024; then
if [ -n "${BACKUP:-}" ] && [ -f "$BACKUP" ]; then mv "$BACKUP" "$HOME/.gbrain/config.json"; fi
echo "gbrain init failed. Existing config (if any) was restored. PGLite at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` to reset." >&2
echo "Continuing setup without local code search; you can re-run /setup-gbrain to retry." >&2
fi
else
if ! gbrain init --pglite --json; then
if [ -n "${BACKUP:-}" ] && [ -f "$BACKUP" ]; then mv "$BACKUP" "$HOME/.gbrain/config.json"; fi
echo "gbrain init failed. Existing config (if any) was restored. PGLite at ~/.gbrain/pglite/ may be in a partial state — \`rm -rf ~/.gbrain/pglite\` to reset." >&2
echo "Continuing setup without local code search; you can re-run /setup-gbrain to retry." >&2
fi
fi
```

View File

@ -24,7 +24,6 @@ import {
mkdirSync,
writeFileSync,
readFileSync,
existsSync,
rmSync,
chmodSync,
} from "fs";
@ -40,6 +39,24 @@ interface FakeEnv {
cleanup: () => void;
}
interface ShellCase {
name: "bash" | "zsh";
command: string;
available: boolean;
}
function shellAvailable(command: string): boolean {
const result = spawnSync(command, ["-c", "exit 0"], {
stdio: "ignore",
});
return result.status === 0;
}
const SHELLS: ShellCase[] = [
{ name: "bash", command: "bash", available: shellAvailable("bash") },
{ name: "zsh", command: "zsh", available: shellAvailable("zsh") },
];
function makeFakeEnv(): FakeEnv {
const tmp = mkdtempSync(join(tmpdir(), "gbrain-voyage-init-"));
const home = join(tmp, "home");
@ -52,7 +69,13 @@ function makeFakeEnv(): FakeEnv {
// succeeds on init (writes a sentinel pglite config), and returns canned
// output for --version. Nothing else is needed for the shape test.
const fake = `#!/bin/sh
echo "$@" >> "${argvLog}"
{
echo "__CALL__"
for arg in "$@"; do
printf 'arg=%s\\n' "$arg"
done
echo "__END__"
} >> "${argvLog}"
case "$1" in
--version)
echo "gbrain 0.37.1.0"
@ -81,23 +104,27 @@ exit 0
}
/**
* Verbatim reimplementation of the skill template's voyage-code-3
* conditional. The template (setup-gbrain/SKILL.md.tmpl Path 3, Step 1.5
* inside the rollback wrapper, Step 4.5 Path 4 Yes branch) instructs the
* model to execute this bash; we execute the same bash here and assert the
* argv passed to gbrain matches the contract.
* Verbatim reimplementation of the skill template's voyage-code-3 conditional.
* The template (setup-gbrain/SKILL.md.tmpl Path 3, Step 1.5 inside the
* rollback wrapper, Step 4.5 Path 4 Yes branch) instructs the model to execute
* this shell; we execute the same shell here and assert the argv passed to
* gbrain matches the contract under both bash and zsh.
*
* If the template changes the flag set or the env-var name, this test
* should fail until the shell here is updated too by design.
*/
function runInitWithVoyageGate(env: FakeEnv, voyageKey: string | undefined): string[] {
function runInitWithVoyageGate(
env: FakeEnv,
voyageKey: string | undefined,
shell: ShellCase,
): string[][] {
const script = `
set -u
GBRAIN_EMBED_FLAGS=""
if [ -n "\${VOYAGE_API_KEY:-}" ]; then
GBRAIN_EMBED_FLAGS="--embedding-model voyage:voyage-code-3 --embedding-dimensions 1024"
gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024
else
gbrain init --pglite --json
fi
gbrain init --pglite --json $GBRAIN_EMBED_FLAGS
`;
const baseEnv: Record<string, string> = {
...process.env,
@ -109,56 +136,86 @@ gbrain init --pglite --json $GBRAIN_EMBED_FLAGS
} else {
baseEnv.VOYAGE_API_KEY = voyageKey;
}
const result = spawnSync("bash", ["-c", script], {
const result = spawnSync(shell.command, ["-c", script], {
encoding: "utf-8",
env: baseEnv,
});
if (result.status !== 0) {
throw new Error(`init script exited ${result.status}: ${result.stderr}`);
throw new Error(
`${shell.name} init script exited ${result.status}: ${result.stderr}`,
);
}
return readFileSync(env.argvLog, "utf-8").trim().split("\n");
return parseArgvLog(env.argvLog);
}
function parseArgvLog(argvLog: string): string[][] {
const calls: string[][] = [];
let current: string[] | undefined;
for (const line of readFileSync(argvLog, "utf-8").trim().split("\n")) {
if (line === "__CALL__") {
current = [];
continue;
}
if (line === "__END__") {
if (current) calls.push(current);
current = undefined;
continue;
}
if (current && line.startsWith("arg=")) {
current.push(line.slice("arg=".length));
}
}
return calls;
}
describe("voyage-code-3 default for gstack-driven PGLite init", () => {
it("passes voyage-code-3 flags when VOYAGE_API_KEY is set", () => {
const env = makeFakeEnv();
try {
const calls = runInitWithVoyageGate(env, "vk_test_set");
expect(calls.length).toBe(1);
const argv = calls[0];
expect(argv).toContain("init --pglite --json");
expect(argv).toContain("--embedding-model voyage:voyage-code-3");
expect(argv).toContain("--embedding-dimensions 1024");
} finally {
env.cleanup();
}
});
for (const shell of SHELLS) {
const describeShell = shell.available ? describe : describe.skip;
it("omits voyage flags when VOYAGE_API_KEY is unset", () => {
const env = makeFakeEnv();
try {
const calls = runInitWithVoyageGate(env, undefined);
expect(calls.length).toBe(1);
const argv = calls[0];
expect(argv).toContain("init --pglite --json");
expect(argv).not.toContain("voyage");
expect(argv).not.toContain("--embedding-model");
expect(argv).not.toContain("--embedding-dimensions");
} finally {
env.cleanup();
}
});
describeShell(`under ${shell.name}`, () => {
it("passes voyage-code-3 flags as four separate argv entries when VOYAGE_API_KEY is set", () => {
const env = makeFakeEnv();
try {
const calls = runInitWithVoyageGate(env, "vk_test_set", shell);
expect(calls).toEqual([
[
"init",
"--pglite",
"--json",
"--embedding-model",
"voyage:voyage-code-3",
"--embedding-dimensions",
"1024",
],
]);
} finally {
env.cleanup();
}
});
it("treats empty-string VOYAGE_API_KEY the same as unset (no false positive)", () => {
const env = makeFakeEnv();
try {
const calls = runInitWithVoyageGate(env, "");
expect(calls.length).toBe(1);
expect(calls[0]).not.toContain("voyage");
} finally {
env.cleanup();
}
});
it("omits voyage flags when VOYAGE_API_KEY is unset", () => {
const env = makeFakeEnv();
try {
const calls = runInitWithVoyageGate(env, undefined, shell);
expect(calls).toEqual([["init", "--pglite", "--json"]]);
} finally {
env.cleanup();
}
});
it("treats empty-string VOYAGE_API_KEY the same as unset (no false positive)", () => {
const env = makeFakeEnv();
try {
const calls = runInitWithVoyageGate(env, "", shell);
expect(calls).toEqual([["init", "--pglite", "--json"]]);
} finally {
env.cleanup();
}
});
});
}
});
describe("template alignment: the .tmpl actually contains the voyage gate", () => {
@ -168,11 +225,13 @@ describe("template alignment: the .tmpl actually contains the voyage gate", () =
const TEMPLATE_PATH = join(import.meta.dir, "..", "setup-gbrain", "SKILL.md.tmpl");
const tmpl = readFileSync(TEMPLATE_PATH, "utf-8");
it("setup-gbrain template gates the embedding-model flag on VOYAGE_API_KEY", () => {
it("setup-gbrain template gates the embedding-model flag on VOYAGE_API_KEY without word-splitting", () => {
// Should appear at least once (currently 3 init sites use the same gate).
expect(tmpl).toContain('if [ -n "${VOYAGE_API_KEY:-}" ]; then');
expect(tmpl).toContain("--embedding-model voyage:voyage-code-3");
expect(tmpl).toContain("--embedding-dimensions 1024");
expect(tmpl).toContain(
"gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024",
);
expect(tmpl).not.toContain("GBRAIN_EMBED_FLAGS");
});
it("setup-gbrain template uses the conditional gate at all 3 PGLite init sites", () => {
@ -180,5 +239,10 @@ describe("template alignment: the .tmpl actually contains the voyage gate", () =
// init site, update this expectation deliberately.
const matches = tmpl.match(/if \[ -n "\$\{VOYAGE_API_KEY:-\}" \]; then/g);
expect(matches?.length).toBe(3);
const voyageInitMatches = tmpl.match(
/gbrain init --pglite --json --embedding-model voyage:voyage-code-3 --embedding-dimensions 1024/g,
);
expect(voyageInitMatches?.length).toBe(3);
});
});