# Contributing to The Nexus ## Issue Assignment — The Lock Protocol **Rule: Assign before you code.** Before starting work on any issue, you **must** assign it to yourself. If an issue is already assigned to someone else, **do not submit a competing PR**. ### For Humans 1. Check the issue is unassigned 2. Assign yourself via the Gitea UI (right sidebar → Assignees) 3. Start coding ### For Agents (Claude, Perplexity, Mimo, etc.) 1. Before generating code, call the Gitea API to check assignment: ``` GET /api/v1/repos/{owner}/{repo}/issues/{number} → Check assignees array ``` 2. If unassigned, self-assign: ``` POST /api/v1/repos/{owner}/{repo}/issues/{number}/assignees {"assignees": ["your-username"]} ``` 3. If already assigned, **stop**. Post a comment offering to help instead. ### Why This Matters On April 11, 2026, we found 12 stale PRs caused by Rockachopa and the `[claude]` auto-bot racing on the same issues. The auto-bot merged first, orphaning the manual PRs. Assignment-as-lock prevents this race condition. --- ## Branch Protection & Review Policy All repositories enforce these rules on `main`: | Rule | Status | |------|--------| | Require Pull Request for merge | ✅ Enabled | | Require 1 approval before merge | ✅ Enabled | | Dismiss stale approvals on new commits | ✅ Enabled | | Require CI to pass (where CI exists) | ⚠️ Conditional | | Block force pushes to `main` | ✅ Enabled | | Block deletion of `main` branch | ✅ Enabled | ### Default Reviewer Assignments | Repository | Required Reviewers | |------------|-------------------| | `hermes-agent` | `@perplexity`, `@Timmy` | | `the-nexus` | `@perplexity` | | `timmy-home` | `@perplexity` | | `timmy-config` | `@perplexity` | ### CI Enforcement Status | Repository | CI Status | |------------|-----------| | `hermes-agent` | ✅ Active | | `the-nexus` | ⚠️ CI runner pending (#915) | | `timmy-home` | ❌ No CI | | `timmy-config` | ❌ Limited CI | --- ## Branch Naming Use descriptive prefixes: | Prefix | Use | |--------|-----| | `feat/` | New features | | `fix/` | Bug fixes | | `epic/` | Multi-issue epic branches | | `docs/` | Documentation only | Example: `feat/mnemosyne-memory-decay` --- ## PR Requirements 1. **Rebase before merge** — PRs must be up-to-date with `main`. If you have merge conflicts, rebase locally and force-push. 2. **Reference the issue** — Use `Closes #NNN` in the PR body so Gitea auto-closes the issue on merge. 3. **No bytecode** — Never commit `__pycache__/` or `.pyc` files. The `.gitignore` handles this, but double-check. 4. **One feature per PR** — Avoid omnibus PRs that bundle multiple unrelated features. They're harder to review and more likely to conflict. --- ## Path Conventions | Module | Canon Path | |--------|-----------| | Mnemosyne (backend) | `nexus/mnemosyne/` | | Mnemosyne (frontend) | `nexus/components/` | | MemPalace | `nexus/mempalace/` | | Scripts/tools | `bin/` | | Git hooks/automation | `.githooks/` | | Tests | `nexus/mnemosyne/tests/` | **Never** create a duplicate module at the repo root (e.g., `mnemosyne/` when `nexus/mnemosyne/` already exists). Check `FEATURES.yaml` manifests for the canonical path. --- ## Feature Manifests Each major module maintains a `FEATURES.yaml` manifest that declares: - What exists (status: `shipped`) - What's in progress (status: `in-progress`, with assignee) - What's planned (status: `planned`) **Check the manifest before creating new PRs.** If your feature is already shipped, you're duplicating work. If it's in-progress by someone else, coordinate. Current manifests: - [`nexus/mnemosyne/FEATURES.yaml`](nexus/mnemosyne/FEATURES.yaml) --- ## Workflow 1. Check the issue is unassigned → self-assign 2. Check `FEATURES.yaml` for the relevant module 3. Create feature branch from `main` 4. Submit PR with clear description and `Closes #NNN` 5. Wait for reviewer approval 6. Rebase if needed, then merge ### Emergency Exceptions Hotfixes require: - ✅ @Timmy approval - ✅ Post-merge documentation - ✅ Follow-up PR for full review --- ## Large-Repo Clone Strategy Some repos in this org (hermes-agent, the-nexus as it grows) can exceed 1000 tracked files, which causes `git clone --depth 1` to time out and also hits the Gitea tree-API cap of 1000 entries. ### Recommended clone patterns for agents **Blobless partial clone** — fastest overall; metadata arrives immediately, blobs are fetched on demand: ```sh git clone --filter=blob:none --depth 1 ``` **Treeless partial clone** — skips tree objects for past commits; best when you need full working tree but not history: ```sh git clone --filter=tree:0 ``` **Sparse checkout** — only materialise the subdirectories you actually need: ```sh git clone --filter=blob:none --no-checkout myrepo cd myrepo git sparse-checkout init --cone git sparse-checkout set nexus tests # only check out these dirs git checkout main ``` ### Gitea tree API workaround When the tree endpoint returns exactly 1000 entries and you suspect truncation, pass `recursive=1` and page through with the `page` parameter: ``` GET /api/v1/repos/{owner}/{repo}/git/trees/{sha}?recursive=1&page=2 ``` ### Why `.gitattributes` export-ignore exists Directories marked `export-ignore` in `.gitattributes` are excluded from `git archive` tarballs and future sparse-export tooling. This reduces the surface area for export-based agent workflows. It does **not** affect `git clone` directly — use the partial-clone flags above for that. --- ## Stale PR Policy A cron job runs every 6 hours and auto-closes PRs that are: 1. **Conflicted** (not mergeable) 2. **Superseded** by a merged PR that closes the same issue or implements the same feature Closed PRs receive a comment explaining which PR superseded them. If your PR was auto-closed but contains unique work, reopen it, rebase against `main`, and update the feature manifest. --- ## CI/CD Requirements All main branch merges require (where applicable): - ✅ Linting - ✅ Unit tests - ⚠️ Integration tests (pending for the-nexus, see #915) - ✅ Security scans