//! Hand-written **reference** tables (selectors, exit codes).
//!
//! Complements schema inventory with CLI exit/`error_kind` contracts.
/// Selector path syntax and CLI exit code / `error_kind` tables.
pub(crate) fn append_reference(out: &mut String, show_cli: bool) {
// Selector path syntax (always shown — used by all doc.* operations)
out.push_str(
"## Selector path syntax\n\n\
All `doc` operations use selector paths to address values inside JSON, YAML, and TOML files.\n\n\
| Syntax | Meaning | Example |\n\
|--------|---------|---------|\n\
| `name` | Object key | `database.host` |\n\
| `[N]` | Array index (zero-based) | `servers[0].port` |\n\
| `[*]` | Wildcard (all array elements) | `jobs[*].timeout` |\n\
| `[key=val]` | Predicate (filter by field value) | `deps[name=express].version` |\n\n\
Segments are separated by `.` or adjacent brackets. A single leading `/` is treated as \
\"from root\" and stripped (JSON Pointer habit), so `/feature_flag` sets key `feature_flag`, not a key literally named `/feature_flag` (#1794). Only one leading slash is special; `//a` keeps a key named `/a`. Prefer bare keys in prompts (`feature_flag`, `server.port`). Examples:\n\n\
```text\n\
scripts.test # simple selector path\n\
/feature_flag # same as feature_flag (leading slash stripped)\n\
jobs[0].steps[*].name # index + wildcard\n\
dependencies[name=react].version # predicate filter\n\
```\n\n\
**Write ops and predicates:** `doc set` / `doc ensure` / `doc delete` / `doc move` are \
**single-path only** (keys and indexes such as `items.0.val`). Wildcards and predicates \
(`items[id=b].val`, `items[*].enabled`) belong on `doc update` (multi-match write) or \
`doc delete-where` (array filter). If you pass a predicate to `doc set`, the error points \
you at `doc update` or an index path.\n\n\
**Multi-document YAML:** Files with multiple `---` documents parse as a **top-level array** \
(one element per document). Address a field with a document index first, e.g. `0.metadata.name` \
or `[0].metadata.name`, not a bare key at the stream root. A bare root key on multi-doc \
(or any top-level array) fails with `error_kind: type_error` and an index-form hint on \
`doc get`/`select`/`has` and `doc set` (not soft `no_matches` / not soft `has: false`). \
`doc keys .` on multi-doc root is also `type_error` (list keys on `0` / `[0]` first). \
`doc merge` of any overlay into multi-doc **root** is `type_error` (object *or* array \
overlay would replace the whole stream). Merge into one document with \
`--selector 0` / plan `\"selector\": \"0\"` (or `doc.set` under `0.`). Bare-key `doc append`/`prepend`/`delete`/`update`/`move`/`ensure` on multi-doc \
root are also `type_error` with `0.key` / `[0].key` hints (not soft no-match or \
`invalid_input`). Address fields under `0.` / `[0].` instead. Writes keep the multi-doc \
form (still `---` separators, not a single YAML sequence).\n\n\
**Markdown section bounds:** `md_replace_section` / `md_insert_after_section` / section moves \
end at the next heading of the **same or higher** level. Nested lower-level headings belong to \
the parent (replacing `# Intro` also rewrites following `##` children until the next `#`). Prefer \
peer-level headings when siblings must survive. `md_dedupe_headings` removes later **whole \
sections** with a duplicate level+text heading (body under the second heading is discarded, not \
merged).\n\
**Markdown insert placement:** `md_insert_after_heading` inserts **under the heading line** \
(before existing body). To add a sibling `##` section after the full section body, use \
`md_insert_after_section`.\n\n",
);
// Exit codes (CLI only — MCP tools return results as JSON, not exit codes)
if show_cli {
out.push_str(
"## Exit codes\n\n\
| Code | Meaning |\n\
|------|---------|\n\
| 0 | Success (operation completed, or no changes needed) |\n\
| 1 | Failure (error during execution, CLI usage/invalid args/unknown flags/subcommands), or tx `rollback_failed` when mid-commit rollback could not fully restore files |\n\
| 2 | Changes detected (`--check` / write preview including `undo` without `--apply`, or CLI/MCP/plan/tx `search` `assert_count` mismatch; `error_kind: changes_detected` for assert_count). For undo, re-run with `--apply` to restore. |\n\
| 3 | No matches (search/replace pattern miss, undo with no sessions, missing AST symbol for extract/insert/reorder/wrap/move, or tx/plan AST/md/doc target not found; `error_kind: no_matches`) |\n\
| 4 | Parse error (malformed input file or plan, invalid AST search pattern/query, or AST validate parse failure; `error_kind: parse_error`) |\n\
| 5 | Ambiguous (CLI/tx `unique` multi-match, or stale/missing patch context; `error_kind: ambiguous` in CLI/tx JSON) |\n\
| 6 | Validation failed (tx plan validation step returned non-zero; writes may remain when not strict) |\n\
| 7 | Rollback (tx mid-commit failure or strict lifecycle failure; changes were rolled back) |\n\
| 8 | Patch merge conflicts (CLI `patch merge` or plan/tx `patch.apply` with `on_stale: merge` without `allow_conflicts`; `error_kind: conflicts`) |\n\
| 9 | Tx operation staging failure (`operation_failed`) |\n\n\
**JSON `error_kind` (exit 1):** Prefer branching on kind, not English text. File ops set \
`already_exists` (create/rename without force, create race), `not_found` (delete/append/prepend/rename missing source, \
missing `--files-from`/batch input, missing plan file, missing git blob for AST, \
or `read` when every path fails), `invalid_input` (bad flags, non-file target, malformed `read --lines` \
(0-based or end before start; past-EOF range is `no_matches` exit 3), replace `--nth` past the last \
match with live match count in the message, \
`status` outside a git repo, AST map non-dir, doc merge flag conflicts, invalid `normalize_eol`, \
md table-append row/table failures, bad selector/for_each templates, MCP bind/TLS config, \
CLI usage errors under `--json`/`--jsonl`, empty path arguments, \
all-explicit-paths-missing for search/replace/tidy, \
invalid search/replace regex patterns (unclosed groups, etc.), \
and plan op option conflicts such as replace whole_line+multiline, tidy dedent+indent, \
md.move_section before/after, search invert_match+multiline), \
`guard_rejected` (PathGuard / `--contain` path escape or plan cwd escape; #1935; not empty-path `invalid_input`), \
`format_failed` (post-write `--format` command non-zero exit or format-timeout; write may already be on disk; JSON includes `backup_session` when a session was created so agents can `patchloom undo --session <id>`, plus `applied: true` (canonical; #1831), `write_applied: true` (deprecated alias), `files_changed`, and `files[].path` for every path already written so agents need not re-scan disk (#1795); re-run the formatter or undo). Empty `--files-from` is `invalid_input` (not `no_matches`; #1796). Doc type mismatches set \
`type_error` (`doc keys`/`len` on wrong type, library doc mutation type errors). \
Clap usage failures with `--json`/`--jsonl` emit the same envelope on stdout before any subcommand runs.\n\n\
**JSON `error_kind` (exit 4):** Batch line parse failures, `explain` plan parse failures, \
invalid AST search patterns/queries, AST validate parse failures, and \
malformed JSON/YAML/TOML document content set `parse_error` so agents can distinguish syntax \
mistakes from runtime failures.\n\n\
**Doc write JSON tip:** With `--json` (CLI) or MCP write tools / `execute_plan`, \
doc delete success includes `changed` (bool) and `removed` (usize). Multi-op \
plans also list per-op rows under `mutations`. Exit 0 / `ok: true` with \
`removed: 0` means idempotent cleanup (nothing matched); do not assume data \
was deleted from exit status alone.\n\n",
);
}
}