Development record
A committed positioning registry may name a surface inside .git (ValidRelPath accepts .git/config and it is inside the containment root), so abcd identity --json quotes .git/config — including a credential-bearing remote URL — into SurfaceResult.Found. Pre-existing; not an escape, and the output reaches only the operator's own stdout. Detector: a Config.Validate test refusing surface candidates under .git/. Found during the itd-102 containment review.
issueresolvedminoriss-150
A committed positioning registry may name a surface inside .git (ValidRelPath accepts .git/config and it is inside the containment root), so abcd identity --json quotes .git/config — including a credential-bearing remote URL — into SurfaceResult.Found. Pre-existing; not an escape, and the output reaches only the operator's own stdout. Detector: a Config.Validate test refusing surface candidates under .git/. Found during the itd-102 containment review.
Frontmatter
| id | iss-150 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-150-a-committed-positioning-registry-may.md
open on GitHub ↗ · commit history ↗
mentions2
- iss-2609081940475662manifest.validate holds home.hero.page and home.chapters[].page to fsutil.ValidRelPath only, and that function accepts .git/config; loadPage then composes whatever the named file holds into index.html through ReadGuardedInRoot, which contains the read to the repo root but does not constrain the selection. The image pipeline documents and tests this exact threat (assetRootPrefix, TestAssetsRefuseAnythingOutsideTheAssetRoot) and positioning already refuses .git surfaces (iss-150), but the site composer did not copy the gate. identity.file and policy.file are not whole-file page sources (heading extract, one-bullet quote). A gitignored .env cannot merge because CI has no file and site-render goes red, and persist-credentials: false keeps the CI .git/config token-free, so the live exposure is bounded; the missing gate is not. .abcd/site.json is not in CODEOWNERS. Fix: refuse page sources under .git/ (case-folded, as the positioning denylist does) and restrict hero.page and chapters[].page to docs/ or site-src/, keeping identity.file and policy.file on their legitimate locations while applying the .git refusal; add .abcd/site.json to CODEOWNERS. Detector: LoadManifest with home.hero.page set to .git/config, to .env, and to a path outside docs//site-src/ must each fail validation, while the committed manifest still loads. Twin of iss-150. Reported as GitHub issue 622 against ec7f40d6.
- itd-102**Your repo says the same thing about itself everywhere — because abcd asked you once and holds every surface to it.** A project's positioning fragments silently: the README strapline, the package or plugin manifest description, and the agent conventions file are each edited at different moments, and soon three surfaces tell three stories about what the project is. When abcd prepares a repo it interviews the user for the project's identity — title, tagline, and a short elevator pitch — and records the answers in one canonical home in the development record. Every surface rendering derives from that home, and a deterministic positioning check compares the surfaces against it on every audit: a mismatch is highlighted with the exact drifted line, and the fix is either re-render from the record or a deliberate, recorded identity change — never a silent extra variant, and never a silent rewrite by abcd. "I changed my tagline once, in one place, and abcd chased it everywhere else," said Alice, a solo founder. "When a stray edit crept into the README, the next audit pointed at the exact line instead of letting the drift settle in."