File size: 14,371 Bytes
a10b189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
{-# LANGUAGE DeriveEq #-}
{-# LANGUAGE DeriveShow #-}
{-# LANGUAGE DeriveGeneric #-}

{-|
Module      : Assertica.Core.Assertion
Description : Explicit assertion system for mathematical obligations
Copyright   : (c) 2026 Ahmad Ali Parr
License     : BSL-1.0

The assertion system represents mathematical obligations as first-class program entities.
An assertion is an explicit proof goal that must be discharged by the proof-term checker.

Key design principles:
  1. Explicit representation: Every assertion is a data structure, not a string or comment.
  2. Fail-closed: An unknown or malformed assertion is UNPROVEN, not silently accepted.
  3. Proof obligation boundary: The assertion clearly identifies what must be proven.
  4. Composable propositions: Build complex propositions from simpler ones.
  5. No hidden theorem proving: Assertions state obligations; the proof checker discharges them.

Integration:
  - Input: Elaborator (Agent 3A) produces surface assertions
  - Processing: This module represents obligations
  - Output: Proof-term checker (Agent 2B) discharges proof obligations
-}

module Assertica.Core.Assertion
  ( -- * Core types
    Assertion(..)
  , ProofObligation(..)
  , ObligationState(..)
  , ObligationStore

  -- * Assertion construction and querying
  , mkAssertion
  , assertionName
  , assertionProp
  , assertionLoc
  , getObligations
  , lookupObligation
  , countByState

  -- * Proposition utilities
  , propFreeVariables
  , propBoundVariables
  , propSubstitute
  , propAlphaRename
  , prettyPrintProposition

  -- * Obligation tracking
  , emptyStore
  , insertObligation
  , updateObligation
  , markProven
  , markUnproven
  , markRejected
  , allProven
  , unprovenObligations

  -- * Elaboration interface
  , SurfaceAssertion(..)
  , elaborateAssertion

  -- * Re-exports from AST
  , Proposition(..)
  , Term(..)
  , Var(..)
  , SourceLoc(..)
  ) where

import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Data.Typeable (Typeable)
import GHC.Generics (Generic)
import Data.List (intercalate)

import Assertica.Core.AST
  ( Proposition(..)
  , Term(..)
  , Var(..)
  , SourceLoc(..)
  , freeVars
  , substitute
  , alphaRename
  )

-- ============================================================================
-- Core Assertion Type
-- ============================================================================

-- | An assertion is a named proof obligation with a source location
data Assertion = Assertion
  { assertionName :: String
    -- ^ Unique name for the assertion
  , assertionProp :: Proposition
    -- ^ The proposition to be proven
  , assertionLoc :: SourceLoc
    -- ^ Source location for error reporting
  }
  deriving (Eq, Show, Generic, Typeable)

-- | Proof obligation state tracking
data ObligationState
  = Asserted    -- ^ The obligation has been stated but not yet proven
  | Proven      -- ^ A valid proof has been accepted
  | Unproven    -- ^ The proof attempt was invalid or no proof provided
  | Rejected    -- ^ The obligation was explicitly rejected
  deriving (Eq, Show, Ord, Generic, Typeable)

-- | A proof obligation with its current state
data ProofObligation = ProofObligation
  { obligationAssertion :: Assertion
  , obligationState :: ObligationState
  , obligationProofTerm :: Maybe String
    -- ^ Optional reference to the proof term that discharges this obligation
  }
  deriving (Eq, Show, Generic, Typeable)

-- | Storage for proof obligations
-- Maps assertion names to their obligations
type ObligationStore = Map.Map String ProofObligation

-- ============================================================================
-- Assertion Construction
-- ============================================================================

-- | Create an assertion with default Asserted state
mkAssertion :: String -> Proposition -> SourceLoc -> Assertion
mkAssertion name prop loc = Assertion name prop loc

-- ============================================================================
-- Obligation Store Operations
-- ============================================================================

-- | Create an empty obligation store
emptyStore :: ObligationStore
emptyStore = Map.empty

-- | Insert a new obligation into the store
insertObligation :: Assertion -> ObligationStore -> ObligationStore
insertObligation assertion store =
  let obligation = ProofObligation assertion Asserted Nothing
  in Map.insert (assertionName assertion) obligation store

-- | Look up an obligation by assertion name
lookupObligation :: String -> ObligationStore -> Maybe ProofObligation
lookupObligation = Map.lookup

-- | Get all obligations from the store
getObligations :: ObligationStore -> [ProofObligation]
getObligations = Map.elems

-- | Update an obligation's state
updateObligation :: String -> ObligationState -> Maybe String -> ObligationStore -> Either String ObligationStore
updateObligation name state proofTerm store =
  case Map.lookup name store of
    Nothing -> Left $ "Obligation not found: " ++ name
    Just obligation ->
      let updated = obligation { obligationState = state, obligationProofTerm = proofTerm }
      in Right $ Map.insert name updated store

-- | Mark an obligation as proven
markProven :: String -> Maybe String -> ObligationStore -> Either String ObligationStore
markProven name proofTerm = updateObligation name Proven proofTerm

-- | Mark an obligation as unproven
markUnproven :: String -> ObligationStore -> Either String ObligationStore
markUnproven name store = updateObligation name Unproven Nothing store

-- | Mark an obligation as rejected
markRejected :: String -> ObligationStore -> Either String ObligationStore
markRejected name store = updateObligation name Rejected Nothing store

-- | Check if all obligations are proven
allProven :: ObligationStore -> Bool
allProven store = all (\ob -> obligationState ob == Proven) (getObligations store)

-- | Get all unproven obligations
unprovenObligations :: ObligationStore -> [ProofObligation]
unprovenObligations store =
  filter (\ob -> obligationState ob /= Proven) (getObligations store)

-- | Count obligations by state
countByState :: ObligationState -> ObligationStore -> Int
countByState state store =
  length $ filter (\ob -> obligationState ob == state) (getObligations store)

-- ============================================================================
-- Proposition Utilities
-- ============================================================================

-- | Free variables in a proposition
propFreeVariables :: Proposition -> Set.Set Var
propFreeVariables (EqualityProp t1 t2) = Set.union (freeVars t1) (freeVars t2)
propFreeVariables (UniversalQuantProp v prop) =
  Set.delete v (propFreeVariables prop)
propFreeVariables (ImplicationProp p q) =
  Set.union (propFreeVariables p) (propFreeVariables q)
propFreeVariables (ConjunctionProp p q) =
  Set.union (propFreeVariables p) (propFreeVariables q)
propFreeVariables (TypingProp t _) = freeVars t
propFreeVariables (PredicateApp _ terms) =
  Set.unions $ map freeVars terms
propFreeVariables (NegationProp p) = propFreeVariables p
propFreeVariables (DisjunctionProp p q) =
  Set.union (propFreeVariables p) (propFreeVariables q)

-- | Bound variables in a proposition
propBoundVariables :: Proposition -> Set.Set Var
propBoundVariables (EqualityProp t1 t2) = Set.empty  -- Terms have their own binding
propBoundVariables (UniversalQuantProp v prop) =
  Set.insert v (propBoundVariables prop)
propBoundVariables (ImplicationProp p q) =
  Set.union (propBoundVariables p) (propBoundVariables q)
propBoundVariables (ConjunctionProp p q) =
  Set.union (propBoundVariables p) (propBoundVariables q)
propBoundVariables (TypingProp _ _) = Set.empty
propBoundVariables (PredicateApp _ _) = Set.empty
propBoundVariables (NegationProp p) = propBoundVariables p
propBoundVariables (DisjunctionProp p q) =
  Set.union (propBoundVariables p) (propBoundVariables q)

-- | Substitute a variable in a proposition
-- Must handle binding correctly to avoid variable capture
propSubstitute :: Var -> Term -> Proposition -> Proposition
propSubstitute var replacement prop = go prop
  where
    go (EqualityProp t1 t2) =
      EqualityProp (substitute var replacement t1) (substitute var replacement t2)

    go (UniversalQuantProp v p)
      | v == var = UniversalQuantProp v p  -- Bound variable shadows the substitution
      | v `Set.member` freeVars replacement =
          -- Need to rename v to avoid capture
          let freshVar = freshenPropVar v prop replacement
          in UniversalQuantProp freshVar (propSubstitute var replacement (propAlphaRename v freshVar p))
      | otherwise = UniversalQuantProp v (go p)

    go (ImplicationProp p q) =
      ImplicationProp (go p) (go q)

    go (ConjunctionProp p q) =
      ConjunctionProp (go p) (go q)

    go (TypingProp t ty) =
      TypingProp (substitute var replacement t) ty

    go (PredicateApp name terms) =
      PredicateApp name (map (substitute var replacement) terms)

    go (NegationProp p) =
      NegationProp (go p)

    go (DisjunctionProp p q) =
      DisjunctionProp (go p) (go q)

-- | Alpha-rename a variable in a proposition
propAlphaRename :: Var -> Var -> Proposition -> Proposition
propAlphaRename oldVar newVar = go
  where
    go (EqualityProp t1 t2) =
      EqualityProp (alphaRename oldVar newVar t1) (alphaRename oldVar newVar t2)

    go (UniversalQuantProp v p)
      | v == oldVar = UniversalQuantProp newVar (go p)
      | otherwise = UniversalQuantProp v (go p)

    go (ImplicationProp p q) = ImplicationProp (go p) (go q)
    go (ConjunctionProp p q) = ConjunctionProp (go p) (go q)
    go (TypingProp t ty) = TypingProp (alphaRename oldVar newVar t) ty
    go (PredicateApp name terms) =
      PredicateApp name (map (alphaRename oldVar newVar) terms)
    go (NegationProp p) = NegationProp (go p)
    go (DisjunctionProp p q) = DisjunctionProp (go p) (go q)

-- | Generate a fresh variable for propositions
freshenPropVar :: Var -> Proposition -> Term -> Var
freshenPropVar v prop replacement =
  let occupied = Set.union (propFreeVariables prop) (freeVars replacement)
      Var varName = v
      candidates = map (\n -> Var (varName ++ "_" ++ show n)) [1..]
  in head [c | c <- candidates, c `Set.notMember` occupied]

-- ============================================================================
-- Pretty Printing
-- ============================================================================

-- | Pretty-print a proposition for human-readable output
prettyPrintProposition :: Proposition -> String
prettyPrintProposition (EqualityProp t1 t2) =
  prettyTerm t1 ++ " ≡ " ++ prettyTerm t2

prettyPrintProposition (UniversalQuantProp v prop) =
  "∀ " ++ prettyVar v ++ ". " ++ prettyPrintProposition prop

prettyPrintProposition (ImplicationProp p q) =
  "(" ++ prettyPrintProposition p ++ " → " ++ prettyPrintProposition q ++ ")"

prettyPrintProposition (ConjunctionProp p q) =
  "(" ++ prettyPrintProposition p ++ " ∧ " ++ prettyPrintProposition q ++ ")"

prettyPrintProposition (TypingProp t ty) =
  prettyTerm t ++ " : " ++ ty

prettyPrintProposition (PredicateApp name terms) =
  name ++ "(" ++ intercalate ", " (map prettyTerm terms) ++ ")"

prettyPrintProposition (NegationProp p) =
  "¬(" ++ prettyPrintProposition p ++ ")"

prettyPrintProposition (DisjunctionProp p q) =
  "(" ++ prettyPrintProposition p ++ " ∨ " ++ prettyPrintProposition q ++ ")"

-- | Pretty-print a term (minimal)
prettyTerm :: Term -> String
prettyTerm (TVar v) = prettyVar v
prettyTerm (TAbs v body) = "(λ" ++ prettyVar v ++ ". " ++ prettyTerm body ++ ")"
prettyTerm (TApp f x) = "(" ++ prettyTerm f ++ " " ++ prettyTerm x ++ ")"
prettyTerm (TConst c) = c
prettyTerm (TLet v val body) = "(let " ++ prettyVar v ++ " = " ++ prettyTerm val ++ " in " ++ prettyTerm body ++ ")"
prettyTerm (TPair a b) = "(" ++ prettyTerm a ++ ", " ++ prettyTerm b ++ ")"
prettyTerm (TFst t) = "fst(" ++ prettyTerm t ++ ")"
prettyTerm (TSnd t) = "snd(" ++ prettyTerm t ++ ")"

-- | Pretty-print a variable
prettyVar :: Var -> String
prettyVar (Var name) = name

-- ============================================================================
-- Elaboration Interface
-- ============================================================================

-- | Surface-level assertion (produced by elaborator, Agent 3A)
-- This is the input type for elaboration
data SurfaceAssertion = SurfaceAssertion
  { surfaceName :: String
    -- ^ The assertion name
  , surfacePropositionText :: String
    -- ^ The proposition as text (needs parsing by Agent 3A)
  , surfaceSourceLoc :: SourceLoc
    -- ^ Source location
  }
  deriving (Eq, Show, Generic, Typeable)

-- | Elaboration interface
-- This signature shows how surface assertions are converted to core assertions
-- Implementation of this function is the responsibility of Agent 3A
-- when they implement the parser/elaborator
--
-- Type signature for Agent 3A to implement:
-- elaborateSurfaceAssertion :: SurfaceAssertion -> Either String Assertion
--
-- For now, we provide a stub that always fails with an appropriate message
elaborateAssertion :: SurfaceAssertion -> Either String Assertion
elaborateAssertion surface =
  Left $ "Elaboration not yet implemented. "
      ++ "Agent 3A must implement parsing of: "
      ++ surfacePropositionText surface

-- ============================================================================
-- Integration Interface for Agent 2B (Proof-Term Checker)
-- ============================================================================

{-|
Proof-term checker integration (Agent 2B)

The proof checker receives:
1. A Proposition (the obligation to discharge)
2. A ProofTerm (the evidence for the proposition)

Expected type signature in Agent 2B's module:
  proofChecks :: ProofTerm -> Proposition -> Either String ()

Invariants the proof checker assumes about well-formed propositions:
  - All bound variables are properly scoped (no captures)
  - Free variables are explicitly mentioned in universal quantifications
  - Terms in equality propositions are well-typed (type checking by Agent 3B)
  - Predicate applications reference known predicates
  - No circular references in implication chains

When a proof is invalid or the proposition is malformed, the checker
MUST return a descriptive error message, never silently reject.
-}