Skip to main content

Modal-Temporal Architectural Calculus (MTAC)

Static architectural typing answers what is permitted right now. Real systems also need answers to when was this decided?, who saw it?, and under what modality does it hold?. The Modal-Temporal Architectural Calculus (MTAC) is ATS-V's extension into time, observer-roles, and modal qualification.

MTAC adds primitives the static checker uses to enforce a new class of anti-patterns — defects that involve when and by whom rather than what. The six MTAC anti-patterns (AP-027 .. AP-032) live in this layer.

This page covers the primitives, the modal operators, the five canonical observer roles, and how the audit gates consume them.

1. Why a temporal/modal layer?

Three concrete bug classes that pure static typing cannot catch:

1.1 Temporal inconsistency

An architectural invariant fails to hold across two sampled time-points. The cog's declared properties are not stable in time; what was true at t₀ no longer holds at t₁.

Example: a cog's requires set at the start of a session includes Read(Session), but at end of session the same cog reads Persist(Session) — the invariant "this cog only reads ephemeral state" was true at t₀, false at t₁.

This is AP-027 TemporalInconsistency.

1.2 Counterfactual brittleness

A verdict is fragile under a counterfactual decision swap. The same scenario with one different decision flips the verdict; the architecture is not robust to small variations in decision-making.

Example: "the audit passes if we choose AES-256, but fails if we choose AES-128 with the same threat model" — the verdict is brittle relative to a parameter the threat model treats as free.

This is AP-028 CounterfactualBrittleness.

1.3 Universal-property violation

A universal-property uniqueness claim is made without a uniqueness witness. "There exists a unique X satisfying P" — without exhibiting the witness, the claim is unverifiable.

Example: a cog claims "the Frobenius adjunction has unique unit and counit witnesses for our category" without exhibiting the natural transformations.

This is AP-030 UniversalPropertyViolation.

2. The MTAC primitive set

Verum's kernel ships seven MTAC primitives:

public type TimePoint is { value: Int };

public type Decision is {
point: TimePoint,
by_observer: Observer,
proposition: ArchProposition,
modality: ModalAssertion,
};

public type Observer is
| Architect
| Auditor
| Developer
| Operator
| Adversary;

public type ModalAssertion is
| Necessarily(prop: ArchProposition) // □ P
| Possibly(prop: ArchProposition) // ◇ P
| Before(point: TimePoint, prop: ArchProposition)
| After(point: TimePoint, prop: ArchProposition)
| Counterfactually(prop: ArchProposition)
| Intentionally(prop: ArchProposition);

public type ArchProposition is
| InvariantHolds(name: Text)
| CapabilityGranted(cap: Capability)
| LifecycleAt(stage: Lifecycle)
| Composes(a: Text, b: Text);

public type ArchEvolution is {
decisions: List<Decision>,
};

public type CounterfactualPair is {
base: ArchEvolution,
counterfactual: ArchEvolution,
};

public type AdjunctionWitness is {
left: Text,
right: Text,
proof: Text,
};

Seven primitives — together they form the vocabulary for expressing when, by whom, under which modality.

3. The five-roster observer set

MTAC's five canonical observer roles are exhaustive. Every architectural decision is attributable to exactly one of:

ObserverWhat they do
ArchitectMakes top-level architectural decisions; sets the canonical Shape policy.
AuditorVerifies decisions against the audit protocol; produces sign-off chronicles.
DeveloperImplements code under the architecture; refines invariants in working scope.
OperatorRuns the deployed system; observes runtime state.
AdversaryAttempts to violate invariants; the threat model's persona.

The roles are not a permission system — they are a narrative discipline. A decision tagged "by Operator" is distinguishable from a decision tagged "by Architect" even when they assert the same proposition; the audit chronicle records who made each call so reviewers can attribute responsibility.

Observer-role mismatches are caught by the CVE-AH band's AP-036 ObserverImpersonation, which fires when role A asserts in role B's register — for example, when a Developer-level decision claims Architect-level authority without an explicit promotion. Distinct from AP-029 MissedAdjoint: AP-029 fires on architectural decisions (refactoring claimed without its inverse adjoint), AP-036 on audit-chronicle prose where the observer register and the assertion content disagree.

4. The six modal operators

The six modal operators correspond to a small fragment of modal logic plus two architectural-domain operators:

OperatorReadingOperational meaning
Necessarily(P) (□P)"P must hold in every world"Strong invariant; load-bearing across all execution paths.
Possibly(P) (◇P)"P holds in some world"Reachability; useful for "this design accommodates X".
Before(t, P)"P holds before time t"Temporal precedence.
After(t, P)"P holds after time t"Temporal successor.
Counterfactually(P)"P would hold under the counterfactual"Bridges to the counterfactual engine.
Intentionally(P)"P holds by design"Distinguishes intentional from incidental properties.

The first four are standard modal logic. The last two are domain-specific: Counterfactually integrates MTAC with the counterfactual engine, and Intentionally distinguishes "deliberate design" from "accidental observation".

5. The diagnostic context

Verum's DiagnosticContext carries seven MTAC-specific fields beside the existing diagnostic state:

FieldWhat it tracks
current_observerWhich observer is currently driving the diagnostic.
time_point_lower_boundEarliest time the diagnostic applies to.
time_point_upper_boundLatest time the diagnostic applies to.
modal_assertionThe modal qualification active at this site.
propositionThe proposition the diagnostic concerns.
arch_evolutionThe decision sequence being checked.
counterfactual_pairThe (base, counterfactual) pair if any.

These fields are consumed by the six MTAC anti-pattern checks:

Anti-patternFields consumed
AP-027 TemporalInconsistencycurrent_observer, time_point_*, proposition
AP-028 CounterfactualBrittlenesscounterfactual_pair, modal_assertion
AP-029 MissedAdjointarch_evolution
AP-030 UniversalPropertyViolationmodal_assertion, proposition
AP-031 PhantomEvolutionarch_evolution, time_point_*
AP-032 YonedaInequivalentRefactorcounterfactual_pair, proposition, current_observer
AP-036 ObserverImpersonation (CVE-AH)current_observer

6. Adjunctions — Counterfactually made systematic

The Counterfactually(P) modal operator is the entry point to the adjunction analyzer, which recognises four canonical architectural adjunctions and reports their preservation/gain manifests:

  • Inline ⊣ Extract — folding a function inline (left adjoint) vs extracting a method to its own module (right adjoint).
  • Specialise ⊣ Generalise — making a generic interface concrete (left) vs lifting a concrete one to generic (right).
  • Decompose ⊣ Compose — splitting a cog into sub-cogs (left) vs merging sub-cogs into one (right).
  • Strengthen ⊣ Weaken — tightening a precondition (left) vs relaxing one (right).

For each recognised pair, the analyzer produces a preservation/gain manifest: which invariants are conserved across the move, which obligations are lifted, which are acquired. Audit chronicles archive these manifests so the project's architectural-evolution history is itself auditable.

7. The MTAC anti-pattern band

The six MTAC anti-patterns (AP-027 .. AP-032) all fire at the bundle phase — they require the full project graph as input, including the temporal order of decisions in the audit chronicle. They cannot fire during incremental compilation.

A summary:

  • AP-027 TemporalInconsistency — an architectural invariant fails to hold across two sampled time-points; the cog's declared properties are not stable in time.
  • AP-028 CounterfactualBrittleness — a verdict is fragile under a counterfactual decision swap (the same scenario with one different decision flips the verdict).
  • AP-029 MissedAdjoint — a refactoring is claimed without its inverse adjoint pair documented; the move is irreversible.
  • AP-030 UniversalPropertyViolation — a universal-property uniqueness claim is made without a uniqueness witness.
  • AP-031 PhantomEvolution — the declared evolution path passes through an unsatisfiable trigger (a step depends on conditions that cannot be reached).
  • AP-032 YonedaInequivalentRefactor — a refactoring claimed observer-equivalent under Yoneda actually changes the observer-functor; the shapes look the same but observers disagree.

Observer role-register mismatches (Developer asserting in Architect register, etc.) are caught by the CVE-AH band's AP-036 ObserverImpersonation, not by an MTAC anti-pattern — the distinction matters because AP-036 fires on prose-level register collisions while the MTAC band fires on architectural decisions.

Each is fully specified in Modal-temporal anti-patterns.

8. Worked example — a temporal contract

A real example: a key-rotation system declares that every key must be rotated within 90 days of its creation. The MTAC encoding:

@arch_module(
lifecycle: Lifecycle.Theorem("v1.0"),
@mtac(
decision: Decision {
point: TimePoint { value: 0 }, // creation time
by_observer: Observer.Operator,
proposition: ArchProposition.InvariantHolds("key_rotated"),
modality: ModalAssertion.Before(
point: TimePoint { value: 90 * 86400 }, // 90 days later
prop: ArchProposition.InvariantHolds("key_rotated"),
),
}
),
)
module my_app.crypto.key_rotation;

The annotation reads: the Operator is responsible for ensuring that the key_rotated invariant holds before the 90-day deadline. The audit gate checks:

  • The Decision has a by_observer (it does — Operator). ✓ AP-028 doesn't fire.
  • The modal is Before(90d, P) — temporally well-founded. ✓ AP-031 doesn't fire.
  • The proposition is consistent across the project. ✓ AP-030 doesn't fire.

If the Operator fails to rotate within 90 days, runtime observation will show the invariant violated after the deadline — but the architectural type system has already documented the obligation. The temporal contract is part of the audit chronicle.

9. Why MTAC is part of ATS-V, not the property system

A natural question: why is MTAC architectural rather than per- function? Two reasons:

  1. Granularity. MTAC tracks decisions across the project's lifetime — a decision made at v1.0 may still be load-bearing at v3.5. Per-function modal annotations would multiply the annotation burden without adding precision.
  2. Observer-attribution. The five-roster observer set is a narrative concept that applies at the cog level (or project level), not the function level. The Architect role does not vary per function.

MTAC therefore lives in the architectural surface, where decisions are declared once per cog and attributed once per decision. The narrative discipline scales.

10. Cross-references