# `<PROJECT_NAME>` — Contributor Directives

These directives govern all development in this repository.  Follow them in
every session without being asked.

<!-- PROJECT: This file is the operational rulebook for AI / human
contributors.  It is consulted at the start of every working session.
Keep the directives short, specific, and rooted in concrete failure
modes you have actually seen — abstract guidance gets ignored. -->

**Project orientation:** if this is your first read, also skim
[`README.md`](README.md) for the quickstart and
[`ARCHITECTURE.md`](ARCHITECTURE.md) for the design overview.
[`CHANGELOG.md`](CHANGELOG.md) is the authoritative current-state
shipping log; [`docs/CAPABILITIES.md`](docs/CAPABILITIES.md) is the
operator-facing capabilities + known-limitations document.

---

## What this project is

<!-- PROJECT: Name your two (or N) main concerns here.  Examples:
"Backup" + "Migration", "Frontend" + "Backend", "Library" + "CLI",
"Ingestion" + "Query", "Compiler" + "Runtime".  If your project is
single-concern, just say so — the template's "two concerns" framing
is illustrative, not mandatory.  The point is to give a contributor a
mental model in 30 seconds. -->

`<PROJECT_NAME>` handles `<N>` independent (but co-hosted) concerns:

1. **`<CONCERN_A>`** — `<one-line description; pointer at the package /
   module / route group that owns it>`.  Mocked in tests at a single
   entry point: `<entry_point_name>`.
2. **`<CONCERN_B>`** — `<one-line description; pointer at the package /
   module / route group that owns it>`.

A change to one concern rarely touches the other.  When in doubt about
where code belongs, ask: `<one-line decision rule that distinguishes
the concerns>`.

---

## Parallel platform development

<!-- PROJECT: Only relevant if your project ships on multiple
platforms (web + desktop, library + CLI + service, server + mobile,
etc.).  If single-platform, delete this section and the next.  The
Netcanon source uses this section to enforce that web and desktop
shells stay at feature parity. -->

`<PROJECT_NAME>` ships on `<N>` platforms.  All must be kept at feature
parity.

| Platform | Package | Entry point |
|---|---|---|
| `<PLATFORM_A>` | `<package_name>` | `<entry command>` |
| `<PLATFORM_B>` | `<package_name>` | `<entry command>` |

**Rule:** whenever a functional feature is added or changed on one
platform, the equivalent must be implemented on the other platform in
the same commit (or the same branch, if it spans multiple commits).
Never leave one platform behind.

This applies to:
- New API endpoints / public functions
- New UI surfaces
- New schema fields persisted to disk
- New external integrations
- New application settings that affect behaviour

### Platform-specific exceptions

The following are intentionally platform-specific and do **not**
require cross-platform equivalents:

<!-- PROJECT: List per-platform features that are intentionally
asymmetric.  Example from Netcanon: system tray icon (desktop only),
public-network binding flags (web only).  Be explicit so contributors
don't waste cycles on parity work that isn't wanted. -->

* `<EXAMPLE: native window chrome — desktop only>`
* `<EXAMPLE: --host / --port flags for public binding — web only>`

---

## Documentation Sync Checklist

Code and docs drift silently — the only reliable defence is to treat
docs as part of the definition of "done".  When you change something,
update its documentation in the SAME commit, not a follow-up.  The
mapping below is concrete; audit every applicable row before you run
`git commit`.

<!-- PROJECT: This is the heart of the matrix-honesty discipline.
Each row maps "kind of change" → "doc that must be updated in the
same commit".  Start with the rows below; add your own as failure
modes surface (a stale docstring that misled a contributor → new row
about that docstring class). -->

| If you change / add … | Then touch … |
|---|---|
| A new interactive HTML element (button, input, link, row, `<select>`, `<option>`) | `<your project's testid reference doc>` — document the new `data-testid` in the appropriate page section.  *(Only applies if your project has a UI tested with stable selectors.)* |
| A new `<COMPONENT>` under `<core/>` | The top-level `README.md` (if it lists components) and `ARCHITECTURE.md` (if a new pattern). |
| A new public function or endpoint | Its docstring (Google-style sections for Args / Returns / Raises) — and the module docstring if it enumerates contents. |
| A function gains a new parameter or changes return shape | Its docstring.  Stale `Args:` lists are a top-three source of contributor confusion. |
| A pipeline-stage signature (frozen for API stability) | **DON'T.**  Add a NEW function instead; the module docstring tracks which are frozen.  *(Only applies if you have frozen public-surface contracts.)* |
| A new pytest marker in `pyproject.toml` or a fixture that meaningfully changes how a whole test tier runs | `tests/README.md` — markers without doc entries are invisible to contributors running `pytest -m <name>`. |
| A new hard rule / cross-cutting invariant surfaced by a bug | This file — add to the "Hard Rules (Never Break)" section with a one-line rationale pointing at the failure mode. |
| A capability / feature declaration changes (a `<COMPONENT>` flips between supported / lossy / unsupported) | Matching test or expectation file under `<your project's expectation directory>`.  *(Only applies if your project has explicit capability declarations.)* |
| A file-tree listing or "contents map" in any doc | Either update the listing in the same commit as the new file, OR convert the listing to a pointer ("see `<directory>/` for the current set").  Exhaustive inventories that enumerate every file become a maintenance tax — prefer one-line pointers unless the enumeration carries load-bearing explanation. |
| `<CORE_ABSTRACTION>` gains a new field | The walkthrough doc for "adding a field to `<CORE_ABSTRACTION>`" — the worked example must stay accurate.  *(Only applies if your project has a single central data model.)* |
| A new `<EXTERNAL_INTEGRATION>` (vendor, plugin, backend) | The README's integration list and the worked example doc for "adding a `<EXTERNAL_INTEGRATION>`". |
| The Nth commit (3-5+) of a thematic series shipping pieces of the same conceptual subsystem | `ARCHITECTURE.md` — at the Nth commit, ask: "does the architecture doc have a section describing this concept, or only the piecemeal mechanics?"  If the concept is absent, add a short section in that same commit. |

Rule of thumb: if a future contributor could plausibly search for the
thing you just added and not find its rationale, there's a doc gap to
close.

---

## Cross-reference discipline

Every doc in `tests/`, `docs/`, and top-level `*.md` should open with
(or end with) a "See also" line pointing to its two or three closest
peers.  Concretely:

<!-- PROJECT: List the canonical cross-references you want to enforce.
The pattern is: each doc lists 2-3 peer docs in its footer, and the
peers list it back in their footers.  When you add a new sibling doc,
add the reciprocal link in the existing peers in the same commit —
one-way cross-references rot faster than numbers do. -->

* `<EXAMPLE: tests/README.md → testid_reference.md, fixtures/README.md, integration/README.md>`
* `<EXAMPLE: ARCHITECTURE.md → CONTRIBUTING.md, docs/CAPABILITIES.md>`
* `<EXAMPLE: README.md → ARCHITECTURE.md, AGENTS.md, tests/README.md>`

A contributor who lands on one doc should be one hop from the others.
When you add a new sibling doc, add the reciprocal link in the
existing peers in the same commit.

---

## Code organisation

<!-- PROJECT: Brief pointer at where the code tree's layout is
documented.  In Netcanon this is a single sentence pointing at
tests/README.md and the project root README's Layout section.  The
goal: a contributor finds the layout in ONE hop from this file. -->

See [`tests/README.md`](tests/README.md) for the full test-tier layout
and [`README.md`](README.md) for the top-level repo tree.

---

## Selectors / public-surface stability

<!-- PROJECT: One section per category of stable public surface that
contributors must not silently break.  Examples:
- UI test selectors (data-testid)
- Public function signatures (frozen via tests)
- Database schema migrations (forward-only)
- API route URLs (versioned)
- File naming conventions (parsed at runtime — see Hard Rules below)
Add or remove categories based on what your project actually has. -->

`<EXAMPLE: All interactive HTML elements must carry data-testid
attributes.  E2E tests use these exclusively — never CSS classes or
element structure.  See tests/testid_reference.md for the inventory.>`

`<EXAMPLE: Pipeline-stage function signatures in
<core_pipeline_module> are frozen.  Tests and external callers
depend on the exact shape — add NEW functions instead.>`

---

## Hard Rules (Never Break)

These are invariants surfaced by real bugs.  Each rule has a one-line
rationale because abstract guidance gets ignored.  When a new bug
surfaces a new invariant, add it here in the same commit that fixes
the bug.

<!-- PROJECT: The list below is GENERIC scaffolding — every project
benefits from these.  Add your project-specific rules in the
PROJECT-SPECIFIC RULES section below as failure modes surface. -->

### Generic rules (apply to most projects)

- **Never** commit real credentials, secrets, API keys, or production
  identifiers (IPs, hostnames, real customer names) — even in test
  fixtures.  Synthetic test data should LOOK realistic but be
  obviously fake (e.g. `192.0.2.x` from RFC 5737, `device-1`,
  `$5$REDACTED$REDACTED01`).
- **Never** skip pre-commit hooks (`--no-verify`) or bypass signing
  unless the user has explicitly asked for it.  If a hook fails,
  investigate and fix the underlying issue — the hook is doing what
  it's there for.
- **Never** patch external SDK clients (e.g. SSH library, HTTP
  client, third-party API wrapper) directly in tests.  Patch the
  project's single factory / dependency-injection point instead.
  Multiple patch sites drift apart and create flaky tests that pass
  individually but fail in the suite.
  <!-- PROJECT: Replace with your project's actual factory point.
  In Netcanon this is `netcanon.api.routes.backups.get_collector`. -->
- **Never** land a code change without updating the docs it renders
  stale.  The Documentation Sync Checklist above is concrete — if a
  row applies to your change, touch the listed doc in the same
  commit.  Stale docs are a bug; they just don't crash loud enough
  to be caught by CI, which is why the discipline has to live in the
  workflow.
- **Never** hard-code a count, LOC figure, test tally, or other
  number-that-changes in prose documentation unless the same commit
  adds a CI / test guard that keeps the number honest.  Use
  qualitative phrasing ("the largest module in the app", "dozens of
  fixtures across N integrations") or link to the authoritative
  source (`pytest --collect-only`, the file itself, the live
  results doc).  Numbers in *test assertions* fail loudly and are
  fine; numbers in *prose* rot silently.
- **Never** make active lies in operator-facing messages, docstrings,
  or capability declarations.  If a feature is half-shipped, declare
  the half that doesn't work as `unsupported` (or whatever your
  project's "we don't do this" tier is called).  Silently dropping
  data the user supplied is a worse outcome than a banner saying
  "this section was not translated."
- **Never** change the signatures of frozen public-surface functions.
  External callers and tests depend on the exact shape.  Later phases
  add NEW public functions; existing surfaces stay frozen.
  <!-- PROJECT: Identify your frozen surfaces explicitly.  In
  Netcanon the migration_pipeline.py public functions are frozen. -->
- **Never** assert on a request's *initial* response body for final
  job state on async / background-task endpoints — the initial
  response always serialises before the task runs.  Always poll /
  re-fetch by ID to read the completed state.
  <!-- PROJECT: Drop this rule if your project doesn't have async
  job endpoints. -->

### Project-specific rules

<!-- PROJECT: Add your project's specific rules here.  Examples from
real projects:
- "Never use `terminal length 0` for CLI paging — use space-injection
  via the device YAML setting."
- "Never author a `type_key` containing `_` or `.` — these are the
  separators the file-store filename grammar uses."
- "Never bind the dev server to 0.0.0.0; it must stay on 127.0.0.1."
Each rule should have a one-line rationale pointing at the failure
mode that motivated it. -->

`<EXAMPLE: Never <project-specific anti-pattern>.  <One-line rationale
pointing at the bug class this prevents>.>`

---

## Operator-trust language

When writing operator-facing copy (error messages, banners, validation
output, docstrings consumed via help text), apply the matrix-honesty
discipline to the *language*:

* **Don't claim what you can't deliver.** "Migration successful" when
  half the source was silently dropped is a lie that erodes trust at
  exactly the moment the operator can't catch it.
* **Don't over-warn until operators tune you out.** A warning on every
  page becomes invisible.  Warn on real problems; trust the operator
  to read the explicit "this is what we don't do" page.
* **Name the boundary.**  When you decline to do something, say *why*
  in operator-relevant terms (not implementation terms).  "This tool
  doesn't auto-translate firewall rules because cross-vendor semantics
  don't translate cleanly — see CAPABILITIES.md" is a useful
  declination; "Tier 3 unsupported" alone is a non-answer.

---

## See also

- [`README.md`](README.md) — project orientation and quickstart
- [`ARCHITECTURE.md`](ARCHITECTURE.md) — design overview
- [`docs/CAPABILITIES.md`](docs/CAPABILITIES.md) — operator-facing
  capabilities + known limitations
- [`tests/README.md`](tests/README.md) — test-suite layout and mocking
  strategy
- [`docs/templates/METHODOLOGY.md`](docs/templates/METHODOLOGY.md) —
  the matrix-honesty discipline behind this file's shape
  *(template authors: pointer at the upstream methodology doc)*
