Summary
gstack-gbrain-sync (v1.26.3.0) fails the cwd code stage when the slug derived from the git remote contains a . or exceeds 32 chars. gbrain sources add rejects the ID:
ERR code source registration failed: gbrain sources add gstack-code-github.com-michalczwarno-pelnygrafik failed:
Invalid source id "gstack-code-github.com-michalczwarno-pelnygrafik".
Must be 1-32 lowercase alnum chars with optional interior hyphens (e.g. "wiki", "yc-media").
Repro
Any repo whose origin is https://github.com/<owner>/<repo>.git where <owner> + <repo> push the slug past 32 chars. Mine: michalczwarno/pelnygrafik.
$ git remote get-url origin
https://github.com/michalczwarno/pelnygrafik.git
$ bun run ~/.claude/skills/gstack/bin/gstack-gbrain-sync.ts --full
[gbrain-sync] mode=full engine=unknown
gstack-gbrain-sync (full):
ERR code source registration failed: ... (see above)
OK memory ingest pass complete (43.2s)
OK brain-sync curated artifacts pushed (0.2s)
2 ok, 1 error, 0 skipped
Memory + brain-sync stages succeed; only the code stage fails — so the user gets 2/3 of the value, but gbrain code-def / code-refs / code-callers / code-callees (the headline of v1.26.3.0) won't work against the cwd repo.
Root cause
bin/gstack-gbrain-sync.ts:170:
return `gstack-code-${remote.replace(/[\/\s]+/g, "-").replace(/-+/g, "-")}`;
Two issues vs. gbrain's source-id rule (1-32 lowercase alnum + interior hyphens):
- Regex only replaces
/ and whitespace — ., :, @ survive. github.com/foo/bar becomes github.com-foo-bar, not github-com-foo-bar.
- No length cap — even after proper sanitization,
gstack-code-github-com-michalczwarno-pelnygrafik is 47 chars vs. the 32 limit.
The fallback path (no remote) on line 174 already sanitizes correctly:
return `gstack-code-${base.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-")}`;
…but it also lacks the length cap.
Suggested fix
Unify both paths through one helper, sanitize via [^a-z0-9-]+, and cap output at 32 chars (the unique part is the repo name, so truncating from the start of the slug — keeping the right side — preserves uniqueness across same-owner repos):
function deriveSourceId(remote: string | undefined, root: string): string {
const raw = (remote ?? path.basename(root)).toLowerCase();
const slug = raw
.replace(/[^a-z0-9-]+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
const prefix = "gstack-code-";
const budget = 32 - prefix.length; // 20
const tail = slug.length > budget ? slug.slice(slug.length - budget) : slug;
return `${prefix}${tail.replace(/^-/, "")}`;
}
For my repo this yields gstack-code-czwarno-pelnygrafik (30 chars, valid). For deterministic uniqueness across owners with similarly-named repos, a short hash suffix would be more robust than tail-truncation — happy to defer the exact strategy to your taste.
A regression test seeding https://github.com/<long-owner>/<long-repo>.git and asserting the produced id matches ^[a-z0-9](-?[a-z0-9])*$ and length <= 32 would pin this.
Environment
- gstack
1.26.3.0
- gbrain
0.18.2 (the validator that rejected the ID — note: older than the >= 0.20.0 floor implied by the v1.26.3.0 release notes, but the validation rule the error cites would presumably still apply on newer gbrain)
- macOS 25.4.0 (Apple Silicon), bun
- Discovered in a fresh repo upgraded from gstack v1.26.0.0 → v1.26.3.0 via
/gstack-upgrade
Summary
gstack-gbrain-sync(v1.26.3.0) fails the cwd code stage when the slug derived from the git remote contains a.or exceeds 32 chars.gbrain sources addrejects the ID:Repro
Any repo whose origin is
https://github.com/<owner>/<repo>.gitwhere<owner>+<repo>push the slug past 32 chars. Mine:michalczwarno/pelnygrafik.Memory + brain-sync stages succeed; only the code stage fails — so the user gets 2/3 of the value, but
gbrain code-def/code-refs/code-callers/code-callees(the headline of v1.26.3.0) won't work against the cwd repo.Root cause
bin/gstack-gbrain-sync.ts:170:Two issues vs. gbrain's source-id rule (
1-32 lowercase alnum + interior hyphens):/and whitespace —.,:,@survive.github.com/foo/barbecomesgithub.com-foo-bar, notgithub-com-foo-bar.gstack-code-github-com-michalczwarno-pelnygrafikis 47 chars vs. the 32 limit.The fallback path (no remote) on line 174 already sanitizes correctly:
…but it also lacks the length cap.
Suggested fix
Unify both paths through one helper, sanitize via
[^a-z0-9-]+, and cap output at 32 chars (the unique part is the repo name, so truncating from the start of the slug — keeping the right side — preserves uniqueness across same-owner repos):For my repo this yields
gstack-code-czwarno-pelnygrafik(30 chars, valid). For deterministic uniqueness across owners with similarly-named repos, a short hash suffix would be more robust than tail-truncation — happy to defer the exact strategy to your taste.A regression test seeding
https://github.com/<long-owner>/<long-repo>.gitand asserting the produced id matches^[a-z0-9](-?[a-z0-9])*$andlength <= 32would pin this.Environment
1.26.3.00.18.2(the validator that rejected the ID — note: older than the>= 0.20.0floor implied by the v1.26.3.0 release notes, but the validation rule the error cites would presumably still apply on newer gbrain)/gstack-upgrade