This document defines the working agreement for AI-assisted pair programming. The goal is to maximise code quality, preserve architectural integrity, and prevent long-term technical debt while working iteratively with AI.
This protocol is binding for for this sessions.
AI effectiveness depends on correct global context. Before feature work begins, the AI must acquire a stable, high-signal mental model of the system.
The following context must be provided once per project or session reset:
System Identity
- Product purpose and primary user
- Core constraints (correctness, scale, latency, money, safety)
- Explicit non-goals
Architectural Skeleton
- Major layers (e.g. UI, Application, Domain, Infrastructure)
- Direction of dependencies
- Where state is allowed to exist
- Where side effects are allowed to occur
Boundaries
- External systems and integrations
- Trust boundaries
- Serialization boundaries
- Async and IO boundaries
Invariants
- What must always be true
- What must never happen
- What is allowed to be eventually consistent, slow, or lossy
Conventions
- Naming rules
- File placement rules
- Testing philosophy
- Error handling conventions
Project Map
- Directory tree
- Purpose of each major folder
- Entry points
- No file bodies unless explicitly requested
This context is treated as canonical and must not be reinterpreted or overridden unless explicitly stated.
Architectural decisions, invariants, and conventions are assumed stable across turns.
If any of these change, the change must be stated explicitly before proceeding.
Full project dumps are allowed once, during onboarding only.
After onboarding:
- Only changed files, diffs, or affected modules are provided
- The phrase -“All other context remains unchanged”- must be assumed unless stated otherwise
The AI must not request or expect repeated full reloads.
If required context is missing or ambiguous, the AI must stop and request clarification before proposing code.
Guessing architecture or invariants is not allowed.
These rules are non-negotiable and apply to all work.
- Test-Driven Development is mandatory. No production code is written without a failing test.
- Behaviour over implementation. Tests validate observable behaviour, not internal structure.
- Immutability by default. State is immutable unless explicitly justified.
- Schema-first design, with nuance. Validate data at trust boundaries at runtime; rely on static types internally.
- Semantic refactoring. Abstractions are driven by meaning and intent, not by file structure or convenience.
- Explicit documentation. Capture decisions and learnings while context is fresh.
TDD is the foundation of this workflow.
Every line of production code must exist to satisfy a failing test. Tests define the contract; implementation is secondary.
Before writing a test, identify:
- The affected layer
- The relevant boundary
- The invariant(s) involved
Explain why this behaviour belongs here.
- Do not write production code without a failing test
- Tests must describe desired behaviour, not implementation details
- Failures must be intentional, meaningful, and observable
- Write the minimum code required to pass the test
- Do not introduce behaviour not demanded by a test
- Commit immediately once the test passes
- Assess refactoring opportunities after every green state
- Refactor only to improve clarity, safety, or maintainability
- Commit before refactoring
- All tests must remain green throughout
Commit history must clearly demonstrate the RED → GREEN → REFACTOR cycle.
Expected pattern:
test: add failing test for pricing calculation
feat: implement pricing logic to satisfy test
refactor: extract pricing rules into pure functions
If this evidence is not visible, it must be explicitly justified. Exceptions are rare and documented.
Coverage claims are not trusted by default.
- Always run coverage locally before approving changes
- Do not rely solely on CI or third-party reports
- Coverage must be meaningful; line coverage without behavioural validation is insufficient
- Domain first. Define models and business rules before services or UI
- No logic in the UI. UI components are purely presentational
- All computation, state changes, and decisions live in the domain or application layer
- Strict typing. No
dynamic - Immutability. Use
finalby default; data models supportcopyWithsemantics - Consistency. Use Material 3 theming; no hardcoded values
- Small, focused commits
- Immediate review and course correction when divergence is detected
- Failures are handled explicitly
- Use result-based patterns or
AsyncValue - Unhandled states are defects
- Refactors are explicit and labelled
- When reporting issues, specify the affected layer
- Naming must describe observable behaviour and intent
- The first behaviour to test must be explicitly justified
- If context is insufficient, the AI must request clarification before proceeding
This protocol exists to ensure the AI behaves like a senior engineering partner; not an autocomplete engine.