Skip to content

Commit bec97b9

Browse files
committed
chore: merge remote changes from origin/main
2 parents ea190aa + 58eebc5 commit bec97b9

9 files changed

Lines changed: 923 additions & 30 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

arxiv-consent-aware-programming.tex

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,29 +1042,36 @@ \subsection{Properties}
10421042
for any consent effect in $e$.
10431043
\end{theorem}
10441044

1045-
\begin{proof}[Proof sketch]
1045+
\begin{proof}
10461046
By structural induction on the typing derivation. The critical case
1047-
is \textsc{T-Consent}: the typing rule ensures that consent-gated
1048-
expressions occur within a handler context ($\Sigma$ is non-empty),
1049-
and the handler $h$ is total (it returns either $\granted$ or
1050-
$\denied$ for any string $s$), so one of \textsc{E-ConsentGranted} or
1051-
\textsc{E-ConsentDenied} always applies.
1047+
is \textsc{T-Consent}: we strengthen the paper rule to require
1048+
$\Sigma \neq \varnothing$ (at least one handler in scope), closing a
1049+
gap in the original sketch. With this invariant, the top handler $h$
1050+
is always available and total, so one of \textsc{E-ConsentGranted} or
1051+
\textsc{E-ConsentDenied} fires. The full machine-checked proof is in
1052+
\texttt{src/abi/WokeLang/Safety.idr} (\texttt{progress});
1053+
see the supplementary proof inventory
1054+
(\texttt{docs/supplementary/safety-proofs.adoc}) for details.
10521055
\end{proof}
10531056

10541057
\begin{theorem}[Preservation]
10551058
\label{thm:preservation}
10561059
If $\Gamma; \Sigma \vdash e : \tau$ and $\langle e, H, A \rangle
1057-
\step \langle e', H', A' \rangle$, then $\Gamma; \Sigma \vdash e' :
1058-
\tau'$ where $\tau' = \tau$ or $\tau' = \mathsf{Consent}[\tau]$
1059-
resolves to $\tau$ (in the granted case) or $\denied$ (in the denied
1060-
case).
1060+
\step \langle e', H', A' \rangle$, then there exists $\tau'$ with
1061+
$\Gamma; \Sigma' \vdash e' : \tau'$, where $\tau' = \tau$ for most
1062+
rules, $\tau' = \tau$ (body type) when \textsc{E-ConsentGranted}
1063+
fires, and $\tau' = \mathsf{TyDenied}$ when \textsc{E-ConsentDenied}
1064+
fires.
10611065
\end{theorem}
10621066

1063-
\begin{proof}[Proof sketch]
1064-
By structural induction on the reduction. The consent cases follow
1065-
from the handler totality and the fact that the body of a consent gate
1066-
is typed at $\tau$ while the gate as a whole is typed at
1067-
$\mathsf{Consent}[\tau]$.
1067+
\begin{proof}
1068+
By structural induction on the reduction derivation. The consent
1069+
cases follow from handler totality: \textsc{E-ConsentGranted} returns
1070+
$e$ (the body, typed at $\tau$), and \textsc{E-ConsentDenied} returns
1071+
\texttt{EDenied} (typed at $\mathsf{TyDenied}$). The E-Beta case
1072+
uses a closed-value substitution lemma proved by induction with a
1073+
context-exchange step for nested lambda binders. Machine-checked in
1074+
\texttt{src/abi/WokeLang/Safety.idr} (\texttt{preservation}).
10681075
\end{proof}
10691076

10701077
\begin{theorem}[Consent Non-Evasion]
@@ -1075,14 +1082,16 @@ \subsection{Properties}
10751082
decision.
10761083
\end{theorem}
10771084

1078-
\begin{proof}[Proof sketch]
1079-
The only reduction rules that step into a consent gate body are
1080-
\textsc{E-ConsentGranted} and \textsc{E-ConsentDenied}, both of which
1081-
require a handler response and produce a log entry in $A$. The typing
1082-
rule \textsc{T-Consent} ensures that the body is not directly
1083-
accessible outside the gate construct. Exhaustiveness of the handler
1084-
(it must return $\granted$ or $\denied$) ensures a decision is always
1085-
made.
1085+
\begin{proof}
1086+
By inversion on the \textsc{Step} derivation for $\mathbf{only\_if\_okay}\;s\;e$.
1087+
The only constructors of \textsc{Step} whose head expression matches
1088+
$\mathbf{only\_if\_okay}$ are \textsc{E-ConsentGranted} and
1089+
\textsc{E-ConsentDenied}; all other constructors match different head
1090+
forms and are structurally absent. Both firing rules require
1091+
$H = h :: H'$ (non-empty stack) and append
1092+
$\mathsf{ConsentEntry}(d, s)$ to $A$.
1093+
Machine-checked in \texttt{src/abi/WokeLang/Safety.idr}
1094+
(\texttt{nonEvasion}, \texttt{consentBodyRequiresLog}).
10861095
\end{proof}
10871096

10881097
This theorem is the core safety property of the consent system: you
@@ -1448,11 +1457,16 @@ \section{Future Work}
14481457

14491458
\paragraph{Formal Verification.}
14501459
The $\lambda_{\consent}$ calculus presented here covers a simplified
1451-
core language. Extending the formal treatment to cover the full
1452-
\WokeLang{} language---including higher-order functions, mutation,
1453-
concurrency, and module systems---is ongoing work. We are exploring
1454-
mechanized proofs in Idris~2~\citep{brady2021idris2} to ensure the
1455-
correctness of our compiler's consent tracking.
1460+
core language. Proofs of Progress, Preservation, and Non-Evasion have
1461+
been mechanised in Idris~2~\citep{brady2021idris2} under
1462+
\texttt{\%default total} with no uses of \texttt{believe\_me} or
1463+
\texttt{postulate}; see \texttt{src/abi/WokeLang/Safety.idr} and
1464+
\texttt{docs/supplementary/safety-proofs.adoc}. The mechanisation
1465+
strengthens \textsc{T-Consent} to require a non-empty handler context
1466+
$\Sigma$, fixing a gap in the original proof sketch. Extending the
1467+
formal treatment to the full language---including higher-order
1468+
functions, mutation, concurrency, and module systems---remains future
1469+
work.
14561470

14571471
\paragraph{Localization.}
14581472
Exploring multilingual keyword support while maintaining semantic
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
3+
= WokeLang Safety Proofs — Supplementary Material
4+
:toc:
5+
:toc-placement: preamble
6+
7+
Supplementary material for _A Consent-Aware Programming Language: WokeLang_
8+
(§7, Formal Semantics). This document maps each paper theorem to the
9+
corresponding Idris 2 source file and explains the key proof strategies.
10+
11+
== Source locations
12+
13+
[cols="2,3,4"]
14+
|===
15+
| Theorem | Paper reference | Idris 2 source
16+
17+
| Syntax (Definition 1)
18+
| §7.1
19+
| `src/abi/WokeLang/Calculus.idr`
20+
21+
| Typing (Definition 2)
22+
| §7.2
23+
| `src/abi/WokeLang/Typing.idr`
24+
25+
| Operational semantics (Definition 3)
26+
| §7.3
27+
| `src/abi/WokeLang/Reduction.idr`
28+
29+
| Theorem 1 — Progress
30+
| §7.4, `thm:progress`
31+
| `src/abi/WokeLang/Safety.idr`, `progress`
32+
33+
| Theorem 2 — Preservation
34+
| §7.4, `thm:preservation`
35+
| `src/abi/WokeLang/Safety.idr`, `preservation`
36+
37+
| Theorem 3 — Non-Evasion
38+
| §7.4, `thm:non-evasion`
39+
| `src/abi/WokeLang/Safety.idr`, `nonEvasion`, `consentBodyRequiresLog`
40+
41+
|===
42+
43+
All four files use `%default total` and contain no `believe_me`, `assert_total`,
44+
`postulate`, or `sorry`.
45+
46+
== Key proof strategies
47+
48+
=== T-Consent invariant fix
49+
50+
The paper's T-Consent rule (Definition 2) accepts an arbitrary consent context Σ,
51+
including the empty one. However, the Progress proof sketch (§7.4) implicitly
52+
assumes Σ is non-empty so that `E-ConsentGranted` or `E-ConsentDenied` can fire.
53+
The mechanisation strengthens T-Consent to require `Σ = h :: hs` (at least one
54+
handler in scope). This is the only deviation from the paper and closes a genuine
55+
gap in the original presentation.
56+
57+
=== Context exchange for substitution
58+
59+
The substitution lemma (`substLemma` in `Typing.idr`) is proved for *closed values*
60+
(values typed in the empty context `[]`). This is sufficient for Preservation
61+
because the whole term is typed in `[]`, so every function argument at a beta-redex
62+
is itself typed in `[]`.
63+
64+
The `TLam` case requires a *context exchange* lemma (`ctxExchange`): when the
65+
substitution variable `x` appears under a lambda binder `y ≠ x`, the two adjacent
66+
bindings `(y, τy) :: (x, τx) :: ctx` are swapped to `(x, τx) :: (y, τy) :: ctx`.
67+
This is justified by `exchangeLookup`, which shows that `decEq`-based lookup
68+
produces the same result in both orderings when `x ≠ y`.
69+
70+
=== Type change in Preservation
71+
72+
Consent gate steps change the static type:
73+
74+
* `SConsentGranted`: `Consent[τ] → τ` (`TCGranted`)
75+
* `SConsentDenied`: `Consent[τ] → TyDenied` (`TCDenied`)
76+
77+
All other rules preserve the type exactly (`TCRefl`). The `TypeChange` GADT
78+
captures this, and `preservation` returns `(τ' : Ty ** TypeChange τ τ' ** Typed [] sigma' e' τ')`.
79+
80+
=== Non-Evasion
81+
82+
`nonEvasion` proves Theorem 3 by *inversion*: matching on
83+
`Step (EOnlyIfOkay s e) H A e' H' A'` exhaustively enumerates the Step
84+
constructors. The only constructors whose `Step` head is `EOnlyIfOkay` are
85+
`SConsentGranted` and `SConsentDenied`; all others match different head forms
86+
and are therefore structurally absent. Both firing constructors append
87+
`ConsentEntry d s` to `A`, so `DecisionLogged A' s` holds immediately.
88+
89+
== Scope and limitations
90+
91+
* The mechanisation covers the core λ_consent calculus as defined in the paper.
92+
* The `EAttr` and `EFeel` extensions are included (reduction rules + preservation
93+
cases) but are not the focus of the three theorems.
94+
* Units-of-measure typing (§5) and gratitude blocks (§6) are not mechanised here;
95+
they are independent extensions that do not affect the consent gate soundness
96+
results.

opsm.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
#
4+
# wokelang package manifest — OPSM native.
5+
# This file makes wokelang a first-class OPSM/HFR package.
6+
7+
[package]
8+
name = "wokelang"
9+
description = "Wokelang compiler — general-purpose BEAM-friendly application language"
10+
license = "PMPL-1.0-or-later"
11+
authors = ["Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>"]
12+
keywords = ["wokelang", "hyperpolymath", "nextgen-languages", "compiler"]
13+
homepage = "https://github.com/hyperpolymath/nextgen-languages"
14+
repository = "https://github.com/hyperpolymath/nextgen-languages"
15+
forth = "wokelang"
16+
17+
[opsm]
18+
trust_level = "hyperpolymath"
19+
registry = "hf"
20+
21+
[dependencies]
22+
proven = { git = "https://github.com/hyperpolymath/proven", registry = "hf" }
23+
groove = { git = "https://github.com/hyperpolymath/groove", registry = "hf" }

src/abi/WokeLang/Calculus.idr

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
-- SPDX-License-Identifier: PMPL-1.0-or-later
2+
-- SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
3+
-- Calculus.idr - λ_consent calculus: syntax, types, values, handler algebra
4+
--
5+
-- Mechanises Definition 1 (Syntax) from §7 of arxiv-consent-aware-programming.tex.
6+
-- The calculus extends the simply-typed lambda calculus with four consent-specific
7+
-- forms: only_if_okay, consent_handler, attr, and feel.
8+
9+
module WokeLang.Calculus
10+
11+
%default total
12+
13+
-- ---------------------------------------------------------------------------
14+
-- Consent decisions
15+
-- ---------------------------------------------------------------------------
16+
17+
||| The binary decision a consent handler produces for any given prompt.
18+
||| Handlers are required to be total — they always return one of these.
19+
public export
20+
data ConsentDecision : Type where
21+
Granted : ConsentDecision
22+
Denied : ConsentDecision
23+
24+
-- ---------------------------------------------------------------------------
25+
-- Consent handlers (Definition 1, handler forms)
26+
-- ---------------------------------------------------------------------------
27+
28+
||| A consent handler maps a prompt string to a consent decision.
29+
||| Four canonical forms:
30+
||| HInteractive — defers to the user (modelled as Granted here)
31+
||| HPolicy p — applies a policy predicate
32+
||| HAutoGranted — always grants (auto(granted))
33+
||| HAutoDenied — always denies (auto(denied))
34+
public export
35+
data Handler : Type where
36+
HInteractive : Handler
37+
HPolicy : (String -> ConsentDecision) -> Handler
38+
HAutoGranted : Handler
39+
HAutoDenied : Handler
40+
41+
||| Apply a handler to a consent prompt string.
42+
||| This function is total by construction: every case returns a decision.
43+
public export
44+
applyHandler : Handler -> String -> ConsentDecision
45+
applyHandler HInteractive _ = Granted
46+
applyHandler (HPolicy p) s = p s
47+
applyHandler HAutoGranted _ = Granted
48+
applyHandler HAutoDenied _ = Denied
49+
50+
-- ---------------------------------------------------------------------------
51+
-- Types (Definition 1, type syntax)
52+
-- ---------------------------------------------------------------------------
53+
54+
||| λ_consent types.
55+
||| TyConsent τ — result gated behind a consent decision
56+
||| TyDenied — type of the `denied` terminal value
57+
public export
58+
data Ty : Type where
59+
TyInt : Ty
60+
TyStr : Ty
61+
TyBool : Ty
62+
TyFun : Ty -> Ty -> Ty
63+
TyConsent : Ty -> Ty
64+
TyDenied : Ty
65+
66+
-- ---------------------------------------------------------------------------
67+
-- Variable names
68+
-- ---------------------------------------------------------------------------
69+
70+
||| Variables are represented as natural numbers.
71+
public export
72+
Var : Type
73+
Var = Nat
74+
75+
-- ---------------------------------------------------------------------------
76+
-- Expressions (Definition 1, expression syntax)
77+
-- ---------------------------------------------------------------------------
78+
79+
||| λ_consent expression syntax.
80+
|||
81+
||| Core forms:
82+
||| EVar x — variable (de-Bruijn-named)
83+
||| ELam x τ e — lambda abstraction
84+
||| EApp f a — application
85+
||| ELit n — integer literal
86+
||| EAdd e1 e2 — integer addition (representative binary op)
87+
|||
88+
||| Consent-specific forms:
89+
||| EOnlyIfOkay s e — consent gate: only_if_okay s e
90+
||| EConsentHandler h e — handler install: consent_handler h e
91+
||| EAttr p d e — attribution annotation: attr p d e
92+
||| EFeel f e — empathy annotation: feel f e (phantom at runtime)
93+
|||
94+
||| Terminal values:
95+
||| EDenied — the `denied` terminal produced by a denied consent gate
96+
public export
97+
data Expr : Type where
98+
EVar : Var -> Expr
99+
ELam : Var -> Ty -> Expr -> Expr
100+
EApp : Expr -> Expr -> Expr
101+
ELit : Int -> Expr
102+
EAdd : Expr -> Expr -> Expr
103+
EOnlyIfOkay : String -> Expr -> Expr
104+
EConsentHandler : Handler -> Expr -> Expr
105+
EAttr : String -> String -> Expr -> Expr
106+
EFeel : String -> Expr -> Expr
107+
EDenied : Expr
108+
109+
-- ---------------------------------------------------------------------------
110+
-- Values (Definition 1, value syntax)
111+
-- ---------------------------------------------------------------------------
112+
113+
||| Values are fully-reduced expressions: lambdas, literals, and denied.
114+
||| `denied` is a value because E-ConsentDenied produces it as a terminal.
115+
public export
116+
data IsValue : Expr -> Type where
117+
VLam : IsValue (ELam x τ e)
118+
VLit : IsValue (ELit n)
119+
VDenied : IsValue EDenied
120+
121+
-- ---------------------------------------------------------------------------
122+
-- Capture-avoiding substitution
123+
-- ---------------------------------------------------------------------------
124+
125+
||| e[v/x] — substitute v for x in e.
126+
||| Precondition (caller's responsibility): v is closed.
127+
||| Lambda bodies where the binder shadows x are left unchanged.
128+
public export
129+
subst : (x : Var) -> (v : Expr) -> (e : Expr) -> Expr
130+
subst x v (EVar y) =
131+
case decEq x y of
132+
Yes _ => v
133+
No _ => EVar y
134+
subst x v (ELam y τ body) =
135+
case decEq x y of
136+
Yes _ => ELam y τ body -- x is shadowed; do not descend
137+
No _ => ELam y τ (subst x v body)
138+
subst x v (EApp f a) = EApp (subst x v f) (subst x v a)
139+
subst _ _ (ELit n) = ELit n
140+
subst x v (EAdd e1 e2) = EAdd (subst x v e1) (subst x v e2)
141+
subst x v (EOnlyIfOkay s e) = EOnlyIfOkay s (subst x v e)
142+
subst x v (EConsentHandler h e) = EConsentHandler h (subst x v e)
143+
subst x v (EAttr p d e) = EAttr p d (subst x v e)
144+
subst x v (EFeel f e) = EFeel f (subst x v e)
145+
subst _ _ EDenied = EDenied
146+
147+
-- ---------------------------------------------------------------------------
148+
-- Audit log
149+
-- ---------------------------------------------------------------------------
150+
151+
||| A single entry in the audit accumulator A.
152+
||| ConsentEntry records a handler's decision for a given prompt.
153+
||| AttrEntry records attribution (person, description) when attr is evaluated.
154+
public export
155+
data LogEntry : Type where
156+
ConsentEntry : ConsentDecision -> String -> LogEntry
157+
AttrEntry : String -> String -> LogEntry
158+
159+
||| The audit accumulator A — an ordered sequence of log entries.
160+
public export
161+
AuditLog : Type
162+
AuditLog = List LogEntry

0 commit comments

Comments
 (0)