/-
  Infoductor.Foundation.Meta — meta-mirror types
  =================================================
  Phase A of `docs/ALGEBRA_PLAN.md`.  Defines the meta-level
  vocabulary on which the universal `restructure` macro is built —
  the meta-mirror of the cubical AST (`CType`, `FaceFormula`,
  `CTerm`).

  These types are pure data; no semantic content is committed in
  this module.  Their semantics — how `restructure` consumes them
  to emit `MakeEditLinkProps.ofReplaceRange` calls in the `Edit`
  monad — lives in `Algebra/Restructure.lean`.

  | Cubical-AST type | Meta-mirror | Role                           |
  |------------------|-------------|--------------------------------|
  | `CType`          | `MetaCType` | meta-types of source artifacts |
  | `FaceFormula`    | `MetaClassifier` | "where in the codebase"   |
  | `CTerm`          | `MetaArtifact` | the new content / fallback   |

  The point: every restructuring operation has the *same five
  fields* as `comp i A φ u t`, with each field promoted from the
  cubical CTerm world to the meta-Lean-source world.

  See `docs/ALGEBRA_PLAN.md` §2.2 for the formal table.
-/

import Lean.Syntax
import Lean.Data.Name

namespace Infoductor

-- ── MetaCType — the meta-mirror of `CType` ──────────────────────────────────
-- Every artifact in a Lean source has a meta-type that classifies its
-- structural role.  These are the "cubical-types" of the source-code
-- universe: a theorem is an artifact whose meta-type is `theorem`,
-- a `def` is an artifact whose meta-type is `definition`, etc.

/-- Meta-mirror of `CType`: classifies the structural role of an
    artifact in Lean source code.

    Each constructor names a kind of declaration / structural unit
    that `restructure` can operate on.  The list is closed: extending
    it requires updating the universal macro to handle the new
    artifact kind.

    See `docs/ALGEBRA_PLAN.md` §2.2 for the design rationale. -/
inductive MetaCType where
  /-- A `theorem` declaration: `theorem foo : T := proof`. -/
  | theorem_      : MetaCType
  /-- A `def` declaration: `def foo := body`. -/
  | definition    : MetaCType
  /-- An `instance` declaration. -/
  | instance_     : MetaCType
  /-- A `structure` declaration. -/
  | structure_    : MetaCType
  /-- A Lean `inductive` declaration. -/
  | inductive_    : MetaCType
  /-- An entire file. -/
  | file          : MetaCType
  /-- A `namespace` block. -/
  | namespace_    : MetaCType
  /-- A `class` declaration. -/
  | class_        : MetaCType
  /-- A logical "set of classifiers" — abstract collection used by
      methodology dispatch. -/
  | classifierSet : MetaCType
  /-- An edge in the dependency graph between declarations.  Used
      by `transp`-along-MetaPath to identify what gets reorganised
      together. -/
  | dependencyEdge : MetaCType
  /-- A custom-attribute annotation site (e.g., the position of a
      `@[simp]` or `@[methodology]` annotation on a declaration). -/
  | attributeSite : MetaCType
  deriving Repr, Inhabited, DecidableEq

-- ── MetaClassifier — the meta-mirror of `FaceFormula` ──────────────────────
-- "Where in the codebase does this restructuring apply?"  The face
-- lattice on dimensions has its analogue at the meta level: face = a
-- predicate over dimension-coordinates picking out a sub-cube; meta-
-- classifier = a predicate over codebase-coordinates picking out a
-- sub-region.

/-- Meta-mirror of `FaceFormula`: a predicate over codebase
    positions.  Combined via `meet` and `join` to form composite
    "where in the codebase" predicates.

    The lattice structure mirrors the cubical face lattice:
    `meet` is intersection, `join` is union; `always` is the top
    (`.top` analogue), `never` is the bottom (`.bot` analogue). -/
inductive MetaClassifier where
  /-- "Everywhere" — the top of the meta-classifier lattice;
      analogue of `FaceFormula.top`. -/
  | always : MetaClassifier
  /-- "Nowhere" — the bottom; analogue of `FaceFormula.bot`. -/
  | never  : MetaClassifier
  /-- "At this declaration"; analogue of `FaceFormula.eq0`/`eq1`
      on a specific dim coordinate. -/
  | atDecl : Lean.Name → MetaClassifier
  /-- "In this file." -/
  | inFile : String → MetaClassifier
  /-- "Under this attribute" — anywhere a particular attribute is
      attached. -/
  | underAttribute : Lean.Name → MetaClassifier
  /-- "In any declaration that depends on this one." -/
  | dependencyOf : Lean.Name → MetaClassifier
  /-- "In the same namespace as this name." -/
  | inNamespace : Lean.Name → MetaClassifier
  /-- Conjunction; analogue of `FaceFormula.meet`. -/
  | meet : MetaClassifier → MetaClassifier → MetaClassifier
  /-- Disjunction; analogue of `FaceFormula.join`. -/
  | join : MetaClassifier → MetaClassifier → MetaClassifier
  deriving Repr, Inhabited

-- DecidableEq for MetaClassifier — manual mutual decision because
-- the type is recursive (meet/join arms) and mixes Lean.Name / String
-- carriers.

def MetaClassifier.decEq : (a b : MetaClassifier) → Decidable (a = b)
  | .always, .always => isTrue rfl
  | .never,  .never  => isTrue rfl
  | .atDecl n, .atDecl m =>
      if h : n = m then isTrue (by rw [h])
      else isFalse (fun heq => h (by cases heq; rfl))
  | .inFile s, .inFile t =>
      if h : s = t then isTrue (by rw [h])
      else isFalse (fun heq => h (by cases heq; rfl))
  | .underAttribute n, .underAttribute m =>
      if h : n = m then isTrue (by rw [h])
      else isFalse (fun heq => h (by cases heq; rfl))
  | .dependencyOf n, .dependencyOf m =>
      if h : n = m then isTrue (by rw [h])
      else isFalse (fun heq => h (by cases heq; rfl))
  | .inNamespace n, .inNamespace m =>
      if h : n = m then isTrue (by rw [h])
      else isFalse (fun heq => h (by cases heq; rfl))
  | .meet a₁ b₁, .meet a₂ b₂ =>
      match MetaClassifier.decEq a₁ a₂, MetaClassifier.decEq b₁ b₂ with
      | isTrue ha, isTrue hb => isTrue (by rw [ha, hb])
      | isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
      | _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
  | .join a₁ b₁, .join a₂ b₂ =>
      match MetaClassifier.decEq a₁ a₂, MetaClassifier.decEq b₁ b₂ with
      | isTrue ha, isTrue hb => isTrue (by rw [ha, hb])
      | isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
      | _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
  | .always, .never | .always, .atDecl _ | .always, .inFile _
  | .always, .underAttribute _ | .always, .dependencyOf _
  | .always, .inNamespace _ | .always, .meet _ _ | .always, .join _ _
  | .never, .always | .never, .atDecl _ | .never, .inFile _
  | .never, .underAttribute _ | .never, .dependencyOf _
  | .never, .inNamespace _ | .never, .meet _ _ | .never, .join _ _
  | .atDecl _, .always | .atDecl _, .never | .atDecl _, .inFile _
  | .atDecl _, .underAttribute _ | .atDecl _, .dependencyOf _
  | .atDecl _, .inNamespace _ | .atDecl _, .meet _ _ | .atDecl _, .join _ _
  | .inFile _, .always | .inFile _, .never | .inFile _, .atDecl _
  | .inFile _, .underAttribute _ | .inFile _, .dependencyOf _
  | .inFile _, .inNamespace _ | .inFile _, .meet _ _ | .inFile _, .join _ _
  | .underAttribute _, .always | .underAttribute _, .never
  | .underAttribute _, .atDecl _ | .underAttribute _, .inFile _
  | .underAttribute _, .dependencyOf _ | .underAttribute _, .inNamespace _
  | .underAttribute _, .meet _ _ | .underAttribute _, .join _ _
  | .dependencyOf _, .always | .dependencyOf _, .never
  | .dependencyOf _, .atDecl _ | .dependencyOf _, .inFile _
  | .dependencyOf _, .underAttribute _ | .dependencyOf _, .inNamespace _
  | .dependencyOf _, .meet _ _ | .dependencyOf _, .join _ _
  | .inNamespace _, .always | .inNamespace _, .never
  | .inNamespace _, .atDecl _ | .inNamespace _, .inFile _
  | .inNamespace _, .underAttribute _ | .inNamespace _, .dependencyOf _
  | .inNamespace _, .meet _ _ | .inNamespace _, .join _ _
  | .meet _ _, .always | .meet _ _, .never | .meet _ _, .atDecl _
  | .meet _ _, .inFile _ | .meet _ _, .underAttribute _
  | .meet _ _, .dependencyOf _ | .meet _ _, .inNamespace _
  | .meet _ _, .join _ _
  | .join _ _, .always | .join _ _, .never | .join _ _, .atDecl _
  | .join _ _, .inFile _ | .join _ _, .underAttribute _
  | .join _ _, .dependencyOf _ | .join _ _, .inNamespace _
  | .join _ _, .meet _ _ =>
      isFalse (fun heq => by cases heq)

instance : DecidableEq MetaClassifier := MetaClassifier.decEq

-- ── MetaCTerm — the structural meta-mirror of `CTerm` ──────────────────────
-- A faithful AST-level mirror of the cubical term language.  Cubical
-- `CTerm` constructors map onto `MetaCTerm` constructors structurally,
-- so embedding round-trips through a partial inverse (`toCTerm?`).
-- See `Embed.lean` in the cubical bridge for the concrete maps and the
-- coreflection theorems.
--
-- Generative basis (8 constructors):
--   · `ident n`        — closed reference to a Lean declaration.
--   · `sym  s`         — open leaf (string-named local or dim-binder).
--   · `app  f a`       — universal application; encodes `papp`, `fst`,
--                        `snd`, `pair`, `glueIn`, `unglue`, schema
--                        ctors, and eliminators via `.ident`-headed
--                        chains.
--   · `lam  x t`       — term-level lambda (binds `x`).
--   · `plam i t`       — path-level lambda (binds dim-name `i`).
--   · `comp i A φ u t` — five-field composition, mirroring `compⁱ A φ u t`.
--   · `transp i A φ t` — four-field transport, mirroring `transpⁱ A φ t`.
--   · `empty`          — placeholder for deletion / missing content.
--
-- The `comp` and `transp` arms keep their face-formula slot as a
-- `MetaClassifier` (rather than a nested `MetaCTerm` encoding) so that
-- classifier-at-position semantics flow through unchanged from
-- `Edit.lean`.

inductive MetaCTerm where
  /-- A reference to a Lean declaration — the closed leaf shape. -/
  | ident   : Lean.Name → MetaCTerm
  /-- A symbolic name — open leaf (string-named local variable or
      dim-binder).  Distinct from `ident` so the round-trip through
      `CTerm.var` preserves the original string. -/
  | sym     : String → MetaCTerm
  /-- Application — the universal combinator.  `papp`, `fst`, `snd`,
      `pair`, schema ctors, and eliminators all encode as `.ident`-
      headed `.app` chains. -/
  | app     : MetaCTerm → MetaCTerm → MetaCTerm
  /-- Term-level lambda — binds `x` in the body. -/
  | lam     : String → MetaCTerm → MetaCTerm
  /-- Path-level lambda — binds a dim-name `i`.  Distinct from `lam`
      because dim-binders inhabit a different kind of universe; the
      structural distinction is preserved here for downstream
      analyses that care. -/
  | plam    : String → MetaCTerm → MetaCTerm
  /-- Five-field composition — mirrors `compⁱ A φ u t`.  The dim-
      binder is stored as a string (the `DimVar.name`); the body and
      the witness are `MetaCTerm`s; the face is a `MetaClassifier`. -/
  | comp    : String → MetaCTerm → MetaClassifier
            → MetaCTerm → MetaCTerm → MetaCTerm
  /-- Four-field transport — mirrors `transpⁱ A φ t`. -/
  | transp  : String → MetaCTerm → MetaClassifier
            → MetaCTerm → MetaCTerm
  /-- The empty meta-term — placeholder for deletion or missing
      content.  Distinct from `MetaArtifact.empty` (which is at the
      artifact level). -/
  | empty   : MetaCTerm
  deriving Repr, DecidableEq

instance : Inhabited MetaCTerm := ⟨MetaCTerm.empty⟩

-- ── MetaArtifact — the meta-mirror of `CTerm` ───────────────────────────────
-- The "content" half of restructure.  An artifact is what gets put in
-- the source: a chunk of raw text, a parsed syntax tree, a reference
-- to another decl, or "remove this" (the empty artifact).

/-- Meta-mirror of `CTerm`: the content placed at a meta-position by a
    restructuring operation.

    Four shapes: raw source text (string), parsed syntax (Lean.Syntax),
    a reference to an existing decl by `Name`, and the empty artifact
    (used for deletion).

    `Lean.Syntax` is opaque-by-default in Lean 4 — its internal
    structure is large.  We don't derive `DecidableEq` on `MetaArtifact`
    because comparing arbitrary Syntax trees structurally is heavy and
    not needed by `restructure`'s dispatch logic.  Use `MetaArtifact.toString`
    for printable comparisons. -/
inductive MetaArtifact where
  /-- Raw Lean source text (will be parsed at apply time). -/
  | source : String → MetaArtifact
  /-- A parsed Lean syntax tree. -/
  | declAt : Lean.Syntax → MetaArtifact
  /-- A structural meta-term — the AST-level representation of a
      cubical-flavoured artifact.  Preserves the cubical shape so
      downstream tooling can decompose / reflect it without parsing.
      The structural mirror is a real coreflection: every cubical
      `CTerm` embeds losslessly via `CTerm.toMetaCTerm` (in the
      cubical bridge) and reflects back via `MetaCTerm.toCTerm?`. -/
  | cterm  : MetaCTerm → MetaArtifact
  /-- A reference to an existing declaration by name; resolved at
      apply time. -/
  | refTo  : Lean.Name → MetaArtifact
  /-- The empty artifact — "remove this position." -/
  | empty  : MetaArtifact
  deriving Inhabited

/-- A printable summary of an artifact, useful for diagnostics and
    widget rendering. -/
def MetaArtifact.toString : MetaArtifact → String
  | .source s => s!"source({s})"
  | .declAt _ => "declAt(<syntax>)"
  | .cterm t  => s!"cterm({repr t})"
  | .refTo n  => s!"refTo({n})"
  | .empty    => "empty"

instance : ToString MetaArtifact := ⟨MetaArtifact.toString⟩

-- ── Source rendering — meta-mirrors → valid Lean source ─────────────────────
-- The poetic move: each meta-mirror value renders to a Lean
-- expression that, when elaborated, reconstructs the *same* value.
-- This closes the bridge's loop at the source level: an `Edit`'s
-- `.cterm` content can be written to a `.lean` file and parsed
-- back via Lean's own parser/elaborator without us having to
-- write a parser.  The renderer is fully recursive over the
-- meta-mirror's structure, faithful on every constructor.

/-- Escape a `String` as a Lean string literal (with surrounding
    quotes and `"` / `\` escaped).  Kernel-reducible alternative
    to `repr` for strings — built from `String.toList` /
    `String.join` / `Char.toString`, all of which the kernel
    unfolds.  Inverse of the parser's `readStrLit` on the inner
    body. -/
def escapeStrLit (s : String) : String :=
  let body := String.join (s.toList.map (fun c =>
    match c with
    | '"'  => "\\\""
    | '\\' => "\\\\"
    | c    => c.toString))
  "\"" ++ body ++ "\""

/-- Render a `Lean.Name` as Lean source that reconstructs it via
    the `Lean.Name` constructors.  Faithful on `.anonymous`,
    `.str`, and `.num` arms — the `.str` arm uses `escapeStrLit`
    for kernel-reducible string-literal escaping.

    Local helper inside `namespace Infoductor` (full name
    `Infoductor.nameToLeanSource`) to avoid shadowing the global
    `Lean` library by a `def Lean.Name.…` here.

    Recursive sub-calls render *without* extra parens — the
    `.str`/`.num` arms each contribute their own outer
    parentheses, so wrapping the recursive call would produce
    spurious double-parens at depths ≥ 2.  Consistent with
    `MetaClassifier.toLeanSource` and `MetaCTerm.toLeanSource`,
    which also don't double-wrap. -/
def nameToLeanSource : Lean.Name → String
  | .anonymous => "Lean.Name.anonymous"
  | .str p s   =>
      s!"(Lean.Name.str {nameToLeanSource p} {escapeStrLit s})"
  | .num p n   =>
      s!"(Lean.Name.num {nameToLeanSource p} {n})"

-- ── Source rendering for `Lean.Syntax` (the `.declAt` payload) ─────────────
-- The structural-mirror identity for parsed `Lean.Syntax` trees: we render
-- each constructor as a Lean expression that constructs the same value via
-- `Lean.Syntax.<ctor>` / `Lean.SourceInfo.<ctor>` / `Substring.Raw.mk` /
-- `String.Pos.Raw.mk`.  The renderer's image is parsed back by `MetaParse`'s
-- mirror parser; together they form a true coreflection on `Syntax`.

/-- Render a `String.Pos.Raw` as Lean source. -/
def stringPosRawToLeanSource (p : String.Pos.Raw) : String :=
  s!"(String.Pos.Raw.mk {p.byteIdx})"

/-- Render a `Substring.Raw` as Lean source.  Faithful triple-encode:
    the underlying string + start position + stop position. -/
def substringRawToLeanSource (s : Substring.Raw) : String :=
  s!"(Substring.Raw.mk {escapeStrLit s.str} \
      {stringPosRawToLeanSource s.startPos} \
      {stringPosRawToLeanSource s.stopPos})"

/-- Render a `Lean.SourceInfo` as Lean source, faithful on every arm. -/
def sourceInfoToLeanSource : Lean.SourceInfo → String
  | .original lead pos trail endPos =>
      s!"(Lean.SourceInfo.original {substringRawToLeanSource lead} \
          {stringPosRawToLeanSource pos} \
          {substringRawToLeanSource trail} \
          {stringPosRawToLeanSource endPos})"
  | .synthetic pos endPos canonical =>
      s!"(Lean.SourceInfo.synthetic {stringPosRawToLeanSource pos} \
          {stringPosRawToLeanSource endPos} {canonical})"
  | .none => "Lean.SourceInfo.none"

/-- Encode a `List String` as `List.cons`/`List.nil` chain of string
    literals.  Mirrors the `nameToLeanSource` parenthesisation
    convention.  Used by the `Preresolved.decl` field-list arm. -/
def stringListToLeanSource : List String → String
  | []        => "List.nil"
  | s :: rest =>
      s!"(List.cons {escapeStrLit s} {stringListToLeanSource rest})"

/-- Render a `Lean.Syntax.Preresolved` as Lean source. -/
def preresolvedToLeanSource : Lean.Syntax.Preresolved → String
  | .namespace ns =>
      s!"(Lean.Syntax.Preresolved.namespace {nameToLeanSource ns})"
  | .decl n fields =>
      s!"(Lean.Syntax.Preresolved.decl {nameToLeanSource n} \
          {stringListToLeanSource fields})"

/-- Encode a `List Lean.Syntax.Preresolved` as a `List.cons`/`List.nil`
    chain. -/
def preresolvedListToLeanSource : List Lean.Syntax.Preresolved → String
  | []        => "List.nil"
  | p :: rest =>
      s!"(List.cons {preresolvedToLeanSource p} \
          {preresolvedListToLeanSource rest})"

-- Renderer for `Lean.Syntax` and its array children, defined via a
-- `mutual` block so the recursion is *structural* on the
-- `Lean.Syntax` constructors (and on `List Lean.Syntax` through
-- `Array.toList`).  Lean 4.30's auto-derived size function on
-- `Lean.Syntax` is sufficient for the well-founded measure on
-- `args.toList`, so no fuel parameter is needed.  This makes the
-- encoder and its companion depth function fully structural — the
-- round-trip proofs proceed by induction on `Lean.Syntax` directly.
mutual
  /-- Renderer arm for `Lean.Syntax`. -/
  def syntaxToLeanSource : Lean.Syntax → String
    | .missing => "Lean.Syntax.missing"
    | .atom info val =>
        s!"(Lean.Syntax.atom {sourceInfoToLeanSource info} {escapeStrLit val})"
    | .ident info rawVal val preresolved =>
        s!"(Lean.Syntax.ident {sourceInfoToLeanSource info} \
            {substringRawToLeanSource rawVal} \
            {nameToLeanSource val} \
            {preresolvedListToLeanSource preresolved})"
    | .node info kind args =>
        s!"(Lean.Syntax.node {sourceInfoToLeanSource info} \
            {nameToLeanSource kind} \
            {syntaxArrayToLeanSource args.toList})"

  /-- Companion list-renderer for `List Lean.Syntax`.  Encodes the
      list as a `List.cons` / `List.nil` chain. -/
  def syntaxArrayToLeanSource : List Lean.Syntax → String
    | []        => "List.nil"
    | s :: rest =>
        s!"(List.cons {syntaxToLeanSource s} {syntaxArrayToLeanSource rest})"
end

-- Structural depth measure on `Lean.Syntax`, defined mutually with
-- `syntaxArrayDepth`.  A true structural depth — not fuel-bounded —
-- so depth bounds on the encoded form are provable by induction on
-- `Lean.Syntax` directly.
mutual
  /-- Structural depth on `Lean.Syntax`. -/
  def syntaxDepth : Lean.Syntax → Nat
    | .missing => 1
    | .atom _ _ => 1
    | .ident _ _ _ _ => 1
    | .node _ _ args => syntaxArrayDepth args.toList + 1

  /-- Companion structural depth on `List Lean.Syntax`. -/
  def syntaxArrayDepth : List Lean.Syntax → Nat
    | []        => 1
    | s :: rest => max (syntaxDepth s) (syntaxArrayDepth rest) + 1
end

/-- Render a `MetaClassifier` as Lean source.  Each lattice arm
    becomes a constructor call in the `Infoductor.MetaClassifier`
    namespace; recursive arms (`meet`, `join`) render their
    children parenthesised. -/
def MetaClassifier.toLeanSource : MetaClassifier → String
  | .always         => "Infoductor.MetaClassifier.always"
  | .never          => "Infoductor.MetaClassifier.never"
  | .atDecl n       =>
      s!"(Infoductor.MetaClassifier.atDecl {nameToLeanSource n})"
  | .inFile s       =>
      s!"(Infoductor.MetaClassifier.inFile {escapeStrLit s})"
  | .underAttribute n =>
      s!"(Infoductor.MetaClassifier.underAttribute {nameToLeanSource n})"
  | .dependencyOf n =>
      s!"(Infoductor.MetaClassifier.dependencyOf {nameToLeanSource n})"
  | .inNamespace n  =>
      s!"(Infoductor.MetaClassifier.inNamespace {nameToLeanSource n})"
  | .meet a b       =>
      s!"(Infoductor.MetaClassifier.meet {toLeanSource a} {toLeanSource b})"
  | .join a b       =>
      s!"(Infoductor.MetaClassifier.join {toLeanSource a} {toLeanSource b})"

/-- Render a `MetaCTerm` as Lean source that reconstructs it via
    the `Infoductor.MetaCTerm` constructors.  Every arm renders
    to a parenthesised constructor call; sub-`MetaCTerm`s recurse,
    sub-`MetaClassifier`s use `MetaClassifier.toLeanSource`,
    sub-`Lean.Name`s use `nameToLeanSource`. -/
def MetaCTerm.toLeanSource : MetaCTerm → String
  | .ident n        =>
      s!"(Infoductor.MetaCTerm.ident {nameToLeanSource n})"
  | .sym s          =>
      s!"(Infoductor.MetaCTerm.sym {escapeStrLit s})"
  | .app f a        =>
      s!"(Infoductor.MetaCTerm.app {toLeanSource f} {toLeanSource a})"
  | .lam x t        =>
      s!"(Infoductor.MetaCTerm.lam {escapeStrLit x} {toLeanSource t})"
  | .plam i t       =>
      s!"(Infoductor.MetaCTerm.plam {escapeStrLit i} {toLeanSource t})"
  | .comp s A φ u t =>
      s!"(Infoductor.MetaCTerm.comp {escapeStrLit s} {toLeanSource A} \
          {MetaClassifier.toLeanSource φ} {toLeanSource u} {toLeanSource t})"
  | .transp s A φ t =>
      s!"(Infoductor.MetaCTerm.transp {escapeStrLit s} {toLeanSource A} \
          {MetaClassifier.toLeanSource φ} {toLeanSource t})"
  | .empty          => "Infoductor.MetaCTerm.empty"

/-- Render a `MetaArtifact` as Lean source.  The structural arm
    (`cterm`) recurses through `MetaCTerm.toLeanSource`; `source`
    arms wrap the raw string in a `.source` constructor; the
    `declAt` arm renders the carried `Lean.Syntax` via the
    constructor-call mirror (`syntaxToLeanSource`) so the round-
    trip closes through the `MetaParse` parser. -/
def MetaArtifact.toLeanSource : MetaArtifact → String
  | .source s => s!"(Infoductor.MetaArtifact.source {escapeStrLit s})"
  | .declAt s =>
      s!"(Infoductor.MetaArtifact.declAt {syntaxToLeanSource s})"
  | .cterm m  =>
      s!"(Infoductor.MetaArtifact.cterm {MetaCTerm.toLeanSource m})"
  | .refTo n  =>
      s!"(Infoductor.MetaArtifact.refTo {nameToLeanSource n})"
  | .empty    => "Infoductor.MetaArtifact.empty"

-- ── MetaPosition — where an artifact lives in source ───────────────────────
-- The first field of `restructure i (Context = MetaCType) φ witness fallback`
-- — analogue of the dim-binder `i` in `comp i A φ u t`.

/-- The position of an artifact within a Lean source file or
    project.  Used as the `MetaPosition` argument of `restructure`,
    analogous to the dim-binder `i` of `comp`.

    The `binder` field carries the dim-binder identity when the
    position represents a comp-style operation; `none` for
    non-cubical positions.  This is the meta-mirror of the dim-
    binder slot in `comp i A φ u t` — kept as a position-level
    field rather than folded into the classifier so the binder
    role is preserved structurally. -/
structure MetaPosition where
  /-- The fully qualified declaration name (or namespace) the
      position lives under.  `Name.anonymous` for file-level. -/
  declName : Lean.Name
  /-- Path to the source file (or empty for in-memory). -/
  filePath : String
  /-- Optional byte offset / range info; `none` if positional via
      declName alone is sufficient. -/
  range    : Option (Nat × Nat)
  /-- Dim-binder identity for cubical-shaped positions.  `none` for
      non-cubical or unbound positions.  Preserves the comp-style
      binder slot structurally. -/
  binder   : Option Lean.Name := none
  deriving Inhabited

instance : Repr MetaPosition where
  reprPrec p _ := s!"MetaPosition(declName={p.declName}, filePath={p.filePath}, range={repr p.range}, binder={repr p.binder})"

end Infoductor
