Implement private Q2 signal contract and durable reference receiver

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
This commit is contained in:
tegwick 2026-09-06 19:17:59 +02:00
parent 31884baf4e
commit cecef79f31
14 changed files with 655 additions and 20 deletions

7
.gitignore vendored
View file

@ -3,3 +3,10 @@
.claude/*
!.claude/rules/
!.claude/rules/*.md
__pycache__/
*.pyc
*.db
*.db-journal
*.db-wal
*.db-shm

View file

@ -121,7 +121,56 @@ curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
---
{CREDENTIAL_ROUTING}
## Credential and access routing
**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect**
for inference. Run this check **before** requesting secrets, API keys, SSH access,
login tokens, or database passwords — in any repo, not only `ops-warden`.
ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every
other credential need belongs to another subsystem. **Do not** message
`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key.
### Lookup (do this first)
```bash
warden route find "<describe your need>" --json
warden route show <catalog-id> --json
```
Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`).
| Agent runtime | How to orient |
| --- | --- |
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=railiance-platform` is for coordination, not secret vending |
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership |
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
### Quick routing table
| I need… | Owner | ops-warden executes? |
| --- | --- | --- |
| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes**`warden sign` |
| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only |
| Login / OIDC / MFA | key-cape / Keycloak | No — route only |
| Authorization decision | flex-auth | No — route only |
| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` |
| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only |
### Anti-patterns (do not do these)
- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc.
- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist
- Pasting secrets into Git, State Hub, workplans, logs, or chat
### Other capabilities (reuse-surface)
Non-credential capabilities are usually discovered through **reuse-surface** federation
(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in
every repo's agent instructions because it is high-frequency, high-risk, and easy to
get wrong.
**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml`
<!-- REPO-AGENTS-EXTENSIONS -->
<!-- Append repo-specific agent instructions below this marker.
@ -197,3 +246,10 @@ To create a new workplan:
1. Write the file following the format above
2. Run `statehub fix-consistency` locally; ask the operator only if the CLI or
State Hub API is unavailable.
## Local implementation workflow — 2026-09-06
Python 3.11+ standard library; no install step. Run
`python3 -m unittest discover -s tests -v`. See docs/signal-contract.md for the
private CLI workflow. No live monitoring deployment target exists. RTEL-WP-0002
owns runtime/recipient acceptance; local inbox evidence is not notification proof.

View file

@ -3,8 +3,10 @@
Observability for Railiance providing monitoring, metrics, alerting etc to
enable the self-organizing control loop.
**Status: seeded.** `INTENT.md` is written; there is no implementation yet. The
live cluster currently has no monitoring namespace — this is greenfield.
**Status: private reference implementation.** A versioned signal contract,
SQLite receipt/inbox CLI and platform adapter are implemented and tested.
No monitoring runtime or notification schedule is installed by this work.
See [the contract and workflow](docs/signal-contract.md).
## Why this repo exists
@ -23,9 +25,11 @@ carries and surfaces their output.
3. [`AGENTS.md`](AGENTS.md) — session protocol
4. `workplans/`
## Open question
## Placement and verification
Stack-dimension placement (S3 platform capability, or a Quality-dimension
concern outside the stack) is **undecided** and belongs to `railiance-master`.
See `INTENT.md` → "Open Placement Question". It is called out explicitly so this
repo does not drift into the unplaced state `railiance-forge` ended up in.
Quality dimension, Q2 Observability, as recorded in INTENT/SCOPE. Monitoring
workload packaging remains outside this repo.
Run `python3 -m unittest discover -s tests -v` (Python 3.11+, no dependencies).
Live acceptance is tracked by RTEL-WP-0002-T04; local receipts are not external
operator delivery proof.

View file

@ -68,8 +68,10 @@ output.
## Current State
- Status: **seeded**`INTENT.md` written 2026-08-11, no implementation yet
- The live cluster has no monitoring namespace; this is greenfield
- Status: **private reference implementation** — contract, durable local inbox,
platform adapter and missing-emission checks implemented September 6
- No deployed monitoring runtime or external delivery acceptance established;
live work is tracked in RTEL-WP-0002-T04
- Placement: **Quality dimension, Q2 Observability** — canon defines Q2 as
"telemetry and monitoring: metrics, logs, traces". Ratification sits with
`railiance-master`; the question is answered, not open

View file

@ -8,7 +8,12 @@
| Kind | ID | Status | Lane | Source |
| --- | --- | --- | --- | --- |
| workplan | RTELE-WP-0001 | proposed | — | workplans/RTELE-WP-0001-statehub-bootstrap.md |
| task | RTELE-WP-0001-T01 | todo | — | workplans/RTELE-WP-0001-statehub-bootstrap.md |
| task | RTELE-WP-0001-T02 | todo | — | workplans/RTELE-WP-0001-statehub-bootstrap.md |
| task | RTELE-WP-0001-T03 | todo | — | workplans/RTELE-WP-0001-statehub-bootstrap.md |
| workplan | RTEL-WP-0002 | active | — | workplans/RTEL-WP-0002-signal-contract.md |
| workplan | RTELE-WP-0001 | finished | — | workplans/RTELE-WP-0001-statehub-bootstrap.md |
| task | RTEL-WP-0002-T01 | done | — | workplans/RTEL-WP-0002-signal-contract.md |
| task | RTEL-WP-0002-T02 | done | — | workplans/RTEL-WP-0002-signal-contract.md |
| task | RTEL-WP-0002-T03 | done | — | workplans/RTEL-WP-0002-signal-contract.md |
| task | RTEL-WP-0002-T04 | wait | — | workplans/RTEL-WP-0002-signal-contract.md |
| task | RTELE-WP-0001-T01 | done | — | workplans/RTELE-WP-0001-statehub-bootstrap.md |
| task | RTELE-WP-0001-T02 | done | — | workplans/RTELE-WP-0001-statehub-bootstrap.md |
| task | RTELE-WP-0001-T03 | done | — | workplans/RTELE-WP-0001-statehub-bootstrap.md |

View file

@ -0,0 +1,34 @@
{
"schema": "railiance-telemetry.stream.v1",
"stream": "railiance-platform.service-assurance",
"producer": "railiance-platform",
"recipient": "railiance-platform-operator",
"signals": [
"apps-pg.ready",
"apps-pg.backup",
"apps-pg.wal",
"apps-pg.restore",
"apps-pg.headroom",
"platform-pg.ready",
"platform-pg.backup",
"platform-pg.wal",
"platform-pg.restore",
"platform-pg.headroom",
"platform-pg-2.ready",
"platform-pg-2.backup",
"platform-pg-2.wal",
"platform-pg-2.restore",
"platform-pg-2.headroom",
"openbao.seal",
"openbao.snapshot",
"openbao.restore",
"offsite.upload",
"offsite.restore",
"eso.ready",
"eso.refresh",
"forgejo-db.restore"
],
"max_event_age_seconds": 900,
"heartbeat_seconds": 900,
"retention_days": 30
}

72
docs/signal-contract.md Normal file
View file

@ -0,0 +1,72 @@
# Private signal contract v1
Q2 owns carriage and visibility; producers own signal meaning. This initial
contract and CLI receiver are a local reference implementation, not a deployed
monitoring package or authenticated network service.
The stream contract pins producer, stream, exact signal names, intended local
recipient, freshness budget, heartbeat budget and retention. See
`contracts/platform-assurance.json`. Its 15-minute budgets and 30-day retention
are proposed defaults pending runtime/operator acceptance. The recipient
`railiance-platform-operator` names an intended role, not a confirmed person or
external messaging address. No notification is sent by these tools.
An event has exactly `schema` (`railiance-telemetry.signal.v1`), canonical UUID
`id`, `stream`, `producer`, timezone-aware `observed_at`, and `states`. Every
registered signal is present, with one of healthy/stale/missing/unavailable/
failed. There are no values, logs, URLs or free-text details. Duplicate JSON
keys, unknown fields, payloads over 32 KiB, wrong producer/stream/signal names,
future/expired events and out-of-order events are rejected. Local process/file
access is the trust boundary; producer names are not authentication. Do not
expose this CLI through an unauthenticated HTTP wrapper.
Acceptance commits an event and an operator notice in one SQLite transaction.
The returned receipt says `local-inbox-only`. Reusing an ID with identical data
is idempotent and does not advance liveness; changed data under that ID fails.
Equal timestamps under new IDs also fail. Retry the same event file, not a newly
translated event. Contract changes require an explicit database migration.
`check` detects both never-seen and expired emissions, based on original event
time. It suppresses repeated missing-emission notices until a new event arrives.
An independent accepted scheduler must run it. If that scheduler or this receiver
stops, these tools cannot report their own absence: runtime acceptance must add
an external receiver heartbeat check. Inbox reads never acknowledge implicitly.
`ack` records explicit local operator acknowledgment; it proves no external
notification. Run in a private directory (`umask 077`), one database per stream.
Retention is an explicit `prune` operation: retain at least the contract's days,
all unacknowledged notices/events and the latest replay/absence anchors. Pending
acknowledgments can extend retention indefinitely. At 10,000 events ingestion
fails rather than silently dropping evidence. Monitor capacity and prune through
the approved owner; no background expiry is installed. This is operational
storage, not immutable audit custody or independently backed-up evidence.
## Local workflow
Python 3.11+ standard library only. From the repository root:
```bash
python3 -m unittest discover -s tests -v
umask 077
mkdir -p /tmp/rtel-private
python3 scripts/platform_event.py --contract contracts/platform-assurance.json /path/to/fresh-platform-report.json > /tmp/rtel-private/event.json
python3 scripts/receiver.py --contract contracts/platform-assurance.json --database /tmp/rtel-private/receiver.db ingest /tmp/rtel-private/event.json
python3 scripts/receiver.py --contract contracts/platform-assurance.json --database /tmp/rtel-private/receiver.db check
python3 scripts/receiver.py --contract contracts/platform-assurance.json --database /tmp/rtel-private/receiver.db inbox
```
The platform adapter consumes the existing assurance evaluator report, preserves
`evaluated_at` as producer observation time, and carries each existing state.
Backup/restore age remains S3's classification; Q2 does not re-age those receipts.
The adapter does not change the source report's unmonitored/unsupported status.
Its signal inventory is pinned to platform source at initial review; changes
require coordinated contract review and tests.
## Required live acceptance
RTEL-WP-0002-T04 tracks runtime/package owner selection, private authenticated
execution, durable storage/backup, confirmed operator recipient, accepted cadence
and retention, independent receiver-watchdog delivery, and controlled failure /
producer-absence delivery acknowledged by that operator. Request scope already
covers reference implementation; no live scheduler or messaging authorization
is inferred. RPF-WP-0036-T04 stays open until those receipts exist.

View file

@ -0,0 +1,15 @@
{
"schema": "railiance-telemetry.local-acceptance.v1",
"recorded_at": "2026-09-06T17:06:47.655166+00:00",
"scope": "controlled local fixture; separate CLI processes; no production notification",
"acceptance": {
"status": "accepted",
"id": "f5dc8e8c-6462-4224-8be6-df440cb7d322",
"delivery": "local-inbox-only"
},
"recipient": "test-operator",
"failure_visible": true,
"missing_emission_visible": true,
"watchdog_invocation": "manual test, not installed cadence",
"temporary_storage_removed": true
}

View file

@ -0,0 +1,22 @@
# First receiving contract — 2026-09-06
Implemented under RTEL-WP-0002 in response to RPF-WP-0036-T04. The former
bootstrap plan is finished; its live residual is RTEL-WP-0002-T04.
Delivered strict metadata-only v1 stream/event contracts, a standard-library
SQLite receipt/inbox CLI, producer absence checks, explicit acknowledgment and
retention, and an adapter preserving platform assurance classifications and
evaluation time. Nine tests pass. A separate-process CLI fixture proves failure
and missing-emission visibility after restart; see the adjacent JSON receipt.
The proof uses a test recipient and temporary storage, not production delivery.
README now agrees with INTENT/SCOPE on Q2 placement and describes the implemented
workflow. Historical plan identity/UUIDs were preserved. Existing classification
says infotech while AGENTS/workplan metadata uses financials; no domain identity
migration was inferred from this implementation request.
Remaining live task: select package/runtime owner and private authenticated
execution; accept recipient, cadence, retention/storage/backup; install scheduler
and independent receiver watchdog; prove controlled failure and absence delivery
acknowledged by the actual operator. No public endpoint, deployment, outgoing
notification, credential retrieval or monitoring package was introduced.

44
scripts/platform_event.py Normal file
View file

@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Translate S3-owned state classifications without redefining their meaning."""
import argparse
import json
import sys
import uuid
from pathlib import Path
from receiver import read_json, instant, STATES
def translate(report, contract):
if (set(report) != {'schema', 'cluster_uid', 'evaluated_at', 'signals', 'transport',
'guarantees', 'threshold_status', 'healthy'}
or report['schema'] != 'railiance-platform.assurance-signal.v1'
or report['cluster_uid'] != 'a553c742-0115-43d4-99a4-a5ca56fe0786'
or report['transport'] != 'unmonitored'
or report['guarantees'] != 'unsupported'
or report['threshold_status'] != 'local-diagnostic-only'
or set(report['signals']) != set(contract['signals'])):
raise ValueError('unexpected platform report')
instant(report['evaluated_at'])
states = {}
for name, value in report['signals'].items():
if (set(value) != {'state', 'owner'} or value['state'] not in STATES
or value['owner'] not in ('railiance-platform', 'rapp-postgres')):
raise ValueError('unexpected signal')
states[name] = value['state']
if type(report['healthy']) is not bool or report['healthy'] != all(s == 'healthy' for s in states.values()):
raise ValueError('inconsistent summary')
return dict(schema='railiance-telemetry.signal.v1', id=str(uuid.uuid4()),
stream=contract['stream'], producer=contract['producer'],
observed_at=report['evaluated_at'], states=states)
if __name__ == '__main__':
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--contract', required=True, type=Path)
p.add_argument('report', type=Path)
a = p.parse_args()
try:
print(json.dumps(translate(read_json(a.report), read_json(a.contract))))
except (OSError, ValueError, KeyError, TypeError, AttributeError):
print(json.dumps({'status': 'rejected', 'error': 'invalid-platform-report'}))
sys.exit(2)

189
scripts/receiver.py Normal file
View file

@ -0,0 +1,189 @@
#!/usr/bin/env python3
"""Private reference receiver. No listener, credentials or notification sending."""
import argparse
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import sqlite3
import sys
import uuid
STATES = {'healthy', 'stale', 'missing', 'unavailable', 'failed'}
MAX_BYTES = 32768
def instant(value):
parsed = datetime.fromisoformat(value.replace('Z', '+00:00'))
if parsed.tzinfo is None:
raise ValueError('timezone required')
return parsed.astimezone(timezone.utc)
def strict_json(raw):
def pairs(items):
result = {}
for key, value in items:
if key in result:
raise ValueError('duplicate field')
result[key] = value
return result
if len(raw) > MAX_BYTES:
raise ValueError('payload too large')
return json.loads(raw, object_pairs_hook=pairs)
def read_json(path):
with path.open('rb') as stream:
return strict_json(stream.read(MAX_BYTES + 1))
def contract_check(c):
if set(c) != {'schema', 'stream', 'producer', 'recipient', 'signals',
'max_event_age_seconds', 'heartbeat_seconds', 'retention_days'}:
raise ValueError('contract fields')
if c['schema'] != 'railiance-telemetry.stream.v1':
raise ValueError('contract version')
for key in ('stream', 'producer', 'recipient'):
if not isinstance(c[key], str) or not c[key] or len(c[key]) > 100:
raise ValueError('identity missing')
if (not isinstance(c['signals'], list) or not c['signals']
or not all(isinstance(s, str) and 0 < len(s) <= 100 for s in c['signals'])
or len(c['signals']) != len(set(c['signals']))):
raise ValueError('signal inventory')
for key in ('max_event_age_seconds', 'heartbeat_seconds', 'retention_days'):
if type(c[key]) is not int or not 0 < c[key] <= 31536000:
raise ValueError('invalid budget')
class Receiver:
def __init__(self, database, contract):
contract_check(contract)
self.contract = contract
self.db = sqlite3.connect(database, timeout=10)
self.db.executescript('''
CREATE TABLE IF NOT EXISTS binding (id INTEGER PRIMARY KEY, digest TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS events (
id TEXT PRIMARY KEY, digest TEXT NOT NULL, emitted REAL NOT NULL,
received REAL NOT NULL, states TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS notices (
id INTEGER PRIMARY KEY, kind TEXT NOT NULL, created REAL NOT NULL,
event_id TEXT, acknowledged REAL);
''')
digest = hashlib.sha256(json.dumps(contract, sort_keys=True).encode()).hexdigest()
with self.db:
self.db.execute('INSERT OR IGNORE INTO binding VALUES (1, ?)', (digest,))
if self.db.execute('SELECT digest FROM binding WHERE id=1').fetchone()[0] != digest:
self.db.close()
raise ValueError('contract drift: explicit migration required')
def close(self):
self.db.close()
def ingest(self, event, now):
c = self.contract
if set(event) != {'schema', 'id', 'stream', 'producer', 'observed_at', 'states'}:
raise ValueError('event fields')
if (event['schema'] != 'railiance-telemetry.signal.v1'
or event['stream'] != c['stream'] or event['producer'] != c['producer']
or str(uuid.UUID(event['id'])) != event['id']):
raise ValueError('event identity')
states = event['states']
if (not isinstance(states, dict) or set(states) != set(c['signals'])
or not all(isinstance(s, str) and s in STATES for s in states.values())):
raise ValueError('signal scope')
emitted = instant(event['observed_at']).timestamp()
age = now.timestamp() - emitted
if not 0 <= age <= c['max_event_age_seconds']:
raise ValueError('stale or future event')
digest = hashlib.sha256(json.dumps(event, sort_keys=True).encode()).hexdigest()
with self.db:
self.db.execute('BEGIN IMMEDIATE')
existing = self.db.execute('SELECT digest FROM events WHERE id=?', (event['id'],)).fetchone()
if existing:
if existing[0] != digest:
raise ValueError('event id collision')
return {'status': 'duplicate', 'id': event['id']}
latest = self.db.execute('SELECT MAX(emitted) FROM events').fetchone()[0]
if latest is not None and emitted <= latest:
raise ValueError('out of order event')
if self.db.execute('SELECT COUNT(*) FROM events').fetchone()[0] >= 10000:
raise ValueError('capacity reached; explicit retention required')
self.db.execute('INSERT INTO events VALUES (?, ?, ?, ?, ?)',
(event['id'], digest, emitted, now.timestamp(), json.dumps(states, sort_keys=True)))
self.db.execute('INSERT INTO notices(kind, created, event_id) VALUES (?, ?, ?)',
('healthy' if all(s == 'healthy' for s in states.values()) else 'unhealthy',
now.timestamp(), event['id']))
return {'status': 'accepted', 'id': event['id'], 'delivery': 'local-inbox-only'}
def check(self, now):
"""Must be invoked by an independent scheduler; absence is not self-executing."""
with self.db:
self.db.execute('BEGIN IMMEDIATE')
last = self.db.execute('SELECT MAX(emitted) FROM events').fetchone()[0]
if last is not None and now.timestamp() < last:
raise ValueError('clock moved backwards')
absent = last is None or now.timestamp() - last > self.contract['heartbeat_seconds']
previous = self.db.execute('SELECT kind FROM notices ORDER BY id DESC LIMIT 1').fetchone()
if absent and (previous is None or previous[0] != 'missing-emission'):
self.db.execute('INSERT INTO notices(kind, created) VALUES (?, ?)',
('missing-emission', now.timestamp()))
return {'state': 'missing-emission' if absent else 'receiving',
'recipient': self.contract['recipient'], 'delivery': 'local-inbox-only'}
def inbox(self):
rows = self.db.execute('''SELECT n.id, n.kind, n.created, n.event_id, e.states
FROM notices n LEFT JOIN events e ON e.id=n.event_id
WHERE n.acknowledged IS NULL ORDER BY n.id''').fetchall()
return {'recipient': self.contract['recipient'], 'delivery': 'local-inbox-only',
'notices': [dict(id=r[0], kind=r[1], created_at=datetime.fromtimestamp(r[2], timezone.utc).isoformat(),
event_id=r[3], states=json.loads(r[4]) if r[4] else None) for r in rows]}
def ack(self, notice_id, now):
with self.db:
updated = self.db.execute('UPDATE notices SET acknowledged=? WHERE id=? AND acknowledged IS NULL',
(now.timestamp(), notice_id)).rowcount
return {'acknowledged': bool(updated), 'id': notice_id}
def prune(self, now):
cutoff = now.timestamp() - self.contract['retention_days'] * 86400
with self.db:
self.db.execute('BEGIN IMMEDIATE')
# Keep the latest observation and notice as replay/absence anchors.
self.db.execute('''DELETE FROM notices WHERE created < ? AND acknowledged IS NOT NULL
AND id != (SELECT MAX(id) FROM notices)''', (cutoff,))
deleted = self.db.execute('''DELETE FROM events WHERE received < ?
AND emitted != (SELECT MAX(emitted) FROM events)
AND id NOT IN (SELECT event_id FROM notices WHERE event_id IS NOT NULL)''', (cutoff,)).rowcount
return {'events_pruned': deleted}
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--contract', required=True, type=Path)
p.add_argument('--database', required=True, type=Path)
sub = p.add_subparsers(dest='command', required=True)
sub.add_parser('ingest').add_argument('event', type=Path)
for name in ('check', 'inbox', 'prune'):
sub.add_parser(name)
sub.add_parser('ack').add_argument('notice_id', type=int)
a = p.parse_args()
receiver = None
try:
receiver = Receiver(a.database, read_json(a.contract))
now = datetime.now(timezone.utc)
if a.command == 'ingest': result = receiver.ingest(read_json(a.event), now)
elif a.command == 'inbox': result = receiver.inbox()
elif a.command == 'ack': result = receiver.ack(a.notice_id, now)
else: result = getattr(receiver, a.command)(now)
print(json.dumps(result))
return 0
except (ValueError, TypeError, KeyError, AttributeError, OSError, sqlite3.Error):
print(json.dumps({'status': 'rejected', 'error': 'invalid-input-or-unavailable'}))
return 2
finally:
if receiver is not None: receiver.close()
if __name__ == '__main__':
sys.exit(main())

102
tests/test_receiver.py Normal file
View file

@ -0,0 +1,102 @@
from datetime import datetime, timezone, timedelta
import json
from pathlib import Path
import sys
import tempfile
import unittest
import uuid
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts'))
from receiver import Receiver, strict_json
from platform_event import translate
NOW = datetime(2026, 9, 6, tzinfo=timezone.utc)
class ReceiverTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.path = Path(self.temp.name) / 'receiver.db'
self.contract = dict(schema='railiance-telemetry.stream.v1', stream='test',
producer='platform', recipient='test-operator', signals=['db.restore'],
max_event_age_seconds=900, heartbeat_seconds=900, retention_days=30)
self.receiver = Receiver(self.path, self.contract)
self.addCleanup(lambda: self.receiver.close())
def event(self, state='healthy', when=NOW):
return dict(schema='railiance-telemetry.signal.v1', id=str(uuid.uuid4()),
stream='test', producer='platform', observed_at=when.isoformat(), states={'db.restore': state})
def test_failure_is_durable_and_addressed(self):
self.receiver.ingest(self.event('failed'), NOW)
self.receiver.close()
self.receiver = Receiver(self.path, self.contract)
inbox = self.receiver.inbox()
self.assertEqual(inbox['recipient'], 'test-operator')
self.assertEqual(inbox['notices'][0]['states'], {'db.restore': 'failed'})
self.assertEqual(inbox['notices'][0]['kind'], 'unhealthy')
self.assertTrue(self.receiver.ack(inbox['notices'][0]['id'], NOW)['acknowledged'])
self.assertEqual(self.receiver.inbox()['notices'], [])
def test_never_seen_and_stopped_producer_detected(self):
self.assertEqual(self.receiver.check(NOW)['state'], 'missing-emission')
self.receiver.check(NOW)
self.assertEqual(len(self.receiver.inbox()['notices']), 1)
self.receiver.ingest(self.event(), NOW)
self.assertEqual(self.receiver.check(NOW)['state'], 'receiving')
self.assertEqual(self.receiver.check(NOW + timedelta(seconds=901))['state'], 'missing-emission')
def test_replay_does_not_renew_heartbeat(self):
event = self.event()
self.receiver.ingest(event, NOW)
self.assertEqual(self.receiver.ingest(event, NOW + timedelta(seconds=899))['status'], 'duplicate')
self.assertEqual(self.receiver.check(NOW + timedelta(seconds=901))['state'], 'missing-emission')
with self.assertRaises(ValueError):
self.receiver.ingest(self.event(when=NOW - timedelta(seconds=1)), NOW)
def test_duplicate_id_changed_payload_rejected(self):
event = self.event(); self.receiver.ingest(event, NOW)
event['states']['db.restore'] = 'failed'
with self.assertRaises(ValueError): self.receiver.ingest(event, NOW)
def test_invalid_scope_future_stale_and_extra_data(self):
variants = [dict(producer='other'), dict(states={'other': 'healthy'}),
dict(states={'db.restore': 'secret'}), dict(password='PRIVATE_CANARY'),
dict(observed_at=(NOW + timedelta(seconds=1)).isoformat()),
dict(observed_at=(NOW - timedelta(seconds=901)).isoformat()),
dict(observed_at='2026-09-06T00:00:00')]
for variant in variants:
with self.subTest(variant=variant), self.assertRaises(ValueError):
self.receiver.ingest(dict(self.event(), **variant), NOW)
self.assertEqual(self.receiver.inbox()['notices'], [])
def test_contract_drift_refused(self):
with self.assertRaises(ValueError): Receiver(self.path, dict(self.contract, recipient='other'))
def test_retention_keeps_unacknowledged_and_latest_anchors(self):
self.receiver.ingest(self.event('failed'), NOW)
self.receiver.ingest(self.event(when=NOW + timedelta(seconds=1)), NOW + timedelta(seconds=1))
later = NOW + timedelta(days=31)
self.assertEqual(self.receiver.prune(later)['events_pruned'], 0)
first = self.receiver.inbox()['notices'][0]['id']
self.receiver.ack(first, later)
self.assertEqual(self.receiver.prune(later)['events_pruned'], 1)
self.assertEqual(self.receiver.check(later)['state'], 'missing-emission')
def test_duplicate_fields_and_oversize_rejected(self):
for raw in (b'{"a":1,"a":2}', b' ' * 32769):
with self.assertRaises(ValueError): strict_json(raw)
def test_adapter_keeps_time_and_owner_meaning(self):
report = dict(schema='railiance-platform.assurance-signal.v1',
cluster_uid='a553c742-0115-43d4-99a4-a5ca56fe0786', evaluated_at=NOW.isoformat(),
signals={'db.restore': {'state': 'stale', 'owner': 'railiance-platform'}},
transport='unmonitored', guarantees='unsupported', threshold_status='local-diagnostic-only', healthy=False)
event = translate(report, self.contract)
self.assertEqual(event['observed_at'], report['evaluated_at'])
self.assertEqual(event['states'], {'db.restore': 'stale'})
report['healthy'] = True
with self.assertRaises(ValueError): translate(report, self.contract)
if __name__ == '__main__': unittest.main()

View file

@ -0,0 +1,79 @@
---
id: RTEL-WP-0002
type: workplan
title: "Provide the Q2 receiving contract and prove signal delivery"
domain: financials
repo: railiance-telemetry
status: active
owner: codex
created: "2026-09-06"
updated: "2026-09-06"
related:
- RTELE-WP-0001
- RPF-WP-0036
state_hub_workstream_id: "08a5db92-7293-50d3-b589-55287b9850b3"
---
Bounded initial Q2 reference implementation for platform assurance. Producer
meaning stays in S3; deployable monitoring packaging stays with a selected
package owner. No public listener, credential custody or incident workflow.
## Define metadata-only receiving and retention contract
```task
id: RTEL-WP-0002-T01
status: done
priority: high
state_hub_task_id: "ac73553d-5bd4-5dff-8307-95a29bffe474"
```
Implemented `docs/signal-contract.md` and a pinned platform stream contract.
Exact identities/signals, finite states, payload limit, freshness, replay,
proposed retention and local recipient semantics are explicit. Live acceptance
of the proposed budgets and recipient remains T04, not implied by this schema.
## Implement durable receipt and operator inbox reference
```task
id: RTEL-WP-0002-T02
status: done
priority: high
state_hub_task_id: "dacc9e2d-9321-5d39-afc4-88326f5c6930"
```
SQLite acceptance and notices commit together; strict rejection, duplicate
idempotence, explicit acknowledgments and bounded retention are tested. This
is a local private CLI implementation; acceptance says local-inbox-only.
## Prove platform adaptation and absent-emission semantics locally
```task
id: RTEL-WP-0002-T03
status: done
priority: high
state_hub_task_id: "a8ed3588-4240-5278-b03f-04c3237a52f9"
```
Adapter preserves producer evaluation time and classifications. Tests prove
failure survives receiver restart, inbox addressing/acknowledgment, never-seen
and stopped-producer detection, replay refusal, wrong-scope rejection and
retention of unacknowledged evidence. Local simulated-time tests establish
implementation behavior, not actual scheduled notification delivery.
## Accept private runtime and controlled end-to-end delivery
```task
id: RTEL-WP-0002-T04
status: wait
priority: high
state_hub_task_id: "ceb14fdc-b3fd-5a22-870d-d835a9d52055"
```
Choose package/runtime execution owner with cluster/activity-core; confirm the
operator recipient, producer/watchdog cadence, retention/storage/backup and
private authenticated access. Install only through accepted authority. Prove a
controlled failure and stopped producer reach and are acknowledged by the named
operator, and receiver/scheduler failure is independently detectable. Preserve
receipts across restart; demonstrate capacity/retention handling. Only these
receipts can satisfy RPF-WP-0036-T04. No package repo name or deployment grant is
invented here. This live task holds the residual explicitly.

View file

@ -4,11 +4,11 @@ type: workplan
title: "Bootstrap State Hub integration"
domain: financials
repo: railiance-telemetry
status: proposed
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-11"
updated: "2026-08-15"
updated: "2026-09-06"
related:
- ADR-0008
- RMASTER-WP-0023
@ -25,14 +25,14 @@ seed the first *signal-contract* workplan. Do not deploy a monitoring
stack here. Placement is Quality / Q2 (INTENT/SCOPE). That is not an
open `railiance-master` question.
This id uses the historical `RAILIANCE-WP-` prefix. New telemetry
This id uses the historical `RTELE-WP-` prefix. New telemetry
workplans use `RTEL-WP-`. Do not change this file's `id` or hub UUID.
## Review Generated Integration Files
```task
id: RTELE-WP-0001-T01
status: todo
status: done
priority: high
state_hub_task_id: "4aca4677-6712-553b-ac26-0682fdf211de"
```
@ -46,7 +46,7 @@ open S3-vs-Quality question). Set the *next* workplan prefix to
```task
id: RTELE-WP-0001-T02
status: todo
status: done
priority: high
state_hub_task_id: "b6632ce1-dc84-532d-b277-adce8510a304"
```
@ -59,7 +59,7 @@ the first verification command.
```task
id: RTELE-WP-0001-T03
status: todo
status: done
priority: medium
state_hub_task_id: "4877f6e2-8298-5bab-b6a8-9a1ec1718289"
```
@ -78,3 +78,7 @@ checkout:
```bash
statehub fix-consistency
```
Completed September 6: reviewed integration and Q2 boundary, corrected README,
documented the standard-library test workflow and seeded RTEL-WP-0002. Live
runtime/notification acceptance remains explicitly in RTEL-WP-0002-T04.