You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
compose and decompose read as a dual pair but are unrelated constructs living on different ontological layers. Worse, the actual missing counterpart of decompose — the ampliative upward move, generalize — is not expressible in current gaia lang at all.
1. The compose word family spans three layers with zero duality
lexeme
layer
actual semantics
decompose
Claim (logic)
declares whole ≡ formula(parts) — a constrained equivalence (parts↔atoms bijection, acyclic, once per claim)
composite
legacy Strategy surface (dsl/strategies.py)
composite strategy constructor, deprecated compat
compose / composition
Action (template)
decorator capturing an action DAG into a named+versioned Compose action (runtime/composition.py:153)
gaia lang has genuine dual pairs elsewhere (land/lor, forall/exists, implies/iff), so users — and the agents that author most packages — reasonably infer compose = inverse of decompose. The language actively teaches a wrong mental model. The CLI asymmetry (17 statement-emitting author verbs vs the file-based compose/composition registration verbs) is a downstream symptom.
decompose itself needs no structural inverse: whole ≡ formula(parts) is an equivalence, belief flows both ways under BP, and "build the whole from parts" is the same statement written from the other authoring perspective.
2. The real gap: generalize was flattened out by the operator redesign
The operator/strategy redesign (#412, #414) moved Strategy down into IR. The migration guidance for legacy induction (docs/foundations/gaia-lang/knowledge-and-reasoning.md:345) is:
declare each support step with derive / observe and let factor-graph topology accumulate evidence
This dropped a load-bearing speech act. Legacy induction() forced a composition warrant ("Are observations independent? Do they support the same law?"). With flat per-instance arrows, each action carries its own implication warrant, but the joint validity question — observation independence — has no syntactic home. Nothing forces an author to assert it; topology accumulates evidence regardless. That is exactly the double-counting failure mode we have already paid for in real packages (the Galileo package re-formalization).
IR still has the machinery: StrategyType.INDUCTION (ir/strategy.py:49), formalize.py templates, and the induction special-case in coarsen.py — but for new-style packages nothing produces it; it is a dead path kept alive for compat.
3. The key structural fact: induction is a composition
Generalization is not an atomic inference form. It is a bundle of shared-conclusion evidential supports plus one joint warrant. The per-instance layer already exists in the language: infer(obs, hypothesis=law, given=[aux...], p_e_given_h=..., p_e_given_not_h=...) is precisely "law (+ auxiliary premises) derives this observation", with Bayes inverting the belief flow. Per-experiment auxiliary premises stay heterogeneous and live in each instance's given= (load-bearing, shares modus-tollens blame — Duhem–Quine) or background= (context only). A generalize verb must NOT absorb or restate any of that.
So generalize should be a blessed composition pattern, not a new primitive:
e1=infer(obs_mars, hypothesis=law, given=[aux_mars...], p_e_given_h=...)
e2=infer(obs_jupiter, hypothesis=law, given=[aux_jupiter...], p_e_given_h=...)
generalize(law, over=[e1, e2],
reason="independent instruments and independent orbit fits; no shared systematics")
lowering to the existing Compose action / IR (runtime/action.py:216, ir/compose.py) — no new Action class, no ir-schema bump.
What the sugar adds over a bare @composition (and why bare composition is not enough):
Mandatory independence warrant — bare warrants= is optional free text; the entire anti-double-counting value is that the question cannot be skipped.
Shared-conclusion validation — every bundled action must support the same law.
Recognizable identity — coarsen / review / render need to see "this is an inductive argument", not an anonymous DAG. Prefer a type tag on Compose over matching a magic name= string.
Plain-call ergonomics — the @compose(name=, version=) decorator + pyproject registration ceremony is built for reusable templates, not a one-off induction inside a paper formalization.
Proposal
Two independent steps, strictly incremental:
Step 1 — naming (small). Deprecate the verb-form alias compose; make the noun composition the canonical name for the action-template mechanism (the alias composition = compose already exists at runtime/composition.py:200; CLI already has gaia author composition). This dissolves the false compose/decompose pairing. "generalize is a composition" then reads naturally.
Step 2 — generalize sugar (moderate). Plain-call sugar constructing a Compose action as above, plus the identity tag and the coarsen/review/render recognition work. Author CLI verb can follow in a second phase.
Non-goals. No claim-level structural compose (low value — the equivalence is already bidirectional). decompose unchanged. The deliberate non-duality of decompose (equivalence, no information gain, no warrant needed) vs generalize (ampliative, inductive risk, mandatory warrant) is an epistemological fact the language should surface, not paper over with a morphologically symmetric word pair.
Problem
composeanddecomposeread as a dual pair but are unrelated constructs living on different ontological layers. Worse, the actual missing counterpart ofdecompose— the ampliative upward move, generalize — is not expressible in current gaia lang at all.1. The
composeword family spans three layers with zero dualitydecomposewhole ≡ formula(parts)— a constrained equivalence (parts↔atoms bijection, acyclic, once per claim)compositedsl/strategies.py)compose/compositionComposeaction (runtime/composition.py:153)gaia lang has genuine dual pairs elsewhere (
land/lor,forall/exists,implies/iff), so users — and the agents that author most packages — reasonably infercompose= inverse ofdecompose. The language actively teaches a wrong mental model. The CLI asymmetry (17 statement-emitting author verbs vs the file-basedcompose/compositionregistration verbs) is a downstream symptom.decomposeitself needs no structural inverse:whole ≡ formula(parts)is an equivalence, belief flows both ways under BP, and "build the whole from parts" is the same statement written from the other authoring perspective.2. The real gap:
generalizewas flattened out by the operator redesignThe operator/strategy redesign (#412, #414) moved
Strategydown into IR. The migration guidance for legacyinduction(docs/foundations/gaia-lang/knowledge-and-reasoning.md:345) is:This dropped a load-bearing speech act. Legacy
induction()forced a composition warrant ("Are observations independent? Do they support the same law?"). With flat per-instance arrows, each action carries its own implication warrant, but the joint validity question — observation independence — has no syntactic home. Nothing forces an author to assert it; topology accumulates evidence regardless. That is exactly the double-counting failure mode we have already paid for in real packages (the Galileo package re-formalization).IR still has the machinery:
StrategyType.INDUCTION(ir/strategy.py:49),formalize.pytemplates, and the induction special-case incoarsen.py— but for new-style packages nothing produces it; it is a dead path kept alive for compat.3. The key structural fact: induction is a composition
ir/strategy.py:49already says it:Generalization is not an atomic inference form. It is a bundle of shared-conclusion evidential supports plus one joint warrant. The per-instance layer already exists in the language:
infer(obs, hypothesis=law, given=[aux...], p_e_given_h=..., p_e_given_not_h=...)is precisely "law (+ auxiliary premises) derives this observation", with Bayes inverting the belief flow. Per-experiment auxiliary premises stay heterogeneous and live in each instance'sgiven=(load-bearing, shares modus-tollens blame — Duhem–Quine) orbackground=(context only). Ageneralizeverb must NOT absorb or restate any of that.So
generalizeshould be a blessed composition pattern, not a new primitive:lowering to the existing
Composeaction / IR (runtime/action.py:216,ir/compose.py) — no new Action class, no ir-schema bump.What the sugar adds over a bare
@composition(and why bare composition is not enough):warrants=is optional free text; the entire anti-double-counting value is that the question cannot be skipped.coarsen/ review / render need to see "this is an inductive argument", not an anonymous DAG. Prefer a type tag onComposeover matching a magicname=string.@compose(name=, version=)decorator + pyproject registration ceremony is built for reusable templates, not a one-off induction inside a paper formalization.Proposal
Two independent steps, strictly incremental:
Step 1 — naming (small). Deprecate the verb-form alias
compose; make the nouncompositionthe canonical name for the action-template mechanism (the aliascomposition = composealready exists atruntime/composition.py:200; CLI already hasgaia author composition). This dissolves the falsecompose/decomposepairing. "generalize is a composition" then reads naturally.Step 2 —
generalizesugar (moderate). Plain-call sugar constructing aComposeaction as above, plus the identity tag and thecoarsen/review/render recognition work. Author CLI verb can follow in a second phase.Non-goals. No claim-level structural
compose(low value — the equivalence is already bidirectional).decomposeunchanged. The deliberate non-duality ofdecompose(equivalence, no information gain, no warrant needed) vsgeneralize(ampliative, inductive risk, mandatory warrant) is an epistemological fact the language should surface, not paper over with a morphologically symmetric word pair.References
gaia/engine/lang/dsl/decompose.py:54,gaia/engine/lang/runtime/composition.py:153,gaia/engine/lang/runtime/action.py:216gaia/engine/ir/strategy.py:49,gaia/engine/ir/compose.py,gaia/engine/ir/coarsen.py:97docs/foundations/gaia-lang/knowledge-and-reasoning.md:55,345decomposeshould accept a reasoning action (composite / infer) for the part→whole link, not only a Booleanformula#735 (decomposeaccepting reasoning actions)