6.0 KiB
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
- Check the issue is unassigned
- Assign yourself via the Gitea UI (right sidebar → Assignees)
- Start coding
For Agents (Claude, Perplexity, Mimo, etc.)
- Before generating code, call the Gitea API to check assignment:
GET /api/v1/repos/{owner}/{repo}/issues/{number} → Check assignees array - If unassigned, self-assign:
POST /api/v1/repos/{owner}/{repo}/issues/{number}/assignees {"assignees": ["your-username"]} - 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
- Rebase before merge — PRs must be up-to-date with
main. If you have merge conflicts, rebase locally and force-push. - Reference the issue — Use
Closes #NNNin the PR body so Gitea auto-closes the issue on merge. - No bytecode — Never commit
__pycache__/or.pycfiles. The.gitignorehandles this, but double-check. - 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:
Workflow
- Check the issue is unassigned → self-assign
- Check
FEATURES.yamlfor the relevant module - Create feature branch from
main - Submit PR with clear description and
Closes #NNN - Wait for reviewer approval
- 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:
git clone --filter=blob:none --depth 1 <repo-url>
Treeless partial clone — skips tree objects for past commits; best when you need full working tree but not history:
git clone --filter=tree:0 <repo-url>
Sparse checkout — only materialise the subdirectories you actually need:
git clone --filter=blob:none --no-checkout <repo-url> 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:
- Conflicted (not mergeable)
- 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