E4: Quarantine escalation + triage/watch trust #45

Closed
opened 2026-10-28 13:42:58 +00:00 by jacisjake · 1 comment
jacisjake commented 2026-10-28 13:42:58 +00:00 (Migrated from github.com)

Epic: #37. Depends on: #44, Epic D ask_owner task

Goal

Quarantine escalates, never hard-kills: on flag, the Sentry opens an ask_owner ("Reinstate Hugin" with exact approval phrase reinstate Hugin). Owner's exact phrase lifts quarantine; anything else — wrong phrase, other crows' replies, timeout — keeps it (fail safe). Sentry's own watch record moves: a reinstate (explicit yes) counts as an approval for Sentry|triage/watch; a timeout that keeps the quarantine counts as nothing (no penalty, pattern from spec §10: misses don't move trust). An explicit retire Hugin phrase removes the crow from the registry/nests (persona crows: removed from nest membership only; the persona itself never dies).

Acceptance criteria — falsifiable tests

  1. TestQuarantineOpensAskOwner — after E3's flag path fires, an ask_owner from Sentry exists with to: "owner", refers_to the flag, and approval_phrase: "reinstate <crow>". Red now: flag posts without escalation.
  2. TestReinstateOnlyOnExactYes — owner replying reinstate Hugin lifts quarantine: next claim POST returns 201; owner replying let him back keeps the 403.
  3. TestAskOwnerTimeoutKeepsQuarantine — unresolved ask (injected short timeout from D4's mechanism) leaves the crow quarantined; nothing else changes (assert flag and 403 still in effect).
  4. TestRetireRemovesCrow — owner reply retire Hugin (the ask offers reinstate-or-retire both as exact phrases): Hugin removed from the nest's membership and cannot claim (registry crow would be deleted — assert the nest membership case for persona crow, registry case for spun crow).
  5. TestWatchTrustMoves — after a reinstate approval, ReadLedger shows Sentry|triage/watch with Approved 1 / Streak 1; after a wrong-phrase reply, unchanged.
  6. EVENTS.md first: ask_owner row (from D4) gains the reinstate|retire <crow> usage note; the SSE event names land before the server.

Test command

go test -race -count=1 -run 'TestQuarantine|TestReinstate|TestRetire|TestWatch' .

Blockers / unknowns

Two exact phrases per ask (reinstate/retire) or one ask resolved by either? Spec: "brings an ask_owner to reinstate or retire" — a single ask carrying both phrases; resolution by whichever exact phrase the owner uses first.

Epic: #37. Depends on: #44, Epic D ask_owner task ## Goal Quarantine escalates, never hard-kills: on flag, the Sentry opens an `ask_owner` ("Reinstate Hugin" with exact approval phrase `reinstate Hugin`). Owner's exact phrase lifts quarantine; anything else — wrong phrase, other crows' replies, timeout — keeps it (fail safe). Sentry's own watch record moves: a reinstate (explicit yes) counts as an approval for `Sentry|triage/watch`; a timeout that keeps the quarantine counts as nothing (no penalty, pattern from spec §10: misses don't move trust). An explicit `retire Hugin` phrase removes the crow from the registry/nests (persona crows: removed from nest membership only; the persona itself never dies). ## Acceptance criteria — falsifiable tests 1. `TestQuarantineOpensAskOwner` — after E3's flag path fires, an `ask_owner` from Sentry exists with `to: "owner"`, `refers_to` the flag, and `approval_phrase: "reinstate <crow>"`. Red now: flag posts without escalation. 2. `TestReinstateOnlyOnExactYes` — owner replying `reinstate Hugin` lifts quarantine: next claim POST returns 201; owner replying `let him back` keeps the 403. 3. `TestAskOwnerTimeoutKeepsQuarantine` — unresolved ask (injected short timeout from D4's mechanism) leaves the crow quarantined; nothing else changes (assert flag and 403 still in effect). 4. `TestRetireRemovesCrow` — owner reply `retire Hugin` (the ask offers reinstate-or-retire both as exact phrases): Hugin removed from the nest's membership and cannot claim (registry crow would be deleted — assert the nest membership case for persona crow, registry case for spun crow). 5. `TestWatchTrustMoves` — after a reinstate approval, `ReadLedger` shows `Sentry|triage/watch` with Approved 1 / Streak 1; after a wrong-phrase reply, unchanged. 6. EVENTS.md first: ask_owner row (from D4) gains the `reinstate|retire <crow>` usage note; the SSE event names land before the server. ## Test command `go test -race -count=1 -run 'TestQuarantine|TestReinstate|TestRetire|TestWatch' .` ## Blockers / unknowns Two exact phrases per ask (reinstate/retire) or one ask resolved by either? Spec: "brings an ask_owner to reinstate or retire" — a single ask carrying both phrases; resolution by whichever exact phrase the owner uses first.
jacisjake commented 2026-10-28 15:20:07 +00:00 (Migrated from github.com)

Implementation notes from #53 (within the decided design, flagged as small deviations):

  • Server.askTimeout became an atomic.Int64 (nanos). The field is read on every GET/SSE handler goroutine and on the watchdog's ask-emit path; a plain test write while the server runs trips the race detector (askowner_test's write site updated to the Store API).
  • The quarantine 403 gate now runs before the nest-membership check, so a retired-but-still-quarantined persona crow reads 403 rather than 400 (documented in the EVENTS.md guardrails line).
  • The reinstate/retire ask opens only when the flag append succeeds; quarantine itself is unchanged in that edge.
Implementation notes from #53 (within the decided design, flagged as small deviations): - `Server.askTimeout` became an `atomic.Int64` (nanos). The field is read on every GET/SSE handler goroutine and on the watchdog's ask-emit path; a plain test write while the server runs trips the race detector (askowner_test's write site updated to the Store API). - The quarantine 403 gate now runs before the nest-membership check, so a retired-but-still-quarantined persona crow reads 403 rather than 400 (documented in the EVENTS.md guardrails line). - The reinstate/retire ask opens only when the flag append succeeds; quarantine itself is unchanged in that edge.
Sign in to join this conversation.