Environment
gstack 1.60.1.0 · gbrain 0.42.66.0 (Postgres) · Linux
What happens
buildTranscriptPage stores the raw git remote on the page but writes the fallback into the frontmatter, and the later filter tests the fallback string:
// bin/gstack-memory-ingest.ts:704
const remote = resolveGitRemote(session.cwd); // returns "" on failure (:681)
// :726 — frontmatter gets the fallback
`git_remote: ${remote || "_unattributed"}`,
// :747 — the page field gets the RAW value
git_remote: remote,
// bin/gstack-memory-ingest.ts:1170
if (!args.includeUnattributed && page.git_remote === "_unattributed") {
page.git_remote is "" whenever the remote could not be resolved, so "" === "_unattributed" is always false and the branch is dead. repoSlug (:687) does apply the fallback, so the slug and frontmatter both read _unattributed — only the guard's input doesn't.
buildArtifactPage gets this right: :784 stores git_remote: slug_repo, the already-defaulted value.
The earlier guard at :1165 (!session.cwd) doesn't cover this — these sessions do have a cwd, it just no longer resolves.
What I expected
With --include-unattributed off (the default), a session whose repo cannot be determined is skipped and counted in skipped_unattributed.
Minimal repro
mkdir -p /tmp/not-a-repo && cd /tmp/not-a-repo
# run any agent session here, then:
gstack-memory-ingest.ts # no --include-unattributed
# → page transcripts/<agent>/_unattributed/<date>-<id> is imported
# → summary reports "skipped (unattrib): 0"
Why it matters
On one brain, 354 of 650 transcript pages carry git_remote: _unattributed — 54% of the corpus imported against the default policy.
The compounding factor is that the remote is resolved at ingest time from a cwd captured at session time. Of those 354 pages, 333 (94%) point at a directory that no longer exists, and 84 are under worktree paths. Agent sessions that run in ephemeral git worktrees are therefore systematically unattributable: git -C <deleted dir> remote get-url origin throws at :673 and returns "" every time.
The user-facing counter also reads 0 (:1887), so nothing signals that the policy isn't being applied.
Suggested fix
Normalise once, at the source — one line at :747:
git_remote: remote || "_unattributed",
Optionally, recover the attribution instead of discarding it: Claude Code encodes the project directory in the transcript path (~/.claude/projects/<encoded-cwd>/…), which survives worktree deletion and would correctly attribute most of these.
Environment
gstack 1.60.1.0 · gbrain 0.42.66.0 (Postgres) · Linux
What happens
buildTranscriptPagestores the raw git remote on the page but writes the fallback into the frontmatter, and the later filter tests the fallback string:page.git_remoteis""whenever the remote could not be resolved, so"" === "_unattributed"is always false and the branch is dead.repoSlug(:687) does apply the fallback, so the slug and frontmatter both read_unattributed— only the guard's input doesn't.buildArtifactPagegets this right::784storesgit_remote: slug_repo, the already-defaulted value.The earlier guard at
:1165(!session.cwd) doesn't cover this — these sessions do have acwd, it just no longer resolves.What I expected
With
--include-unattributedoff (the default), a session whose repo cannot be determined is skipped and counted inskipped_unattributed.Minimal repro
Why it matters
On one brain, 354 of 650 transcript pages carry
git_remote: _unattributed— 54% of the corpus imported against the default policy.The compounding factor is that the remote is resolved at ingest time from a
cwdcaptured at session time. Of those 354 pages, 333 (94%) point at a directory that no longer exists, and 84 are underworktreepaths. Agent sessions that run in ephemeral git worktrees are therefore systematically unattributable:git -C <deleted dir> remote get-url originthrows at:673and returns""every time.The user-facing counter also reads
0(:1887), so nothing signals that the policy isn't being applied.Suggested fix
Normalise once, at the source — one line at
:747:Optionally, recover the attribution instead of discarding it: Claude Code encodes the project directory in the transcript path (
~/.claude/projects/<encoded-cwd>/…), which survives worktree deletion and would correctly attribute most of these.