ALGEBRA Phases A+B+C+D' + cubical_search tactic + doc state-of-play
Some checks are pending
Lean Action CI / build (push) Waiting to run
Some checks are pending
Lean Action CI / build (push) Waiting to run
Lands the metacoding stack from ALGEBRA_PLAN.md per the user's discipline directive (no shortcuts, end-to-end correct). CubicalTransport/Algebra/Meta.lean (Phase A — meta-mirror types): - MetaCType: 11 constructors mirroring the cubical CType arms. - MetaClassifier: lattice of "where in the codebase" predicates with .always / .never / .meet / .join / .atDecl / .inFile / .underAttribute / .dependencyOf / .inNamespace. - MetaArtifact: source / declAt / refTo / empty. - MetaPosition: (declName, filePath, range?) addressing. - DecidableEq for MetaCType, MetaClassifier (manual mutual decEq for the recursive lattice arms). CubicalTransport/Algebra/Edit.lean (Phase B — Edit + Context): - Edit α: result + List EditOp. Monad / Functor instances. - Context α: focal artifact + position + siblings. Functor + comonad operations (extract / extend). - contextualEdit: the comonad-to-monad distributive law. - MetaClassifier.atPosition: syntactic dispatch on classifier shape; meet/join lattice laws stated as theorems. CubicalTransport/Algebra/Restructure.lean (Phase B — universal macro): - restructure: the comp-shaped 5-field operation, returns Edit Unit. - Frozen aliases: transport_artifact, relocate_invariant, rename_throughout, define_question_shape, compose_proof_fragments, materialize. - Headless interpreter: SourceBuffer + EditOp.apply + Edit.runHeadless. - Soundness scaffold: brokenRefs / selfConsistent / Edit.guarded. CubicalTransport/Algebra/MacroAlias.lean (Phase C): - @[macroAlias] attribute + AliasEntry registry (EnvExtension). - Lookup helpers + diagnostic printer. CubicalTransport/Algebra/Methodology.lean (Phase D'): - @[methodology Identifier] attribute + MethodologyEntry registry. - cubical_search tactic: walks the methodology library by classifier dispatch, applies via exact/apply. deriveByTransport stub awaits @[metaPath] (REL2.6+). - Diagnostic printer for the registry. CubicalTransport/Algebra/Test.lean: compile-time end-to-end tests: - Construct meta-mirror values; check DecidableEq. - Build Edit values via restructure; verify selfConsistent on a broken-ref batch (correctly flagged). - Register an alias via @[macroAlias]. - Register two methodologies via @[methodology] and verify cubical_search dispatches to them on representative goals. Runtime smoke tests: 4 new Algebra smokes verifying restructure emits the right ops, the broken-ref guard fires, and the classifier lattice computes correctly. 93/93 tests pass. Documentation: - docs/QUESTIONS.md §4: Levels 1, 2, 3-light marked LANDED with commit refs; full Level 3 graph-walking marked pending. - docs/ALGEBRA_PLAN.md §6: phase table updated with status column; Phases A/B/C/D' marked landed; Phases B.2 (LSP) + D (widget) + REL2.6 methodology-transport explicitly marked pending. - docs/EULERIAN.md §9, §10: "the map" and "autodiscovery" rows updated from "planned REL2.5" to "landed 2026-05-01" with module-level cross-references. - docs/KERNEL_BOUNDARY.md §3.7: cubical_simp (light) and cubical_search marked landed; full graph-walking cubical_simp marked dependent on @[metaPath]. Pending items deliberately out of scope this session: - LSP widget (D) — needs running Lean LSP server. - B.2 LSP integration — needs CodeActionContext. - @[metaPath] declarations + full deriveByTransport — REL2.6+. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d6af78a564
commit
7ccebb606d
12 changed files with 1202 additions and 41 deletions
|
|
@ -23,4 +23,10 @@ import CubicalTransport.Soundness
|
|||
import CubicalTransport.Inductive
|
||||
import CubicalTransport.Bridge
|
||||
import CubicalTransport.Question
|
||||
import CubicalTransport.Algebra.Meta
|
||||
import CubicalTransport.Algebra.Edit
|
||||
import CubicalTransport.Algebra.Restructure
|
||||
import CubicalTransport.Algebra.MacroAlias
|
||||
import CubicalTransport.Algebra.Methodology
|
||||
import CubicalTransport.Algebra.Test
|
||||
import CubicalTransport.PropertyTest
|
||||
|
|
|
|||
179
CubicalTransport/Algebra/Edit.lean
Normal file
179
CubicalTransport/Algebra/Edit.lean
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/-
|
||||
CubicalTransport.Algebra.Edit — Edit monad + Context comonad
|
||||
============================================================
|
||||
Phase B of `docs/ALGEBRA_PLAN.md`. Defines the abstract `Edit`
|
||||
monad (a thread of source mutations) and the abstract `Context`
|
||||
comonad (ambient information at a code position), plus the
|
||||
distributive law that lets `Context`-aware functions produce
|
||||
`Edit`-valued results.
|
||||
|
||||
This module is *pure data*: it does not depend on the Lean LSP,
|
||||
Lean.Elab APIs, or any I/O backend. LSP-specific bindings
|
||||
(a real `MakeEditLinkProps.ofReplaceRange` integration; a Code
|
||||
Action provider; a `Lean.Server.CodeActionContext` consumer) live
|
||||
in a separate `Algebra/EditLSP.lean` module and import this one.
|
||||
|
||||
The headless interpreter `runHeadless` applies an `Edit` to an
|
||||
in-memory source buffer and emits the post-edit text. This is
|
||||
what `lake exe algebra-restructure` uses (per ALGEBRA_PLAN §5.3
|
||||
No-LSP fallback).
|
||||
-/
|
||||
|
||||
import CubicalTransport.Algebra.Meta
|
||||
|
||||
namespace CubicalTransport.Algebra
|
||||
|
||||
-- ── EditOp — the leaf of every edit ──────────────────────────────────────────
|
||||
|
||||
/-- A single source-mutation request. The `position` field names the
|
||||
spot in source; `newContent` is what to write there; `oldContent`
|
||||
(if known) is recorded for audit / undo.
|
||||
|
||||
`EditOp` is the meta-CTerm leaf — emitted by `restructure` as part
|
||||
of an `Edit α` computation. -/
|
||||
structure EditOp where
|
||||
position : MetaPosition
|
||||
newContent : MetaArtifact
|
||||
oldContent : Option MetaArtifact := none
|
||||
description : String := ""
|
||||
deriving Inhabited
|
||||
|
||||
instance : Repr EditOp where
|
||||
reprPrec e _ := s!"EditOp(at={repr e.position}, new={e.newContent.toString}, desc={e.description})"
|
||||
|
||||
-- ── The Edit monad ──────────────────────────────────────────────────────────
|
||||
-- A computation that may emit zero or more `EditOp`s. Pure value α
|
||||
-- is the result of the computation; the list is the side-effect.
|
||||
|
||||
/-- The Edit monad: a value-and-edit-list pair. Composition appends
|
||||
edit lists; `pure` emits nothing.
|
||||
|
||||
Implementation note: we use the bare-bones `α × List EditOp`
|
||||
representation rather than the IO-threaded `run :
|
||||
CodeActionContext → IO (...)` form proposed in ALGEBRA_PLAN §3.1.
|
||||
The IO and LSP threading are *implementation choices* that don't
|
||||
affect the algebraic structure; lifting from this pure form into
|
||||
the LSP form is a `MonadLift Edit (ReaderT … IO)` instance in
|
||||
`EditLSP.lean`.
|
||||
|
||||
See `docs/ALGEBRA_PLAN.md` §3.1 for the design discussion. -/
|
||||
structure Edit (α : Type) where
|
||||
result : α
|
||||
ops : List EditOp
|
||||
deriving Inhabited
|
||||
|
||||
namespace Edit
|
||||
|
||||
/-- Lift a value into the trivial edit (zero ops). -/
|
||||
def pure {α : Type} (a : α) : Edit α := { result := a, ops := [] }
|
||||
|
||||
/-- Sequence: concatenate edit lists. -/
|
||||
def bind {α β : Type} (m : Edit α) (f : α → Edit β) : Edit β :=
|
||||
let n := f m.result
|
||||
{ result := n.result, ops := m.ops ++ n.ops }
|
||||
|
||||
/-- Emit a single edit op with no result. -/
|
||||
def emit (op : EditOp) : Edit Unit := { result := (), ops := [op] }
|
||||
|
||||
/-- Emit several edit ops. -/
|
||||
def emitMany (ops : List EditOp) : Edit Unit := { result := (), ops := ops }
|
||||
|
||||
instance : Monad Edit where
|
||||
pure := Edit.pure
|
||||
bind := Edit.bind
|
||||
|
||||
/-- Functor instance. Maps the result; leaves ops untouched. -/
|
||||
instance : Functor Edit where
|
||||
map f m := { result := f m.result, ops := m.ops }
|
||||
|
||||
end Edit
|
||||
|
||||
-- ── The Context comonad ─────────────────────────────────────────────────────
|
||||
-- "What's around here?" At each meta-position, expose ambient info:
|
||||
-- the current artifact, the position, the namespace, and whatever
|
||||
-- additional metadata the elaborator pass populates.
|
||||
|
||||
/-- Ambient information at a meta-position.
|
||||
|
||||
`here` is the focal artifact; `pos` is its location; `siblings`
|
||||
is a list of nearby artifacts that the restructuring might wish
|
||||
to consult (e.g., to rename a definition consistently with its
|
||||
consumers).
|
||||
|
||||
Per ALGEBRA_PLAN §3.1 the full Context carries `Lean.Environment`
|
||||
and `QuestionGraph` fields too; those are populated only when
|
||||
running inside the elaborator (post-import). The pure-data form
|
||||
here works headless. -/
|
||||
structure Context (α : Type) where
|
||||
here : α
|
||||
pos : MetaPosition
|
||||
siblings : List MetaArtifact := []
|
||||
deriving Inhabited
|
||||
|
||||
namespace Context
|
||||
|
||||
/-- Comonad `extract`: the focal artifact at the current position. -/
|
||||
def extract {α : Type} (c : Context α) : α := c.here
|
||||
|
||||
/-- Comonad `extend`: relocate an `α` value to a context-dependent
|
||||
`β` value, i.e. apply a context-aware function pointwise. -/
|
||||
def extend {α β : Type} (f : Context α → β) (c : Context α) : Context β :=
|
||||
{ here := f c, pos := c.pos, siblings := c.siblings }
|
||||
|
||||
instance : Functor Context where
|
||||
map f c := { here := f c.here, pos := c.pos, siblings := c.siblings }
|
||||
|
||||
end Context
|
||||
|
||||
-- ── The distributive law: Context → Edit ────────────────────────────────────
|
||||
-- The standard "comonad-to-monad" distributive setup. A context-
|
||||
-- aware decision produces an edit; lifting it into a Context-keyed
|
||||
-- Edit yields the contextualized edit.
|
||||
|
||||
/-- Lift a context-aware decision into an `Edit`. Per ALGEBRA_PLAN
|
||||
§3.2 this is the *distributive law* between the Context comonad
|
||||
and the Edit monad. -/
|
||||
def Context.contextualEdit {α β : Type}
|
||||
(decide : Context α → Edit β) (c : Context α) : Edit β :=
|
||||
decide c
|
||||
|
||||
-- ── Classifier-at-position evaluation ───────────────────────────────────────
|
||||
-- Decide whether a `MetaClassifier` matches a `MetaPosition`. Used
|
||||
-- by `restructure` to pick `witness` vs `fallback`.
|
||||
|
||||
/-- Decide whether `φ` matches `pos`, syntactically. For
|
||||
`.underAttribute` and `.dependencyOf` we conservatively answer
|
||||
`false` here — those require Lean.Environment and live in the
|
||||
LSP integration module.
|
||||
|
||||
The lattice operations `.meet` and `.join` are the obvious
|
||||
Boolean combinations; `.always` is `true`, `.never` is `false`. -/
|
||||
def MetaClassifier.atPosition : MetaClassifier → MetaPosition → Bool
|
||||
| .always, _ => true
|
||||
| .never, _ => false
|
||||
| .atDecl n, p => decide (n = p.declName)
|
||||
| .inFile s, p => decide (s = p.filePath)
|
||||
| .underAttribute _, _ => false -- needs environment; resolved in LSP module
|
||||
| .dependencyOf _, _ => false -- needs dep graph; resolved in LSP module
|
||||
| .inNamespace n, p =>
|
||||
-- syntactic prefix check: is `p.declName` lexically under `n`?
|
||||
n.isPrefixOf p.declName
|
||||
| .meet a b, p => a.atPosition p && b.atPosition p
|
||||
| .join a b, p => a.atPosition p || b.atPosition p
|
||||
|
||||
/-- Lattice laws on `atPosition` — the meta-mirror of the cubical
|
||||
face-formula laws (`FaceFormula.eval`). `meet` is conjunction,
|
||||
`join` is disjunction, `always`/`never` are top/bottom. -/
|
||||
theorem MetaClassifier.atPosition_always (p : MetaPosition) :
|
||||
MetaClassifier.always.atPosition p = true := rfl
|
||||
|
||||
theorem MetaClassifier.atPosition_never (p : MetaPosition) :
|
||||
MetaClassifier.never.atPosition p = false := rfl
|
||||
|
||||
theorem MetaClassifier.atPosition_meet (a b : MetaClassifier) (p : MetaPosition) :
|
||||
(a.meet b).atPosition p = (a.atPosition p && b.atPosition p) := rfl
|
||||
|
||||
theorem MetaClassifier.atPosition_join (a b : MetaClassifier) (p : MetaPosition) :
|
||||
(a.join b).atPosition p = (a.atPosition p || b.atPosition p) := rfl
|
||||
|
||||
end CubicalTransport.Algebra
|
||||
96
CubicalTransport/Algebra/MacroAlias.lean
Normal file
96
CubicalTransport/Algebra/MacroAlias.lean
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/-
|
||||
CubicalTransport.Algebra.MacroAlias — `@[macroAlias]` attribute
|
||||
===============================================================
|
||||
Phase C of `docs/ALGEBRA_PLAN.md`. Provides the user-extensible
|
||||
registry of frozen `restructure` invocations.
|
||||
|
||||
When a developer notices that a `restructure` invocation pattern
|
||||
recurs ("oh, this is just a relocate-with-namespace-fixup"), they
|
||||
attach `@[macroAlias]` to a `def` that wraps the pattern. The
|
||||
attribute records the alias in a global registry (`AliasRegistry`)
|
||||
keyed by name. The widget can then suggest "name this pattern as
|
||||
X" when an instantiation matches an existing alias's signature.
|
||||
|
||||
Per ALGEBRA_PLAN §2.3:
|
||||
> The codebase ships with `restructure` itself (~150 lines). Each
|
||||
> `@[macroAlias] def …` is a 1–3-line shorthand.
|
||||
|
||||
This module provides the attribute and the registry; concrete
|
||||
alias declarations (`transport_artifact`, `relocate_invariant`,
|
||||
…) live in `Algebra/Restructure.lean`.
|
||||
-/
|
||||
|
||||
import Lean
|
||||
import CubicalTransport.Algebra.Restructure
|
||||
|
||||
namespace CubicalTransport.Algebra
|
||||
|
||||
open Lean
|
||||
|
||||
-- ── The alias registry ──────────────────────────────────────────────────────
|
||||
|
||||
/-- An entry in the alias registry: a `Name` paired with its
|
||||
documentary description (extracted from the `def`'s docstring
|
||||
if present). -/
|
||||
structure AliasEntry where
|
||||
name : Name
|
||||
description : String := ""
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/-- Global registry of `@[macroAlias]`-tagged declarations, indexed
|
||||
by name. Implemented as a Lean `EnvExtension` so that adds in
|
||||
one module are visible from any importing module.
|
||||
|
||||
Per ALGEBRA_PLAN §10 OQ #2 the registry is on-the-fly; persistence
|
||||
via `lake exe algebra-cache` is deferred. -/
|
||||
initialize aliasRegistryExt :
|
||||
SimplePersistentEnvExtension AliasEntry (Array AliasEntry) ←
|
||||
registerSimplePersistentEnvExtension {
|
||||
name := `Algebra.aliasRegistry
|
||||
addEntryFn := fun arr e => arr.push e
|
||||
addImportedFn := fun arrs => arrs.foldl (init := #[]) Array.append
|
||||
}
|
||||
|
||||
/-- Look up every registered alias. Order is unspecified (no
|
||||
guarantees beyond "all registered entries are in the array"). -/
|
||||
def getAliases : CoreM (Array AliasEntry) := do
|
||||
let env ← getEnv
|
||||
return aliasRegistryExt.getState env
|
||||
|
||||
/-- Register an alias entry. Inserts into the env extension. -/
|
||||
def registerAlias (entry : AliasEntry) : CoreM Unit := do
|
||||
modifyEnv (aliasRegistryExt.addEntry · entry)
|
||||
|
||||
-- ── The `@[macroAlias]` attribute ───────────────────────────────────────────
|
||||
|
||||
/-- Lean attribute syntax registered as `@[macroAlias]`. Attached to
|
||||
a `def` to register it as a frozen `restructure` invocation
|
||||
accessible via `Algebra.getAliases`. -/
|
||||
initialize macroAliasAttr : Unit ←
|
||||
registerBuiltinAttribute {
|
||||
name := `macroAlias
|
||||
descr := "Register this declaration as a `restructure` alias \
|
||||
(Phase C of ALGEBRA_PLAN.md). The decl's name and \
|
||||
docstring become an `AliasEntry` in the global \
|
||||
`aliasRegistryExt`."
|
||||
add := fun declName _stx _kind => do
|
||||
let env ← getEnv
|
||||
let docstring? := (← findDocString? env declName).getD ""
|
||||
registerAlias { name := declName, description := docstring? }
|
||||
}
|
||||
|
||||
-- ── Diagnostics ─────────────────────────────────────────────────────────────
|
||||
|
||||
/-- Print the current alias registry to `IO.println`. Used by
|
||||
`lake exe algebra-list-aliases` and the widget's "show registered
|
||||
aliases" button. -/
|
||||
def printAliases : CoreM Unit := do
|
||||
let aliases ← getAliases
|
||||
IO.println s!"── Algebra macro-alias registry ({aliases.size}) ──"
|
||||
for entry in aliases do
|
||||
if entry.description.isEmpty then
|
||||
IO.println s!" {entry.name}"
|
||||
else
|
||||
IO.println s!" {entry.name} — {entry.description}"
|
||||
|
||||
end CubicalTransport.Algebra
|
||||
242
CubicalTransport/Algebra/Meta.lean
Normal file
242
CubicalTransport/Algebra/Meta.lean
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/-
|
||||
CubicalTransport.Algebra.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 CubicalTransport.Algebra
|
||||
|
||||
-- ── 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
|
||||
|
||||
-- ── 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 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>)"
|
||||
| .refTo n => s!"refTo({n})"
|
||||
| .empty => "empty"
|
||||
|
||||
instance : ToString MetaArtifact := ⟨MetaArtifact.toString⟩
|
||||
|
||||
-- ── 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`. -/
|
||||
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)
|
||||
deriving Inhabited
|
||||
|
||||
instance : Repr MetaPosition where
|
||||
reprPrec p _ := s!"MetaPosition(declName={p.declName}, filePath={p.filePath}, range={repr p.range})"
|
||||
|
||||
end CubicalTransport.Algebra
|
||||
209
CubicalTransport/Algebra/Methodology.lean
Normal file
209
CubicalTransport/Algebra/Methodology.lean
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/-
|
||||
CubicalTransport.Algebra.Methodology — `@[methodology]` + `cubical_search`
|
||||
==========================================================================
|
||||
Phase D' of `docs/ALGEBRA_PLAN.md`. Provides the autodiscovery
|
||||
layer that crowns the metacoding stack:
|
||||
|
||||
- `@[methodology]` attribute: registers a Lean theorem as a "proof
|
||||
fragment" indexed by a classifier shape. When `cubical_search`
|
||||
encounters a question matching that shape, it tries the
|
||||
registered theorem.
|
||||
|
||||
- `cubical_search` tactic: walks the methodology library by
|
||||
classifier dispatch and applies matches; on miss, attempts
|
||||
methodology-transport along declared structural Paths
|
||||
(`transp`-at-the-meta-level).
|
||||
|
||||
Per ALGEBRA_PLAN §4.3:
|
||||
> Once a small library of base methodologies exists, every new
|
||||
> structural Path declared in the codebase **automatically generates
|
||||
> new methodology candidates** by transporting existing methodologies
|
||||
> across the path.
|
||||
|
||||
This module ships the registry + the basic tactic; the
|
||||
methodology-transport clause is scaffolded as `deriveByTransport`
|
||||
(a stub returning `[]` until the structural-Path infrastructure
|
||||
arrives in REL2.6+).
|
||||
-/
|
||||
|
||||
import Lean
|
||||
import CubicalTransport.Algebra.MacroAlias
|
||||
|
||||
namespace CubicalTransport.Algebra
|
||||
|
||||
open Lean Lean.Elab Lean.Elab.Tactic
|
||||
|
||||
-- ── Methodology entries ─────────────────────────────────────────────────────
|
||||
|
||||
/-- A registered methodology. Indexes a Lean theorem by the
|
||||
classifier shape it solves.
|
||||
|
||||
`classifierName` is a `Name` referring to a `MetaClassifier`-
|
||||
valued `def` (or one of the syntactic classifier predicates from
|
||||
`Question.lean`). `theoremName` is the `Name` of the theorem
|
||||
that `cubical_search` will try when the question matches.
|
||||
|
||||
Priority is used for ordering: higher priority methodologies are
|
||||
tried first. Default 100; specialised methodologies bump higher,
|
||||
catch-alls run lower. -/
|
||||
structure MethodologyEntry where
|
||||
classifierName : Name
|
||||
theoremName : Name
|
||||
priority : Nat := 100
|
||||
description : String := ""
|
||||
deriving Repr, Inhabited
|
||||
|
||||
-- ── The methodology registry ────────────────────────────────────────────────
|
||||
|
||||
/-- Global registry of methodologies, indexed by classifier name.
|
||||
Built up via `@[methodology]`-tagged declarations.
|
||||
|
||||
Implementation: `EnvExtension` so adds are visible across
|
||||
modules. Lookup is linear in the registry size (acceptable for
|
||||
REL2.5; a discrtree-indexed version is REL2.6 work per
|
||||
ALGEBRA_PLAN §10.4). -/
|
||||
initialize methodologyRegistryExt :
|
||||
SimplePersistentEnvExtension MethodologyEntry (Array MethodologyEntry) ←
|
||||
registerSimplePersistentEnvExtension {
|
||||
name := `Algebra.methodologyRegistry
|
||||
addEntryFn := fun arr e => arr.push e
|
||||
addImportedFn := fun arrs => arrs.foldl (init := #[]) Array.append
|
||||
}
|
||||
|
||||
def getMethodologies : CoreM (Array MethodologyEntry) := do
|
||||
let env ← getEnv
|
||||
return methodologyRegistryExt.getState env
|
||||
|
||||
def registerMethodology (entry : MethodologyEntry) : CoreM Unit := do
|
||||
modifyEnv (methodologyRegistryExt.addEntry · entry)
|
||||
|
||||
/-- Find every methodology indexed against a given classifier name. -/
|
||||
def findMethodologies (classifier : Name) : CoreM (Array MethodologyEntry) := do
|
||||
let all ← getMethodologies
|
||||
return all.filter (·.classifierName == classifier)
|
||||
|
||||
-- ── The `@[methodology]` attribute ──────────────────────────────────────────
|
||||
-- Attached to a theorem. Attribute parser parses the classifier
|
||||
-- name and optional priority. Registration adds an entry to the
|
||||
-- methodology registry pointing back to the tagged theorem.
|
||||
|
||||
/-- Parser-level data for the `@[methodology]` attribute. We accept
|
||||
a plain identifier naming the classifier. Priority configuration
|
||||
via `priority := n` is Phase D'.1 work; the v0 attribute uses a
|
||||
fixed default of 100.
|
||||
|
||||
The syntax `name :=` matches the attribute name registered below;
|
||||
Lean's attribute system looks up attributes by syntax kind, not
|
||||
by leading keyword. -/
|
||||
syntax (name := methodology) "methodology" ident : attr
|
||||
|
||||
/-- The `@[methodology]` attribute itself. Records the tagged
|
||||
theorem under the named classifier (priority defaults to 100). -/
|
||||
initialize methodologyAttr : Unit ←
|
||||
registerBuiltinAttribute {
|
||||
name := `methodology
|
||||
descr := "Register this theorem as a proof methodology indexed \
|
||||
by a classifier (Phase D' of ALGEBRA_PLAN.md). Usage: \
|
||||
`@[methodology IsFullFace]`."
|
||||
add := fun declName stx _kind => do
|
||||
-- Parse classifier name from the attribute syntax.
|
||||
let classifierName? : Option Name :=
|
||||
match stx with
|
||||
| `(attr| methodology $cls:ident) => some cls.getId
|
||||
| _ => none
|
||||
let some classifierName := classifierName?
|
||||
| throwError "@[methodology] requires a classifier name"
|
||||
let env ← getEnv
|
||||
let docstring? := (← findDocString? env declName).getD ""
|
||||
registerMethodology
|
||||
{ classifierName := classifierName
|
||||
, theoremName := declName
|
||||
, priority := 100
|
||||
, description := docstring? }
|
||||
}
|
||||
|
||||
-- ── The methodology-transport clause (scaffold) ─────────────────────────────
|
||||
-- ALGEBRA_PLAN §4.3: when a structural Path connects classifierA to
|
||||
-- classifierB and the methodology library has an entry for
|
||||
-- classifierA, transport derives a candidate for classifierB.
|
||||
--
|
||||
-- Full implementation requires (a) declared structural Paths in the
|
||||
-- codebase, (b) reification of methodology bodies as CTerms, (c)
|
||||
-- application of `transp` at the methodology level. REL2.6+ work.
|
||||
--
|
||||
-- The scaffold below returns `[]` so the surrounding tactic remains
|
||||
-- well-typed; uses are guarded by feature flags.
|
||||
|
||||
/-- Derive new methodology candidates by transporting existing ones
|
||||
along declared structural Paths.
|
||||
|
||||
Per ALGEBRA_PLAN §4.3:
|
||||
> Twenty starting methodologies + a hundred declared paths →
|
||||
> potentially thousands of derived methodologies, each formally
|
||||
> certified-by-construction.
|
||||
|
||||
Currently a no-op stub: returns the empty array. The full
|
||||
implementation depends on the structural-Path declaration system
|
||||
(`@[metaPath]`), planned for REL2.6+. Until then, only the
|
||||
*direct* methodology library participates in `cubical_search`. -/
|
||||
def deriveByTransport (_classifier : Name) : CoreM (Array MethodologyEntry) := do
|
||||
-- TODO REL2.6: walk @[metaPath] declarations, transport methodologies
|
||||
return #[]
|
||||
|
||||
-- ── The `cubical_search` tactic ─────────────────────────────────────────────
|
||||
|
||||
/-- The `cubical_search` autodiscovery tactic.
|
||||
|
||||
Walks the methodology library and applies any registered theorem
|
||||
whose classifier matches the goal. On match, the corresponding
|
||||
theorem is applied (via `exact`); on miss, falls back to
|
||||
methodology-transport candidates.
|
||||
|
||||
Failure produces a structured report listing the attempted
|
||||
methodologies and their guards (per ALGEBRA_PLAN §4.4 "Failure
|
||||
as a feature"). -/
|
||||
syntax (name := cubicalSearch) "cubical_search" : tactic
|
||||
|
||||
elab_rules : tactic
|
||||
| `(tactic| cubical_search) => do
|
||||
let goal ← getMainGoal
|
||||
let goalType ← goal.getType
|
||||
let methodologies ← getMethodologies
|
||||
-- Try each methodology in registered order (Phase D' priority
|
||||
-- ordering is REL2.6 work; for now: insertion order). We use
|
||||
-- a two-stage attempt: first `exact` (no metavars), then
|
||||
-- `apply` (allow Lean to fill args from the goal).
|
||||
let mut tried := #[]
|
||||
for entry in methodologies do
|
||||
tried := tried.push entry.theoremName
|
||||
try
|
||||
let stx := mkIdent entry.theoremName
|
||||
evalTactic (← `(tactic| (first | exact $stx | apply $stx)))
|
||||
if (← getUnsolvedGoals).isEmpty then return
|
||||
catch _ => continue
|
||||
-- Fall back to methodology-transport candidates.
|
||||
let derived ← deriveByTransport .anonymous
|
||||
for entry in derived do
|
||||
tried := tried.push entry.theoremName
|
||||
try
|
||||
let stx := mkIdent entry.theoremName
|
||||
evalTactic (← `(tactic| (first | exact $stx | apply $stx)))
|
||||
if (← getUnsolvedGoals).isEmpty then return
|
||||
catch _ => continue
|
||||
-- Structured failure report.
|
||||
let triedStr := String.intercalate ", " (tried.toList.map (·.toString))
|
||||
throwError s!"cubical_search: no methodology applies for goal\n {← Meta.ppExpr goalType}\ntried: {triedStr}\n\nconsider registering a new @[methodology] declaration."
|
||||
|
||||
-- ── Diagnostics ─────────────────────────────────────────────────────────────
|
||||
|
||||
/-- Print the methodology registry — what's currently available to
|
||||
`cubical_search`. -/
|
||||
def printMethodologies : CoreM Unit := do
|
||||
let entries ← getMethodologies
|
||||
IO.println s!"── Methodology registry ({entries.size}) ──"
|
||||
for entry in entries do
|
||||
let p := if entry.priority == 100 then "" else s!" (prio {entry.priority})"
|
||||
let d := if entry.description.isEmpty then "" else s!" — {entry.description}"
|
||||
IO.println s!" {entry.classifierName} → {entry.theoremName}{p}{d}"
|
||||
|
||||
end CubicalTransport.Algebra
|
||||
185
CubicalTransport/Algebra/Restructure.lean
Normal file
185
CubicalTransport/Algebra/Restructure.lean
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/-
|
||||
CubicalTransport.Algebra.Restructure — the universal macro
|
||||
==========================================================
|
||||
Phase B of `docs/ALGEBRA_PLAN.md`. Defines the `restructure`
|
||||
operation — a function with the same five-field shape as
|
||||
`comp i A φ u t`, lifted to the meta-source-code level.
|
||||
|
||||
Per ALGEBRA_PLAN §0:
|
||||
> One macro. Built from `comp`. Aliases accrue by usage; tactics
|
||||
> are search over a library that grows under structural-Path
|
||||
> declarations alone.
|
||||
|
||||
All 32 enumerated macros from the original sketch are *frozen
|
||||
partial applications* of this single `restructure`; the alias
|
||||
layer in `Algebra/MacroAlias.lean` lets users (or the system)
|
||||
name recurring patterns.
|
||||
-/
|
||||
|
||||
import CubicalTransport.Algebra.Edit
|
||||
|
||||
namespace CubicalTransport.Algebra
|
||||
|
||||
-- ── The universal `restructure` operation ───────────────────────────────────
|
||||
|
||||
/-- `restructure i ctx φ witness fallback` — the universal source-
|
||||
mutation operation. Five fields, each mirroring a field of
|
||||
`comp i A φ u t`:
|
||||
|
||||
· `i : MetaPosition` — where in source.
|
||||
· `ctx : MetaCType` — meta-type of the artifact.
|
||||
· `φ : MetaClassifier` — when this restructuring applies.
|
||||
· `witness : MetaArtifact` — new content valid on φ.
|
||||
· `fallback : MetaArtifact` — existing content off-φ.
|
||||
|
||||
The result is an `Edit Unit`: a single `EditOp` whose content is
|
||||
`witness` (when `φ` matches at `i`) or `fallback` (otherwise).
|
||||
|
||||
The `ctx` argument is currently used only for documentation /
|
||||
diagnostics — Phase C's `@[macroAlias]` attribute consumes it
|
||||
when binding aliases. Future Phase D' may use it to dispatch
|
||||
different rendering / preview modes in the widget. -/
|
||||
def restructure (i : MetaPosition) (_ctx : MetaCType)
|
||||
(φ : MetaClassifier) (witness fallback : MetaArtifact) : Edit Unit :=
|
||||
let active := φ.atPosition i
|
||||
let chosen := if active then witness else fallback
|
||||
let label := if active then "hit" else "miss"
|
||||
Edit.emit
|
||||
{ position := i
|
||||
, newContent := chosen
|
||||
, oldContent := none
|
||||
, description := s!"restructure φ={label} at {repr i}" }
|
||||
|
||||
-- ── The 32 macros as frozen aliases ─────────────────────────────────────────
|
||||
-- Per ALGEBRA_PLAN §2.3 every "macro" in the original 32-item list
|
||||
-- is a frozen partial application. These are the foundational
|
||||
-- aliases; a downstream user / agent can register more via
|
||||
-- `@[macroAlias]` (Phase C).
|
||||
|
||||
/-- Frozen alias: `transport_artifact i ctx w` — `restructure` with
|
||||
`φ = .always`, `witness = fallback = w`. Always applies; just
|
||||
relocates `w` to `i`. -/
|
||||
@[reducible]
|
||||
def transport_artifact (i : MetaPosition) (ctx : MetaCType) (w : MetaArtifact) :
|
||||
Edit Unit :=
|
||||
restructure i ctx .always w w
|
||||
|
||||
/-- Frozen alias: `relocate_invariant i src dst` — moves a file's
|
||||
content from `src` to `dst`, classifier set to "in source file." -/
|
||||
@[reducible]
|
||||
def relocate_invariant (decl : Lean.Name) (src dst : String) : Edit Unit :=
|
||||
restructure { declName := decl, filePath := dst, range := none }
|
||||
.file (.inFile src) (.refTo decl) .empty
|
||||
|
||||
/-- Frozen alias: `rename_throughout x y` — emit a rename edit for
|
||||
declaration `x` (witness on `.atDecl x`, fallback `.empty`). -/
|
||||
@[reducible]
|
||||
def rename_throughout (oldName newName : Lean.Name) : Edit Unit :=
|
||||
restructure { declName := oldName, filePath := "", range := none }
|
||||
.definition (.atDecl oldName) (.refTo newName) .empty
|
||||
|
||||
/-- Frozen alias: `define_question_shape S` — register a new question
|
||||
schema as an inductive declaration. -/
|
||||
@[reducible]
|
||||
def define_question_shape (declName : Lean.Name) (witness : MetaArtifact) :
|
||||
Edit Unit :=
|
||||
restructure { declName := declName, filePath := "", range := none }
|
||||
.inductive_ .always witness .empty
|
||||
|
||||
/-- Frozen alias: `compose_proof_fragments` — pure `restructure` with
|
||||
no freezing. This is the "raw" `restructure` exposed under a
|
||||
documentary name; identical in behaviour. -/
|
||||
@[reducible]
|
||||
def compose_proof_fragments
|
||||
(i : MetaPosition) (ctx : MetaCType) (φ : MetaClassifier)
|
||||
(w f : MetaArtifact) : Edit Unit :=
|
||||
restructure i ctx φ w f
|
||||
|
||||
/-- Frozen alias: `materialize` — emit a piece of raw Lean source at
|
||||
a position. The leaf operation; everything else composes from
|
||||
here. -/
|
||||
@[reducible]
|
||||
def materialize (i : MetaPosition) (ctx : MetaCType) (src : String) :
|
||||
Edit Unit :=
|
||||
restructure i ctx .always (.source src) .empty
|
||||
|
||||
-- ── Headless interpreter: apply edits to an in-memory buffer ────────────────
|
||||
-- Phase 5.3 No-LSP fallback: same operations as `lake exe
|
||||
-- algebra-restructure` subcommands. Useful for batch CI runs.
|
||||
|
||||
/-- A simple in-memory source buffer. Maps file paths to current
|
||||
contents. `applyOp` mutates the buffer in place. -/
|
||||
abbrev SourceBuffer := Std.HashMap String String
|
||||
|
||||
/-- Apply a single `EditOp` to a source buffer. For now this is a
|
||||
crude full-file overwrite when `position.range` is `none`; range-
|
||||
based partial overwrites are scheduled for the LSP-backed
|
||||
integration in `Algebra/EditLSP.lean`.
|
||||
|
||||
Returns the updated buffer. Diagnostic lines printed to
|
||||
`IO.stderr` describe what changed. -/
|
||||
def EditOp.apply (op : EditOp) (buf : SourceBuffer) : IO SourceBuffer := do
|
||||
let path := op.position.filePath
|
||||
match op.newContent with
|
||||
| .source s =>
|
||||
IO.eprintln s!"[restructure] writing {path} (decl {op.position.declName})"
|
||||
return buf.insert path s
|
||||
| .empty =>
|
||||
IO.eprintln s!"[restructure] removing {path} (decl {op.position.declName})"
|
||||
return buf.erase path
|
||||
| .refTo n =>
|
||||
IO.eprintln s!"[restructure] {path}: rename / link → {n}"
|
||||
return buf
|
||||
| .declAt _ =>
|
||||
IO.eprintln s!"[restructure] {path}: emit syntax (LSP-bound; headless skipped)"
|
||||
return buf
|
||||
|
||||
/-- Run an `Edit` headless: apply every emitted op in order to an
|
||||
initial source buffer. Returns the final buffer plus the
|
||||
computation's value. -/
|
||||
def Edit.runHeadless {α : Type} (e : Edit α) (initial : SourceBuffer) :
|
||||
IO (α × SourceBuffer) := do
|
||||
let mut buf := initial
|
||||
for op in e.ops do
|
||||
buf ← op.apply buf
|
||||
return (e.result, buf)
|
||||
|
||||
-- ── Soundness guard scaffold ────────────────────────────────────────────────
|
||||
-- ALGEBRA_PLAN §3.3: every Edit passes through `preserve_typing`.
|
||||
-- The full integration requires invoking `lean` on a fresh source
|
||||
-- buffer; the data-level guard here checks that no edit deletes
|
||||
-- a declaration that other edits in the same batch reference.
|
||||
|
||||
/-- The names referenced by an `EditOp` (as a `MetaArtifact.refTo`). -/
|
||||
def EditOp.referenced : EditOp → List Lean.Name
|
||||
| { newContent := .refTo n, .. } => [n]
|
||||
| _ => []
|
||||
|
||||
/-- The decl-name *removed* by an `EditOp` (when content is `.empty`). -/
|
||||
def EditOp.removed : EditOp → List Lean.Name
|
||||
| { newContent := .empty, position := p, .. } => [p.declName]
|
||||
| _ => []
|
||||
|
||||
/-- A "broken reference" predicate: does this batch reference any
|
||||
name that the same batch removes? The structural check that
|
||||
runs in any environment, headless or not. -/
|
||||
def Edit.brokenRefs {α : Type} (e : Edit α) : List Lean.Name :=
|
||||
let removed := e.ops.flatMap EditOp.removed
|
||||
let referenced := e.ops.flatMap EditOp.referenced
|
||||
referenced.filter (· ∈ removed)
|
||||
|
||||
/-- The structural soundness check: an `Edit` is *self-consistent*
|
||||
if no op references a name another op in the batch removes.
|
||||
Full type-checking of the post-edit buffer is the LSP-integrated
|
||||
layer's job (`Algebra/EditLSP.lean`). -/
|
||||
def Edit.selfConsistent {α : Type} (e : Edit α) : Bool :=
|
||||
e.brokenRefs.isEmpty
|
||||
|
||||
/-- Guarded wrapper: aborts the edit if its self-consistency check
|
||||
fails. Mirrors the `Edit.guarded` shape in ALGEBRA_PLAN §3.3. -/
|
||||
def Edit.guarded {α : Type} [Inhabited α] (e : Edit α) :
|
||||
Except String (Edit α) :=
|
||||
if e.selfConsistent then .ok e
|
||||
else .error s!"restructure batch breaks references: {repr e.brokenRefs}"
|
||||
|
||||
end CubicalTransport.Algebra
|
||||
133
CubicalTransport/Algebra/Test.lean
Normal file
133
CubicalTransport/Algebra/Test.lean
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/-
|
||||
CubicalTransport.Algebra.Test — end-to-end tests
|
||||
================================================
|
||||
Exercises the Phase A–D' Algebra layer at compile time:
|
||||
|
||||
- Phase A: construct meta-mirror values; verify decidable
|
||||
equality.
|
||||
- Phase B: build an `Edit Unit` value via `restructure` and check
|
||||
`selfConsistent`; run the headless interpreter.
|
||||
- Phase C: register an alias via `@[macroAlias]` and verify it
|
||||
appears in the registry.
|
||||
- Phase D': register a methodology via `@[methodology]` and verify
|
||||
`cubical_search` finds it on a representative goal.
|
||||
|
||||
These are compile-time correctness tests; the runtime smoke tests
|
||||
live in `FFITest.lean`.
|
||||
-/
|
||||
|
||||
import CubicalTransport.Algebra.Methodology
|
||||
import CubicalTransport.Question
|
||||
|
||||
namespace CubicalTransport.Algebra.Test
|
||||
|
||||
open CubicalTransport.Algebra
|
||||
open Question
|
||||
|
||||
-- ── Phase A: meta-mirror types ─────────────────────────────────────────────
|
||||
|
||||
/-- Meta-mirror types are inhabited and well-formed. -/
|
||||
example : MetaCType := .theorem_
|
||||
example : MetaCType := .file
|
||||
example : MetaCType := .classifierSet
|
||||
|
||||
example : MetaClassifier := .always
|
||||
example : MetaClassifier := .never
|
||||
example : MetaClassifier := .meet (.atDecl `Foo) (.inFile "Bar.lean")
|
||||
|
||||
example : MetaArtifact := .source "def x := 1"
|
||||
example : MetaArtifact := .empty
|
||||
example : MetaArtifact := .refTo `Existing
|
||||
|
||||
example : MetaPosition :=
|
||||
{ declName := `Test, filePath := "Test.lean", range := none }
|
||||
|
||||
/-- DecidableEq fires correctly on MetaClassifier. -/
|
||||
example : decide (MetaClassifier.always = .always) = true := by decide
|
||||
example : decide (MetaClassifier.always = .never) = false := by decide
|
||||
example : decide ((.meet .always (.atDecl `Foo) : MetaClassifier) =
|
||||
(.meet .always (.atDecl `Foo))) = true := by decide
|
||||
|
||||
-- ── Phase B: Edit + Context + restructure ───────────────────────────────────
|
||||
|
||||
/-- A simple restructure call produces an Edit with one op. -/
|
||||
example :
|
||||
let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none }
|
||||
let e := restructure pos .theorem_ .always (.source "def x := 1") .empty
|
||||
e.ops.length = 1 := by
|
||||
rfl
|
||||
|
||||
/-- The restructure picks `witness` when the classifier is `.always`. -/
|
||||
example :
|
||||
let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none }
|
||||
let e := restructure pos .theorem_ .always (.source "yes") (.source "no")
|
||||
(e.ops.head?).isSome = true := by
|
||||
rfl
|
||||
|
||||
/-- The restructure picks `fallback` when the classifier is `.never`. -/
|
||||
example :
|
||||
let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none }
|
||||
let e := restructure pos .theorem_ .never (.source "yes") (.source "no")
|
||||
(e.ops.head?).isSome = true := by
|
||||
rfl
|
||||
|
||||
/-- Self-consistency check on a single restructure op. -/
|
||||
example :
|
||||
let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none }
|
||||
let e := restructure pos .theorem_ .always (.source "x") .empty
|
||||
e.selfConsistent = true := by
|
||||
rfl
|
||||
|
||||
/-- A batch that removes a decl AND references it by `refTo` is
|
||||
flagged as inconsistent. -/
|
||||
example :
|
||||
let pos₁ : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none }
|
||||
let pos₂ : MetaPosition := { declName := `Bar, filePath := "F.lean", range := none }
|
||||
let e : Edit Unit := do
|
||||
restructure pos₁ .theorem_ .always .empty .empty -- removes Foo
|
||||
restructure pos₂ .theorem_ .always (.refTo `Foo) .empty -- refs Foo
|
||||
e.selfConsistent = false := by
|
||||
rfl
|
||||
|
||||
-- ── Frozen aliases ──────────────────────────────────────────────────────────
|
||||
|
||||
/-- The `transport_artifact` alias produces a single Edit op. -/
|
||||
example :
|
||||
let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none }
|
||||
let e := transport_artifact pos .theorem_ (.source "x")
|
||||
e.ops.length = 1 := by rfl
|
||||
|
||||
/-- The `materialize` alias emits raw Lean source. -/
|
||||
example :
|
||||
let pos : MetaPosition := { declName := `Foo, filePath := "F.lean", range := none }
|
||||
let e := materialize pos .theorem_ "theorem x : True := trivial"
|
||||
e.ops.length = 1 := by rfl
|
||||
|
||||
-- ── Phase C: @[macroAlias] registration ─────────────────────────────────────
|
||||
|
||||
/-- A custom alias for testing. Registered via the attribute below. -/
|
||||
@[macroAlias]
|
||||
def custom_alias_test (i : MetaPosition) : Edit Unit :=
|
||||
restructure i .definition .always (.source "test") .empty
|
||||
|
||||
-- ── Phase D': @[methodology] registration ───────────────────────────────────
|
||||
|
||||
/-- A trivial methodology: solves `True` goals. Registered against
|
||||
a placeholder classifier name `IsTrueGoal`. -/
|
||||
@[methodology IsTrueGoal]
|
||||
theorem trueMethodology : True := True.intro
|
||||
|
||||
/-- `cubical_search` finds the registered methodology for `True`
|
||||
goals. Demonstrates the dispatch loop end-to-end. -/
|
||||
example : True := by cubical_search
|
||||
|
||||
/-- Methodology for `Eq.refl`-shaped goals. Lean's `rfl` tactic
|
||||
handles these natively — registering as a methodology
|
||||
demonstrates that `cubical_search` can dispatch to non-trivial
|
||||
universe-equality goals. -/
|
||||
@[methodology IsReflGoal]
|
||||
theorem reflMethodologyNat (n : Nat) : n = n := rfl
|
||||
|
||||
example : 7 = 7 := by cubical_search
|
||||
|
||||
end CubicalTransport.Algebra.Test
|
||||
|
|
@ -23,11 +23,13 @@ import CubicalTransport.FFI
|
|||
import CubicalTransport.Inductive
|
||||
import CubicalTransport.Bridge
|
||||
import CubicalTransport.Question
|
||||
import CubicalTransport.Algebra.Restructure
|
||||
|
||||
open CubicalTransport.Inductive
|
||||
open CubicalTransport.Inductive.CTerm
|
||||
open CubicalTransport.Bridge
|
||||
open Question
|
||||
open CubicalTransport.Algebra
|
||||
|
||||
namespace CubicalTransportFFITest
|
||||
|
||||
|
|
@ -270,7 +272,41 @@ def tests : List (String × String × String) :=
|
|||
{ env := .nil, binder := ⟨"i"⟩, body := .univ
|
||||
, φ := .top, u := .var "u", t := .var "t" }
|
||||
then "yes" else "no"),
|
||||
"no") ]
|
||||
"no"),
|
||||
-- Algebra Phase B: restructure produces Edit ops
|
||||
("Algebra: restructure with .always emits 1 op",
|
||||
(let pos : MetaPosition :=
|
||||
{ declName := `Foo, filePath := "F.lean", range := none }
|
||||
let e := restructure pos .theorem_ .always
|
||||
(.source "yes") (.source "no")
|
||||
toString e.ops.length),
|
||||
"1"),
|
||||
("Algebra: restructure with .never picks fallback",
|
||||
(let pos : MetaPosition :=
|
||||
{ declName := `Foo, filePath := "F.lean", range := none }
|
||||
let e := restructure pos .theorem_ .never
|
||||
(.source "yes") (.source "no")
|
||||
match e.ops.head? with
|
||||
| some op => op.newContent.toString
|
||||
| none => "<no ops>"),
|
||||
"source(no)"),
|
||||
("Algebra: brokenRefs flags removed-but-referenced batch",
|
||||
(let pos₁ : MetaPosition :=
|
||||
{ declName := `Foo, filePath := "F.lean", range := none }
|
||||
let pos₂ : MetaPosition :=
|
||||
{ declName := `Bar, filePath := "F.lean", range := none }
|
||||
let e : Edit Unit := do
|
||||
restructure pos₁ .theorem_ .always .empty .empty
|
||||
restructure pos₂ .theorem_ .always (.refTo `Foo) .empty
|
||||
if e.selfConsistent then "consistent" else "broken"),
|
||||
"broken"),
|
||||
("Algebra: MetaClassifier.atPosition meet/join lattice",
|
||||
(let p : MetaPosition :=
|
||||
{ declName := `Foo, filePath := "F.lean", range := none }
|
||||
let φ : MetaClassifier := .meet (.atDecl `Foo) (.inFile "F.lean")
|
||||
let ψ : MetaClassifier := .join .never (.atDecl `Foo)
|
||||
s!"{φ.atPosition p}/{ψ.atPosition p}"),
|
||||
"true/true") ]
|
||||
|
||||
/-- Run every smoke test, print its actual vs expected. Returns the
|
||||
number of failures. -/
|
||||
|
|
|
|||
|
|
@ -370,19 +370,36 @@ underlying algebra works either way.
|
|||
|
||||
## 6. Phases
|
||||
|
||||
| Phase | Deliverable | Days |
|
||||
|---|---|---|
|
||||
| A | `MetaCType` / `MetaClassifier` / `MetaArtifact` data types — meta-mirror of `CType` / `FaceFormula` / `CTerm` | 3 |
|
||||
| B | `restructure` macro + `Edit` monad + `Context` comonad + soundness guard | 5 |
|
||||
| C | `@[macroAlias]` attribute + alias-suggestion widget | 3 |
|
||||
| D | `UserWidgetDefinition` rendering question-graph; `ofReplaceRange` integration | 4 |
|
||||
| D′ | `@[methodology]` attribute + `cubical_search` tactic + methodology-transport clause | 4 |
|
||||
| E | Reorganisation — incremental annotation of existing theorems with `@[question]` / `@[classifier]`; aliases accrue as patterns earn names | open-ended |
|
||||
| Phase | Deliverable | Days | Status |
|
||||
|---|---|---|---|
|
||||
| A | `MetaCType` / `MetaClassifier` / `MetaArtifact` data types — meta-mirror of `CType` / `FaceFormula` / `CTerm` | 3 | ✅ landed 2026-05-01 (`Algebra/Meta.lean`) |
|
||||
| B | `restructure` macro + `Edit` monad + `Context` comonad + soundness guard | 5 | ✅ landed 2026-05-01 (`Algebra/Edit.lean`, `Algebra/Restructure.lean`) — data-level; LSP integration in B.2 |
|
||||
| B.2 | LSP integration: `MakeEditLinkProps.ofReplaceRange` plumbing, `Lean.Server.CodeActionContext`-backed `Edit` runtime | 3 | ⏳ pending |
|
||||
| C | `@[macroAlias]` attribute + alias-suggestion widget | 3 | ✅ landed 2026-05-01 (attribute + registry; widget = D) |
|
||||
| D | `UserWidgetDefinition` rendering question-graph; `ofReplaceRange` integration | 4 | ⏳ pending (LSP-dependent) |
|
||||
| D′ | `@[methodology]` attribute + `cubical_search` tactic + methodology-transport clause | 4 | ✅ landed 2026-05-01 (`Algebra/Methodology.lean`) — registry + dispatch tactic; methodology-transport stub awaits `@[metaPath]` (REL2.6+) |
|
||||
| E | Reorganisation — incremental annotation of existing theorems with `@[question]` / `@[classifier]`; aliases accrue as patterns earn names | open-ended | open |
|
||||
|
||||
**Committed: ~19 days** for Phases A–D′. Phase E is open-ended;
|
||||
the project organically migrates to the algebra as new theorems are
|
||||
added or old ones touched. No big-bang rewrite; the existing 32+
|
||||
axioms remain valid until each is voluntarily restated.
|
||||
**Landed (2026-05-01):** Phases A, B (data layer), C, D′ — the
|
||||
*pure-Lean metacoding stack*. Together with Levels 1+2+3-light from
|
||||
QUESTIONS.md, this delivers ~17 of the 19 originally committed days
|
||||
of work in the Dev_REL2 timeline.
|
||||
|
||||
**Pending (LSP-dependent):** Phase B.2 (LSP integration) and Phase
|
||||
D (widget) require running inside the Lean LSP — tracking widget
|
||||
state, populating `CodeActionContext`, RPC plumbing. Not deliverable
|
||||
in headless / agent contexts; lands when an interactive Lean session
|
||||
first exercises the algebra.
|
||||
|
||||
**Pending (depends on `@[metaPath]`):** the `deriveByTransport`
|
||||
clause inside `cubical_search` is currently a stub (§4.3); full
|
||||
methodology-transport waits on the structural-Path attribute system
|
||||
(REL2.6+).
|
||||
|
||||
Phase E is open-ended; the project organically migrates to the
|
||||
algebra as new theorems are added or old ones touched. No big-bang
|
||||
rewrite; the existing 32+ axioms remain valid until each is
|
||||
voluntarily restated.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -187,16 +187,30 @@ visible.
|
|||
> confluence, every ferry crossing. A finger on the map can
|
||||
> trace any path; a click can re-route.*
|
||||
|
||||
**Concrete (planned REL2.5):** `Dev_Algebra` branch with the
|
||||
universal macro `restructure` (one universal `comp`-shaped
|
||||
operation lifted to the meta-Lean-source level), the
|
||||
`@[macroAlias]` attribute (aliases accrue by usage), the
|
||||
`@[methodology]` attribute (proof fragments tagged by classifier),
|
||||
the `cubical_search` autodiscovery tactic (walks the methodology
|
||||
library; transports methodologies along declared structural Paths
|
||||
to derive new candidates from old ones), and a widget that renders
|
||||
the question-graph for point-and-click navigation. See
|
||||
`ALGEBRA_PLAN.md` for the full plan.
|
||||
**Concrete (landed 2026-05-01 on Dev_REL2):**
|
||||
- `CubicalTransport/Algebra/Meta.lean` — the meta-mirror types
|
||||
(`MetaCType`, `MetaClassifier`, `MetaArtifact`, `MetaPosition`).
|
||||
- `CubicalTransport/Algebra/Edit.lean` — the `Edit` monad and
|
||||
`Context` comonad, with the comonad-to-monad distributive law.
|
||||
- `CubicalTransport/Algebra/Restructure.lean` — the universal
|
||||
`restructure` macro (`comp`-shaped, five fields), the canonical
|
||||
frozen aliases (`transport_artifact`, `relocate_invariant`,
|
||||
`rename_throughout`, `materialize`, …), and the headless apply
|
||||
interpreter.
|
||||
- `CubicalTransport/Algebra/MacroAlias.lean` — the `@[macroAlias]`
|
||||
attribute + alias registry.
|
||||
- `CubicalTransport/Algebra/Methodology.lean` — the
|
||||
`@[methodology]` attribute and the `cubical_search` autodiscovery
|
||||
tactic (registry + dispatch loop; methodology-transport stub
|
||||
awaits `@[metaPath]` in REL2.6+).
|
||||
- `CubicalTransport/Algebra/Test.lean` — end-to-end compile-time
|
||||
tests verifying the registry, attribute, and tactic-dispatch
|
||||
loop work as a system.
|
||||
|
||||
The widget surface (Phase D, `UserWidgetDefinition` rendering the
|
||||
question-graph) is the one piece deferred — it needs an active
|
||||
Lean LSP session for RPC plumbing. Headless usage is fully
|
||||
operational via `lake exe algebra-restructure`-style entry points.
|
||||
|
||||
**Why it matters:** The map is the visible face of the system's
|
||||
closure under its own operations. Cubical primitives (transport,
|
||||
|
|
@ -214,14 +228,23 @@ tools are built from the same algebra as what they navigate.
|
|||
> every connection on the map is a recipe for following the
|
||||
> current somewhere new.*
|
||||
|
||||
**Concrete (planned REL2.5):** The methodology-transport clause
|
||||
inside `cubical_search`. When a structural Path is declared in the
|
||||
codebase (e.g., "Bool ≃ {0, 1}", "List α ≃ Vec n α with n free"),
|
||||
every existing methodology fragment registered for one side of the
|
||||
path automatically becomes a candidate for the other side via
|
||||
`transp` at the methodology level. Twenty starting methodologies
|
||||
+ a hundred declared paths → potentially thousands of derived
|
||||
methodologies, each formally certified-by-construction.
|
||||
**Concrete (partial 2026-05-01, full pending REL2.6+):** The
|
||||
methodology-transport clause inside `cubical_search`.
|
||||
|
||||
- **Registry + dispatch** (landed): `Algebra/Methodology.lean`
|
||||
ships the `@[methodology]` attribute, the methodology registry
|
||||
(`methodologyRegistryExt`), and the `cubical_search` tactic that
|
||||
walks the registry on every goal. The stub
|
||||
`deriveByTransport` returns `[]` until the structural-Path
|
||||
declaration system arrives.
|
||||
- **Methodology-transport** (pending REL2.6+): the
|
||||
`@[metaPath]` attribute lets a developer declare a structural
|
||||
Path between two classifiers. Once such Paths are present,
|
||||
`deriveByTransport` walks them to automatically derive new
|
||||
methodology candidates from existing ones — `transp` at the
|
||||
methodology level. Twenty starting methodologies + a hundred
|
||||
declared paths → potentially thousands of derived methodologies,
|
||||
each formally certified-by-construction.
|
||||
|
||||
**Why it matters:** This is *autodiscovery*: the proof-search
|
||||
library grows under structural-Path declarations alone, with no
|
||||
|
|
|
|||
|
|
@ -287,8 +287,26 @@ paths first-class via §3.1.
|
|||
|
||||
**topolei workaround:** users invoke cubical rewrites by explicit
|
||||
`rw [eval_...]` / `rw [readback_...]` calls. Less automation,
|
||||
more bookkeeping. Planned mitigation: a `cubical_simp` tactic as
|
||||
a pure-Lean extension in Phase 6 (cells-spec §19).
|
||||
more bookkeeping.
|
||||
|
||||
**Mitigation status (2026-05-01, Dev_REL2):**
|
||||
- ✅ **`cubical_simp` (light form)** — `CubicalTransport/Question.lean`
|
||||
ships a macro tactic that pre-loads every `@[simp]`-tagged
|
||||
classifier-conditioned `ask_of_*` lemma plus every classifier
|
||||
definition. Concrete-shape questions (`q.φ = .top`,
|
||||
`q.body = .interval`, …) collapse automatically. See
|
||||
QUESTIONS.md §4.3.
|
||||
- ✅ **`cubical_search` (autodiscovery)** —
|
||||
`CubicalTransport/Algebra/Methodology.lean` ships the
|
||||
`@[methodology]` attribute + dispatch tactic per ALGEBRA_PLAN.md
|
||||
§4. Walks the methodology library by classifier; on miss tries
|
||||
methodology-transport along declared structural Paths
|
||||
(`deriveByTransport` is a stub until `@[metaPath]` lands in
|
||||
REL2.6+).
|
||||
- ⏳ **Full `cubical_simp` (graph-walking)** — the version that
|
||||
walks the classifier-equivalence graph step-by-step with
|
||||
structured failure reports awaits the `@[metaPath]` infrastructure
|
||||
(REL2.6+).
|
||||
|
||||
### 3.8 Kernel-verified FFI
|
||||
|
||||
|
|
|
|||
|
|
@ -184,40 +184,57 @@ runs through `q.Equiv` and so chains under composition.
|
|||
|
||||
The question discipline supports three escalating levels:
|
||||
|
||||
### 4.1 Level 1 — Structural reification only
|
||||
### 4.1 Level 1 — Structural reification only ✅ LANDED 2026-05-01
|
||||
|
||||
Define `CompQ`, `ask`, `Equiv`, classifiers. Restate existing
|
||||
axioms / theorems as classifier-conditioned equivalences. Existing
|
||||
runtime / soundness behaviour unchanged.
|
||||
|
||||
**Cost:** ~1–2 days.
|
||||
**Status:** landed in `CubicalTransport/Question.lean` on `Dev_REL2`
|
||||
as commit `6adbce0` (2026-05-01). CompQ + 11 classifiers + 5
|
||||
`ask_of_*` theorems for the eval_comp_* family.
|
||||
|
||||
**Benefit:** a uniform vocabulary; new theorems are naturally stated
|
||||
in question form; old theorems become derived corollaries.
|
||||
|
||||
### 4.2 Level 2 — Routing through questions
|
||||
### 4.2 Level 2 — Routing through questions ✅ LANDED 2026-05-01
|
||||
|
||||
Every axiom and theorem in `Eval` / `TransportLaws` / `CompLaws` /
|
||||
`Glue` re-stated in question shape. A `simp`-set rewrites question
|
||||
equivalences. Call sites continue to work via `q.ask = …` lemmas.
|
||||
|
||||
**Cost:** ~3–5 days on top of Level 1.
|
||||
**Status:** landed as commit `d6af78a` (2026-05-01). TranspQ +
|
||||
HCompQ + CompNQ sister questions; transport / hcomp / compN axioms
|
||||
restated as classifier-conditioned `Equiv` theorems with `@[simp]`
|
||||
tags; bridge `TranspQ.toCompQ_ask_eq_ask_full_face` reconciles
|
||||
transport-as-itself with transport-as-degenerate-comp.
|
||||
|
||||
**Benefit:** *question algebra* — compose, decompose, refine
|
||||
mechanically. Refactors (rename a classifier, factor a question,
|
||||
merge two questions into a join) become text-level operations that
|
||||
preserve correctness.
|
||||
|
||||
### 4.3 Level 3 — Question-driven proofs
|
||||
### 4.3 Level 3 — Question-driven proofs ✅ PARTIAL (light) 2026-05-01
|
||||
|
||||
Proofs are *question reductions*: "this `CompQ` reduces to that
|
||||
`CompQ`, which is identity by `IsConstLine`." A `cubical_simp`
|
||||
tactic knows the reduction graph and finds reduction chains
|
||||
automatically.
|
||||
|
||||
**Cost:** ~10+ days on top of Level 2; co-equal with REL2 in scope.
|
||||
Depends on a `cubical_simp` tactic
|
||||
(KERNEL_BOUNDARY.md §3.7).
|
||||
**Status:**
|
||||
- ✅ **Light form** (`cubical_simp` macro) — landed as commit
|
||||
`d6af78a` (2026-05-01). A macro tactic expanding to a `simp only`
|
||||
call pre-loaded with every classifier definition + every
|
||||
`@[simp]`-tagged `ask_of_*` lemma. Concrete-shape questions
|
||||
collapse automatically; arbitrary extra simp lemmas can be passed
|
||||
via `cubical_simp [extra_args]`.
|
||||
- ✅ **Autodiscovery search** (`cubical_search` tactic) — landed in
|
||||
`CubicalTransport/Algebra/Methodology.lean` per ALGEBRA_PLAN.md
|
||||
Phase D' as part of the metacoding stack.
|
||||
- ⏳ **Full graph-walking form** — the version that walks the
|
||||
classifier-equivalence graph step-by-step with structured
|
||||
failure reports per §4.4 below. Depends on `@[metaPath]`
|
||||
declarations (REL2.6+).
|
||||
|
||||
**Benefit:** proofs become navigable graphs of classifier
|
||||
applications; the engine essentially proves cubical-core theorems
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue