Skip to content

Latest commit

 

History

History
176 lines (149 loc) · 9.49 KB

File metadata and controls

176 lines (149 loc) · 9.49 KB

AGENTS.md — guidance for autonomous coding agents

This file is read by autonomous coding agents (the decomp-agents orchestrator, ad-hoc Claude Code sessions, or any future tool) when working on meteor-decomp. Humans should still read PLAN.md + docs/matching-workflow.md first; this file just makes the worklfow legible to an automated worker.

The unit of work is one function

Each row of config/<binary>.yaml is one function. Pick one row, claim it, match it, PR it. Do not try to "clean up" unrelated functions, headers, or modules in the same session — other agents may be holding adjacent work.

Claiming is GitHub-native, not a YAML edit. config/<binary>.yaml is gitignored and regenerated by make split, so it cannot be a claim ledger. Reserve a VA by posting /claim FUN_<va> <binary> on the coordination issue and waiting for a granted/extended reply; the deliverable is exactly one src/<binary>/_rosetta/FUN_<va>.cpp opened as a PR into develop. Opening the PR pins the claim; merging it auto-releases it. Full lifecycle in docs/claim-protocol.md; fork-based checklist in CONTRIBUTING.md. The local decomp-agents orchestrator instead uses a SQLite claim queue inside one shared checkout — that's the alternative coordination layer for in-process parallelism (see ../decomp-agents/README.md).

The exit criterion is byte-identical

make diff FUNC=<symbol> returns one of three verdicts (exit codes per tools/compare.py's docstring):

  • GREEN (exit 0) — every byte matches the original. The only acceptable commit state for matching decomps.
  • PARTIAL (exit 1) — same length but some bytes differ. Not a commit.
  • MISMATCH (exit 2) — different lengths / wholesale wrong. Not a commit.

If you cannot reach GREEN within the iteration budget, do not commit a half-match. Revert your src/ edits, leave the YAML row alone, and bail (append a post-mortem to decomp-notes/blocked/<binary>/<rva>_<sym>.md).

Inputs available to autonomous agents

You CANNOT open Ghidra (*.gpr files are GUI-only). What you have:

  • Asm dump: asm/<binary>/<rva>_<symbol>.s — per-function x86 asm with seed-symbol labels. Generated by make split BINARY=<X>.exe.
  • Headless pseudo-C (when present): build/ghidra-decomp/<binary>/ <rva>_<symbol>.c — Ghidra's decompiler output, exported via make decompile-headless BINARY=<X>.exe. Treat as a hint, not gospel.
  • Symbol metadata: config/<binary>.symbols.json (rva, size, section, name).
  • RTTI catalog: config/<binary>.rtti.json (vtable RVAs + class names — useful for identifying member functions).
  • Sibling matches: src/<binary>/_rosetta/*.cpp — already-matched hand-written examples. Read 2-3 in the same module before writing your own.
  • Curator idioms: decomp-notes/idioms/<binary>.md — binary-specific codegen patterns.
  • Prior post-mortems: decomp-notes/blocked/<binary>/<rva>_<sym>.md — what previous workers tried on this same function.
  • Type catalog: decomp-notes/types/<binary>/<rva>.md — discovered classes/structs from earlier matches.
  • Name overrides (collaborator-sourced): config/<binary>.name_overrides.json — RVA → name for ~150 functions the binary still carries as FUN_xxx, recovered from the FFXIVLegacyClientStructs + ffxivDecomp collaborator repos. If the function you're matching is listed here, use that name. Cross-checked against symbols.json but not byte-verified — confirm the role against the asm as you match. (Not folded into symbols.json to avoid forcing a work-pool re-split; this is the override layer.)
  • Collaborator RE findings (used with the author's permission — see NOTICE.md):
    • ../ffxivDecomp/docs/re/ — opcode rosters, Lua-binding, inbound dispatch, cutscene, and receiver findings. Grep for your subsystem. Distilled in docs/ffxivdecomp_opcode_binding_map.md (outbound + bindings), docs/ffxivdecomp_inbound_opcodes.md (the ~70-entry server→client inbound roster), and (for the cutscene/kick path) docs/seq005_kick_gate_analysis.md.
    • ../FFXIVLegacyClientStructs/ — struct field layouts. Catalog in config/<binary>.legacy_structs.json; per-class notes in decomp-notes/types/<binary>/; on-demand layout for any class via tools/analyze_legacy_struct.sh <Class> (runs against our own binary).

Don't touch what isn't yours

In a parallel-agent session, multiple workers are editing different files in different worktrees simultaneously. The discipline that keeps merges trivial:

  • Touch exactly one new file: src/<binary>/_rosetta/FUN_<va>.cpp (the fork/PR path), or src/<binary>/<module>/<symbol>.cpp for a curated hand-named match. One function, one file.
  • In the local decomp-agents path only, you may also flip your own row in config/<binary>.yaml (status + owner) — see "How a finished match is recorded" below. In the fork/PR path, leave the YAML untouched.
  • Do not add to shared headers (include/<binary>/...) unless the addition is unambiguously correct AND no sibling-in-progress could be writing the same enum/struct.
  • Do not modify include/_pending/ unless that's the explicit staging area documented in PLAN.md (it isn't, as of 2026-05-18).
  • Do not edit tools/, Makefile, PLAN.md, README.md, docs/, or any other coordination surface. Those are the curator's domain; raise a note instead.

Match-vs-functional vs passthrough

The YAML row's type field tells you which workflow applies:

  • type: matching — the default, the byte-identical path described above. The _rosetta/ source-of-truth, validated via objdiff.
  • type: functional — semantic decomp validated by a test fixture rather than a byte diff. Use make test FUNC=<symbol> instead of make diff. Used for code paths where MSVC 2005 lowering is too unstable to chase byte-by-byte (e.g. heavily templated CRT inlines).
  • type: middleware-crt, type: middleware-miles — skip these. Vendor code, not in scope for clean-room.

If your row is middleware-*, release the claim immediately with outcome skipped and pick another. Don't try to match vendor code.

The 13 canonical fixes

When make diff returns PARTIAL/MISMATCH, the most common root causes in MSVC 2005 frequency order:

Symptom Fix
Wrong register allocation Reorder local declarations (MSVC allocates in source order)
Off-by-one stack frame Add a dead local of the right type
Branch direction flipped Negate the if-condition
__stdcall / __cdecl mismatch Check ret N in epilogue
Member fn looks __cdecl Should be __thiscall
FP code mismatched MSVC 2005 uses x87, not SSE2
if (a && b) vs if (a) if (b) Try the other lowering
for vs while Same body, different prologue
Switch jump table MSVC builds at ≥4 cases
String literal positions /GF pooling — try __declspec(selectany)
__security_cookie dropouts /GS triggers on local arrays ≥5 bytes
Tail call missing Use __forceinline or if(...) return f();
Element-wide ptrs vs index loops MSVC-2005 idiom: *p++ over arr[i++]

See docs/matching-workflow.md §7 for the full table with examples.

How a finished match is recorded

The durable record of a solved function is the committed src/<binary>/_rosetta/FUN_<va>.cpp file itself — the _rosetta tree IS the solved set. There is no status: matched flag to flip as the source of truth, and the YAML work pool's status: column is no longer maintained.

  • Fork-based / PR path (the GitHub-native flow in CONTRIBUTING.md): your branch adds exactly one _rosetta/FUN_<va>.cpp. Do not edit config/<binary>.yaml at all — opening the PR pins the claim and merging it auto-recomputes progress (reconcile.yml). Keeping the diff to one file is what makes the PR trivially reviewable.
  • Local decomp-agents path: the SQLite queue inside the shared checkout is the claim authority; that orchestrator still flips the function's YAML row (status + owner) as a local bookkeeping side effect. If you are that worker and you DO edit the row, change ONLY status + owner — never rva, end, size, module, symbol, type, section; do not reorder rows, strip trailing whitespace, or "normalize" quoting. Every change beyond those two makes the merge harder.

When in doubt

  • Read docs/matching-workflow.md end to end — it's the canonical guide.
  • Read 2-3 sibling matched files in the same module before writing.
  • The Project Meteor Discord dump (/Users/swstegall/.claude/projects/-Users-swstegall-Documents-Programming-server-workspace/project_meteor_discord_context.md) has first-hand contributor notes on MSVC 2005 quirks — grep for symbol names that look like they'd be familiar to the Meteor team.