From 6529efefc7ca05c626849c9e7f7920785d86ab84 Mon Sep 17 00:00:00 2001 From: REPPL <77722411+REPPL@users.noreply.github.com> Date: Sun, 12 Jul 2026 11:31:32 +0100 Subject: [PATCH 1/2] fix: report IsGitRepo in worktrees and submodules (iss-72) core.Status used isDir(.git), but .git is a regular gitfile (a gitlink), not a directory, in a linked git worktree (git worktree add) or a submodule checkout. A genuine checkout was reported IsGitRepo=false. Test existence instead of dir-ness for .git via a new exists() helper; HasRecord/WorkTiers stay dir-only. Assisted-by: Claude:claude-opus-4-8 --- CHANGELOG.md | 5 +++++ internal/core/core.go | 15 +++++++++++++-- internal/core/core_test.go | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4985c7dd..206cf1b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,11 @@ called out in a **Breaking** section. - The `created` and `updated` frontmatter fields on issues. Git is the canonical source of an issue's timeline; the ledger no longer duplicates it. +### Fixed + +- `abcd` status now reports `IsGitRepo` correctly in a linked git worktree or a + submodule, where `.git` is a regular gitfile rather than a directory (iss-72). + ### Security - **Release receipt-gate hardening** (iss-70). The `receipt_gate` record-lint diff --git a/internal/core/core.go b/internal/core/core.go index e2023256..6137f28e 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -43,8 +43,11 @@ func Status(dir string) (StatusInfo, error) { return StatusInfo{}, err } s := StatusInfo{ - Dir: abs, - IsGitRepo: isDir(filepath.Join(abs, ".git")), + Dir: abs, + // .git is a directory in a normal clone but a regular gitfile in a linked + // worktree or submodule — both are genuine checkouts, so test existence, not + // dir-ness. HasRecord/WorkTiers stay dir-only (those must be directories). + IsGitRepo: exists(filepath.Join(abs, ".git")), HasRecord: isDir(filepath.Join(abs, ".abcd", "development")), } for _, tier := range []struct{ path, name string }{ @@ -63,3 +66,11 @@ func isDir(p string) bool { fi, err := os.Stat(p) return err == nil && fi.IsDir() } + +// exists reports whether p exists (a file or a directory). A .git gitfile in a +// worktree/submodule is a regular file, so a plain existence check is the correct +// "is this a git checkout" test. +func exists(p string) bool { + _, err := os.Stat(p) + return err == nil +} diff --git a/internal/core/core_test.go b/internal/core/core_test.go index a6e94317..52c600e7 100644 --- a/internal/core/core_test.go +++ b/internal/core/core_test.go @@ -30,6 +30,23 @@ func TestStatusBareDir(t *testing.T) { } } +// TestStatusGitfileWorktree (iss-72) proves a linked worktree or submodule — +// where .git is a regular gitfile, not a directory — is still reported as a git +// repo. isDir would report false for a genuine checkout. +func TestStatusGitfileWorktree(t *testing.T) { + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, ".git"), []byte("gitdir: /somewhere/.git/worktrees/wt\n"), 0o644); err != nil { + t.Fatal(err) + } + s, err := Status(dir) + if err != nil { + t.Fatal(err) + } + if !s.IsGitRepo { + t.Fatal("a worktree/submodule .git gitfile must report IsGitRepo=true") + } +} + func TestStatusWithRecordAndGit(t *testing.T) { dir := t.TempDir() mustMkdir(t, filepath.Join(dir, ".git")) From 33fde347e3dbbe666bc583d47fcb47eac275a3db Mon Sep 17 00:00:00 2001 From: REPPL <77722411+REPPL@users.noreply.github.com> Date: Sun, 12 Jul 2026 11:32:49 +0100 Subject: [PATCH 2/2] chore: resolve iss-72 in the ledger Assisted-by: Claude:claude-opus-4-8 --- .../{open => resolved}/iss-72-core-isgitrepo-misses-worktrees.md | 1 + 1 file changed, 1 insertion(+) rename .abcd/work/issues/{open => resolved}/iss-72-core-isgitrepo-misses-worktrees.md (66%) diff --git a/.abcd/work/issues/open/iss-72-core-isgitrepo-misses-worktrees.md b/.abcd/work/issues/resolved/iss-72-core-isgitrepo-misses-worktrees.md similarity index 66% rename from .abcd/work/issues/open/iss-72-core-isgitrepo-misses-worktrees.md rename to .abcd/work/issues/resolved/iss-72-core-isgitrepo-misses-worktrees.md index c9b64a86..64e68339 100644 --- a/.abcd/work/issues/open/iss-72-core-isgitrepo-misses-worktrees.md +++ b/.abcd/work/issues/resolved/iss-72-core-isgitrepo-misses-worktrees.md @@ -7,6 +7,7 @@ category: "bug" source: "agent-finding" found_during: "clean-slate-sweep" found_at: "internal/core/core.go" +resolution: "core.Status now tests .git existence (file or dir) not dir-ness, so a linked worktree/submodule (where .git is a gitfile) reports IsGitRepo=true. os.Stat is correct (rejects dangling symlinks). ruthless SHIP; scoped to IsGitRepo only. PR opened." --- core.Status.IsGitRepo=false in a linked git worktree or submodule: isDir(.git) requires a directory but .git is a regular gitfile (gitlink) in worktrees/submodules, so a genuine checkout reports not-a-git-repo (core.go:47, C1). Detector: an exists-not-isDir check for .git with a worktree fixture. Corpus: C1. \ No newline at end of file