.agents/ Config Hierarchy Design โ
Date: 2026-07-13 Status: DRAFT / whiteboard (co-edited by Mechelle + agent; expect churn). Not yet an issue or a committed plan. Goal: Replace the single monolithic AGENTS.md with a scoped, tool-agnostic source of truth under .agents/ that houses rules, skills, and personal context, with a shared/personal split (personal = gitignored) and a first-run onboarding flow. AGENTS.md and every harness's native files become generated outputs.
Why โ
- The monolithic
AGENTS.md(project overview + 17 non-negotiable rules + testing + linting + conventions + admin UI conventions) has outgrown one file. In a monorepo, an agent working inservices/api/authshould not be carrying the admin UI conventions. - The ecosystem already went here: Cursor
.cursor/rules/*.mdc(glob-scoped), Copilot.github/instructions/*.instructions.md(applyTo:path scoping).AGENTS.mdis the lowest-common-denominator flat format. - We already bet on "one source, compile out to every tool" for skills (the shipped skill-sync:
2026-03-28-skill-consolidation-design.mdandnpm run sync:skills). This extends the same pattern to rules and context, so it is architecturally consistent, not a new mechanism. - Claude Code already models the shared/personal split we want:
.claude/settings.json(committed) +.claude/settings.local.json(gitignored) +~/.claude/(user-global). This generalizes that to rules/skills/context across every tool.
Proposed structure โ
.agents/
rules/
project/ # committed, repo-wide
workspace/<ws>/ # committed, path-scoped (auth-api, admin, web, ...)
personal/ # gitignored (user-specific overrides)
skills/
<shared>/ # committed
personal/ # gitignored (user-specific skills)
context/ # ALWAYS personal, gitignored, per-user
my-voice.md # communication style / voice (today: user_communication_style.md)
people.md # people the user works with (most sensitive file; justifies the gitignore tier)
active-context.md # what is in flight right now (today: MEMORY.md "Active Projects"); volatile
projects.yaml # structured registry: project names, categories, links, config info
memories/ # durable observations/facts (today: project_/feedback_/reference_ memory files)
# naming TBD: "memories" vs "decisions", see Open QuestionsAGENTS.md (root), CLAUDE.md, .github/copilot-instructions.md, .github/instructions/*, .cursor/rules/*, .gemini/*, and .claude/skills/* are all generated from .agents/ by an extended sync step. We never hand-edit them (they carry an AUTO-SYNCED banner, same as skills today).
Scope tiers โ
| Tier | Committed? | Holds |
|---|---|---|
| project | yes | repo-wide rules everyone follows (today's AGENTS.md core) |
| workspace | yes | rules scoped to a workspace/path (admin UI conventions, web React-import rule, services cloud-service checklist) |
| personal | no (gitignored) | one user's own rule/skill overrides |
| context | no (gitignored) | one user's voice, people, active work, project registry, memories |
Precedence (most specific wins): personal > workspace > project, mirroring settings.local.json overriding settings.json. This must be stated explicitly or agents get whiplash. Extends the existing CLAUDE-vs-AGENTS "Precedence" note.
context/ schema (per Mechelle, 2026-07-13) โ
my-voice.md: first-person communication preferences. Migrated from today'suser_communication_style.md.people.md: people context. The single most sensitive file; the personal/gitignored tier exists largely for this.active-context.md: the "what am I working on right now" layer. Volatile, high update cadence, goes stale fast (see the 11-day staleness warnings on memory today). Separated from durable memories precisely because their cadences differ.projects.yaml: structured data (not prose): project names, relevant categories, links, additional config. YAML because the tooling queries it; the.mdfiles are narrative the model reads. Mixed format is intentional (data vs narrative).memories/(ordecisions/): durable facts and rationale. Generalizes today's Claude memory files.
Loading model (per Mechelle, 2026-07-13) โ
Progressive disclosure, mirroring how Claude Code already treats skills and how MEMORY.md indexes memory.
Always loaded (eager, every session):
- All in-scope rules (project + active-workspace + personal). Rules are guardrails; they must always be present, never recall-on-relevance.
- Every skill's YAML frontmatter (
name+description, the "USE WHEN ..." trigger). This is the lightweight index that lets an agent know a skill exists and when to fire it. The skill body is NOT loaded until the skill is invoked. - Context essentials:
my-voice.md,active-context.md,projects.yaml.
Loaded on relevance (lazy):
- Skill bodies (everything after the frontmatter): loaded when the skill is invoked.
memories/: recalled when relevant to the task, as today.
Net effect: the always-on budget stays small and bounded (rules + one line per skill + a little context), while the heavy content (skill bodies, the memory corpus) stays out of context until needed.
Note on the workspace tier: "rules always loaded" combined with workspace scoping means the in-scope rules always load. Working in apps/admin loads the admin workspace rules eagerly; services/api/auth's do not. Scoping decides which rules are relevant; eager loading keeps those always present.
Active context under parallel sessions (per Mechelle, 2026-07-13) โ
Mechelle runs many sessions at once: some in their own git worktree, some as Claude Code sessions on their own remote branch, plus possibly a branch at the repo root. A single active-context.md is a contention point (last write clobbers) and does not fit N concurrent workstreams. This is the same reality that motivates rule 17 (never switch branches, parallel sessions share the working tree).
Core moves:
- Key active context by branch, not by session. Each parallel workstream is almost always a distinct branch, so one file per branch removes contention by construction:
active/<branch-slug>.md. Anactive/INDEXlists the in-flight branches so any session has peripheral awareness of the others. - Put it at a machine-level home keyed by the repo, NOT in-repo. A gitignored
.agents/context/active/fragments across worktrees: each worktree has its own working directory, so the file in worktree A is not the file in worktree B and they cannot see each other. A single per-user home (~/.agents/context/<repo>/active/) is shared by every local worktree of that repo. Key it by the repo's git common dir or remote URL (stable across worktrees), not the checkout path. This matches where Claude memory already lives (~/.claude/projects/<repo>/memory/), with the same caveat that path-keying must resolve to the repo, not the worktree. - Remote/isolated sessions only share git. A Claude Code session on its own remote branch runs in an isolated environment; the only thing crossing that boundary is the branch itself. Gitignored personal active-context cannot reach it. So for remote sessions the portable "what this branch is doing" truth must live in COMMITTED artifacts on the branch (a
docs/planning/plans/<branch>.md, the PR body). Treat gitignored active-context as a LOCAL ergonomic layer; the committed plan/PR is the portable source of truth.
Seeding new worktrees (per Mechelle, 2026-07-13). When a new worktree is created, carry the durable/personal context into it but give it a fresh active-context:
- Symlink, do not copy, the durable context (
my-voice.md,people.md,projects.yaml,memories/) from the machine-level home into the new worktree's gitignored.agents/context/. Symlinks keep it self-contained-looking with zero drift (a memory added in any worktree is visible in all of them), and they match the pattern the repo already uses of symlinking.env.localinto worktrees (rule 12). Copying would refragment the knowledge base, the exact drift problem this whole spec fights. - Create a fresh
active/<branch>.mdfor the new worktree/branch/session. Active context is per-workstream and is never carried over. - No native git hook fires on
git worktree add, so this is a worktree-creation helper (e.g.npm run worktree:add <branch>) that runsgit worktree add, links the durable context, and stamps a blank active-context. Same scripted-setup model as the current.env.localsymlink step. - Scope: local worktrees only. A remote/isolated session has no access to the machine home or its symlinks, so it still relies on committed artifacts (above). Worktree carry-over is not remote portability.
Open within this:
- Same-branch parallelism (two sessions on one branch/worktree) still contends on one file. Rarer; if needed, append-only entries or per-session sub-sections.
- Staleness/cleanup: prune
active/<branch>.mdwhen the branch is merged or deleted (hook into branch deletion). - Portability vs privacy: the more you want remote sessions to see active context, the more it must be committed, which fights the gitignored-personal principle. Split cleanly: committed = portable work-state, gitignored = personal/local ergonomics.
First-run onboarding โ
When a workspace is set up for the first time, gather context on the user (title, communication style, etc.) and write .agents/context/ (gitignored).
- Trigger: an explicit
npm run onboardor/onboardcommand, NOT an invisible auto-run. Interactive prompts inpostinstallwould hang CI, and an agent interviewing a dev unprompted is jarring. Explicit and re-runnable. - Keying: consider keying per user (e.g. by
git config user.email) so multiple local contributors coexist without clobbering each other. - Seeding the existing operator: for Mechelle, onboarding is a migration of today's
~/.claude/.../memory/intocontext/. For a new contributor, it is a short interview.
Hard problems to solve โ
- Precedence (above): personal > workspace > project, stated explicitly.
- Gitignore-but-still-readable. Tools read from fixed committed paths today (
.claude/skills/, Copilot instructions). A personal skill/rule must land somewhere the tool still scans but git ignores. Options: a gitignoredpersonal/subdir inside each tool's scan path, or a*.local.mdnaming convention ignored by pattern. This decoupling of "where the tool reads" from "what git tracks" is the make-or-break detail. - One source of truth for context. If
.agents/context/holds the voice/memories AND Claude memory (~/.claude) holds them, we recreate the exact drift the AGENTS.md canonicalization just fixed. Decide: eithercontext/is the source and Claude memory is a compiled target, or memory stays Claude-native andcontext/mirrors to the other tools. Not both authored independently. - Cross-tool reach of personal context. Copilot/Cursor/Gemini do not natively read a
context/folder, so the compiler must fan context out into each tool's personal-readable (gitignored) location. More sync surface.
Open questions โ
memories/vsdecisions/: these may be two different things, not one. Memories = observations/facts. Decisions = ADR-style "we chose X because Y." Today's memory conflates them (project_ files often ARE decisions). Do we split them into two subfolders?- Loader/index mechanism (eager/lazy split now decided, see Loading model). Still open: how each tool surfaces the always-on skill-frontmatter index and in-scope rules. Claude Code does this natively; Copilot/Cursor/Gemini need the compiler to assemble an equivalent always-loaded index for them.
- What does "personal" mean here? Confirmed 2026-07-13: user-specific, gitignored. Not the same as the project-level Operator Preferences, which stay committed because there is effectively one operator.
- Source location naming:
.agents/(neutral, discoverable) vstooling/agents/(consistent with today's.agents/skills/). If we adopt.agents/, today's.agents/skills/source relocates under it, andsync:skillsrepoints. Keep skills-source and rules-source together either way.
Staging / rollout โ
Do NOT build the whole compiler at once, and keep it OFF the current skills PR.
- Carve the workspace-specific sections out of
AGENTS.mdinto scoped rule files (admin UI conventions, web React rules, services cloud-service checklist). Highest value, teaches the shape. - Stand up the rules compiler that regenerates
AGENTS.mdfrom.agents/rules/, reusing the skill-sync machinery; add a validate gate (drift check) like Skill Sync. - Add the
context/schema +/onboardflow; migrate Claude memory into it. - Fan personal tiers out to each tool's gitignored read-location.
Relationship to prior work โ
- Builds directly on the shipped skill-sync (
2026-03-28-skill-consolidation-design.md). That solved "one source for skills"; this generalizes it to rules + context with shared/personal tiers. - Supersedes nothing yet;
AGENTS.mdremains canonical until step 2 makes it generated.