L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Données et recherche · SKILL.md

mainframe-lineage-parsers

mainframe-lineage-parsers

Use when you want a STANDALONE, DETERMINISTIC (no-LLM) data-lineage extractor for legacy mainframe code — fixed-format COBOL + JCL + embedded EXEC SQL (DB2), plus deterministic Control-M Automation-API jobs-as-code (scheduler->program->data, job->job event DAG) -> OpenLineage 2.0.2 ndjson. The deterministic plug-in track under lineage-extract-static (a comp…

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: mainframe-lineage-parsers description: "Use when you want a STANDALONE, DETERMINISTIC (no-LLM) data-lineage extractor for legacy mainframe code — fixed-format COBOL + JCL + embedded EXEC SQL (DB2), plus deterministic Control-M Automation-API jobs-as-code (scheduler->program->data, job->job event DAG) -> OpenLineage 2.0.2 ndjson. The deterministic plug-in track under lineage-extract-static (a complement to the LLM-as-parser family — run both and compare yourself; no diff harness in scope). Pure stdlib + OPTIONAL sqlglot (regex fallback, NO silent LLM fallback) + OPTIONAL networkx. Non-interactive CLI (run_lineage.py): flags only, zero prompts, scriptable in CI/cron. Static analysis only (v1). The mainframe sibling of lineage-extract-static (flow), structure-recovery (shape), legacy-code-intel (symbols). Also trigger on "deterministic mainframe lineage", "COBOL/JCL/DB2 lineage", "EXEC SQL lineage", "OpenLineage from mainframe", "DD-join lineage", "host-var to column lineage", "Control-M lineage", "scheduler lineage"." disambiguation: Deterministic, no-LLM parsers for fixed-format COBOL, JCL and embedded EXEC SQL. The LLM-driven multi-stack framework is lineage-extract-static.

mainframe-lineage-parsers — deterministic COBOL/JCL/DB2 lineage extractor

Design at docs/plans/2026-06-16-mainframe-lineage-parsers-design.md.

What it does

Reads fixed-format COBOL, JCL (incl. PROC/symbol expansion), embedded EXEC SQL (DB2), and Control-M Automation-API jobs-as-code JSON, and emits OpenLineage 2.0.2 ndjson describing the data lineage — entirely deterministically, with NO LLM in the loop, ever (C2). Re-running with the same inputs produces a byte-identical ndjson (the emitter sorts keys and uses a fixed default eventTime; set SOURCE_DATE_EPOCH for a reproducible non-default eventTime).

The pipeline (design §3):

preprocess        — source-format detect (fixed v1; free -> gap), col 8-72 strip,
                    comment/continuation folding, EXEC SQL block split
  -> copybook_resolver  — COPY ... REPLACING resolution over --copybook-path
    -> jcl_extract / cobol_extract / sql_extract / controlm_extract  — 4 extractors, ONE internal IR
      -> graph_assemble   — networkx-or-stdlib canonical sort + dedupe + DDNAME stitch
        -> openlineage_emit — IR -> OL 2.0.2 ndjson (extractor_id, engine,
                              confidence/provenance + columnLineage / controlmDependencies
                              / contentSha256 facets)

Output is one JobEvent / DatasetEvent per line, each carrying extractor_id=mainframe-lineage-parsers, the SQL engine actually used, and confidence/provenance facets (grounded / inferred / speculative) plus explicit gaps (unresolved_copy, free_format_unsupported, symbolic_dsn, catalog_less_column, and the Control-M unresolved_variable, unresolved_connection, runtime_path, unresolved_event_dep). Gaps are normal output — they are diagnostics, never a blocking question.

Purpose — testing & comparison (NO comparison harness in scope)

This skill is the sanctioned deterministic v1.1 plug-in track under the lineage-extract-static anti-pattern #7 — a complement, not a replacement, of the LLM-as-parser family. The intended workflow is:

  1. Run this deterministic flow over a mainframe estate.
  2. Run the LLM-as-parser flow (lineage-extract-static) over the same estate separately.
  3. Compare the two outputs yourself.

There is NO comparison / diff / scoring harness in scope (decision A): the skill produces lineage; you do the comparison. The two flows deliberately use distinct producer URIs so their ndjson is easy to diff side by side.

When to use it

  • You have legacy fixed-format COBOL + JCL + embedded EXEC SQL (DB2) and you want reproducible, no-model lineage you can re-run in CI/cron and trust to be byte-identical.
  • You want a deterministic baseline to compare against the LLM-as-parser lineage flow.
  • You need the two precision-win edge classes — JCL DD/DSN -> DDNAME bind-key stitch to the COBOL SELECT ... ASSIGN TO, and embedded-SQL host-variable -> table.column — with honest confidence/provenance facets.

NOT for (non-goals — explicit diagnostics, not silent best-effort, design §8): Pick/MultiValue (no grammar — use lineage-extract-static for the LLM lineage path, legacy-code-intel for symbols, pick-developer for reading the source); Java (not mainframe legacy, and outside this stdlib-only / no-JVM engine by design D1 — use lineage-extract-static for Java lineage, legacy-code-intel / java-backend / java-frontend for symbols + source); free-format COBOL (gap: free_format_unsupported); dynamic SQL; CICS/IMS/VSAM-internal/GDG-temporal semantics beyond dataset identity; full field-level dataflow; non-DB2 SQL dialects; Control-M Job:EmbeddedScript inline bodies (opaque — a job node + diagnostic only, NOT parsed; that is the LLM path); cron / shell-stream schedulers (still LLM-only — optional deterministic later); the L3 functional-fusion view (deferred to v1.1).

Engine flag, sqlglot-optional, regex fallback — NO silent LLM fallback

The SQL extractor has two deterministic engines and never falls back to a model (C2):

  • --engine auto (default) — use sqlglot if importable, else the stdlib regex engine (with an sql.engine_degraded diagnostic, lower SQL precision, never a failure).
  • --engine regex — force the stdlib regex engine.
  • --engine sqlglot-sql — require sqlglot; if it is absent the CLI exits 2 and prints a fail-loud handoff pointer ("for the LLM-as-parser path use lineage-extract-static"). A model is never auto-invoked.

sqlglot and networkx are both optional, import-if-present. There is no pip install at runtime, no new mandatory dependency, no network/egress, and no shell beyond python3 (design §11.2 / D1).

Confidence / provenance facets & advisory-until-gold host-var edges

Every edge carries a confidence facet:

  • grounded — literally present in the source (e.g. a literal DSN= in JCL, a table named in EXEC SQL).
  • inferred — derived by a documented rule (e.g. the DD-join stitch bridge; a host-var -> column binding resolved with --schema).
  • speculative — best-available but unverified (e.g. a host-var -> column edge with no --db2-catalog/--schema, which also emits a catalog_less_column gap).

The two precision-win edge classes (DD-join, host-var -> column) are advisory until a gold/ fixture clears a precision bar (#158): they are for human review and comparison, and never feed a gate — there is no G_*, no INTEGRATED -> VERIFIED arc, and no CI hard-block driven by them. See gold/README.md.

Control-M scheduler lineage (deterministic; design §3)

The skill also extracts deterministic lineage from Control-M Automation-API jobs-as-code JSON — a structural twin of the JCL extractor, parsed with stdlib json only (no LLM, no new dep). It augments the scheduler layer; it does not touch COBOL/SQL extraction.

Flags:

  • --controlm <path> — a Control-M jobs-as-code JSON file (repeatable). Explicit-flag-authoritative: files passed here are forced kind=controlm and bypass the COBOL/JCL classifier (a Control-M .json is neither a JCL nor a COBOL suffix). This is the canonical, documented Control-M invocation. As a convenience only, a .json passed via --src that carries a "Type": "Job:" leaf is sniffed and tagged controlm ahead of the COBOL fallback.
  • --controlm-connection-profiles <json> — a JSON object (inline or a file path) mapping profile_name -> {host,port,db,schema} (the Control-M analogue of --db2-catalog). A Job:Database edge resolves to a real DB2 table node only when its ConnectionProfile is present here and the SQL is literal; otherwise the edge is forced speculative + a gap.

Job-type coverage (design §3 table):

TypeEmission
Job:Commandargv[0] → program node mainframe://<program-id> (program-id = basename of argv[0], extension stripped, upper-folded — the locked stitch key that collides with the COBOL upper-folded PROGRAM-ID); bind kind=inferred (cross-artifact name bind, never grounded)
Job:ScriptFileName+FilePath → script artifact edge (grounded when both literal)
Job:EmbeddedScriptjob node + diagnostic only — the inline body is opaque (NOT parsed; LLM path)
Job:FileTransferSrc→job (read) + job→Dest (write) file edges; an AssignFileNameToVariable runtime watched name → speculative + runtime_path gap
Job:Database:*DB2-style table edges only if the SQL is literal AND the ConnectionProfile resolves; else speculative + gap
Job:DummyDAG node only (a scheduling placeholder)

Job→job DAG: eventsToAdd (Out) raised by job A and eventsToWaitFor (In) awaited by job B are matched on the event-name string → an A→B dependency edge, resolved deterministically across the folder set.

Case-sensitivity divergence (naming-contract §2a): Control-M <folder> and <jobname> are case-sensitive and are NOT upper-folded (Control-M treats case-variant names as distinct jobs). Only the program-id side of the scheduler→program edge is upper-folded so it still collides onto the COBOL node.

New gap kinds (naming-contract §5): unresolved_variable (a %%VAR unresolved after Variables substitution), unresolved_connection (a ConnectionProfile absent from the map), runtime_path (a %%-interpolated / runtime-assigned path), unresolved_event_dep (an eventsToWaitFor with no in-scope producer). All four are speculative — never an invented binding (C3).

New OpenLineage facets (additive; OL core stays pinned at 2-0-2):

  • standard columnLineage (1-2-0) on the output table dataset for host-var→column edges (INDIRECT/CONDITIONAL transformations; the custom mainframeLineage confidence facet stays alongside);
  • custom controlmDependencies JOB facet (static design-time scheduling deps; field names mirror JobDependenciesRunFacet for a future Run-stream migration — it is NOT a Run facet);
  • standard sourceCodeLocation.contentSha256 JOB facet (the v1.1 join key) — contentSha256 = sha256 of the raw on-disk source bytes, pre-copybook- expansion / pre-symbol-substitution, no encoding normalization (byte-identical to the lineage-extract-static definition so the future JOB↔artifact join holds).

Run it (three-way modes):

# Control-M only
python3 scripts/run_lineage.py --controlm jobs.json --out lineage.ndjson

# Control-M + DB2 connection profiles
python3 scripts/run_lineage.py --controlm jobs.json \
  --controlm-connection-profiles profiles.json --out lineage.ndjson

# Control-M + COBOL/JCL in one estate (the scheduler→program stitch collides
# GOLDPAY.sh onto the COBOL PROGRAM-ID GOLDPAY)
python3 scripts/run_lineage.py \
  --controlm jobs.json --src cobol_and_jcl/ --out lineage.ndjson

The three ways to run it (design §11)

1. Interactive front-loaded-inputs flow

When invoked conversationally, gather all inputs and the required permissions ONCE at the start, confirm once, then hand off to the non-interactive CLI with zero further prompts:

  1. Ask for the inputs up front (one round):
    • --src — source root(s) / file(s) / glob(s) for COBOL/JCL (repeatable; at least one of --src / --controlm is required)
    • --controlm — Control-M jobs-as-code JSON file(s) (repeatable; explicit-flag-authoritative — forces kind=controlm)
    • --controlm-connection-profiles — JSON object/file: profile -> {host,port,db,schema}
    • --copybook-path — copybook search dir(s) for COPY resolution (repeatable)
    • --jcl-proc-path — JCL PROC/INCLUDE lib dir(s) (repeatable)
    • --out — OpenLineage ndjson output path (required)
    • --engine — auto (default) | regex | sqlglot-sql
    • --source-format — fixed (v1 default; free -> gap)
    • --db2-catalog / --schema — optional, for host-var -> column resolution (absent -> column edges speculative + catalog_less_column gap)
    • --copybook-missing — gap (default) | fail
  2. Surface the allow-list ONCE and confirm:

    This run needs: read <src> + <copybook-path> + <jcl-proc-path>, write <out>. NO network, NO shell beyond python3, NO pip install.

  3. After the single confirmation, run run_lineage.py to completion with zero further questions. Gaps (unresolved copybook, free-format, symbolic DSN, catalog-less column) become diagnostics / speculative edges — never a mid-run prompt.

2. Direct headless path — truest automation (bypasses this instructions file)

The engine is pure deterministic Python, so full automation needs no model, no SKILL.md, no LLM at all. Invoke the CLI directly — scriptable in CI / cron / batch:

python3 skills/mainframe-lineage-parsers/scripts/run_lineage.py \
  --src /path/to/cobol_and_jcl \
  --copybook-path /path/to/copybooks \
  --jcl-proc-path /path/to/proclib \
  --out /tmp/lineage.ndjson \
  --engine auto < /dev/null

All inputs are flags; the CLI never reads stdin and never asks a question. A machine-readable JSON summary is printed on stdout; diagnostics go to stderr. Exit codes:

ExitMeaning
0success (even with gaps — gaps are normal output)
1fatal: no usable input, unreadable --src, or fail-closed emit/validation
2fail-loud handoff: --engine=sqlglot-sql but sqlglot is absent (no model auto-invoked)
3--copybook-missing=fail and an unresolved COPY remained

This direct path is model-neutral: Claude Code, Codex CLI, Copilot CLI, and the Antigravity CLI (agy) can all invoke the same CLI (library rule).

3. Claude-orchestrated headless path (claude -p)

To run unattended through an AI CLI's permission layer:

claude -p "run mainframe-lineage-parsers: \
  --src /path/to/cobol_and_jcl \
  --copybook-path /path/to/copybooks \
  --out /tmp/lineage.ndjson" \
  --dangerously-skip-permissions < /dev/null

Security caveat — --dangerously-skip-permissions: this flag auto-approves the read/write allows so the run never stops on a prompt, but it bypasses ALL permission gating. Use it only in trusted / CI sandbox contexts, and scope the run to a known tree (a checked-out repo or a copied estate) so an unattended run cannot touch anything outside the intended source + output paths. For a manually-supervised run, prefer path 1 or the direct path 2.

Copy-paste headless example

A complete, runnable example over the shipped gold/ precision fixtures (--schema PAYROLL resolves the host-var -> column edges to inferred):

python3 skills/mainframe-lineage-parsers/scripts/run_lineage.py \
  --src skills/mainframe-lineage-parsers/gold/cobol/GOLDPAY.cbl \
  --src skills/mainframe-lineage-parsers/gold/jcl/GOLDPAY.jcl \
  --out /tmp/goldpay.ndjson \
  --schema PAYROLL < /dev/null
# -> exit 0; prints a JSON summary on stdout (events_emitted, job_events,
#    dataset_events, engine, gaps, ...); writes /tmp/goldpay.ndjson.

Without --schema / --db2-catalog, the same run still exits 0 but the host-var -> column edges are forced speculative and a catalog_less_column gap is emitted per unresolved column — honest, never guessed.

Dependencies & environments (CLI / VS Code end users)

The core is pure stdlib and always runs — Control-M/COBOL/JCL/EXEC-SQL extraction, the IR, and OpenLineage 2.0.2 ndjson need zero third-party packages. The libraries in requirements-optional.txt are OPTIONAL enhancers (import-if-present, graceful degradation); this skill never pip-installs at runtime (D1 / air-gap invariant).

See your situation first — the doctor never installs anything:

python3 scripts/run_lineage.py --check-deps   # or: python3 scripts/check_deps.py

It prints the active interpreter, which enhancers are present/missing, exactly what degrades when one is absent, a PEP-668-safe install recipe, and — if it finds a sibling interpreter that already has the deps — points you at it.

EnhancerUnlocksIf absent
jsonschemawrite-time OL 2.0.2 schema validationemit runs unvalidated
sqlglothigher-precision EXEC SQLstdlib regex engine (lower precision) + sql.engine_degraded diagnostic
networkxgraph assembly via networkxstdlib fallback (same output, slower on huge estates)

Install (only if you want the enhancers). Modern distros mark the system python3 PEP-668 externally-managed, so a bare pip install is BLOCKED — use one of:

# Recommended — PEP-668-safe project venv (CLI or VS Code):
python3 -m venv .venv && .venv/bin/pip install -r requirements-optional.txt
.venv/bin/python scripts/run_lineage.py --controlm jobs.json --out out.ndjson

# Per-user (where allowed):   python3 -m pip install --user -r requirements-optional.txt
# Last resort (system py):    python3 -m pip install --break-system-packages -r requirements-optional.txt
# Air-gapped (offline wheels): python3 -m pip install --no-index --find-links=<wheel-dir> -r requirements-optional.txt

VS Code: select the venv interpreter (.venv/bin/python) as the workspace interpreter, or point the integrated terminal at it — the skill runs under whatever python3 the CLI/extension invokes, so an env without sqlglot silently uses the regex engine. Run --check-deps in the integrated terminal to confirm which interpreter is active.

References

  • references/naming-contract.md — the frozen shared naming contract (C1): DB2 / JCL job / COBOL program / dataset DSN canonicalisation, DDNAME bind keys, determinism rules, and the frozen gap set. Parity is asserted output-vs-FROZEN-CONTRACT, never output-vs-live-LLM.
  • references/preprocessing.md — the deterministic stdlib preprocessing recipe (kept in lockstep with scripts/preprocess.py).
  • references/decision-framework.md — keyed guidance for choosing deterministic vs LLM-as-parser vs hybrid for a given legacy estate.
  • gold/README.md — the advisory-until-gold precision fixtures (#158) and the "never feeds a gate" rule.

Sibling skills

The mainframe member of the legacy-comprehension lineage family — each covers a different axis of a legacy estate:

  • lineage-extract-static — data/process flow (the LLM-as-parser family this skill is the deterministic plug-in track of; run both and compare).
  • structure-recovery — data shape (column lists/types, COBOL record layouts + byte offsets, inferred DDL).
  • legacy-code-intel — code symbols (a queryable SCIP-style symbol / occurrence graph; mirrors the same 0.85 advisory-until-gold bar).
  • cobol-developer / ibm-mainframe — reading/writing the COBOL + JCL + DB2 source this skill consumes.
Skills enregistrées