structure-recovery — reverse-engineer table/file structure from legacy artifacts
S-cycle design at docs/plans/2026-06-15-structure-recovery-skill-design.md.
What it does
Reads SQL/DDL, DataStage .dsx, COBOL programs + copybooks, and flat-file layout
docs, and reconstructs the structure of the data the lineage family did not
cover (data flow = lineage-extract-static; code symbols = legacy-code-intel):
structure-index.json — the queryable accumulated catalog (structure-index.v1):
entities (tables / views / COBOL records / flat-file layouts) with fields, types,
computed byte offsets/lengths, resolved relationships, per-entity/per-field
confidence × evidence_kind × enforcement, and gaps.
structure.html — one schema table per entity + a relationship DAG (Cytoscape),
confidence color-coded, SOURCE_DATE_EPOCH-deterministic, XSS-safe, air-gap-safe.
fields.csv + relationships.csv — flat exports (spreadsheet-injection-safe).
structure.xlsx — one sheet per entity + summary + relationships (composes
ms-office-excel-python; formula-injection-safe cells).
wiki/ — one interlinked markdown page per entity, every fact cited to
file:line (composes the wiki skill, under .wiki.lock, anti-pollution-safe).
structure.ddl.sql — inferred CREATE TABLE DDL with a mandatory
human-review header; LIVE constraints only at grounded confidence; everything
inferred/speculative emitted as commented -- INFERRED FK: lines, never executed.
structure.schema-facets.json — OpenLineage SchemaDatasetFacet (fields+types)
per dataset, enriching the existing lineage datasets (M1; extends merge_into_ol.py).
When to use it
- You have legacy SQL/DDL, DataStage exports, COBOL+copybooks, or flat-file layout
docs and you need the column lists, types, and record layouts — including
byte-accurate COBOL field offsets — not just the data flow.
- You need an inferred DDL characterization aid for a system whose schema lives
only in code.
- The artifact set is large — chunking + resumable partial reports across many
files is built in (re-run resumes; nothing is re-analyzed that is already persisted).
NOT for: data flow / lineage edges (use lineage-extract-static); a queryable
symbol/occurrence graph (use legacy-code-intel); runtime/dynamic structure
(static only, v1); executing the inferred DDL (it is advisory-only, never run).
Architecture (the LLM-driven framework)
The skill is the framework, the in-session AI CLI is the parser (decision
N2). There are NO per-format parsers (sqlglot / tree-sitter / xml.sax) in
scripts/. The agent's LLM extracts declared facts per chunk; Python is the
calculator (it computes COBOL byte offsets deterministically — the LLM never
declares an offset). The skill provides:
- 6 model-neutral prompts in
prompts/*.md — analyze-sql, analyze-dsx,
analyze-cobol, analyze-flatfile, merge, redact. Each analyze-* emits ONE
structure-finding.v1 JSON object per chunk, no prose.
- 8 deterministic Python scripts in
scripts/*.py — boundary_hints,
validate_finding, cobol_offset_calc, run_state, accumulate_structure,
relationships, render_structure, run_structure_recovery (the driver).
Plus the extended sibling lineage-extract-static/scripts/chunk_file.py.
- 2 reference docs in
references/*.md — confidence-model, chunking.
- 2 JSON schemas in
schemas/ — structure-finding.v1 (per-chunk emission),
structure-index.v1 (accumulated catalog).
Scripts have NO LLM calls. Prompts have NO Python code. The two halves are
interoperable across Claude Code / Codex CLI / Antigravity CLI (agy) / Copilot CLI
because the prompts are model-neutral.
Invocation flow
When a user issues structure-recovery <path> (or NL: "reverse-engineer the table
structure of /path/to/legacy"):
1. Agent reads this SKILL.md (the orchestration playbook).
2. Agent runs scripts/run_structure_recovery.py over the target (file or tree).
The driver discovers structure-bearing files (.sql/.ddl/.dsx/.cbl/.cob/.cpy/
.fd/.layout/.txt), and for each file:
2a. boundary-hint pre-pass (scripts/boundary_hints.py — pure, no LLM, no XML
parser) emits SAFE split lines so a record is never bisected.
2b. chunk (chunk_file.py with preferred_break_lines) into the 0700 run cache.
3. For each chunk, the AGENT applies the matching prompts/analyze-<format>.md and
writes ONE structure-finding.v1 object to chunk_NNNN.jsonl
(COBOL: DECLARED facts ONLY; byte_offset + length MUST be null — the validator
rejects non-null; offsets are computed in step 4).
The driver VALIDATES each finding (scripts/validate_finding.py) before it is
trusted, then accumulates:
4. accumulate (scripts/accumulate_structure.py): merge per-chunk findings for the
same (object_kind, qualified_name), dedup by (name, ordinal), lower-confidence-
wins; for COBOL records, invoke scripts/cobol_offset_calc.py to compute
byte_offset/length/ranged + gaps. Output: per-file summary.json.
5. Cross-file: the driver folds per-file summaries into the structure-index.v1
catalog and runs scripts/relationships.py (K2 honesty caps: declared SQL FK =
grounded; convention *_id = inferred; SQL JOIN = join; COBOL cross-record FK
ONLY with --infer-relationships, capped speculative, name+type+length match).
6. render (scripts/render_structure.py): structure-index.json + structure.html +
fields.csv + relationships.csv + structure.ddl.sql + structure.xlsx + wiki/ +
structure.schema-facets.json.
7. Driver prints the output location + a status (complete|partial) summary.
The per-chunk LLM extraction (step 3) is the ONE step Python cannot do — it is the
LLM-as-parser seam. The driver exposes an analyzer hook so the in-session AI CLI
(or a wrapper) supplies the real analysis; when no finding is present for a chunk,
the catalog records an HONEST gap ("chunk N not yet analyzed") — never a fabricated
entity.
CLI flags
run_structure_recovery.py <target> --output-dir <path>
--infer-relationships opt-in COBOL cross-record FK inference (decision O2;
capped speculative, commented DDL only, name+type+length match)
--project-name <name> label used in the HTML/wiki (default: target basename)
--no-vendor skip vendored Cytoscape; CDN/Mermaid fallback
--source-date-epoch <int> pin output timestamps for byte-deterministic output
--chunk-size-lines <int> override STRUCT_CHUNK_LINES (default 1500)
--overlap-lines <int> override STRUCT_OVERLAP_LINES (default 200)
--no-excel / --no-wiki / --no-ol skip an optional emitter
--list-only discover + print the structure files (no analysis)
Env: STRUCT_CHUNK_LINES, STRUCT_OVERLAP_LINES, STRUCT_MAX_DURATION_S
(wall-clock cap → PARTIAL, default 3600), STRUCT_MODEL_ID (folds into the job
fingerprint so a deliberate model swap invalidates the warm cache).
Resumability & PARTIAL contract
- Job fingerprint (
run_state.py) = an OUTER sha256 folding project_root +
selected-file-set snapshot (sorted relpath:sha) + options + --infer-relationships
- prompt/extractor/schema/normalizer versions + model_id, wrapping (never
widening) the reused
legacy-code-intel pipeline_fingerprint. Same inputs → same
fingerprint → resume; any input delta → a new job (no stale cache served).
- Filesystem = truth. A persisted
chunk_NNNN.jsonl finding is a probe HIT and
is NOT re-analyzed on re-invoke; a partial file resumes at the first missing chunk;
a torn run-state.json self-heals (reconcile rebuilds chunks_done from disk).
- PARTIAL is not failure. The wall-clock cap finalizes
status:partial, renders
whatever is persisted, and lists files_pending/files_partial/files_skipped.
Re-running resumes (every persisted file is a probe HIT).
Confidence / honesty model (P1)
confidence ∈ {grounded, inferred, speculative} × evidence_kind ∈ {declared_constraint, declared_column, inferred_naming, observed_usage} ×
enforcement ∈ {declared, unknown} (DDL proves declared, not deployed).
Interpolation / dynamic (#PARAM#, RCP, ${...}, f-strings) / unresolved-COPY /
ODO-tail / SYNC → forced speculative. Offsets carry their OWN confidence:
grounded for a clean DISPLAY/COMP/COMP-3 chain; inferred for group-propagated
USAGE; speculative/refused-with-gap for SYNC/ODO/unresolved-COPY (a SYNC or
post-ODO offset is emitted RANGED with a gap, never a confident value).
FK/relationship edges are advisory until a gold/ schema oracle clears an
accuracy gate (legacy-code-intel precedent) — they NEVER feed a gate. Full
table in references/confidence-model.md.
HARD-RULEs
- The LLM never declares a byte offset. A non-null
byte_offset/length on a
chunk-level structure-finding is REJECTED by validate_finding.py. Offsets are
computed downstream by cobol_offset_calc.py only.
- SYNC / OCCURS DEPENDING ON offsets are RANGED, never confident. ODO emits
byte_offset_min+byte_offset_max+variable_length:true; SYNC emits
RANGED/UNKNOWN + gap:sync_alignment. Never a single authoritative value.
- COBOL cross-record FK is opt-in, capped, matched. Only with
--infer-relationships; capped speculative; requires name+type+length match;
commented -- INFERRED FK: DDL only; never a live constraint.
- Inferred DDL is advisory. Mandatory human-review header; never executed;
never feeds a gate.
- No per-format parser deps; DSX read as text (N2). No
sqlglot, no XML parser.
If structural DSX parsing is ever needed it MUST use defusedxml — but v1 uses
regex/text-scan, so there is no XXE surface.
- Prompts are model-neutral (Claude Code / Codex / agy / Copilot) — no
model-specific phrasing.
chunk_file.py preferred_break_lines is backward-compatible — None default
(NOT a mutable []) ⇒ byte-identical to the pre-change chunker; the lineage
chunk tests stay green.
Output layout
<output-dir>/
structure-index.json # the queryable structure-index.v1 catalog
structure.html # schema tables + relationship DAG (deterministic, XSS-safe)
fields.csv relationships.csv # flat exports (injection-safe)
structure.ddl.sql # inferred DDL (human-review header; commented inferred FKs)
structure.xlsx # one sheet/entity + summary + relationships (if openpyxl present)
wiki/index.md wiki/<entity>.md # interlinked, file:line-cited pages
structure.schema-facets.json # OL SchemaDatasetFacet enrichment (if the OL sibling present)
.run/ # 0700 resumable run cache (run-state + per-file chunk findings)
Anti-patterns — STOP if you catch yourself
- Adding a per-format parser dependency (sqlglot / an XML parser) — N2 forbids it.
- Letting the LLM emit a byte offset — the validator rejects it; Python computes it.
- Emitting a confident COBOL offset past a SYNC or ODO field — must be ranged + gap.
- Promoting a COBOL cross-record FK above
speculative, or rendering it as a live
constraint — commented advisory only, opt-in only.
- Executing the inferred DDL, or feeding any relationship edge into a gate.
- Widening
legacy-code-intel pipeline_fingerprint's 5-field signature — wrap it.
- Fabricating an entity for an un-analyzed chunk — record an honest gap instead.
Composition with other skills
lineage-extract-static — reuses chunk_file.py (I/O substrate, extended with
preferred_break_lines), the accumulate.py lower-confidence-wins idiom, the
render_report.py HTML shape, redact.py, and extends merge_into_ol.py for the
SchemaDatasetFacet (M1).
legacy-code-intel — reuses the content-addressed store.py flock + skip-ladder
and fingerprint.py (wrapped, never widened).
ms-office-excel-python — composed for the .xlsx output (openpyxl + the
formula-injection safe_cell rule).
wiki — composed for the per-entity cited pages (.wiki.lock, anti-pollution).
mainframe-lineage-parsers — reuses this skill's deterministic COBOL machinery:
its cobol_extract.py imports scripts/cobol_offset_calc.py as a module
(parse_pic / normalize_usage / elementary_length / build_tree /
compute_offsets / compute_finding_offsets) for record length + the field
level-tree — NOT a forked COBOL parser — then adds the lineage layer
(SELECT ... ASSIGN-TO join, FD records, READ/WRITE direction, MOVE
flow) on top. It also imports validate_finding.py's CONFIDENCE_ENUM
byte-identically (single source of truth for grounded | inferred | speculative). Keep these public entry points + the field-dict key names
(pic_clause, usage, occurs, occurs_max, occurs_depending_on,
redefines) stable — the lineage sibling reads them.
Security — XML / structure parsing
- 0700 cache dirs (NEVER
/tmp); atomic .tmp + os.replace + fsync writes.
- Fail-closed secret redaction (
redact.py) before any rendered output.
- DSX is read as chunked text (N2) — no XML parsing, so no XXE surface. The
prompts flag RCP /
#PARAM# dynamic layouts as gaps rather than guessing columns.
- All HTML/CSV/Excel/wiki cells are escaped/sanitized against XSS and
spreadsheet-formula injection (CWE-1236).
See also
docs/plans/2026-06-15-structure-recovery-skill-design.md — full design + decisions.
references/confidence-model.md — the 3-axis honesty model + K2 relationship caps.
references/chunking.md — the chunking discipline (boundary hints, oversized-record gap).
1---2name: structure-recovery3description: Use when you want to reverse-engineer the SHAPE of data — table column lists+types, COBOL record layouts (PIC/USAGE/COMP, computed byte offsets, REDEFINES, OCCURS), flat-file positional layouts, inferred CREATE TABLE DDL — from legacy artifacts (SQL/DDL, DataStage .dsx, COBOL+copybooks, flat-files). The 3rd lineage-family sibling (flow=lineage-extract-static, symbols=legacy-code-intel, structure=this). A cross-model LLM-as-parser framework: model-neutral prompts the in-session AI CLI runs against chunks via its OWN context — NO per-format parser deps; Python does only chunk I/O, validation, DETERMINISTIC COBOL byte-offset computation, accumulation, rendering. Handles large-file chunking + resumable partial reports; renders HTML/CSV/Excel/wiki/inferred-DDL/OpenLineage SchemaDatasetFacet. Static only (v1). Also trigger on "reverse engineer table structure", "COBOL record layout", "copybook byte offsets", "infer DDL from", "flat-file layout", "schema from DSX".4---56# structure-recovery — reverse-engineer table/file structure from legacy artifacts78S-cycle design at `docs/plans/2026-06-15-structure-recovery-skill-design.md`.910## What it does1112Reads SQL/DDL, DataStage `.dsx`, COBOL programs + copybooks, and flat-file layout13docs, and reconstructs the **structure of the data** the lineage family did not14cover (data **flow** = `lineage-extract-static`; code **symbols** = `legacy-code-intel`):1516- **`structure-index.json`** — the queryable accumulated catalog (`structure-index.v1`):17 entities (tables / views / COBOL records / flat-file layouts) with fields, types,18 computed byte offsets/lengths, resolved relationships, per-entity/per-field19 `confidence × evidence_kind × enforcement`, and gaps.20- **`structure.html`** — one schema table per entity + a relationship DAG (Cytoscape),21 confidence color-coded, `SOURCE_DATE_EPOCH`-deterministic, XSS-safe, air-gap-safe.22- **`fields.csv` + `relationships.csv`** — flat exports (spreadsheet-injection-safe).23- **`structure.xlsx`** — one sheet per entity + summary + relationships (composes24 `ms-office-excel-python`; formula-injection-safe cells).25- **`wiki/`** — one interlinked markdown page per entity, every fact cited to26 `file:line` (composes the `wiki` skill, under `.wiki.lock`, anti-pollution-safe).27- **`structure.ddl.sql`** — inferred `CREATE TABLE` DDL with a mandatory28 human-review header; LIVE constraints only at `grounded` confidence; everything29 inferred/speculative emitted as commented `-- INFERRED FK:` lines, never executed.30- **`structure.schema-facets.json`** — OpenLineage `SchemaDatasetFacet` (fields+types)31 per dataset, enriching the existing lineage datasets (M1; extends `merge_into_ol.py`).3233## When to use it3435- You have legacy SQL/DDL, DataStage exports, COBOL+copybooks, or flat-file layout36 docs and you need the **column lists, types, and record layouts** — including37 **byte-accurate COBOL field offsets** — not just the data flow.38- You need an **inferred DDL** characterization aid for a system whose schema lives39 only in code.40- The artifact set is **large** — chunking + resumable partial reports across many41 files is built in (re-run resumes; nothing is re-analyzed that is already persisted).4243NOT for: data flow / lineage edges (use `lineage-extract-static`); a queryable44symbol/occurrence graph (use `legacy-code-intel`); runtime/dynamic structure45(static only, v1); executing the inferred DDL (it is advisory-only, never run).4647## Architecture (the LLM-driven framework)4849The skill is the **framework**, the in-session AI CLI is the **parser** (decision50**N2**). There are NO per-format parsers (`sqlglot` / `tree-sitter` / `xml.sax`) in51`scripts/`. The agent's LLM extracts **declared facts** per chunk; Python is the52**calculator** (it computes COBOL byte offsets deterministically — the LLM never53declares an offset). The skill provides:5455- **6 model-neutral prompts** in `prompts/*.md` — `analyze-sql`, `analyze-dsx`,56 `analyze-cobol`, `analyze-flatfile`, `merge`, `redact`. Each `analyze-*` emits ONE57 `structure-finding.v1` JSON object per chunk, no prose.58- **8 deterministic Python scripts** in `scripts/*.py` — `boundary_hints`,59 `validate_finding`, `cobol_offset_calc`, `run_state`, `accumulate_structure`,60 `relationships`, `render_structure`, `run_structure_recovery` (the driver).61 Plus the extended sibling `lineage-extract-static/scripts/chunk_file.py`.62- **2 reference docs** in `references/*.md` — `confidence-model`, `chunking`.63- **2 JSON schemas** in `schemas/` — `structure-finding.v1` (per-chunk emission),64 `structure-index.v1` (accumulated catalog).6566Scripts have NO LLM calls. Prompts have NO Python code. The two halves are67interoperable across Claude Code / Codex CLI / Antigravity CLI (agy) / Copilot CLI68because the prompts are model-neutral.6970## Invocation flow7172When a user issues `structure-recovery <path>` (or NL: *"reverse-engineer the table73structure of /path/to/legacy"*):7475```761. Agent reads this SKILL.md (the orchestration playbook).772. Agent runs scripts/run_structure_recovery.py over the target (file or tree).78 The driver discovers structure-bearing files (.sql/.ddl/.dsx/.cbl/.cob/.cpy/79 .fd/.layout/.txt), and for each file:80 2a. boundary-hint pre-pass (scripts/boundary_hints.py — pure, no LLM, no XML81 parser) emits SAFE split lines so a record is never bisected.82 2b. chunk (chunk_file.py with preferred_break_lines) into the 0700 run cache.833. For each chunk, the AGENT applies the matching prompts/analyze-<format>.md and84 writes ONE structure-finding.v1 object to chunk_NNNN.jsonl85 (COBOL: DECLARED facts ONLY; byte_offset + length MUST be null — the validator86 rejects non-null; offsets are computed in step 4).87 The driver VALIDATES each finding (scripts/validate_finding.py) before it is88 trusted, then accumulates:894. accumulate (scripts/accumulate_structure.py): merge per-chunk findings for the90 same (object_kind, qualified_name), dedup by (name, ordinal), lower-confidence-91 wins; for COBOL records, invoke scripts/cobol_offset_calc.py to compute92 byte_offset/length/ranged + gaps. Output: per-file summary.json.935. Cross-file: the driver folds per-file summaries into the structure-index.v194 catalog and runs scripts/relationships.py (K2 honesty caps: declared SQL FK =95 grounded; convention *_id = inferred; SQL JOIN = join; COBOL cross-record FK96 ONLY with --infer-relationships, capped speculative, name+type+length match).976. render (scripts/render_structure.py): structure-index.json + structure.html +98 fields.csv + relationships.csv + structure.ddl.sql + structure.xlsx + wiki/ +99 structure.schema-facets.json.1007. Driver prints the output location + a status (complete|partial) summary.101```102103The per-chunk LLM extraction (step 3) is the ONE step Python cannot do — it is the104LLM-as-parser seam. The driver exposes an `analyzer` hook so the in-session AI CLI105(or a wrapper) supplies the real analysis; when no finding is present for a chunk,106the catalog records an HONEST gap ("chunk N not yet analyzed") — never a fabricated107entity.108109## CLI flags110111```112run_structure_recovery.py <target> --output-dir <path>113 --infer-relationships opt-in COBOL cross-record FK inference (decision O2;114 capped speculative, commented DDL only, name+type+length match)115 --project-name <name> label used in the HTML/wiki (default: target basename)116 --no-vendor skip vendored Cytoscape; CDN/Mermaid fallback117 --source-date-epoch <int> pin output timestamps for byte-deterministic output118 --chunk-size-lines <int> override STRUCT_CHUNK_LINES (default 1500)119 --overlap-lines <int> override STRUCT_OVERLAP_LINES (default 200)120 --no-excel / --no-wiki / --no-ol skip an optional emitter121 --list-only discover + print the structure files (no analysis)122```123124Env: `STRUCT_CHUNK_LINES`, `STRUCT_OVERLAP_LINES`, `STRUCT_MAX_DURATION_S`125(wall-clock cap → PARTIAL, default 3600), `STRUCT_MODEL_ID` (folds into the job126fingerprint so a deliberate model swap invalidates the warm cache).127128## Resumability & PARTIAL contract129130- **Job fingerprint** (`run_state.py`) = an OUTER sha256 folding project_root +131 selected-file-set snapshot (sorted `relpath:sha`) + options + `--infer-relationships`132 + prompt/extractor/schema/normalizer versions + model_id, **wrapping** (never133 widening) the reused `legacy-code-intel pipeline_fingerprint`. Same inputs → same134 fingerprint → resume; any input delta → a new job (no stale cache served).135- **Filesystem = truth.** A persisted `chunk_NNNN.jsonl` finding is a probe HIT and136 is NOT re-analyzed on re-invoke; a partial file resumes at the first missing chunk;137 a torn `run-state.json` self-heals (reconcile rebuilds `chunks_done` from disk).138- **PARTIAL is not failure.** The wall-clock cap finalizes `status:partial`, renders139 whatever is persisted, and lists `files_pending/files_partial/files_skipped`.140 Re-running resumes (every persisted file is a probe HIT).141142## Confidence / honesty model (P1)143144`confidence ∈ {grounded, inferred, speculative}` × `evidence_kind ∈145{declared_constraint, declared_column, inferred_naming, observed_usage}` ×146`enforcement ∈ {declared, unknown}` (DDL proves *declared*, not *deployed*).147Interpolation / dynamic (`#PARAM#`, RCP, `${...}`, f-strings) / unresolved-COPY /148ODO-tail / SYNC → forced `speculative`. Offsets carry their OWN confidence:149`grounded` for a clean DISPLAY/COMP/COMP-3 chain; `inferred` for group-propagated150USAGE; `speculative`/refused-with-gap for SYNC/ODO/unresolved-COPY (a SYNC or151post-ODO offset is emitted RANGED with a gap, **never** a confident value).152FK/relationship edges are **advisory until a `gold/` schema oracle clears an153accuracy gate** (legacy-code-intel precedent) — they **NEVER feed a gate**. Full154table in `references/confidence-model.md`.155156## HARD-RULEs1571581. **The LLM never declares a byte offset.** A non-null `byte_offset`/`length` on a159 chunk-level `structure-finding` is REJECTED by `validate_finding.py`. Offsets are160 computed downstream by `cobol_offset_calc.py` only.1612. **SYNC / OCCURS DEPENDING ON offsets are RANGED, never confident.** ODO emits162 `byte_offset_min`+`byte_offset_max`+`variable_length:true`; SYNC emits163 RANGED/UNKNOWN + `gap:sync_alignment`. Never a single authoritative value.1643. **COBOL cross-record FK is opt-in, capped, matched.** Only with165 `--infer-relationships`; capped `speculative`; requires name+type+length match;166 commented `-- INFERRED FK:` DDL only; never a live constraint.1674. **Inferred DDL is advisory.** Mandatory human-review header; never executed;168 never feeds a gate.1695. **No per-format parser deps; DSX read as text (N2).** No `sqlglot`, no XML parser.170 If structural DSX parsing is ever needed it MUST use `defusedxml` — but v1 uses171 regex/text-scan, so there is no XXE surface.1726. **Prompts are model-neutral** (Claude Code / Codex / agy / Copilot) — no173 model-specific phrasing.1747. **`chunk_file.py preferred_break_lines` is backward-compatible** — `None` default175 (NOT a mutable `[]`) ⇒ byte-identical to the pre-change chunker; the lineage176 chunk tests stay green.177178## Output layout179180```181<output-dir>/182 structure-index.json # the queryable structure-index.v1 catalog183 structure.html # schema tables + relationship DAG (deterministic, XSS-safe)184 fields.csv relationships.csv # flat exports (injection-safe)185 structure.ddl.sql # inferred DDL (human-review header; commented inferred FKs)186 structure.xlsx # one sheet/entity + summary + relationships (if openpyxl present)187 wiki/index.md wiki/<entity>.md # interlinked, file:line-cited pages188 structure.schema-facets.json # OL SchemaDatasetFacet enrichment (if the OL sibling present)189 .run/ # 0700 resumable run cache (run-state + per-file chunk findings)190```191192## Anti-patterns — STOP if you catch yourself193194- Adding a per-format parser dependency (sqlglot / an XML parser) — N2 forbids it.195- Letting the LLM emit a byte offset — the validator rejects it; Python computes it.196- Emitting a confident COBOL offset past a SYNC or ODO field — must be ranged + gap.197- Promoting a COBOL cross-record FK above `speculative`, or rendering it as a live198 constraint — commented advisory only, opt-in only.199- Executing the inferred DDL, or feeding any relationship edge into a gate.200- Widening `legacy-code-intel pipeline_fingerprint`'s 5-field signature — wrap it.201- Fabricating an entity for an un-analyzed chunk — record an honest gap instead.202203## Composition with other skills204205- `lineage-extract-static` — reuses `chunk_file.py` (I/O substrate, extended with206 `preferred_break_lines`), the `accumulate.py` lower-confidence-wins idiom, the207 `render_report.py` HTML shape, `redact.py`, and extends `merge_into_ol.py` for the208 SchemaDatasetFacet (M1).209- `legacy-code-intel` — reuses the content-addressed `store.py` flock + skip-ladder210 and `fingerprint.py` (wrapped, never widened).211- `ms-office-excel-python` — composed for the `.xlsx` output (openpyxl + the212 formula-injection `safe_cell` rule).213- `wiki` — composed for the per-entity cited pages (`.wiki.lock`, anti-pollution).214- `mainframe-lineage-parsers` — **reuses this skill's deterministic COBOL machinery**:215 its `cobol_extract.py` imports `scripts/cobol_offset_calc.py` as a module216 (`parse_pic` / `normalize_usage` / `elementary_length` / `build_tree` /217 `compute_offsets` / `compute_finding_offsets`) for record length + the field218 level-tree — NOT a forked COBOL parser — then adds the lineage layer219 (`SELECT ... ASSIGN-TO` join, FD records, `READ`/`WRITE` direction, `MOVE`220 flow) on top. It also imports `validate_finding.py`'s `CONFIDENCE_ENUM`221 byte-identically (single source of truth for `grounded | inferred |222 speculative`). Keep these public entry points + the field-dict key names223 (`pic_clause`, `usage`, `occurs`, `occurs_max`, `occurs_depending_on`,224 `redefines`) stable — the lineage sibling reads them.225226## Security — XML / structure parsing227228- 0700 cache dirs (NEVER `/tmp`); atomic `.tmp + os.replace + fsync` writes.229- Fail-closed secret redaction (`redact.py`) before any rendered output.230- DSX is read as **chunked text** (N2) — no XML parsing, so no XXE surface. The231 prompts flag RCP / `#PARAM#` dynamic layouts as gaps rather than guessing columns.232- All HTML/CSV/Excel/wiki cells are escaped/sanitized against XSS and233 spreadsheet-formula injection (CWE-1236).234235## See also236237- `docs/plans/2026-06-15-structure-recovery-skill-design.md` — full design + decisions.238- `references/confidence-model.md` — the 3-axis honesty model + K2 relationship caps.239- `references/chunking.md` — the chunking discipline (boundary hints, oversized-record gap).