Files
Raptor/AGENTS.md
T
NiccoloN e166ff7e1d
Validate Operations / validate-operations (push) Has been cancelled
better AGENTS.md
2026-06-05 11:36:01 +02:00

12 KiB

  • Always read the full README.md before doing anything
  • Build commands:
    • cmake --build ./build_release
    • cmake --build ./build_debug
  • Never use ninja directly: it bypasses cmake's configuration and invalidates the build cache
  • Always try the release build first before building with the debug version
  • Use the debug build only when it is useful to obtain a clear stack trace with symbols, inspect names, place breakpoints, or test a small case interactively
  • The debug build is very slow, so use it only on small fast tests such as operation validations, not on network validations

Core engineering philosophy

  • Clean architecture matters as much as making the immediate test pass
  • Prefer fixes that preserve clear ownership boundaries, explicit invariants, and simple dataflow
  • Do not stack compensating fixes on top of earlier mistakes. If the current approach is becoming messy, stop and explain why
  • A correct fix should usually make the responsible producer, resolver, verifier, or lowering own the behavior directly
  • Avoid late repair passes, defensive cleanup, or broad rewrites when a cleaner owner-side fix is possible
  • Do not hide an upstream modeling bug by normalizing it later in the pipeline. Fix the producer when the producer owns the invariant
  • Prefer patterns/rewrites for local IR canonicalization. Use module walks only when pass-level structural analysis genuinely requires them
  • Prefer compact, structured designs over long case-by-case implementations

Think before coding

  • State assumptions explicitly before implementing when they affect the design
  • If multiple interpretations exist, present them instead of silently choosing one
  • If a simpler approach exists, say so and prefer it unless there is a clear reason not to
  • If something is unclear, stop, name what is confusing, and ask
  • If the requested or obvious approach would make the architecture worse, push back and propose a cleaner alternative

Code changes

  • Keep changes minimal and localized to the relevant parts of the code
  • Preserve the existing naming conventions and coding style used in the surrounding code
  • Keep code easy to read, well organized, and suitable for future extensibility
  • A function must not exceed roughly 200/250 lines. If a change pushes a function beyond that, extract focused helpers
  • Prefer clear naming and structure over comments. Add comments only when they materially improve clarity
  • Do not rename symbols, move files, or restructure modules unless that is necessary for the requested change
  • Avoid duplicate ad-hoc logic. If the same concept appears in multiple places, consider whether it deserves a shared helper/API
  • When adding a helper or API, ask:
    • Could this be useful to another component now
    • Is another component already implementing the same idea differently
    • Is this likely to be needed by a future adjacent component
    • What is the narrowest useful abstraction
    • What is the correct ownership level for this API
  • If a shared API is justified, place it at the lowest clean layer that can be used by all relevant consumers without creating dependency cycles or leaking policy across layers
  • If an existing component should use a newly introduced shared API, refactor that component in the same patch when doing so is directly related and reduces duplication
  • Do not create broad frameworks just because a helper might someday be useful. Shared APIs should encode a real reusable concept, not speculative generality
  • If the reusable abstraction is plausible but not clearly needed yet, keep the code local and mention the possible future extraction separately

Avoid case-listing designs

  • Avoid solving problems with large chains of if/else, switches, or repeated special cases that enumerate every possible situation
  • Long case listings tend to overfit the current tests, grow the codebase, and hide the underlying abstraction
  • When you see a growing list of special cases, stop and look for the shared concept, data model, interface, or normalization step that would make the cases collapse
  • Prefer table-driven logic, traits/interfaces, small reusable predicates, structured dispatch, or producer-side normalization when they express the invariant more directly
  • A few explicit cases are fine when the domain is genuinely small and closed
  • If the list is likely to grow, refactor toward a cleaner and more compact design instead of adding another branch
  • When keeping a case list is the pragmatic choice, explain why the domain is closed or why a broader abstraction would be premature

Ownership and invariants

Before implementing, identify the owner of the behavior:

  • A producer should emit IR/data that satisfies the contract of the next stage
  • A lowering should make representation changes explicit and semantically correct
  • A resolver should resolve existing structure without silently changing semantics
  • A verifier should reject invalid states with bounded, actionable diagnostics
  • Codegen should assume verified invariants and fail clearly if they are violated

When fixing a bug:

  • State the invariant that was violated
  • State which component should own that invariant
  • Fix that component directly
  • Avoid fixes that merely mask the violation later in the pipeline
  • Add or preserve verification if the invariant is important enough to regress

Refactor and API policy

You may propose or implement a refactor when:

  • the local fix would duplicate logic
  • the local fix would violate a layer boundary
  • the bug exists because responsibility is assigned to the wrong component
  • multiple components already implement ad-hoc variants of the same concept
  • a shared helper/API would make the code smaller, clearer, and easier to maintain
  • existing callers can be migrated cleanly without broad churn
  • the current implementation is turning into a long list of special cases instead of a structured solution

When proposing or implementing a refactor:

  • Explain what responsibility is being moved or shared
  • Justify why the new location is the right ownership level
  • Keep the API narrow and named after the concept or invariant it represents
  • Migrate directly related existing users when that improves compactness and consistency
  • Separate changes required for correctness from optional cleanup
  • Avoid unrelated renames, formatting changes, or module moves
  • Do not expand a justified refactor beyond directly related callers

Do not refactor when:

  • the issue is truly local and a local fix is clearer
  • the abstraction would have only one user and no clear adjacent use
  • the abstraction would mix policies from different layers
  • the refactor would affect unrelated behavior
  • the refactor is mainly aesthetic

Working style

  • Infer style and conventions from the existing code before introducing new patterns
  • When several implementation options are possible, prefer the simplest one that fits the current architecture and minimizes churn
  • Push back when the requested or obvious fix would make the architecture worse
  • If a cleaner fix requires a small refactor or shared helper/API, propose it explicitly and justify it
  • Avoid broad refactors unless explicitly requested or clearly necessary for correctness and maintainability
  • When tests fail, bucket failures by likely root cause and separate patch-related failures from pre-existing or out-of-scope failures

Simplicity first

  • Minimum code that solves the problem cleanly. Nothing speculative
  • No features beyond what was asked
  • No error handling for impossible scenarios
  • If you write 200 lines and it could be 50, rewrite it
  • Ask: “Would a senior engineer say this is overcomplicated?” If yes, simplify
  • Prefer direct, explicit code over generic machinery unless the generic machinery clearly reduces duplication and preserves boundaries

Fallbacks and defaults

  • Avoid silent fallback behavior when the semantic category is unknown
  • Do not treat “unknown” as “safe” unless the codebase already defines that convention
  • If a value cannot be classified, either preserve the existing behavior deliberately or fail with a clear diagnostic
  • When adding a fallback, state why it is semantically valid and what invariant makes it safe

Surgical changes

  • Touch only what you must
  • Clean up only the mess introduced by your own change
  • Do not “improve” adjacent code, comments, or formatting
  • Match existing style, even if you would personally do it differently
  • If you notice unrelated dead code, bad abstractions, or fragile design, mention it separately. Do not delete or rewrite it unless asked
  • When your changes create orphans, remove imports, variables, functions, or files made unused by your change
  • Every changed line should trace directly to the requested fix, a required cleanup, or a justified reuse/refactor decision

Diagnostics and verification

  • Use existing bounded diagnostic mechanisms for pass-level verification or codegen failures
  • Do not emit unbounded repeated diagnostics from loops or parallel workers
  • Diagnostics should identify the violated invariant and the relevant value/op when useful
  • Verifiers should reject invalid states, not repair them
  • Codegen should not compensate for invalid IR/data unless codegen is the owner of that invariant
  • Do not make failing tests pass by weakening verifiers, assertions, or diagnostics unless the check itself is proven wrong
  • If a check is too strict, explain the valid case it rejects and update the invariant accordingly
  • Prefer fixing invalid IR/data producers over relaxing consumers
  • If adding diagnostics only for debugging, remove them or cap them before finalizing

Temporary debugging code

  • Temporary diagnostics, dumps, assertions, and debug-only helpers must be removed or intentionally converted into bounded permanent diagnostics before finalizing
  • If debug instrumentation remains, explain why it is useful as permanent infrastructure
  • Do not leave noisy validation output behind

Performance awareness

  • Avoid algorithmic regressions in compiler passes, especially repeated full-module walks, repeated expensive analyses, or per-op recomputation inside nested loops
  • If a change adds a walk, cache, analysis, or structural traversal, justify why it is needed
  • For hot paths, prefer preserving existing asymptotic behavior unless a better structure is part of the requested change
  • If performance may change, mention the expected impact and suggest a targeted timing check

Goal-driven execution

For multi-step tasks, state a brief plan:

  1. [Step] → verify: [check]
  2. [Step] → verify: [check]
  3. [Step] → verify: [check]

Define success criteria before implementing:

  • For bug fixes, success means reproducing or identifying the failure, fixing the responsible owner, and verifying the targeted case
  • For refactors, success means preserving behavior while making ownership, reuse, or structure cleaner
  • For validation changes, success means checking both valid and invalid cases when applicable

Transform tasks into verifiable goals:

  • “Fix the bug” → identify the invariant, reproduce the failure, fix the owner, verify the targeted case
  • “Add validation” → write or identify tests for invalid inputs, then make them pass/fail as expected
  • “Refactor X” → preserve behavior before and after, then run relevant tests

Final self-review

Before reporting completion, check:

  • Did I fix the owner of the invariant rather than masking the issue downstream
  • Did I avoid broad case lists and ad-hoc special handling
  • Did I introduce a helper/API only at the right ownership level
  • Did I migrate directly related duplicate logic when doing so improves compactness
  • Did I avoid weakening verifiers or assertions unnecessarily
  • Did I remove temporary debugging code or make it bounded and intentional
  • Did I avoid unrelated formatting, renames, or cleanup
  • Did I consider performance impact for added walks, analyses, caches, or repeated computations
  • Did I run the required build/test commands
  • Did I clearly report remaining failures or risks

When reporting back:

  • Say what changed
  • Say what was verified
  • Say what remains
  • When showing code in chat, make it easy to copy-paste into the codebase
  • Keep outputs focused on the changed parts
  • List bad practices, fragile assumptions, or cleaner alternatives separately
  • If a change is intentionally pragmatic rather than architecturally ideal, say so and explain the tradeoff