Skip to content

Git Discipline Standard

Parent contract: Capability Governance Framework (NOT_FOUND) Version: 2.1 Applies to: All roles and systems interacting with Git (Agents, Developers, CI).

1. Core Principles

The repository is a governed state machine. Git history is immutable engineering evidence. - Commit metadata is immutable. Agents and developers must never append trailers, amend commits, rewrite messages, or inject attribution unless explicitly authorized. See commit-metadata-governance.md (GOV-COMMIT-META-001). - Forward-revert only. No history rewrites (rebase, amend, force push). - Commit Frequency: Commit atomic, logical changes. Do not wait for the end of the session to commit a massive diff. - Push Rules: Push to the remote origin as soon as a logical unit of work is verified. Never leave unpushed work in the local environment at the end of a session.

2. Forbidden Commands

  • git add . (Forbidden unless explicitly authorized by a specialized orchestration packet)
  • git add -A
  • Broad git add <folder> without explicit path verification.
  • git restore . (Destroys work, completely forbidden)
  • git restore --worktree .
  • git reset --hard
  • git reset (without explicit authorization)
  • git clean
  • git stash (without explicit authorization)
  • git rebase
  • git commit --amend
  • git push --force
  • Broad checkout/restore of the repository.
  • Deleting untracked files without exact path authorization.

3. Critical Distinctions

  • git restore --staged <path> unstages the file but safely preserves working-tree content.
  • git restore . destroys work and is completely forbidden.

4. Mandatory Rules

  1. Explicit Staging: Stage explicit files only.
  2. Prove Branch: Prove the current branch before any push.
  3. Prove Commit: Prove commit files after the commit.
  4. No Ambiguity: No commit if unexpected staged files exist, or if staged/unstaged copies of the same target create ambiguity.
  5. Named factory branch: Always work on a named branch for the active Bead. Factory prefixes feature/, fix/, bugfix/, chore/, docs/, and work/ are the GitLab MR path. Do not encode operator names or runtime identity. Target MRs at release/v0.1.x. See git-completion-contract.md. Portable CI runners and release-cut ancestry tips: ci-portable-runner-and-topology.md.
  6. No Branch Mismatch: Never push if a branch mismatch exists between local and remote tracking.
  7. Clean History: Ensure no trailing whitespace or conflict markers are committed.
  8. Portable paths: Never commit personal workstation topology. Notation reference: portable-paths-and-workstation-privacy.md. Enforcement owner: blueflyio/security-policies policy repository-portability-new-debt (#24) via blueflyio/gitlab_components. Committed docs use [WORKSPACE-ROOT], [PROJECT-ROOT], repository-relative paths; ${HOME} only in executable shell examples.
  9. Receipts: Every push must be accompanied by an execution summary Receipt.

5. Governance Lifecycle

Every significant action or repair must follow this sequence: 1. STOP: Freeze execution immediately upon identifying a violation or drift. 2. PROVE: Collect immutable evidence (git status, ls, etc.). 3. CLASSIFY: Determine the artifact type, ownership, and violation class. 4. REPAIR: Perform the minimum approved repair required to restore compliance. 5. VALIDATE: Demonstrate that the repair worked via testing or re-inspection. 6. RECEIPT: Produce a machine-readable governance receipt. 7. OWNER REVIEW: Obtain human approval for destructive or high-risk changes.

6. Execution Location

  • Never perform Git development operations on a NAS-mounted path — worktree create, checkout, commit, rebase, status, diff-heavy work, builds, tests — on any path under [NAS-ROOT] or any Synology/NAS mount, on any machine. NAS filesystem latency causes commands to hang indefinitely with no lock file or other cause (observed: a commit hanging ~4 minutes, gc lint and a 47-file rsync repeatedly stalling against a NAS path, then completing instantly once copied to local disk — same command, same repo, only the mount changed). If a git or latency-sensitive command hangs against a NAS-mounted path, do not retry with a longer timeout — reclone/copy to local disk and redo the operation there.
  • NAS role: the authoritative engineering workspace for canonical assets — repositories, shared libraries, Drupal modules, Recipes, Themes, Site Templates, DDEV plugins, documentation, and build assets belong there. It is never an active git working tree, though: read/inspect NAS-mounted files directly, but perform all git operations (commit, push, worktree management) from a local clone — fetch from the NAS-mounted canonical repo into local refs, then create the actual worktree on local disk.
  • Durable sources of truth, and only these: Git repositories, the NAS (in an approved canonical location), 1Password (secrets), and Beads (work tracking/findings). Do not create additional systems of record. Work that is not committed to Git, stored in an approved NAS location, or intentionally recorded in Beads should be treated as temporary and may be lost without notice.
  • Developer workstations are disposable. Uncommitted local changes may be lost at any time — commit and push promptly, do not batch work at end-of-session. <WORKSPACE_ROOT>/Scratch is the only approved local working area for temporary, non-authoritative files (path is per-developer, not a shared literal). Git worktrees (local disk, never NAS) are the only place for source code changes. On the Mac BluCity operator host, that place is CANONICAL_ENGINEERING_WORKTREE_ROOT=[WORKSPACE-ROOT]/worktrees — an independent physical directory at the estate root. FORBIDDEN=[WORKSPACE-ROOT]/BluCity/.gc/worktrees — .gc/worktrees is reserved strictly for Gas City internal runtime and must never be used for operator or agent engineering checkouts. git worktree add must use [WORKSPACE-ROOT]/worktrees/<task>. Never /tmp. .claude/worktrees are tool sandboxes, not the factory root. A change is durable only when (1) committed to a Git worktree and pushed, or (2) stored on the NAS in an authorized canonical location — never ~/.claude, /tmp, runtime scratchpads, hidden tool directories, or any other untracked workstation file, regardless of how long-lived that directory feels.
  • Assume concurrent developers/agents. Coordinate through Git — commits, branches, MRs — not through shared local files or uncommitted state. Before starting work in an existing worktree, check for signs of another writer (unexpected diffs, recent file mtimes not attributable to your own session) before touching anything; treat a detected collision as reason to leave those files untouched and move to a different worktree, not to merge concurrent work manually.
  • Never scp or otherwise copy repository files between machines as a substitute for Git sync. The only path from one machine to another: clone/worktree locally → commit → push → MR → merge → pull on the target machine.

6.1 Sequence of Done

Binding contract: git-completion-contract.md (ES-GITCC). A commit, a push, an open MR, or a green mergeable MR is not done.

  1. Implementation (verified)
  2. Tests (passed)
  3. Documentation (canonical)
  4. Commit (atomic) — valid work must be committed
  5. Push (remote origin — through the gitlab-bluefly alias, see §7) — valid commits must be pushed
  6. Open or update MR targeting release/v0.1.x
  7. CI pass + required review + conflict-free
  8. Merge to release/v0.1.x — do not stop at merge-ready; do not ask Thomas to merge ordinary feature work; if merge is unauthorized, BLOCKED_BY=MERGE_PERMISSION_OR_OPERATOR_GATE
  9. Shared CI publishes the next development package when the project produces an artifact
  10. Verify containment (git merge-base --is-ancestor <branch-sha> origin/release/v0.1.x)
  11. Remove completed worktree and merged local branch (trash, never rm)
  12. Reconcile the feature Bead with MR/commit/package evidence. Do not wait for release → main.
  13. Receipt (verified)
  14. Knowledge Promotion (canonical)

7. Workspace & Mount Discipline

Never perform Git development operations — worktree create, checkout, commit, rebase, status, diff-heavy work, builds/tests — on a NAS-mounted repository path (anything under [NAS-ROOT], or any Synology/NAS mount, on any machine). Clone or create the worktree on that machine's own local disk, do all Git work there, and synchronize exclusively through GitLab (git fetch + branch reset to start, commit + push + MR to finish). The NAS holds mirrors, bundles, backups, build artifacts, model/caches, and archives — never an active working tree.

Observed cause: a git commit hung for minutes on a NAS-mounted repo with no lock file, no hook, no GPG-signing delay — the mount's latency/filesystem semantics were the cause, not the Git operation itself. If a Git command (or any latency-sensitive tool call) hangs against a NAS-mounted path, do not retry with a longer timeout. Confirm no lock/active process holds it, classify the mount as unsuitable, preserve any uncommitted work, re-clone or copy to local disk, redo the operation locally, and sync via GitLab. Never scp or otherwise copy a repository between machines as a substitute for Git sync — every machine keeps its own local clone and syncs only via fetch/pull/push + Merge Requests.

8. Shared Index Ownership

Shared index files — ADR indexes, README indexes, catalogs, registries, inventories, any file whose entire content is a list of entries other files/branches append to — always have a single active curator during convergence. Parallel agents may modify content files freely; only the designated curator updates the shared index itself.

Discovered as a repeat failure mode (not a one-off): Engineering-Standard/decision-records/ADR-INDEX.md collided twice — ADR-0003/0004 (pre-2026-07-07) and ADR-0018 (2026-07-29, two parallel sessions same day). Both collisions were two independent writers appending rows to the same index file in parallel, on separate branches, both targeting main.

Rule: 1. Before appending to any shared index, check for other open branches/MRs touching that exact file. If one exists, coordinate onto that branch instead of opening a sibling one. 2. Large multi-item convergence work (migrating a backlog, a corpus, a batch of entries) goes through one integration branch, not parallel independent branches that each touch the same index. 3. Re-verify the index against origin/<default-branch> immediately before claiming the next sequential slot (a number, a row, a registry key) — not just once at session start — to catch slots claimed by branches that merged in the meantime. 4. On collision, the later-merged entry renumbers/relocates, never the earlier one. Preserve both entries' content; fix any internal self-reference (a header, an ID field) to match the renumbered identity in the same commit — a mismatched self-reference is a defect in its own right, not cosmetic.

See decision-records/README.md for the concrete application of this rule to ADR numbering specifically.

8.1 Required Fields Per Shared Index

Documentation alone is incomplete — every shared index in the estate must have all five of these answered explicitly, not left implied:

Field Question it answers
Authority Who/what decides the canonical current state of this index — a named owner, or "whoever's branch merges first"?
Allocation algorithm How is the next key/number/slot chosen (sequential increment, UUID, name-based)? Where is "current max" read from before allocating?
Collision policy When two entries claim the same key, which one wins by default?
Merge policy Single integration branch, or independent branches merged by whichever lands first?
Recovery policy The exact mechanical steps to resolve a collision once found (rename, header fix, commit)

ADR-INDEX.md (proven collision, 2×): - Authority: whoever's branch merges into main first for that number. - Allocation algorithm: sequential increment; read current max from origin/main's ADR-INDEX.md immediately before claiming. - Collision policy: later-merged entry renumbers. - Merge policy: one integration branch per convergence effort (§8 rule 2). - Recovery policy: rename file + fix internal H1 header + update index row, same commit (§8 rule 4).

8.2 Enforcement Audit (2026-07-29)

Index Allocation Deterministic? Can concurrent branches collide? Recovery defined? Documented? Auto-enforced?
decision-records/ADR-INDEX.md Sequential ADR-NNNN Yes (increment) Yes — proven 2× (0003/0004, 0018) Yes (§8.1) Yes (this doc) No — gap
authority/Decision-Registry.md Sequential DEC-XXX Yes Yes (same pattern as ADR, unpopulated so far — 0 rows) No Partial (schema only) No — gap
authority/Capability-Registry.md CAP-XXX-NNN Yes Yes (same pattern; partially populated) No Partial (schema only) No — gap
authority/Governance-Policy-Registry.md Policy IDs Yes (implied) Yes (same pattern, unpopulated so far — 0 rows) No Partial (schema only) No — gap
authority/Technology-Registry.md Technology name (not sequential) N/A — name-keyed Only via duplicate/near-duplicate name, not numbering race No Partial No
authority/Authority-Registry.md Domain name (not sequential) N/A — name-keyed Same as above No Partial No
reference/addon-reference-catalog.md Addon name (not sequential) N/A — name-keyed Low — external package names are naturally unique No N/A No
catalog/{capabilities,technologies,platforms,products,tools}.md Not yet inspected in depth Unknown Unknown No Partial No
indexes/{BLU-BIBLE,BLUEFLY-PRODUCT-DEFINITION}.md Navigation index, not allocated-ID N/A Low — prose links, not claimed slots No N/A No

Gap: every sequential-ID index (ADR-INDEX, Decision-Registry, Capability-Registry, Governance-Policy-Registry) shares the exact ADR-INDEX failure mode and has zero automated enforcement. ADR-INDEX is the only one with proven, repeated collisions — the other three are structurally identical but currently low-traffic (sparsely populated), so the risk is latent, not yet realized.

Implemented: CI duplicate-ADR-number check, .gitlab-ci.yml validate:adr-index-unique job — fails the pipeline if ADR-INDEX.md or the decision-records/ filenames contain a duplicate ADR number. Scoped to ADR-INDEX only (the one with proven, repeated collisions); the other three sequential-ID registries remain a documented, not-yet-automated gap — extending the same check to them is mechanical once they have real row volume to protect.

9. Remote Authority and SSH Alias

All Bluefly Git traffic uses GitLab as the source authority. Repositories reach it through the governed SSH host alias gitlab-bluefly, never through an ad-hoc hostname, token URL, or embedded credential.

Alias definition (client ~/.ssh/config; the identity file is operator-provisioned and is never committed):

Host gitlab-bluefly
  HostName altssh.gitlab.com
  Port 443
  User git
  IdentitiesOnly yes

Accepted remote URL forms — both resolve through the alias and are in active use across the estate:

gitlab-bluefly:<namespace>/<project>.git
git@gitlab-bluefly:<namespace>/<project>.git

Mandatory rules

  1. Alias only: Push and fetch through gitlab-bluefly. Do not substitute a direct hostname or an alternate transport for convenience.
  2. No embedded credentials: Never place a token, password, or key path in a remote URL, a commit, or any tracked file. Credentials come from the operator's provisioned identity or the governed secret authority.
  3. No key material in the repository: Private keys are never committed, and never stored on a shared or SMB-exposed path.
  4. Prove the remote before pushing: git remote get-url origin must resolve through the alias. This satisfies Rule 4.6 (No Branch Mismatch) at the transport layer.
  5. Identity consistency: Commits pushed through the alias must carry a committer identity the GitLab account owns; pushes with a mismatched committer email are rejected by push rules.

Applies to Rule 4.6 and to Sequence of Done step 5 (Push).