You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: AGENTS.md
+35-4Lines changed: 35 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -737,6 +737,37 @@ function sendWithStdioBackpressure(
737
737
738
738
---
739
739
740
+
## ADR-0019: Durable Command Journal Foundation
741
+
742
+
**Status:** Accepted (2026-03-02)
743
+
744
+
Full ADR: `docs/adr/0019-durable-command-journal-foundation.md`
745
+
746
+
### The Invariant
747
+
748
+
> Explicit client-provided command IDs must remain deterministic across process restarts.
749
+
750
+
### Foundation Rules
751
+
752
+
1.**Append lifecycle durably** - `command_accepted`, `command_started`, `command_finished` are persisted in append-only JSONL when `durableJournal.enabled=true`
753
+
2.**Per-lane monotonic sequence** - each record carries `laneSequence`, resumed from max seen value during startup rehydration
754
+
3.**Recover in-flight explicitly** - explicit IDs left in accepted/started state at crash are deterministically marked failed and written as synthetic recovery `command_finished` records
755
+
4.**Conservative schema policy** - malformed or unsupported schema versions are skipped and counted; no implicit migration in foundation
756
+
5.**Bound observability payloads** - startup recovery and history query responses are bounded with truncation metadata
7.**Redaction hooks at durability seams** - `durableJournal.redaction.beforePersist` and `.beforeExport` support policy-driven data minimization
759
+
760
+
### Key Properties
761
+
762
+
1.**Feature-flagged rollout** - defaults off (`durableJournal.enabled=false`) for rollback safety
763
+
2.**Replay continuity** - recovered explicit outcomes are rehydrated into replay store before serving commands
764
+
3.**Failure-path observability** - `get_startup_recovery` and `get_command_history` remain usable for diagnostics when append strict mode latches durable state failed
765
+
4.**Bounded introspection** - `get_command_history` provides filtered journal access (session filter, command ID, time window) with capped response size
766
+
5.**Retention + compaction scaffold** - `durableJournal.retention.{maxEntries,maxAgeMs,maxBytes}` prunes stale terminal outcomes while preserving in-flight recovery semantics
767
+
6.**Chaos-hardened malformed handling** - partial/truncated lines are safely skipped during recovery and compaction without corrupting retained replay semantics
1. No global total ordering (lane determinism only).
339
340
2. Timeout does not prove cancellation completed.
340
-
3. Durable replay beyond process lifetime is partially implemented behind a feature flag (`durableJournal.enabled`); full history/replay APIs remain Level 4 work.
341
+
3. Durable replay beyond process lifetime is feature-flagged (`durableJournal.enabled`). `get_command_history` is available for bounded journal introspection; deterministic replay/export tooling remains Level 4 work.
341
342
342
343
---
343
344
@@ -360,7 +361,39 @@ Emitted on connection before any other messages. Announces server capabilities.
360
361
361
362
Clients SHOULD check `protocolVersion` for compatibility.
362
363
363
-
### 16.2 `server_shutdown`
364
+
### 16.2 `startup_recovery_summary`
365
+
366
+
Optional convenience event carrying the same payload schema as `get_startup_recovery`.
367
+
368
+
By default, servers MAY redact sensitive fields in this event (for example: `journalPath`, `initializationError`, `recoveredOutcomeIds`, `recoveredInFlight`). Full diagnostics remain available via explicit `get_startup_recovery` requests.
369
+
370
+
```json
371
+
{
372
+
"type": "startup_recovery_summary",
373
+
"data": {
374
+
"enabled": true,
375
+
"initialized": true,
376
+
"initState": "ready",
377
+
"initializationError": "Initialization failed (details redacted; call get_startup_recovery for full diagnostics)",
378
+
"journalPath": "[redacted]",
379
+
"schemaVersion": 1,
380
+
"entriesScanned": 0,
381
+
"malformedEntries": 0,
382
+
"unsupportedVersionEntries": 0,
383
+
"recoveredOutcomes": 0,
384
+
"recoveredOutcomeIds": [],
385
+
"recoveredOutcomeIdsTruncated": false,
386
+
"recoveredInFlightFailures": 0,
387
+
"recoveredInFlight": [],
388
+
"recoveredInFlightTruncated": false,
389
+
"maxItemsReturned": 100
390
+
}
391
+
}
392
+
```
393
+
394
+
This event is advisory. Clients SHOULD continue using `get_startup_recovery` as the canonical endpoint. Deployments that need full event detail can opt in via server configuration (`PiServerOptions.startupRecoverySummaryEvent.includeSensitiveData = true`).
395
+
396
+
### 16.3 `server_shutdown`
364
397
365
398
Emitted before server closes. Clients should expect connection termination.
366
399
@@ -388,11 +421,35 @@ Emitted before server closes. Clients should expect connection termination.
388
421
|`switch_session`| Subscribe to session |`{ sessionInfo }`|
389
422
|`get_metrics`| Server metrics | See `get_metrics` response |
390
423
|`health_check`| Health status |`{ healthy, issues, hasOpenCircuit, hasOpenBashCircuit }`|
|`list_stored_sessions`| List persisted sessions (ADR-0007) |`{ sessions: StoredSessionInfo[] }`|
392
427
|`load_session`| Load session from disk (ADR-0007) |`{ sessionId, sessionInfo }`|
393
428
394
429
> **Note:**`delete_session` unloads the session from server memory but does NOT delete the session file from disk. The session can be reloaded later via `load_session` or discovered via `list_stored_sessions`.
395
430
431
+
#### `get_command_history` request fields
432
+
433
+
```json
434
+
{
435
+
"type": "get_command_history",
436
+
"sessionIdFilter": "session-123",
437
+
"commandId": "cmd-42",
438
+
"fromTimestamp": 1772412000000,
439
+
"toTimestamp": 1772415600000,
440
+
"limit": 100
441
+
}
442
+
```
443
+
444
+
-`sessionIdFilter` (optional): exact match on journal entry `sessionId`
445
+
-`commandId` (optional): exact match on journal entry command ID
446
+
-`fromTimestamp` / `toTimestamp` (optional): inclusive time bounds on `recordedAt`
447
+
-`limit` (optional): max entries returned (default `100`, hard max `500`)
448
+
449
+
Entries are returned in append order and may be truncated when `limit` is reached.
450
+
Servers MAY also apply internal scan guardrails (line/time budget) and set `truncated: true`
0 commit comments