# Worker: Document One Global Variable

You are processing a single global variable that an upstream auditor has
flagged as not fully documented. Your job is to fix it so it passes the
four-axis bar defined in `step-globals.md` (loaded separately).

## Context you've been given

- **Program**: the Ghidra program path (e.g. `/Vanilla/1.13d/D2Common.dll`)
- **Address**: the global's address (e.g. `0x6fdc1234`)
- **Audit (before)**: the JSON output of `audit_global` for this address.
  It contains the current `name`, `type`, `length`, `plate_comment`,
  `xref_count`, and the `issues` list — exactly what's missing.

## The bar

The global is "fully documented" when the audit reports zero issues.
The four axes are documented in `step-globals.md` — refer to that for
the rules. The Hungarian prefix → type table lives in
`hungarian-table.md` — refer to that for the canonical mapping (it
covers all scopes; globals are the same prefix table with `g_` prepended).

The shape of each issue you may encounter:

| Issue | Severity | What to fix |
|---|---|---|
| `generic_name` | hard | Set a real name with `g_` prefix + Hungarian. |
| `ida_reserved_prefix` | hard | Don't reuse `sub_`/`loc_`/`byte_`/`dword_`/`unk_`/`var_`/`arg_` — those are sentinels. Pick a project-style name. |
| `name_missing_g_prefix` | hard | Add the `g_` prefix. |
| `name_prefix_type_mismatch` | hard | Make Hungarian prefix match the type. |
| `name_short_descriptor` | hard | Add ≥2 chars of descriptor after the Hungarian prefix. |
| `untyped` | hard | Apply a real type (not `undefined1/2/4/8`). |
| `missing_plate_comment` | hard | Add a ≥4-word plate comment summarizing purpose. |
| `plate_comment_too_short` | medium | Expand the plate comment to ≥4 words. |
| `unformatted_bytes_length_mismatch` | medium | Re-apply the type with the correct `array_length`. |
| `unformatted_bytes_should_be_string` | medium | Re-apply as `string` or `unicode`. |
| `xref_summary_missing` | medium | Add `Set by:` / `Read by:` section (xref_count > 5). |
| `bitfield_undocumented` | medium | Add `Bitfield:` section with per-bit table. |
| `callback_signature_missing` | medium | Add call signature to the plate. |
| `generic_descriptor` | **soft** | Replace generic word (Data/Buffer/Flag/Result/etc.) with something specific. **Soft — does not block completion.** Skip if you genuinely can't infer better from context. |
| `bytes_size_unknown` | **soft** | Document the array length. Soft — does not block. |
| `plate_line_too_long` | **soft** | A line in the plate exceeds 80 chars and gets clipped by Ghidra's listing column. Hard-wrap to ≤70 chars — for comma lists like `Set by: A, B, C, ...`, put the header on its own line and indent continuation lines two spaces. See `step-globals.md` "Wrap long lines" for the canonical shape. **Soft — does not block.** |

**Use the audit's `applicable_axes` field** to know which sections matter
for THIS global. The audit returns flags like
`{xref_summary: true, bitfield_decomp: false, callback_sig: false, value_semantics: true}`
so you only fill the sections that apply — don't pad with N/A for
inapplicable axes.

**`fully_documented` is severity-tiered:** the worker treats anything
with no hard + no medium issues as `completed`. Soft issues (e.g.
`generic_descriptor`) appear in the audit but don't keep the worker
spinning on the global.

## Workflow (always)

1. **Read the audit** that was passed in. Identify which issues need fixing.
2. **Investigate only if needed** — check xrefs (`get_xrefs_to`) to
   understand how the global is used. Use `analyze_data_region` or
   `inspect_memory_content` to examine the data itself. **Do not**
   call `decompile_function` or `get_function_signature` on the global's
   address — globals are data, not code; those calls waste tool budget
   and return errors. If you need calling-context, decompile a *caller*
   address from the xrefs, not the global itself.
3. **Apply fixes** — preferred path is one atomic call:
   - `set_global(program=<full_path>, address=<addr>, type_name=<type>, name=<name>, plate_comment=<text>, array_length=<N>)`
     writes type + name + plate in a single transaction. Pre-flight
     validation rejects bad input; successful return = fully documented.
   - **Fallback path** (when `set_global` rejects an edge case): use
     the individual writers below in order. Each is atomic on its own
     axis but you can lose work mid-sequence if one fails.

   **Every** tool call must include `program="<full_path>"` (the full path
   from the CRITICAL banner above). Omitting it routes the call to
   whichever program is currently focused in Ghidra's UI — almost
   certainly NOT the one you're working on. The write will silently
   succeed against the wrong binary.
4. **Re-audit** with `audit_global(program=<full_path>, address=<addr>)`
   to confirm the fix landed. Use the same `program=` value as your
   write calls.

### `set_global` is terminal

If `set_global` returned `{"status": "success"}`, the global is fully
documented. **Stop.** Do not call `apply_data_type`, `rename_or_label`,
or `batch_set_comments` afterward — those would either no-op or risk
overwriting `set_global`'s atomic writes. Move on to the next address
(or end the turn).

### Fallback writers (only when `set_global` rejects)

| Axis | Tool | Notes |
|---|---|---|
| Type | `apply_data_type(program=<full_path>, address=<addr>, type_name=<type>)` | If it fails with `Conflicting data exists`, the address already has a different type applied — try a wider/aligned type, or report blocked. |
| Name | `rename_or_label(program=<full_path>, address=<addr>, name=<name>)` | Use `g_` + Hungarian + descriptor. |
| Plate | `batch_set_comments(program=<full_path>, address="0x<hex>", plate_comment="…")` | **Plate comment for data addresses goes through `batch_set_comments`, NOT `set_plate_comment`.** `set_plate_comment` is function-only and will return "No function at address" for data addresses. The `address` parameter is a **single string**, not a list — pass `"0x6fdc1234"`, **NOT** `["0x6fdc1234"]`, **NOT** `"ram:0x6fdc1234"`, **NOT** `["ram:0x6fdc1234"]`. |

Every cell above includes `program=<full_path>` as the **first** parameter for the same reason as the main flow: omitting it routes the write to the wrong binary.

## Common errors — diagnose without retrying

These are the patterns that have burned the most tool calls in production.
If you see one of these errors, do **not** retry with a different format
— recognize it, accept what it tells you, and move on.

### Address format

Addresses are bare hex (`0x6fdc1234`) or optionally `ram:0xNNNN` for the
default address space. **Never** prefix with the binary name —
`fog.dll:0x6ff82f34`, `D2Game.dll:0x6fc12000`, etc. produce
`Unknown address space 'fog.dll'` and waste a tool call. The binary is
identified by the **`program=` parameter**, not the address prefix.

### Uninitialized memory (.bss)

Many globals live in `.bss` — uninitialized data sections. The bytes
aren't loaded into Ghidra's memory model, so:

- `inspect_memory_content` returns `Unable to read bytes at ram:0x...`
- `read_memory` returns `Failed to read memory: Unable to read bytes`

That **is the answer**, not a problem to solve. The address is real,
the symbol is valid, the data type is whatever was declared — runtime
zero-init produces the actual value. **Do not retry** with different
length params or different addresses. Use `analyze_data_region` (which
works on uninitialized memory) or trust the type declaration. If you
genuinely need to know a default value, read source comments or assume
zero-init.

### Function-only tools on data globals

These tools require a function and will **always** fail on a data global
address:

| Tool | Why it fails | Use instead |
|---|---|---|
| `get_plate_comment` | Function-only — returns "No function at address" for data. | The plate is already in the audit's `plate_comment` field. |
| `force_decompile` | Decompiles functions, not data. Returns "No function found at address". | `analyze_data_region` for data layout; decompile a *caller* if you need usage context. |
| `decompile_function(global_addr)` | Same — addresses targeting data globals are not function entries. | Decompile one of the global's xref *callers* instead. |
| `set_plate_comment` | Function-only. Use `batch_set_comments(program=..., address=..., plate_comment=...)` for data. | See fallback writers table above. |
| `analyze_function_complete` | Function-only scoring. | Globals are scored via `audit_global`. |

If the audit response includes `is_code_address: true` or
`is_function_entry: true`, that confirms the address is code, not data
— skip the global; the worker's outer logic will do this for you.

### Symbol-already-exists on `set_global`

If `set_global` returns `A symbol named X already exists at this
address!`, a previous attempt (possibly with the wrong `program=`)
created a secondary symbol. Pick a slightly different name (e.g. add
the offset suffix as a placeholder: `g_dwFooBar` → `g_dwFooBar_4ec`) and
retry once. Don't loop — if the second attempt also conflicts, report
blocked.

## Documentation goes in the PLATE COMMENT, not in labels

A global has exactly **one** primary symbol (its name — `g_dwActiveQuestState`)
and one plate comment (the multi-line description shown above the
address in Ghidra's listing). **All explanatory text belongs in the
plate comment.** Names are short identifiers, not documentation.

**Do NOT:**
- Use `create_label` to attach a descriptive secondary label (e.g.
  creating a label `"InitializePerformanceCounters_sets_this"` on the
  global). Secondary labels clutter the listing, get picked up by
  decompilation as alternate names, and confuse downstream tools.
- Use `batch_create_labels` for documentation purposes — that tool is
  for multi-target naming, not for adding context.
- Stuff documentation into the symbol's name. The name is `g_*` +
  Hungarian + descriptor (≤30 chars typical). Anything longer or more
  prose-like should be in the plate.

**DO:**
- Use `set_global(program=, address=, name=, type_name=, plate_comment=)`
  — sets primary symbol + type + plate atomically. This is the only
  call you should normally need.
- If `set_global` rejects, fall through to `apply_data_type` (type) +
  `rename_or_label` (primary symbol — replaces the existing one) +
  `batch_set_comments` (plate). All three are operations on a *single
  symbol* and the plate at the same address. None of them create
  secondary labels.

After your writes succeed, the listing should look like:

```
                       <plate comment goes here, multiple lines OK>
                       <Set by: ... / Read by: ... / Bitfield: ... etc.>
g_dwActiveQuestState                                    XREF[N]: ...
6ff82f40 0a 00 00 00    dword     0000000Ah
```

One name (`g_dwActiveQuestState`), one type (`dword`), plate comment
above. No `Ordinal_*` overlays or `XREF` decoration in the plate
itself; those come from Ghidra's listing renderer.

## Hard constraints

- **One global only.** Do not touch other addresses, even if you notice
  related globals nearby. Another worker tick will cover them.
- **No `analyze_function_completeness`.** That's for functions. Scoring
  for globals is done via `audit_global` issues count.
- **No `set_plate_comment` for data addresses.** That tool requires a
  function. Use `batch_set_comments` for plate comments on globals.
- **No `decompile_function` on the target address itself.** Globals
  aren't functions; the call returns an error and burns tool budget.
- **Skip OS-defined / system labels.** If the global's existing name is
  a recognized OS or library symbol (e.g., `ExceptionList`, `StackBase`,
  `SubSystemTib`, `PEB`, `Teb*`, addresses in the `ffdf****` /
  `7ffe****` ranges that map to TIB/PEB), the existing name is the
  canonical one — do **not** rename it to a `g_*` form. Report blocked
  with reason "OS/system label" and move on.
- **Do not delete or refactor data.** If the type or layout looks wrong
  beyond what `set_global` can express, leave the global unchanged and
  report what's blocking — the audit will keep reporting the issue and
  a human can investigate.

## Output

Brief: "Fixed `<name>` at `<address>` — applied `<type>`, set plate, no
remaining issues" — or, if you couldn't fix it: "Blocked: `<reason>`."
