Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions docs/calendar-signed-history.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Opt-in calendar signed history

`shuttle.calendar_history.CalendarScheduledHistory` is separate from the fixed
`ScheduledHistory` path. It adds no CLI, timer, polling, dispatch, private-key
access, graph mutation, profile or runtime activation. The fixed reader and its
9l1 contract are unchanged.

Pin immutable scope, public key/registration/identity graph, weekday (Monday=0),
HH:MM local time, IANA zone, activation boundary, signed completion budget and
maximum start lateness. Supply current declaration eligibility through the same
bounded reader contract. The caller must establish declaration coverage and the
natural origin of its successful control; signatures cannot establish those.

Construct `CalendarOccurrence(scheduled_at, next_deadline, zone)` from the
admitted Chaski WeeklyProjection seam: decode the exact `event_id` run slot,
use `next_checks[job]`, and retain the pinned projection timezone. The current
projection JSON does not itself expose a flat occurrence object. `scheduled_at` is the integer UTC slot;
`next_deadline` is the next **occurrence**, not the completion deadline. Chaski
selects occurrences. This reader checks each supplied occurrence against pinned
local weekday/time and verifies the next local-week date, without deriving a
604800-second cadence. Both sides of a DST overlap and nonexistent local times
refuse. An occurrence whose following date is ambiguous also refuses.

Call `measure(now=utc_integer, occurrence=target, positive=prior_occurrence)`.
A stale target at or beyond next occurrence plus completion budget is UNKNOWN.
Before the target completion budget expires, it returns NOT_DUE. At the exact
budget boundary it reads only the pinned identity graph and explicit monthly
run graph for two deterministic run identities. A successful signed control is
required before a missing target can be MISSED. SUCCEEDED/FAILED/HELD/LATE remain
separate; malformed, partial, duplicated, truncated or unavailable evidence is
UNKNOWN. Exceptions report only their class, never remote text.

Target and control must cover the same month, including their start and
completion windows. October26 to November2 crosses months and is UNKNOWN,
even though the UTC occurrence arithmetic is valid. Refresh the retained
control each month. Never broaden a query or substitute another graph after a
zero. The reader reuses existing public registration/signature/event verification
through composition; it never calls the fixed reader's cadence-based measure.
A private compatibility interval used for verifier construction is not evidence
of calendar coverage and is never used for slot selection.

The seam is explicit input, not authenticated producer provenance. Runtime
admission must bind the actual Chaski projection, zone database, declaration,
config and scope. Query and local clocks are caller responsibilities. There is
no ingestion timestamp: newly published on-time signed completion can resolve
MISSED but cannot reconstruct when publication arrived. This is the latest
supplied deadline window, not an audit of every historic slot.

## Demonstration

Run `python scripts/demo-calendar-history.py`. It uses temporary fixture keys
and explicitly mocked graph transport. Watch missing target become MISSED only
with a valid signed control, then SUCCEEDED after signed publication; damaged
control becomes UNKNOWN. The October26 occurrence has a next UTC difference of
608400 seconds across DST. This proves source behavior, not installation,
natural firing or alert delivery. The recording and timing live in
`docs/demos/calendar-history.log` and `.timing`; download and use `scriptreplay`.
9 changes: 9 additions & 0 deletions docs/demos/calendar-history.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Script started on 2026-10-20 23:37:12-04:00 [COMMAND="python3 scripts/demo-calendar-history.py" <not executed on terminal>]
Explicit occurrence: CalendarOccurrence(scheduled_at=1793020620, next_deadline=1793629020, zone='America/New_York')
UTC difference: 608400
Signed control, missing target: MISSED
Signed target: SUCCEEDED
Empty history: UNKNOWN
Temporary keys + mocked transport only; not natural/runtime acceptance.

Script done on 2026-10-20 23:37:12-04:00 [COMMAND_EXIT_CODE="0"]
4 changes: 4 additions & 0 deletions docs/demos/calendar-history.timing
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0.078410 141
0.001954 40
0.007222 26
0.000189 97
29 changes: 29 additions & 0 deletions scripts/demo-calendar-history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""Real temporary fixture signatures, mocked graph; no runtime action."""
from pathlib import Path
import sys
import warnings

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from tests.test_calendar_history import CalendarHistoryTests


def main():
warnings.simplefilter('ignore', ResourceWarning)
fixture = CalendarHistoryTests()
fixture.setUp()
try:
print('Explicit occurrence:', fixture.target)
print('UTC difference:', fixture.target.next_deadline-fixture.target.scheduled_at)
print('Signed control, missing target:', fixture.measure()['verdict'])
fixture.publish(fixture.slot)
print('Signed target:', fixture.measure()['verdict'])
fixture.f.facts.clear()
print('Empty history:', fixture.measure()['verdict'])
print('Temporary keys + mocked transport only; not natural/runtime acceptance.')
finally:
fixture.doCleanups()


if __name__ == '__main__':
main()
130 changes: 130 additions & 0 deletions shuttle/calendar_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Opt-in calendar signed history; no timer, private key or mutation.

Chaski owns occurrence selection. This reader validates explicit occurrence
coverage against pinned weekly policy, then reuses fixed-reader signature
verification without calling its fixed-cadence measure method.
"""
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
import re
from zoneinfo import ZoneInfo

from . import export, windows
from .scheduled import ScheduledRefused, _rows
from .scheduled_history import ScheduledHistory


@dataclass(frozen=True)
class CalendarOccurrence:
scheduled_at: int
next_deadline: int
zone: str


class CalendarScheduledHistory:
"""Explicit Chaski WeeklyProjection seam; next_deadline is next occurrence.

deadline_s is the separate signed completion budget. Supplied occurrences
are not authenticated by this class; the caller must retain their admitted
Chaski/declaration provenance and the control's natural origin.
"""
def __init__(self, binding, *, weekday, local_time, zone, effective_from_s,
public_key, registration, identity_graph, deadline_s,
max_lateness_s, eligible, query=None):
if (type(weekday) is not int or not 0 <= weekday <= 6 or
type(local_time) is not str or
not re.fullmatch(r'(?:[01][0-9]|2[0-3]):[0-5][0-9]', local_time) or
type(zone) is not str or not 0 < len(zone) <= 255):
raise ScheduledRefused('pinned weekday/HH:MM/IANA zone required')
self.zone = ZoneInfo(zone)
self.zone_name, self.weekday = zone, weekday
self.hour, self.minute = map(int, local_time.split(':'))
self.effective = effective_from_s
# Only reuse exact public-key/registration/event verifiers. The fixed
# cadence below is NEVER used to derive or validate calendar coverage.
self.verifier = ScheduledHistory(binding, public_key=public_key,
registration=registration, identity_graph=identity_graph,
anchor_s=effective_from_s, interval_s=60, effective_from_s=effective_from_s,
deadline_s=deadline_s, max_lateness_s=max_lateness_s,
eligible=eligible, query=query)

def _utc(self, day):
local = datetime(day.year, day.month, day.day, self.hour, self.minute)
candidates = set()
for fold in (0, 1):
utc = local.replace(tzinfo=self.zone, fold=fold).astimezone(timezone.utc)
if utc.astimezone(self.zone).replace(tzinfo=None) == local:
candidates.add(int(utc.timestamp()))
if len(candidates) != 1:
raise ScheduledRefused('gap or overlap in calendar occurrence')
return candidates.pop()

def _occurrence(self, value):
if (type(value) is not CalendarOccurrence or value.zone != self.zone_name or
any(type(s) is not int or not 0 <= s <= 253402214400
for s in (value.scheduled_at, value.next_deadline)) or
value.scheduled_at < self.effective):
raise ScheduledRefused('bounded pinned-zone occurrence required')
day = datetime.fromtimestamp(value.scheduled_at, self.zone).date()
if (day.weekday() != self.weekday or self._utc(day) != value.scheduled_at or
self._utc(day + timedelta(days=7)) != value.next_deadline):
raise ScheduledRefused('canonical occurrence and next local week required')
return value.scheduled_at

def measure(self, *, now, occurrence, positive):
"""Latest deadline-passed explicit occurrence, never fixed-week inference."""
slot = None
v = self.verifier
try:
if type(now) is not int or not 0 <= now <= 253402214400:
raise ScheduledRefused('bounded integer UTC clock required')
slot = self._occurrence(occurrence)
control = self._occurrence(positive)
if (export.entity_ns(), windows.window_ns()) != (v.namespace, v.window_namespace):
raise ScheduledRefused('namespace changed during measurement')
if now < slot:
raise ScheduledRefused('future occurrence does not cover current time')
if control >= slot:
raise ScheduledRefused('prior natural signed positive control required')
if now >= occurrence.next_deadline + v.deadline:
raise ScheduledRefused('stale occurrence does not cover current deadline')
month = v._month(slot)
# Cover the entire signed start/completion windows, not merely the
# scheduled instants. Never broaden into another graph on a zero.
if any(v._month(s + offset) != month
for s in (control, slot) for offset in (0, v.lateness, v.deadline)):
raise ScheduledRefused('same-month control and completion coverage required')
if now < slot + v.deadline:
return {'verdict': 'NOT_DUE', 'slot': None}
if any(v.eligible(dict(v.binding), s) is not True for s in (control, slot)):
raise ScheduledRefused('current declaration eligibility unavailable')
a = export.AEGIS
registration = _rows(v.query(
f'SELECT ?verifier ?pk ?workflow (isIRI(?workflow) AS ?scopeIRI) WHERE {{ '
f'<{v.registration}> a ?type ; <{a}verifier> ?verifier ; '
f'<{a}publicKey> ?pk ; <{a}attests> ?workflow . '
f'FILTER(?type = <{a}VerifierRegistration>) }} LIMIT 8',
graph=v.identity_graph), 8)
if registration != [{'verifier': v.binding['agent'], 'pk': v.public_key,
'workflow': v.binding['workflow'], 'scopeIRI': True}] or \
registration[0].get('scopeIRI') is not True:
raise ScheduledRefused('exact public registration unavailable')
runs = [v._run(s) for s in (control, slot)]
rows = _rows(v.query(
'SELECT (STR(?event) AS ?ev) (STR(?predicate) AS ?p) '
'(STR(?raw) AS ?value) (isIRI(?raw) AS ?iri) '
'(STR(DATATYPE(?raw)) AS ?datatype) WHERE { VALUES ?run { '
+ ' '.join('<'+export.run_iri(run)+'>' for run in runs) + ' } '
+ f'?event <{a}inRun> ?run ; ?predicate ?raw }} LIMIT 64',
graph=windows.window_iri(month)), 64)
grouped = [[r for r in rows if r.get('ev') in
(export.event_iri(run, 2), export.event_iri(run, 3))] for run in runs]
if sum(map(len, grouped)) != len(rows):
raise ScheduledRefused('unexpected event outside exact coverage')
if v._outcome(grouped[0], control, now) != 'SUCCEEDED':
raise ScheduledRefused('successful signed positive control unavailable')
return {'verdict': v._outcome(grouped[1], slot, now), 'slot': slot,
'positive_slot': control, 'graph': windows.window_iri(month),
'next_deadline': occurrence.next_deadline, 'zone': self.zone_name}
except Exception as exc:
return {'verdict': 'UNKNOWN', 'slot': slot, 'reason': type(exc).__name__}
Loading
Loading