Skip to content
Hive

hive · Daily activity digest

Plan

plan.md

On this page
  1. Goal Capsule
  2. Overview
  3. Product Contract
  4. Requirements Trace
  5. Scope Boundaries
  6. Planning Contract
  7. Implementation Units
  8. Verification Contract
  9. Risks
  10. Definition of Done

Hive Daily Activity Digest

Goal Capsule

Build a Hive-owned, durable daily activity record spanning every registered project, then expose that same record through a concise CLI, a complete authenticated Web view, and an opt-in Telegram recap. Preserve each day's configured IANA time-zone boundaries, keep closed records immutable, represent late knowledge as audited amendments, and remain useful when GitHub, PRDigest, or an individual project source is unavailable.

Success is one operator being able to answer what advanced, completed, failed, recovered, remains blocked, or needs input without reconstructing the day from task folders, status output, PRs, CI, and logs.

Overview

Hive already owns the durable ingredients for this feature: registered-project identity, append-only task journals, status and operational-state semantics, task creation timestamps, PR publication snapshots, per-source degradation, a daemon scheduler pattern, native task-answer routes, and a private single-operator Web boundary. The implementation should compose those ingredients into a new host-global projection rather than create a second workflow authority or revive the retired PRDigest-owned digest.

The record for the current local day is an open projection refreshed by the daemon or an explicit CLI mutation. The first materialization after its persisted ends_at closes the base record permanently. Facts observed later are appended as amendments carrying event time when known, observation time, source, and amendment time. Ordinary CLI/Web reads, Telegram delivery, and agent guidance consume this one projection; project filters are view-time filters and never fork or rewrite the global history.

Product Contract

Summary

This plan implements the full brainstorm scope by extending Hive's existing filesystem-ledger, status, daemon, CLI, and Rails patterns. Adjacent work is limited to filling authoritative activity-observation gaps needed by the digest; broad workflow refactors and a live PRDigest integration remain outside the implementation.

Problem Frame

Today, the operator must correlate multiple lossy or differently scoped surfaces to reconstruct a day. Live status emphasizes the present, task folders hold local history, PR and CI observations may be transient, and logs contain noise. None is a durable global day record with historical boundaries, source completeness, attention items, and direct action links.

The digest must therefore be a first-class Hive projection with an auditable lifecycle, not cached prose. Its value comes from materiality, truthful completeness, and reliable handoff to native task actions—not from narrating every event.

Requirements

  • R1 — Actor and authority: One authenticated Hive operator can read the global digest and history across every registered project. Hive owns the record; no team ACL or per-project reader policy is introduced.
  • R2 — Stable daily identity: Store one global record per effective local day using an explicitly persisted Hive-wide IANA zone. Each record owns its monotonic day identity, local_date, zone, UTC starts_at/ends_at, lifecycle, and close timestamp so later configuration changes do not move history or leave an instant unassigned.
  • R3 — Immutable close and audited amendment: A day may evolve while open. Once closed, its base is write-once; late facts and recovered gaps are append-only, idempotent amendments containing known event time, observed_at, source, amended_at, and the affected historical day.
  • R4 — Material activity: Include task creation; durable stage/state transitions; completions, archives, and merges; answered decisions; changed PR, review, or CI outcomes; meaningful failures and recoveries; and changed provider, authority, or capacity holds. Exclude polls, repeated diagnostics, log churn, and retries whose durable outcome did not change.
  • R5 — Work in progress and privacy: Include tasks touched during the day plus every task blocked or awaiting operator input at the boundary. Waiting items expose project, task identity, stage/state, waiting age, and native answer URL, but never question text or answer bindings.
  • R6 — Complete Web surface: Provide an authenticated selected-day global view with date navigation, project filtering, grouped material changes, attention items, source gaps, visible late amendments, and direct task/PR links.
  • R7 — Safe CLI and agent surface: hive digest prints today's concise digest by default and supports explicit date, project, JSON, and open-Web modes. Every read and filter is free of Telegram side effects; JSON is the stable agent contract.
  • R8 — Opt-in Telegram recap: Delivery is explicit-command or daemon owned, disabled by default, scheduled in the digest zone at a configurable hour defaulting to 09:00 for the preceding closed day, and silent for a successfully observed empty day.
  • R9 — PR boundary: Persist project/task identity, PR number and URL, state/draft state, head revision, checks, review and merge outcomes, and known timestamps from Hive-owned facts. GitHub is a fail-soft observation source: an outage creates a gap only when required PR evidence or required freshness cannot be established from Hive-owned facts. PRDigest is never required or authoritative.
  • R10 — Degraded-source truthfulness: Publish a usable record with partial completeness when any required source fails, naming the source, scope, bounded reason, freshness, and stable gap identity. Retry the unresolved source and append a traceable resolution amendment when it recovers.
  • R11 — Retention and pruning: Retain compact records indefinitely by default. An explicit digest-prune operation removes only the digest projection while preserving task, attempt, PR, delivery-deduplication, and audit evidence.
  • R12 — Operational usefulness: Preserve direct actions and enough compact context to establish what changed and whether attention is required, including an explicit successful empty state rather than an absent record.

Success Criteria

  • AE1 — Normal day: Several projects' material changes appear in one selected-day record, can be filtered without changing record identity, and preserve actionable task and PR links.
  • AE2 — Unanswered task: The item shows project, task, stage/state, and waiting age, omits the question, and opens the existing task-answer section where a fresh binding is obtained.
  • AE3 — Partial then recovered: Unavailable PR data produces a named gap without suppressing task activity; recovery appends a gap-resolution amendment to the historical day.
  • AE4 — Historical stability: A later time-zone change leaves existing boundaries byte-stable, and a delayed event appears as an amendment rather than a rewritten close.
  • AE5 — Empty and side-effect free: Web and explicit CLI reads show a successful empty record, scheduled Telegram sends nothing, and no hive digest read invokes Telegram.

Requirements Trace

Contract Primary implementation owner Required proof
R1, R6 U5 Authenticated global day route; date and project navigation; no per-project visibility branch
R2 U1, U3 IANA validation, DST boundary tests, persisted historical interval, zone-change stability
R3 U1, U3 Open-day updates, write-once close, idempotent append-only amendments, close/amend concurrency
R4 U2 Materiality matrix and owner-boundary observation tests; unchanged polls/retries excluded
R5 U2, U5 Boundary snapshot, durable waiting age, task-answer anchor, serializer/render assertions excluding questions
R7 U4 Text/JSON/date/project/open-Web tests and spies proving reads never deliver or mutate delivery state
R8 U6 Opt-in config, prior-day scheduling, DST/restart/retry behavior, empty suppression, send deduplication
R9 U2, U3 Required PR fields from Hive-owned evidence; cached/offline behavior; GitHub and PRDigest absence tests
R10 U2, U3, U6 Per-source failure isolation, gap schema, bounded retry, recovery amendment, partial notification labeling
R11 U3, U4 Indefinite default; dry-run and confirmed prune; evidence-preservation and tombstone tests
R12 U4, U5, U7 Concise attention-first output, direct links, explicit empty/partial distinctions, canonical agent guidance
AE1 U2, U4, U5 Multi-project fixture exercised through CLI JSON/text and Rails project filtering
AE2 U2, U5, U7 Redaction/privacy fixture plus browser handoff to #task-questions and canonical skill routing
AE3 U2, U3, U5, U6 Failed source, partial close/send, successful retry, visible amendment
AE4 U1, U3 Zone reconfiguration and delayed-event fixtures retain original base and boundaries
AE5 U3, U4, U5, U6 Complete-empty record on CLI/Web and zero Telegram calls/cursor advances

Scope Boundaries

In Scope

  • A versioned host-global digest store and schema under Hive's state home.
  • Zone-aware open, close, amendment, gap, recovery, and explicit prune lifecycles.
  • Authoritative collection from registered projects, including the minimum producer instrumentation needed for task creation, PR/check/review/merge changes, holds, failures, and recoveries.
  • Today/history reads in concise terminal text and stable JSON; date/project filtering and open-Web behavior.
  • An authenticated Rails selected-day view with attention-first hierarchy and native task-answer handoff.
  • Separate daemon close/retry and opt-in Telegram delivery lifecycles.
  • Documentation, a new ADR, canonical Hive skill guidance, generated skill parity, schemas, and test coverage.

Deferred to Follow-Up Work

  • A live PRDigest adapter or PRDigest-authored prose. Any future integration must be optional, provenance-tagged, and non-authoritative; V1 adds no unused enrichment schema or runtime hook.
  • Automatic age-based retention, storage quotas, bulk policy configuration, and scheduled pruning.
  • Automatic re-notification when a closed day gains amendments; amendments remain visible in CLI/Web and future explicit reads.
  • Agent-managed schedule mutation, delivery-status management, and an MCP-specific digest wrapper. The explicit v1 operator send/retry command remains in scope.
  • Backfill or synthesis of pre-feature historical days. Date reads return only persisted records and otherwise report missing or pruned; durable coverage begins when the feature is enabled.

Outside V1

  • Team identities, multi-operator tenancy, and per-project reader ACLs.
  • A general conversational Telegram agent or implicit delivery from read commands.
  • Making GitHub or PRDigest a hard dependency, or allowing either to overwrite Hive-owned facts.
  • Rewriting closed base records, recalculating historical boundaries, or deleting underlying task/attempt/PR evidence during digest pruning.
  • Release, publish, deployment, or version-selection work.

Planning Contract

Assumptions

These are conservative technical defaults selected in this non-interactive planning stage; implementation should flag any repository evidence that invalidates them before changing the product contract.

  • A1: Use a new global daily_digest: configuration block rather than reinterpreting the retired top-level digest: block rejected by ADR-040. daily_digest.enabled is validated and defaults to false, including after upgrade. Setup persists the detected host IANA zone; the existing hive migrate/hive migrate --all path backfills a missing zone, coverage-start instant, initial membership snapshot, and first interval before enabling digest work. If detection or initialization cannot complete, the daemon remains operational for unrelated automation, digest schedulers stay disabled, and digest commands return typed remediation rather than failing global config load or daemon startup.
  • A2: Ordinary CLI and Web reads are pure store reads. Only the daemon or explicit hive digest refresh materializes, catches up, closes, or amends feature-era records; --open-web is the sole read option that opens a browser, and --json and --open-web are mutually exclusive.
  • A3: Model three orthogonal axes: lifecycle (open, closed, or tombstoned pruned), effective completeness (complete or partial, derived from the immutable base plus amendments), and content (empty, non_empty, or unknown). missing is a reader outcome, not a persisted lifecycle; empty requires complete observation and no qualifying activity or boundary attention, while a partial record with no known items is unknown, never empty.
  • A4: Waiting age begins at the latest durable transition into the current blocked or unanswered state. If that transition cannot be established, show an unknown age and a source gap rather than deriving history from file mtime.
  • A5: A closed record with partial completeness is still eligible for the recap, with an incomplete-data label and Web link. A later amendment does not automatically send a second Telegram message.
  • A6: An ambiguous Telegram outcome is not automatically retried because Telegram cannot guarantee idempotency; it becomes a visible unknown delivery state recoverable only through the explicit v1 operator send/retry command.
  • A7: Explicit pruning targets only closed records before a supplied date, supports dry-run, rejects the current/open day, and retains a tombstone/audit receipt so an ordinary read does not silently recreate pruned history.
  • A8: The daemon refreshes the open day on a validated, configurable cadence defaulting to five minutes. Readers expose last_materialized_at and a named stale/materializer-unavailable gap when that freshness budget is exceeded.
  • A9: The atomic disabled-to-enabled transition requires and persists a coverage-start instant, initial project-membership snapshot, zone, and first interval. Disabling stops refresh, catch-up, close/recovery, and scheduled delivery without deleting records, frontiers, history, tombstones, or delivery receipts; pure CLI/Web reads of existing records remain available. Re-enabling resumes from the preserved frontier, while pre-coverage dates remain non-backfillable.

Key Technical Decisions

  • KTD1 — One host-global projection: Store one versioned aggregate per date beneath Hive::Paths.state_home, using the repository's atomic-file, content-derived identity, lock, fsync, and event-ledger patterns. Per-project task journals remain source evidence; no project owns the global day. Governs R1, R2, R3, R11.
  • KTD2 — Open base, immutable close, separate amendments: Atomically replace only an open base. Closing writes the canonical immutable base identity; every late fact, correction, or gap recovery is an idempotent append-only amendment. Governs R2, R3, R10.
  • KTD3 — Persisted, continuous calendar intervals: Resolve ordinary calendar days through TZInfo and assign facts by persisted half-open UTC intervals. A zone change becomes effective at the current record's immutable ends_at; the cutover record starts at that exact instant, uses the later of the previous label plus one day or the new zone's local date at cutover, and ends at the following midnight for that chosen label. Persist boundary_kind: zone_cutover, requested/effective instants, and any skipped zero-duration labels. This keeps lookup unambiguous and assigns every instant exactly once across westward, eastward, and date-line changes. Because a monotonic label can diverge from the new wall-clock date, default/“today” reads resolve the persisted interval containing now, “yesterday” and next/previous navigation walk the persisted interval sequence, and explicit labels remain stable record identifiers. Preserve existing host-local behavior for the unrelated answer-digest scheduler. Governs R2, R8.
  • KTD4 — Owner-boundary facts plus one materiality classifier: Emit missing durable observations at their owning task/PR/recovery boundaries, then normalize all inputs through one classifier shared by the record projector. Repeated outcome fingerprints, polls, telemetry, and log churn never become digest items. Governs R4, R9.
  • KTD5 — Boundary status is an as-of fact: Durably observe entry to and exit from blocked and genuinely unanswered states using OperationalStatus/TaskAction semantics, then reconstruct the snapshot as of persisted ends_at. Current status may render the open day but may never substitute for a delayed historical boundary; insufficient transition evidence creates a scoped gap. Governs R5.
  • KTD6 — Gaps are data: Each collector returns facts plus typed source health. A failed project or provider contributes a scoped stable gap while successful sources continue; retry resolves the exact gap through an amendment. Governs R10.
  • KTD7 — One pure reader, many renderers: CLI text, JSON, Rails, Telegram, and agent guidance consume the same non-mutating record reader. A separate coordinator owns refresh/close/amend actions. Date/project filters are views; JSON returns the canonical Web URL and --open-web remains human-only. Governs R6, R7, R8, R12.
  • KTD8 — Closure and delivery are separate: When daily_digest.enabled is true, the daemon may refresh, close, and recover a record regardless of Telegram configuration. Refresh/close/recovery and Telegram delivery use distinct concurrency-controller capacity keys rather than the repository's single global kind: :digest lane; an answer-digest or delivery child in flight cannot gate record publication. The delivery scheduler targets the preceding closed interval at local 09:00 by default and owns an independent intent/outcome ledger so Telegram outages cannot block or corrupt the day record. Governs R3, R8, R10.
  • KTD9 — Hive facts remain sufficient: Journal and publication evidence contains the durable PR core. Live GitHub failure degrades completeness only when those facts cannot satisfy a required field/freshness rule; the compact merge-reconciliation store is not historical authority, and V1 never calls PRDigest. Governs R9.
  • KTD10 — Encode at every output sink: Keep bounded redaction in the normalized record, then apply sink-specific safety: HTML escaping through Rails, control/ANSI sanitization for terminal text, and control stripping plus configured parse-mode escaping for Telegram. Governs R5, R7, R8, R10.

High-Level Technical Design

The sketches are directional contracts. Exact class boundaries may move during implementation if the invariants and ownership remain unchanged.

Component relationships

flowchart LR
  P[Task and PR fact owners] --> C[Daily collector]
  S[Operational status snapshot] --> C
  C --> M[Materiality and source-health normalization]
  M --> D[Host-global daily store]
  D --> R[Shared digest reader]
  R --> CLI[CLI text and JSON]
  R --> WEB[Authenticated Hive Web]
  R --> TG[Explicit or scheduled Telegram renderer]
  GH[Live GitHub, optional] -. fail-soft observation .-> C

Data-flow stages

flowchart LR
  E[Authoritative events and bounded snapshots] --> N[Normalize timestamps, identity, provenance]
  N --> F[Fingerprint changed durable outcomes]
  F --> X{Material?}
  X -- no --> DROP[Exclude polling and churn]
  X -- yes --> A[Assign effective day]
  A --> B[Open base or late amendment]
  B --> V[Filtered views and delivery payloads]

Record lifecycle

stateDiagram-v2
  [*] --> Open: explicit or daemon materialization
  Open --> Open: idempotent refresh before ends_at
  Open --> Closed: first materialization after ends_at
  Closed --> Closed: append fact or gap-resolution amendment
  Closed --> Pruned: explicit confirmed prune

  note right of Open
    completeness = complete or partial
    content = empty, non_empty, or unknown
  end note
  note right of Closed
    effective completeness derives from
    immutable base plus amendments
  end note

No stored lifecycle exists before materialization: a reader returns missing, with coverage metadata indicating whether explicit catch-up is allowed. A prune leaves a tombstone and therefore remains a stored pruned lifecycle.

Close and late-observation protocol

sequenceDiagram
  participant Trigger as explicit refresh or daemon trigger
  participant Store as Daily store lock
  participant Sources as Project source adapters
  participant Base as Base record
  participant Amend as Amendment ledger
  Trigger->>Store: materialize selected date
  Store->>Sources: collect since durable cursors
  Sources-->>Store: facts plus scoped health
  alt date is still open
    Store->>Base: atomically refresh open projection
  else first observation after ends_at
    Store->>Base: write immutable close
  else base already closed
    Store->>Amend: append new facts and gap resolutions
  end
  Store-->>Trigger: record identity and completeness

Per-source decision flow

flowchart TD
  Q[Read one registered-project source] --> OK{Readable and fresh enough?}
  OK -- no --> G[Emit bounded scoped gap]
  G --> KEEP[Keep facts from every healthy source]
  OK -- yes --> CH{Durable outcome changed?}
  CH -- no --> CUR[Advance observation cursor only]
  CH -- yes --> FACT[Emit material fact with provenance]
  FACT --> KEEP
  CUR --> KEEP
  KEEP --> LATE{Historical base closed?}
  LATE -- no --> OPEN[Fold into open base]
  LATE -- yes --> AMEND[Append fact or gap-resolution amendment]

CLI mode contract

Invocation mode Store mutation Output Browser Telegram/delivery ledger
hive digest None Concise text No Never
hive digest --date … --project … None Filtered text No Never
hive digest --json None Schema-valid JSON plus web_url No Never
hive digest --open-web None Short confirmation Explicitly yes Never
hive digest refresh Materialize current, persisted, or missing feature-era intervals Refresh result No Never
Explicit digest send Requires selected day already closed Send result/receipt No Explicitly yes
Daemon close/retry Yes Structured daemon event No Never
Daemon open-day refresh Coalesced on configured cadence Structured daemon event No Never
Daemon recap Requires preceding day already closed Delivery event No Opt-in only

Wireframe Decision

Two day-view hierarchies are viable:

Rough option Shape Benefit Cost Decision
Chronological stream One cross-project event timeline, filters above Strong sequence reconstruction Buries blockers and decisions in busy days Reject as the primary layout; retain timestamps within items
Attention first, then project activity Needs-input/blocker/failure summary above grouped project changes Answers “do I need to act?” immediately while preserving scanability Less literal chronology Select for v1

Selected global day view:

flowchart TB
  H[Date navigation · persisted zone · lifecycle/completeness/content badges] --> F[All-project and project filters]
  F --> A[Needs attention: unanswered · blocked · failed · source gaps]
  A --> P[Per-project material activity groups]
  P --> I[Outcome items with timestamps and native links]
  I --> M[Clearly labeled late amendments and resolved gaps]

Selected answer handoff:

Rough option Shape Benefit Cost Decision
Inline answer in digest Expand question and submit without leaving the day view Fewer visible navigation steps Leaks question content into the digest surface and risks stale answer bindings/duplicate workflow authority Reject
Native task-page deep link Digest shows identity/stage/age, then opens the existing answer section Preserves privacy, fresh bindings, and one answer authority One deliberate navigation step Select for v1
flowchart LR
  D[Digest waiting item: identity · stage · age only] --> T[Native task page at task-questions anchor]
  T --> Q[Fresh question inventory and binding]
  Q --> A[Existing answer submission]
  A --> R[Task resumes through normal workflow]
  R --> N[Digest records answer or late amendment]

Implementation Units

U1 — Establish global configuration, calendar, schema, and durable store

Goal: Create the versioned host-global record foundation with stable IANA-zone boundaries, atomic open updates, immutable closes, append-only amendments, and owner-private storage. Covers R1, R2, R3, R11 through KTD1–KTD3.

Files:

  • lib/hive/paths.rb
  • lib/hive/config.rb
  • lib/hive/local_date_window.rb
  • lib/hive/atomic_file.rb (reuse or extract only if needed)
  • lib/hive/daily_digest.rb
  • lib/hive/daily_digest/calendar.rb
  • lib/hive/daily_digest/record.rb
  • lib/hive/daily_digest/store.rb
  • lib/hive/daily_digest/amendment.rb
  • lib/hive/daily_digest/prune_receipt.rb
  • lib/hive/commands/setup.rb
  • lib/hive/commands/migrate.rb
  • lib/hive/commands/migrate_all.rb
  • templates/hive_config.yml.erb
  • schemas/hive-digest-record.v1.json
  • test/unit/config_test.rb
  • test/unit/local_date_window_test.rb
  • test/unit/daily_digest/calendar_test.rb
  • test/unit/daily_digest/store_test.rb
  • focused setup-time-zone tests under test/unit/commands/setup/
  • focused digest-backfill coverage under test/integration/migrate_test.rb
  • upgraded-config daemon-start coverage under test/integration/daemon_test.rb
  • test/unit/schema_files_test.rb

Approach:

  1. Add a validated global daily_digest: block with enabled: false by default, a required persisted IANA time_zone for enablement, a five-minute default materialization cadence, and nested Telegram defaults. Keep the retired digest: rejection intact so obsolete PRDigest configuration cannot silently acquire new meaning. Config loading must tolerate an upgraded disabled block with no persisted zone; daemon startup records a typed digest-not-initialized diagnostic and omits only digest schedulers. Extend setup/config-writing and the existing idempotent hive migrate/hive migrate --all inventory to detect and persist the host IANA zone and atomically establish the coverage-start frontier, initial registered-project snapshot, and first interval before setting enabled: true; failed or ambiguous detection leaves the feature disabled with exact remediation and never prevents unrelated daemon automation.
  2. Extend the date-window abstraction with a TZInfo-backed calendar that returns a monotonic local-date identity and exact half-open UTC interval, including 23/25-hour DST days and short/long zone-cutover days. Schedule a zone change at the current interval's fixed end; start the cutover interval there, choose the later of the next sequential label or the new zone's date at that instant, and end at the following midnight for that label. Persist skipped zero-duration labels and cutover metadata so no instant overlaps or falls through.
  3. Store records beneath a versioned directory in Hive::Paths.state_home, with restrictive permissions, one aggregate per date, a stable content identity, orthogonal lifecycle/completeness/content fields, and one cross-process lock for materialize/close/amend/prune. Follow WorkLedger, Modules::EventLedger, and AtomicFile conventions for canonical serialization, atomic rename, fsync, conflict detection, and rebuildable indexes.
  4. Permit atomic replacement only while the base is open. Closing creates a write-once canonical base. Store amendments as separately immutable/idempotent entries and preserve a prune receipt/tombstone outside the removed projection.

Test scenarios:

  • Fresh setup detects a valid host zone, persists it with one coverage-start/initial-membership record, and subsequent loads do not change it when the host zone changes.
  • An upgraded config with no daily_digest zone/frontier starts the daemon and continues unrelated task dispatch while both close and refresh schedulers remain absent with typed remediation; migration backfills the zone/frontier idempotently before enablement, and a failed backfill remains safely disabled.
  • Disabling after records exist stops every digest writer and scheduled delivery while CLI/Web can still read preserved history; re-enabling resumes from the unchanged frontier without duplicate intervals or facts.
  • Scalar/unsupported daily_digest configuration and any enabled block with an invalid, ambiguous, or missing zone/frontier fail with a typed digest error; the intentionally tolerated upgraded disabled/no-zone shape keeps unrelated daemon work alive, and the old digest: block remains rejected.
  • Europe/London spring-forward and fall-back dates persist correct UTC boundaries. Eastward, westward, and International Date Line changes leave prior intervals unchanged, create a labeled cutover interval, keep date lookup unique, and assign every instant exactly once.
  • Lifecycle, completeness, and content axes validate independently; gap-resolution amendments may change effective completeness without changing the closed base's lifecycle or bytes.
  • Concurrent writers produce one canonical open/close result; identical amendment IDs are idempotent, conflicting content fails loudly, and no partial file survives an injected write/fsync/rename error.
  • A closed base rejects replacement while accepting a valid amendment. Pruning refuses open/current records, retains its tombstone, and does not touch fixture task/attempt/PR evidence.

Verification:

  • Run the focused config, calendar, store, and schema tests.
  • Inspect one fixture record and amendment set against hive-digest-record.v1.json, including file modes and persisted zone/boundary fields.
  • Re-run the concurrent close/amend/prune tests repeatedly to prove deterministic identities and lock ownership.

U2 — Capture and normalize material activity across registered projects

Goal: Produce complete, deduplicated digest facts and boundary attention items from authoritative owners while surfacing missing legacy or external evidence as gaps. Covers R4, R5, R9, R10 through KTD4–KTD6 and KTD9.

Files:

  • lib/hive/daily_digest/collector.rb
  • lib/hive/daily_digest/materiality.rb
  • lib/hive/daily_digest/project_source.rb
  • lib/hive/daily_digest/source_health.rb
  • lib/hive/daily_digest/coverage.rb
  • lib/hive/daily_digest/task_creation_receipt.rb
  • lib/hive/config.rb
  • lib/hive/task_activity.rb
  • lib/hive/task_journal.rb
  • lib/hive/task_workspace/timeline.rb
  • lib/hive/commands/new.rb
  • lib/hive/operational_status.rb
  • lib/hive/commands/status.rb
  • lib/hive/task_workspace/publication.rb
  • lib/hive/task_workspace/publication_cache.rb
  • lib/hive/daemon/pr_merge_watcher.rb
  • lib/hive/stages/open_pr.rb
  • lib/hive/stages/finalize.rb
  • owning failure/recovery/hold producers identified by the materiality coverage test
  • test/unit/daily_digest/materiality_test.rb
  • test/unit/daily_digest/project_source_test.rb
  • test/integration/daily_digest_collection_test.rb

Approach:

  1. Define a closed materiality matrix mapping every included activity kind to its authoritative producer, stable outcome fingerprint, safe fields, timestamp precedence, and correction behavior. Reuse timeline normalization/redaction semantics; do not reuse the bounded task timeline as the global store.
  2. Persist a task-local creation receipt with project/task identity, created_at, and content-derived creation ID inside the existing task-capture transaction because TaskActivity is attempt-bound. The global collector derives creation facts idempotently from that project-local authority; digest-store failure never fails task creation. Do not synthesize pre-feature creation events.
  3. Add missing observations at the owner boundaries for changed checks, reviews, merges, durable failures/recoveries, entry to and exit from blocked/unanswered states, and changed holds. Use journal facts and PublicationCache for the durable PR core; live GitHub failure creates a gap only when required PR facts/freshness cannot be proven from Hive-owned evidence. Do not call PRDigest.
  4. Enumerate canonical active, done, and archive evidence for every registered project through existing containment-safe readers. Extend the single locked registry-mutation owner so every membership change—new registration, explicit unregistration, stale-project pruning, and replacement/re-registration of an existing project—appends a bounded before/after membership-history entry in the same atomic config write. Replacement records the old membership ending and the new canonical identity/path beginning at one ordered instant. Bind collection to that history plus the coverage-start snapshot. Legacy or corrupt intervals without provable membership emit a registry-history gap rather than claim completeness. Reject symlink/root escapes, and detect non-empty unknown/legacy stage buckets and unreadable projects as named actionable gaps rather than silently dropping tasks.
  5. Use current OperationalStatus/TaskAction only for the open-day view. Reconstruct closing WIP as of persisted ends_at from durable state-entry/exit transitions; derive waiting age from the transition into the as-of state, and emit a boundary-history gap when either cannot be proved. Serialize waiting items through a privacy-specific allowlist.
  6. Produce source frontiers and stable outcome fingerprints with the collected batch. U3 commits each frontier atomically with its base/amendment facts; no external cursor advances first. Replayed batches rely on stable fact IDs so polls, identical snapshots, retries without state change, and log-only churn cannot emit duplicates.

Test scenarios:

  • A multi-project fixture records task creation, stage advance, answered decision, failure/recovery, PR/check/review change, merge, archive, and a changed provider hold exactly once with correct provenance and timestamps.
  • Repeated status polls, repeated GitHub snapshots, repeated diagnostics, log writes, and no-op retries advance observation bookkeeping without adding digest items.
  • A task touched earlier in the day is included; an untouched healthy task is excluded; an untouched blocked or genuinely unanswered task is included at the boundary.
  • A plan approval pause is not mislabeled as an unanswered question. An actual unanswered item contains identity, stage/state, age, and URL but no question text, answer content, prompt excerpt, or binding.
  • A new task either commits with its task-local creation receipt or does not appear; injected digest-store failure cannot roll back task creation, and replay emits the creation fact once. Pre-feature tasks do not gain invented creation events.
  • Registration, explicit unregistration, stale-project pruning, and replacement each commit their ordered membership-history change under the existing global config lock; outage catch-up selects exactly the old/new projects effective in every interval for each mutation path, while malformed legacy history produces a gap.
  • An unknown stage directory, symlink escape, unreadable project, malformed journal row, missing boundary transition, and unavailable required PR evidence each isolate to a scoped redacted gap while healthy projects still contribute facts.
  • Cached Hive-owned PR facts remain complete with GitHub disabled when every required field/freshness rule is satisfied; only a missing required fact creates a gap. Project removal after close does not erase historical identity or links stored in the record.

Verification:

  • Run focused materiality, project-source, task-activity, timeline, publication-cache, status, and collection tests.
  • Validate an exhaustive producer-coverage test that fails when a material journal kind has no owning observation path or declared unsupported legacy fallback.
  • Inspect collected fixtures for both privacy allowlists and source-quality/freshness metadata.

U3 — Implement materialization, close, amendment, gap recovery, and retention lifecycles

Goal: Turn normalized facts into truthful open and historical records, with deterministic close/amend behavior, source-specific retry, and projection-only pruning. Covers R2, R3, R10, R11 and AE3–AE5 through KTD2, KTD6, and KTD8.

Files:

  • lib/hive/daily_digest/coordinator.rb
  • lib/hive/daily_digest/projector.rb
  • lib/hive/daily_digest/reader.rb
  • lib/hive/daily_digest/gap.rb
  • lib/hive/daily_digest/recovery.rb
  • lib/hive/daily_digest/pruner.rb
  • lib/hive/commands/digest_refresh.rb
  • lib/hive/daemon/daily_digest_close_scheduler.rb
  • lib/hive/daemon/digest_scheduler_base.rb (shared mechanics only; preserve answer-digest behavior)
  • test/unit/daily_digest/coordinator_test.rb
  • test/unit/daily_digest/reader_test.rb
  • test/unit/daily_digest/pruner_test.rb
  • test/unit/daemon/daily_digest_close_scheduler_test.rb
  • test/integration/daily_digest_lifecycle_test.rb

Approach:

  1. Make the daemon the periodic open-day materialization owner, coalesced to the configured default five-minute cadence, and expose hive digest refresh as the explicit non-daemon mutation. On startup/refresh, enumerate every absent interval from the persisted coverage frontier through the current interval in chronological order: reconstruct and close elapsed feature-era intervals from durable evidence, recording boundary/membership/source gaps where exact history is unavailable, then materialize the current open interval. Ordinary CLI/JSON/Web reads never mutate. Pre-coverage dates never backfill and remain typed missing results.
  2. On the first materialization after ends_at, reconstruct boundary attention as of that persisted instant from durable transitions, then freeze the base before delivery. If as-of state or waiting age cannot be proved, close with completeness: partial and a scoped boundary-history gap rather than sampling trigger-time status.
  3. Assign a fact by searching the persisted half-open interval sequence using known event time; otherwise use observed_at. If the owning base is already closed, append an amendment with a stable fact ID and explicit late-observation label. Corrections remain append-only and reference the corrected identity.
  4. Commit the normalized facts, gap changes, and each source frontier in the same locked atomic base replacement or amendment transaction. A restart replays the prior frontier and deduplicates by stable identities, preventing both lost activity and duplicate amendments.
  5. Persist gaps as first-class records with stable IDs, source/project/task scope, bounded redacted reason, last successful freshness, observation time, and retry state. Retry only unresolved sources. Recovery appends the recovered facts and exact resolved-gap ID without rewriting the close.
  6. Keep close/recovery work independent from notification state. A record can become closed or gain amendments while Telegram is disabled or unavailable.
  7. Implement explicit dry-run and confirmed pruning of closed records before a supplied date. Remove the base and amendments as one projection unit while retaining the tombstone/audit receipt and delivery deduplication state; underlying evidence is never a prune target. When a later replay, recovered gap, or correction targets a tombstoned interval, never recreate the base: append a bounded, idempotent post-prune discard audit to the tombstone (fact/correction identity, source, observed time, redacted reason) and atomically advance that source frontier in the same lock transaction so the input cannot replay forever.

Test scenarios:

  • Daemon/explicit refresh updates today deterministically while ordinary reads do not mutate it. A stale or unavailable materializer is named after the freshness budget; the day closes once after its persisted boundary and remains byte-stable after later reads and configuration changes.
  • After the daemon is stopped for one or several complete feature-era days, restart/explicit refresh creates each missing interval in order, closes elapsed days with truthful gaps as needed, opens the current day, and makes the prior day eligible for recap. Pre-coverage dates remain missing.
  • A close delayed past midnight reconstructs blocked/unanswered state at ends_at; changes after the boundary do not leak backward, and insufficient transition history becomes a gap.
  • A fact whose event time belongs to yesterday but is first observed today becomes a yesterday amendment; a fact without event time belongs to its observation day.
  • Concurrent close and late observation produce either a fact in the final base or one amendment—never loss, duplication, or base mutation.
  • One project source fails while another succeeds: the day closes with partial completeness, a targeted retry later resolves the gap with an amendment, and repeated recovery is idempotent.
  • No activity with healthy sources creates a complete empty record. No known activity with a failed required source creates content: unknown under partial completeness, never an empty success claim.
  • A crash between collection and store commit replays the old frontier and admits facts once; no committed frontier can omit its corresponding base/amendment facts.
  • A late fact, recovered-gap fact, and correction targeting a pruned interval each append one bounded tombstone discard and atomically advance the matching frontier; replay is idempotent, the base stays absent, and no source loops on the discarded input.
  • Prune dry-run lists only eligible closed dates; confirmed prune preserves evidence and prevents silent reconstruction; current/open, malformed, or already pruned dates return typed outcomes.

Verification:

  • Run coordinator, reader, pruner, close-scheduler, and lifecycle integration tests with a controlled clock.
  • Compare pre/post hashes of closed bases in late-event, source-recovery, zone-change, and project-removal cases.
  • Assert daemon close/retry logs expose record, day, source/gap, attempt, and terminal outcome without leaking paths or source payloads.

U4 — Add the safe CLI, JSON contract, explicit delivery/prune verbs, and agent-readable URLs

Goal: Make today's digest immediately useful in the terminal and stable for automation, while structurally separating reads from external side effects. Covers R7, R11, R12 and AE1/AE5 through KTD7.

Files:

  • lib/hive/commands/digest.rb
  • lib/hive/commands/digest_refresh.rb
  • lib/hive/commands/digest_send.rb
  • lib/hive/commands/digest_prune.rb
  • lib/hive/cli.rb
  • lib/hive.rb
  • schemas/hive-digest.v1.json
  • schemas/hive-digest-send.v1.json
  • test/unit/commands/digest_test.rb
  • test/unit/commands/digest_send_test.rb
  • test/unit/commands/digest_prune_test.rb
  • test/integration/digest_test.rb

Approach:

  1. Add hive digest as a pure read command: the persisted interval containing now and concise text by default, with --date, --project, --json, and --open-web. “Yesterday” and adjacent-date navigation resolve through the persisted interval sequence, not calendar-label arithmetic, so zone-cutover labels cannot make the current record appear missing. Preserve the selected record identity/project in the canonical Web URL. Reject incompatible --json --open-web rather than allowing a machine read to open a browser.
  2. Return a closed-shape JSON envelope with schema version, record identity/lifecycle, local date, persisted zone/boundaries, completeness, content state, freshness/gaps, material items, boundary attention, amendments, native task/PR URLs, and web_url. Project filtering returns a view over the same identity and never drops global gaps or mutates the record.
  3. Keep explicit mutations separate in CLI routing: hive digest refresh materializes the current/persisted day, hive digest send invokes the U6 delivery service, and hive digest prune --before DATE invokes U3 with --dry-run or explicit confirmation. Ordinary hive digest code must not reference the coordinator, Telegram clients, or delivery cursors.
  4. Render lifecycle, completeness, and content independently, with typed missing/pruned reader outcomes and last_materialized_at freshness. Concise text leads with date/zone/freshness/completeness and attention, then material project groups, amendments, and actionable gap recovery guidance.
  5. Pass every dynamic terminal field through Hive::Tui::Text.sanitize or an extracted shared equivalent after redaction, preventing control bytes, ANSI/OSC sequences, and embedded newlines from changing terminal structure.

Test scenarios:

  • Bare hive digest prints today's concise persisted record; explicit date and project select the requested view without changing record identity, stored boundaries, gaps, amendments, or materialization time.
  • After eastward, westward, and date-line cutovers, bare/today and yesterday resolve to the intervals covering now and the immediately preceding interval even when their monotonic labels differ from wall-clock dates; explicit label lookup remains stable.
  • JSON for non-empty, complete-empty, partial-completeness, recovered, and amended days validates against hive-digest.v1.json and includes the same canonical Web URL as text/open-Web mode.
  • Unknown project, invalid date, future date, missing configuration, missing history, stale materialization, and pruned history produce stable typed exits/envelopes rather than internal errors or false empty records; only explicit refresh changes those records.
  • Telegram, browser-opener, and delivery-ledger spies prove all read/JSON/filter paths are side-effect free. --open-web opens exactly once only when explicit; its combination with JSON is rejected before any opener call.
  • Malicious project/task/PR/gap strings containing ANSI, OSC, C0 controls, embedded newlines, or terminal hyperlinks remain inert single-field text without losing the safe human-readable content.
  • hive digest send and hive digest prune require their explicit action syntax; help text and parsing cannot confuse them with the existing sendful hive answer-digest.

Verification:

  • Run focused command/schema tests and the CLI integration matrix for every state and option combination.
  • Capture representative text and JSON snapshots and compare their counts, identity, ordering, filtering, links, gaps, and amendment semantics.
  • Run a no-network/no-Telegram fixture to prove the read command remains complete from Hive-owned evidence.

U5 — Build the authenticated attention-first Hive Web experience

Goal: Provide the complete selected-day reading surface and a safe one-click handoff to the existing task-answer flow. Covers R1, R5, R6, R12 and AE1–AE5 through KTD5 and KTD7.

Files:

  • web/config/routes.rb
  • web/app/models/daily_digest.rb
  • web/app/controllers/digests_controller.rb
  • web/app/views/digests/show.html.erb
  • focused digest partials under web/app/views/digests/
  • web/app/views/layouts/application.html.erb
  • web/app/helpers/application_helper.rb
  • web/app/assets/stylesheets/application.css
  • web/app/views/tasks/_state.html.erb (stable answer anchor only if needed)
  • web/test/models/daily_digest_test.rb
  • web/test/integration/digests_test.rb
  • web/test/helpers/application_helper_test.rb
  • web/test/system/digest_flow_test.rb
  • affected primary-navigation tests

Approach:

  1. Add an authenticated, read-only GET /digests/:date resource and primary navigation entry. The Rails model is a thin adapter over DailyDigest::Reader; it never materializes, reconstructs activity from StatusBroadcaster, or makes live GitHub/PRDigest calls. A stale or missing feature-era record names the explicit refresh path; a pre-coverage missing page explains that v1 does not backfill and links to the nearest persisted date.
  2. Implement the selected attention-first wireframe: sequence-based previous/current/next navigation and persisted zone/completeness header, URL-addressed project filter, needs-input/blocker/failure/gap attention area, grouped project changes, and clearly labeled late amendments/resolved gaps. The default route resolves the persisted interval containing now; navigation walks stored predecessor/successor identities rather than adding or subtracting label dates.
  3. Preserve project/date query state in links. A project filter hides only other projects' facts, retains applicable global gaps, and scopes project-source gaps correctly.
  4. Derive filter choices and historical project labels from the selected persisted record, never only from the live registry. Link waiting items to the existing task page at #task-questions only when the project's current registration and the active/archived task route both resolve at read time. Otherwise render the stored project/task identity as explicitly historical and non-actionable, with no dead-link affordance. The digest serializer and view receive no question text or answer binding, so templates cannot leak them accidentally.
  5. Render lifecycle, completeness, content, missing, stale, and pruned outcomes as distinct accessible pages. Use semantic heading/navigation landmarks, programmatically labeled date/project controls, non-color-only state labels, visible focus, logical keyboard order, and existing mobile touch-target conventions. Preserve dense desktop scanning and mobile navigation containment when adding the new primary link.

Test scenarios:

  • An authenticated operator navigates yesterday/today and all-project/project-filter views; an unauthenticated request follows the existing owner-login gate.
  • The normal-day fixture groups projects and outcomes with direct task/PR links; filter changes preserve date and record identity.
  • A project removed or replaced after close remains available in that record's filters and facts; its task identity is labeled historical without an action link unless a current registration and route resolve, in both CLI JSON/text and Web.
  • Following a zone cutover, default/today and yesterday navigation resolve the covering and immediately preceding persisted intervals even when record labels run ahead of the new wall-clock date.
  • An unanswered card contains only identity/stage/state/age and links to the native answer section. DOM and response-body assertions prove the question and bindings are absent.
  • Partial gaps and recovery amendments remain visible and correctly scoped; historical boundaries do not change after configured-zone mutation.
  • A complete empty day is explicitly empty, while partial completeness and missing/stale/pruned reader outcomes use different copy and status tokens. GET requests for every case leave record hashes, frontiers, and materialization timestamps unchanged.
  • Browser tests prove the selected hierarchy, semantic landmarks/labels, non-color-only states, visible focus and logical keyboard order, mobile touch targets/navigation, date/filter interaction, answer handoff, and back navigation without losing selected-day context.

Verification:

  • From web/, run focused Rails model/helper/integration tests and the Playwright digest system test.
  • Inspect desktop and mobile browser output for hierarchy, density, keyboard/focus behavior, long project names, many amendments, and bounded gap text.
  • Confirm Web counts, ordering, identities, and filtered URLs match the CLI JSON fixture for the same record.

U6 — Add independent close/recovery scheduling and opt-in Telegram delivery

Goal: Close and recover daily records reliably, then send the preceding closed day's concise recap exactly through an explicit or configured opt-in path without coupling publication to Telegram. Covers R8, R10 and AE3/AE5 through KTD3, KTD6, and KTD8.

Files:

  • lib/hive/daemon/daily_digest_close_scheduler.rb
  • lib/hive/daemon/daily_digest_delivery_scheduler.rb
  • lib/hive/daemon/digest_scheduler_base.rb
  • lib/hive/daemon/dispatcher.rb
  • lib/hive/daemon/concurrency_controller.rb
  • lib/hive/commands/daemon.rb
  • lib/hive/config.rb
  • lib/hive/commands/digest_send.rb
  • lib/hive/daily_digest/telegram_renderer.rb
  • lib/hive/daily_digest/delivery_ledger.rb
  • lib/hive/bot/telegram.rb (existing client boundary only)
  • templates/hive_config.yml.erb
  • test/unit/daemon/daily_digest_close_scheduler_test.rb
  • test/unit/daemon/daily_digest_delivery_scheduler_test.rb
  • test/unit/daemon/dispatcher_test.rb
  • test/unit/daily_digest/telegram_renderer_test.rb
  • test/unit/daily_digest/delivery_ledger_test.rb
  • test/integration/daily_digest_delivery_test.rb

Approach:

  1. Load no digest scheduler unless daily_digest.enabled is true. When enabled, give open-day refresh, chronological catch-up/close/recovery, and Telegram delivery separate scheduler state and explicit concurrency-controller capacity keys (for example daily_digest_refresh, daily_digest_close, and daily_digest_delivery) instead of tagging all children only as global kind: :digest. Preserve the existing answer-digest lane. ConcurrencyController#can_dispatch_digest? and dispatcher pending-marker behavior must gate/release each identity independently: refresh and close have one slot each and remain dispatchable while answer-digest or delivery is in flight; delivery is one-at-a-time on its own slot and loads only when daily_digest.telegram.enabled is explicitly true.
  2. Calculate due time with the record's TZInfo interval sequence, targeting the preceding closed interval at the configured hour. Reuse daemon restart, backoff, reload, logging, and one-at-a-time patterns without changing the existing answer_digest schedule semantics. Add positive per-identity child timeouts with config/template/reload coverage as a terminal safety bound, not as the mechanism providing close/delivery independence.
  3. Render a bounded Telegram summary from the persisted record only: date/completeness, compact attention and outcome counts, top actionable items, and validated Web link. Never include question text. Strip control bytes and escape all dynamic fields for the configured Telegram parse mode. A complete empty record records suppressed_empty and sends nothing; a partial non-empty/unknown-completeness record is labeled incomplete.
  4. Resolve the sole scheduled/explicit recap destination through the existing single-recipient Config.telegram_chat_id! convention, not the broadcast notification recipient set. Require that stable private chat identity to be present and allowlisted before preparing a send, persist only that stable private identity, and reject a missing or non-allowlisted destination with a typed outcome; additional allowlisted chats receive no recap. Persist exact record identity, amendment frontier, payload hash, and a prepared/sending/sent/suppressed/unknown outcome before and after the external effect. Restart, timeout, or signal discovery of a leftover sending intent atomically promotes it to unknown; neither sent nor unknown auto-resends. Explicit send/retry uses the same service and records operator intent. Structured logs exclude destination identifiers, tokens, and payload text.
  5. Source retry may append amendments but never implicitly re-send a previously delivered recap. Telegram failure affects only delivery state and daemon events, not record completeness or close state.

Test scenarios:

  • Disabled/default configuration closes records but schedules and sends no Telegram work; enabling delivery at 09:00 targets exactly the preceding closed local date.
  • With daily digest disabled, refresh, close/recovery, and delivery schedule nothing while unrelated daemon and answer-digest work continues; enabling requires initialized zone/frontier state.
  • A running answer-digest child or black-holed delivery child does not gate an independently due refresh or close child, pending markers remain owned by the correct identity, and each identity still enforces its own one-at-a-time slot and timeout.
  • Spring-forward, fall-back, daemon restart before/after due time, configuration reload, concurrent read, and retry/backoff each produce one close and at most one successful delivery.
  • A complete empty record sends nothing and persists suppression. A record with partial completeness sends an incomplete warning plus Web link; source recovery appends an amendment without automatic resend.
  • Telegram success persists sent; definite failure follows bounded retry; crash/timeout after dispatch records unknown and does not duplicate automatically.
  • Explicit send has a positive terminal result, uses the same deduplication ledger, rejects a non-closed/future/pruned date, and never alters the digest base.
  • Renderer tests bound message length, redact source reasons, omit question text, neutralize C0/control/newline/Telegram-markup fixtures, and use only validated web.origin links.
  • Destination tests prove Config.telegram_chat_id! selects the one private allowlisted owner chat, missing/non-allowlisted configuration fails before ledger prepare or transport, and every additional allowlisted/broadcast chat receives zero recap calls.
  • A black-holed child reaches the positive digest timeout, transitions sending to unknown, frees daemon capacity, and remains suppressed across restart until the operator explicitly retries.

Verification:

  • Run focused scheduler, dispatcher, renderer, delivery-ledger, bot-client seam, and end-to-end delivery tests with a fake clock and fake Telegram transport.
  • Inspect structured daemon events across prepare/send/success/failure/unknown/suppressed paths and verify no duplicate external call after restart.
  • Run the no-token and unavailable-Telegram cases to prove closed records and Web/CLI reads remain unaffected.

U7 — Document the ownership change and teach operators and agents the canonical flow

Goal: Make the new digest discoverable and prevent operators or agents from confusing it with PRDigest or the existing sendful pending-answer digest. Covers R7–R12 and completes operational handoff.

Files:

  • README.md
  • wiki/index.md
  • wiki/architecture.md
  • wiki/state-model.md
  • wiki/decisions.md
  • wiki/modules/config.md
  • wiki/modules/daemon.md
  • wiki/commands/web.md
  • wiki/commands/digest.md
  • wiki/modules/daily-digest.md
  • wiki/testing.md
  • wiki/log.d/<date>-daily-activity-digest.md
  • skills/hive/SKILL.md
  • skills/hive/references/daily-digest.md
  • skills/hive/skill.json
  • generated openclaw/skills/hive/ projection files required by the canonical-skill contract
  • test/unit/agent_skills/canonical_skill_test.rb
  • test/unit/openclaw_skills_test.rb
  • focused agent-routing/eval fixtures if the existing harness supports them

Approach:

  1. Append a new ADR that narrowly supersedes ADR-040's “no Hive digest” decision for a broad Hive-owned activity record while preserving its no-PRDigest-runtime-dependency and no-PRDigest-authority principles. Document why the new daily_digest: namespace does not revive stale PR-only configuration.
  2. Document record/state directories, schema axes, zone setup/change semantics, gaps/recovery, explicit pruning, daemon close/delivery separation, CLI/Web commands, Telegram opt-in, and recovery guidance for partial completeness or unknown delivery outcomes.
  3. Update the canonical Hive skill so “what happened today/yesterday?” routes to hive digest --json, interprets lifecycle/completeness/content, missing/stale/pruned outcomes, and amendments, and follows native task links. Explicitly forbid reconstructing the digest from logs/status, implicitly invoking hive digest refresh, exposing question text, opening Web in machine mode, creating polling loops, or invoking hive answer-digest as a daily-record read.
  4. Regenerate the OpenClaw projection from the canonical source and pin parity. Record implementation/test evidence in the wiki log without publishing, releasing, deploying, or selecting a version.

Test scenarios:

  • Canonical skill prompts for today, yesterday, one project, partial history, and amendments choose hive digest --json with the correct date/project and never choose logs, status reconstruction, --open-web, Telegram, or hive answer-digest.
  • Documentation examples distinguish successful complete-empty content, partial completeness, missing/pruned reader outcomes, and unknown delivery state, and include exact operator recovery commands.
  • Canonical/generated skill tests fail on drift, and command/help/schema references agree on option names and side-effect boundaries.
  • The ADR and architecture docs state that Hive owns the broad record, GitHub is fail-soft, PRDigest is optional enrichment, and closed history is immutable.

Verification:

  • Run canonical-skill, OpenClaw projection, documentation-link, schema-reference, and focused eval tests.
  • Review generated skill diffs against the canonical source and manually trace one agent question through JSON to Web/task handoff.
  • Confirm the change set contains no release, tag, publish, deploy, or version-selection action.

Verification Contract

Focused checkpoints

  1. Run U1 store/config/calendar/schema tests before adding collectors; failures here block all later units.
  2. Run U2 producer/materiality/collector tests with fixture journals and status/publication snapshots before lifecycle work.
  3. Run U3 lifecycle and race tests with a fake clock across DST, close, amendment, recovery, pruning, and project removal.
  4. Run U4 CLI text/JSON/option tests with Telegram/browser spies.
  5. From web/, run focused Rails model/helper/integration tests and bundle exec ruby bin/rails test:system test/system/digest_flow_test.rb.
  6. Run U6 daemon/delivery tests with fake Telegram transport, restarts, concurrency, and ambiguous outcomes.
  7. Run canonical skill/projection tests and any focused agent-routing evals.

Cross-surface acceptance matrix

Scenario Store CLI Web Telegram Agent
Multi-project normal day Closed base with material facts Concise text + schema-valid JSON Grouped/filterable with links Concise recap + Web link Reads JSON and identifies attention
Unanswered task Allowlisted boundary item only Identity/stage/age/link, no question Native answer handoff, no question leak No question text Follows link/fresh answer flow only on request
Required PR evidence unavailable Closed base with partial completeness + scoped gap Names source/freshness Gap remains visible under filtering Labeled incomplete recap Reports partial confidence
Source recovered late Immutable base + resolution amendment Shows late amendment Shows resolved gap and timestamps No implicit resend Interprets current amendment frontier
Time-zone change Historical interval unchanged Original zone/boundary Original zone/boundary Uses record's closed date Same JSON identity
Complete empty day Explicit empty base Explicit empty result Explicit empty page Suppressed, zero send Reports no action needed
Pruned day Tombstone only Typed pruned result Pruned state Cannot send Does not reconstruct from logs
Zone-cutover current day Persisted interval containing now Default/today selects covering identity Sequence navigation selects covering identity Uses preceding closed interval Reads returned identity, not label arithmetic
Project removed after close Historical identity retained Historical filter; no dead action Historical filter; conditional live link only Stored label only Does not infer from live registry

Broad verification

  • Run bundle exec rake test from the repository root after focused Ruby/CLI/daemon coverage is green.
  • Run bundle exec rake coverage once as the final exhaustive CI-equivalent gate and require HiveTestCoverage.coverage_ok? to report exactly 100.0% line coverage with unloaded_files empty. Each unit must exercise its own typed error, disabled/uninitialized, gap, degraded-source, retry, tombstone-discard, and concurrency-gate branches before this broad checkpoint; no unit may defer newly introduced defensive paths or files to an assumed later test.
  • From web/, run the full Rails test suite and the affected Playwright system suite according to wiki/testing.md.
  • Treat the Playwright browser proof as CI-only when the local environment has no installed browser; focused Rails integration/helper/model coverage remains mandatory locally, and handoff records the CI system-test command rather than claiming it ran.
  • Run repository lint/security/schema/documentation checks already required by CI for touched root and Web files.
  • Exercise a deterministic end-to-end fixture from task creation through close, CLI JSON/text, authenticated Web filtering, explicit answer handoff, scheduled empty suppression, source recovery amendment, and explicit prune.
  • Review the final diff for secret/question leakage, unbounded payloads, source failure isolation, immutable-close enforcement, option/help parity, and accidental PRDigest/Telegram coupling.

Risks

  • Incomplete producer coverage: The journal vocabulary is broader than current call sites. Mitigate with an explicit materiality-to-owner matrix, a failing coverage test, controlled legacy fallbacks, and visible gaps rather than close-time inference from logs.
  • Calendar, DST, and zone-cutover drift: Independent old/new-zone midnights can overlap or leave gaps. Mitigate with persisted half-open intervals, cutover at the prior fixed end, monotonic date identity, labeled short/long transition records, and DST/eastward/westward fixtures proving every instant is assigned once.
  • Close/amend race: A fact observed at the boundary could be lost or duplicated. Mitigate with one store lock, content-derived IDs, atomic close, and concurrency tests proving base-or-amendment exclusivity.
  • False empty records: Treating source failure as no activity would undermine trust. Mitigate with required source-health results, orthogonal lifecycle/completeness/content axes, and typed missing/pruned reader outcomes.
  • Question, secret, or control-sequence leakage: Journal details, repository names, remote PR fields, errors, and task answers may contain sensitive or renderer-active text. Mitigate with closed serializers, existing redaction, bounded reasons, root containment, Rails escaping, terminal control sanitization, Telegram parse-mode escaping, and DOM/JSON/renderer negative assertions.
  • Historical gaps from legacy state: Pre-feature creation/waiting times or unknown stage buckets may be unavailable. Mitigate by refusing pre-feature synthesis, emitting actionable migration/boundary-history gaps for feature-era evidence problems, and never fabricating events or ages.
  • Live-source coupling: GitHub failure or an accidental PRDigest dependency could block publication. Mitigate by making Hive-owned journal/publication evidence sufficient, degrading only missing required PR facts, never calling PRDigest in v1, and testing with GitHub absent.
  • Feature-era daemon outage: A full-day outage could otherwise leave a permanent hole. Mitigate with the coverage frontier, membership history, chronological catch-up, and partial completeness when historical boundary evidence cannot be proved.
  • Upgrade/rollback safety: A missing zone/frontier or an unwanted writer could otherwise stop the daemon or create history implicitly. Mitigate with enabled: false by default, migration-owned atomic initialization, digest-only typed disablement, and a reversible disable path that preserves read-only history and frontiers.
  • Duplicate Telegram sends: A crash after external send but before receipt persistence is ambiguous. Mitigate with a durable intent/outcome ledger, no automatic resend of unknown, and explicit operator retry.
  • Daemon capacity interference: Closing, recovery, and delivery could contend with task dispatch or the answer digest. Mitigate with explicit pseudo-child identities, bounded independent scheduling state, and concurrency-controller tests.
  • Post-prune replay: Delayed facts or corrections could otherwise recreate a tombstoned day or pin a source frontier forever. Mitigate with an idempotent bounded discard audit committed atomically with frontier advancement.
  • Scan cost and stale reads across many projects: Re-reading all task history on every request would be slow, while a stopped daemon can leave the open day stale. Mitigate with atomic source frontiers, incremental fingerprints, coalesced daemon/explicit materialization, last_materialized_at, a named stale gap, and readers that never fleet-scan.
  • Cross-store task-creation loss: A host-global creation write cannot be atomic with project task capture. Mitigate with a task-local receipt committed as part of task creation, idempotent global projection, and crash tests that never expose a visible receipt-less new task as complete evidence.
  • ADR/config naming regression: Reusing the retired digest: key could silently reinterpret old PRDigest settings. Mitigate with the new daily_digest: namespace, preserved rejection, a superseding ADR, and upgrade/config tests.
  • Navigation density: Adding Digest may overflow the mobile primary nav. Mitigate with existing navigation containment tests and a real browser pass at narrow and wide widths.

Definition of Done

  • Every requirement and acceptance example in the Requirements Trace has passing focused proof and no unresolved question.
  • A global open day can be refreshed only by its daemon/explicit mutation owners, closed once, read indefinitely without mutation, amended late, recovered from scoped gaps, and explicitly pruned without touching underlying evidence.
  • Fresh and upgraded installations remain daemon-safe while digest configuration is disabled or uninitialized; migration owns zone/frontier backfill, enablement is atomic, and disabling stops every digest writer/delivery scheduler while preserving pure historical reads and a resumable frontier.
  • A daemon restart catches up every missing interval at or after the persisted coverage frontier in order using membership history that covers registration, explicit unregistration, stale pruning, and replacement; pre-coverage dates remain typed missing and are never synthesized.
  • Historical boundaries remain unchanged across DST and time-zone changes; cutover intervals assign every instant exactly once, and default/today/yesterday plus Web navigation select persisted covering/predecessor intervals rather than wall-clock label arithmetic.
  • The materiality coverage test accounts for task creation, transitions, answers/decisions, failures/recoveries, changed holds, PR/check/review/merge outcomes, completions, and archives while excluding unchanged noise.
  • CLI text/JSON and Web render the same identity, lifecycle/completeness/content axes, freshness, items, gaps, amendments, project filters, and direct links; unanswered question text is absent at serialization and render layers, and every sink neutralizes renderer-active input.
  • CLI text/JSON and Web derive historical project filters from the selected record and render removed projects as historical/non-actionable unless the current registration and task route resolve.
  • Telegram is disabled by default, targets the preceding closed interval at the configured local hour through Config.telegram_chat_id! only, sends to no additional allowlisted/broadcast chat, suppresses complete empty days, labels records with partial completeness, and cannot duplicate a known successful or ambiguous send automatically.
  • Refresh, close/recovery, delivery, and the existing answer digest have tested independent concurrency capacity identities; an in-flight or black-holed send cannot block due refresh/closure, and per-identity timeouts remain a safety bound.
  • A fact, gap recovery, or correction discovered after its day was pruned produces one bounded tombstone discard audit and advances its source frontier atomically without recreating the record or replaying forever.
  • GitHub and PRDigest can both be unavailable without making the Hive-owned record unreadable.
  • Canonical Hive skill guidance uses the JSON digest contract and cannot confuse it with the existing pending-answer digest.
  • Focused tests (including every new error/gap/degraded branch), repository-wide Ruby tests, the exact 100.0%-line/zero-unloaded-files coverage gate, affected Rails integration/system tests, lint/security/schema/docs checks, and the deterministic end-to-end fixture pass. A browserless local environment may defer only the named Playwright proof to its CI merge gate.
  • Documentation and the new ADR match the shipped behavior; no release, publish, deployment, or version choice is included.

← Back to task

HiveDev, when it’s ready.

HiveDev is the hosted product being built around open-source Hive. It isn’t available yet; join the waitlist to hear when it is. We’ll use your email for HiveDev availability updates.

Read our privacy notice.