Skip to content

scaffold: .architect-role.md missing from CODEV_GITIGNORE_ENTRIES — every fresh codev init/adopt leaves a dirty git status after first architect spawn #880

Closed

Description

@amrmelsayed

Symptom

After running `codev init ` or `codev adopt` in a project, the first invocation of `afx architect` (or any operation that triggers Tower to spawn the architect terminal) writes `.architect-role.md` to the workspace root. This file is the architect's role prompt, materialized from the resolver chain for the harness layer to load — a runtime bootstrap artifact, not source.

But the file is not in the gitignore that codev's scaffolding writes. Result:

```
$ codev init my-project && cd my-project && git init . && git add -A && git commit -m initial
$ afx architect

...architect spawns; .architect-role.md is written...

$ git status
On branch main
Untracked files:
(use "git add ..." to include in what will be committed)
.architect-role.md
```

Every user's first interaction with Codev produces an untracked file they didn't create deliberately. First-impression friction. Existing Codev users hit the same problem on every machine where they cloned a Codev-managed repo before the entry was added manually.

Root cause

`packages/codev/src/lib/scaffold.ts:12`:

```ts
export const CODEV_GITIGNORE_ENTRIES = `# Codev
.agent-farm/
.consult/
codev/.update-hashes.json
.builders/
`;
```

Four entries; `.architect-role.md` is missing.

The sibling builder runtime files (`.builder-role.md`, `.builder-prompt.txt`, `.builder-start.sh`) are covered transitively because they live inside `.builders//` and `.builders/` itself is gitignored. But `.architect-role.md` lives at workspace root with no ignored ancestor, so it needs its own explicit entry.

Why this affects every fresh project AND every existing project

  • `codev init` uses `FULL_GITIGNORE_CONTENT` = `CODEV_GITIGNORE_ENTRIES` + node_modules/dist/OS-files. Missing `.architect-role.md` propagates to every new project.
  • `codev adopt` appends `CODEV_GITIGNORE_ENTRIES` to existing `.gitignore` (when `.agent-farm/` isn't already present). Same omission.
  • `codev update` doesn't touch `.gitignore` at all today (verified by grep — zero references in `packages/codev/src/commands/update.ts`). So existing Codev-managed projects with stale gitignore content have no path to receive newer entries.

Verification: this codev repo's own `.gitignore` does include the entry (line 10 in `/.gitignore`). It was almost certainly added manually by the original author. `CODEV_GITIGNORE_ENTRIES` doesn't put it there, and `codev update` wouldn't have added it either.

Fix — two parts (scope decision 2026-05-27)

Part 1 — Add the missing entry to the canonical list

One-line addition to `packages/codev/src/lib/scaffold.ts:12`:

```diff
export const CODEV_GITIGNORE_ENTRIES = `# Codev
.agent-farm/
.consult/
codev/.update-hashes.json
.builders/
+.architect-role.md
`;
```

This covers `codev init` (via `FULL_GITIGNORE_CONTENT`) and `codev adopt` (direct append) — fresh projects get the entry automatically.

Part 2 — Backfill from `codev update` for existing projects

Extend `codev update` so it scans the existing `.gitignore` for each entry in `CODEV_GITIGNORE_ENTRIES` and appends any missing ones. This makes the upgrade path self-healing: a user running `codev update` on a long-lived project picks up the new entries without having to know they need to.

Design rules:

  • Idempotent. Re-running `codev update` after a clean state does nothing — every line check is "if not present, append."
  • Append-only. Never delete or reorder existing lines. The user may have curated their `.gitignore` with custom entries; preserve all of them.
  • Grouped append. Missing entries are appended together under a `# Codev (added by codev update YYYY-MM-DD)` header comment so the user can see where they came from. Avoids scattering the additions through the file.
  • Dry-run support. `codev update --dry-run` (if it exists, or add it) prints what would be added without writing — lets users review before accepting.
  • Don't touch a missing .gitignore. If the project has no `.gitignore`, that's a deliberate signal (not all projects use git); skip rather than create one. Creation belongs to `init` / `adopt`.

Why this is a new pattern, not an extension of existing `codev update` behavior.

`codev update` today uses two patterns for the files it manages — verified by reading `packages/codev/src/commands/update.ts` (309 lines, zero references to `gitignore`):

Existing pattern Used for Mechanism
`{ skipExisting: true }` `.claude/skills//` Add new skills from package skeleton; never modify existing ones (preserves user customizations)
`{ handleConflicts: true }` `CLAUDE.md`, `AGENTS.md` Add if missing, OR write a `.codev-new` sibling when content differs from the package template — user merges manually

Neither pattern fits `.gitignore`:

  • Skills pattern doesn't work — the file already exists (from `init`/`adopt`); the problem is that it's missing one line inside it. "Skip if exists" leaves the gap intact forever.
  • Conflict-file pattern doesn't fit — gitignore isn't whole-file ownership. Codev owns SOME lines (the `# Codev` block) and the user owns OTHER lines (their custom ignores). A `.gitignore.codev-new` sibling forces the user to manually merge what's trivially line-mergeable.

This issue introduces a third pattern: line-level append-only merge. Distinct from the existing two but structurally similar — scan an existing file for entries from a managed block, append the missing ones under a labeled header, never touch anything else.

Day-one scope: keep it gitignore-specific. The function is `backfillGitignore(targetDir, entries)` with gitignore-specific decisions (read the file, append at end, header comment format) baked in. Do not generalize to `backfillManagedBlock(filePath, entries, headerComment)` on day one — that's premature abstraction with no second use case. If a future Codev feature needs the same behavior for another managed-block-in-shared-file case (e.g. a managed `.editorconfig` section, a managed `package.json` `scripts` entry), the abstraction can be promoted then. Until that second case appears, the gitignore-specific shape is the right surface.

Implementation:

  • New function `backfillGitignore(targetDir, entries)` in `packages/codev/src/lib/scaffold.ts` next to `createGitignore` / `updateGitignore`. Returns a result like `{ added: string[], alreadyPresent: string[] }`.
  • `packages/codev/src/commands/update.ts` calls it as part of its existing `# A. One-time migration` and `# B. Framework refresh` flow, prints a summary line.

Acceptance criteria

Part 1 (fresh projects)

  • `CODEV_GITIGNORE_ENTRIES` in `packages/codev/src/lib/scaffold.ts` includes `.architect-role.md`
  • `codev init ` writes a `.gitignore` containing `.architect-role.md`
  • `codev adopt` in a project without an existing `.gitignore` writes one containing `.architect-role.md`
  • `codev adopt` in a project with an existing `.gitignore` (without `.agent-farm/`) appends the block including `.architect-role.md`

Part 2 (existing projects via codev update)

  • `backfillGitignore(targetDir, entries)` helper exists in `scaffold.ts`; idempotent, append-only, returns `{ added, alreadyPresent }`
  • `codev update` calls the helper for `CODEV_GITIGNORE_ENTRIES` against the project's existing `.gitignore`
  • Missing entries appended together under `# Codev (added by codev update YYYY-MM-DD)` header
  • Existing entries (including any custom user entries) are never deleted, reordered, or duplicated
  • If the project has no `.gitignore`, `codev update` does nothing (skips silently) — creation belongs to init/adopt
  • `codev update --dry-run` (if/where the flag exists) prints what would be added but doesn't write
  • Update summary line: `gitignore: added entries` or `gitignore: up to date`

Verification

  • Unit tests in `packages/codev/src/tests/scaffold.test.ts` cover: (a) `.architect-role.md` present in both constants, (b) `backfillGitignore` adds missing entries, (c) `backfillGitignore` is idempotent (second run = no-op), (d) `backfillGitignore` preserves custom user entries
  • Integration test for `codev update`: project with stale `.gitignore` (missing `.architect-role.md`) → run `codev update` → `.gitignore` now contains the entry, other lines unchanged
  • End-to-end smoke: after `codev init my-test && cd my-test && git init . && afx architect`, `git status` shows clean working tree (no untracked `.architect-role.md`)

Out of scope

  • Other workspace-root runtime files — none currently exist outside `.builders/`. If future Codev features add new runtime artifacts at workspace root, they'd be addressed in their own PRs (and would benefit from this same backfill mechanism)
  • `.codev/config.json` gitignore handling — intentionally not added to the codev-managed block; it's project config that some users will want to commit and some won't
  • Builder runtime files (`.builder-role.md`, `.builder-prompt.txt`, `.builder-start.sh`) — already covered transitively by the `.builders/` entry; no change needed
  • Removing entries from `.gitignore` when Codev no longer needs them — append-only design intentionally; pruning belongs to a separate `codev maintain`-style operation, not `update`
  • Reformatting existing `.gitignore` content — never touch existing lines; only append

Discovered while

Auditing the architect role file lifecycle on 2026-05-27. Traced `.architect-role.md` to `buildArchitectArgs` in `packages/codev/src/agent-farm/servers/tower-utils.ts:184` (Tower writes it on every architect spawn). Cross-checked the codev scaffolding's gitignore generation and found the gap — neither init, adopt, nor update places `.architect-role.md` in the gitignore. Scope expanded 2026-05-27 to include the update backfill so existing projects self-heal on next `codev update`.

Activity

  1. self-assigned this
    on May 27, 2026
  2. added
    bugSomething isn't working
    area/configArea: .codev/config.json / workspace setup
    on May 27, 2026
  3. amrmelsayed commented on May 27, 2026

    @amrmelsayed
    CollaboratorAuthor

    On it! Working on a fix now.

  4. added 2 commits that reference this issue on May 27, 2026
    15036ca
    594d76d
  5. added a commit that references this issue on May 27, 2026
    fb0bc42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/configArea: .codev/config.json / workspace setupbugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions