feat(proto): unify time fields on Timestamp/Duration #348

Merged
KofTwentyTwo merged 1 commits from story/ws-a2-timestamps into dev 2026-07-31 21:00:49 +00:00
KofTwentyTwo commented 2026-07-31 20:59:02 +00:00 (Migrated from github.com)

Summary

Closes #274 (WS-A2). Unifies every wall-clock instant on the proto boundary to google.protobuf.Timestamp and every duration to google.protobuf.Duration, replacing the prior mix of unix-seconds and unix-nanoseconds int64/uint64 scalars.

Fields converted (Timestamp):

  • Message.received_at
  • CalendarEvent.start_time / end_time
  • CalendarSyncRequest.start_window / end_window
  • ListEventsRequest.start_window / end_window
  • FilterRule.created_at / updated_at
  • FilterExecutionLog.matched_at
  • AuditRecord.timestamp_ns -> renamed timestamp (nanosecond precision preserved losslessly)

Fields converted (Duration):

  • AccountConfig.sync_interval_secs -> renamed sync_interval
  • PreviewRuleResponse.execution_time_us -> renamed execution_time

Implementation:

  • Added prost-types as a workspace dependency (only to nuncio-proto, since it re-exports Timestamp/Duration via a new nuncio_proto::time module so downstream crates don't need their own dependency).
  • nuncio_proto::time is the single conversion point between the store's native integer instants (unix-seconds i64/u64, or unix-nanoseconds for the WORM audit ledger) and the wire types: timestamp_from_unix_secs/timestamp_to_unix_secs, timestamp_from_unix_nanos/timestamp_to_unix_nanos (lossless split/join of the audit ledger's sub-second precision), duration_from_secs/duration_to_secs, duration_from_micros/duration_to_micros.
  • nunciod::grpc's mappers now go through these helpers at the proto boundary; core/store models are unchanged (still native seconds/ns integers).
  • nuncio-cli's runner.rs renders Timestamp fields as RFC 3339 strings (format_timestamp) for both human and --json output, and Duration fields as their original seconds/microseconds units for output-shape continuity. The audit ledger's --json output keeps both a human timestamp (RFC 3339) and an exact timestamp_ns field.
  • Regenerated crates/nuncio-proto/proto/nuncio/v1/descriptor.bin; contract_stability::descriptor_matches_committed_golden passes.
  • Added round-trip tests: nuncio_proto::time's own unit tests (seconds/nanos/micros round trips, including a negative pre-epoch nanos regression), plus nunciod::grpc's map_email_to_proto_round_trips_received_at_exactly, map_audit_record_to_proto_preserves_nanosecond_precision, and account_sync_interval_round_trips_through_duration.

Gate output

cargo fmt --all -- --check   -> clean
cargo check-all              -> clean (clippy --all-targets --workspace -D warnings)
cargo test-all               -> 498 passed; 0 failed; 0 ignored (workspace total)

Test plan

  • cargo test -p nuncio-proto — 32 unit tests incl. contract_stability::descriptor_matches_committed_golden
  • cargo test -p nunciod — lib + all e2e suites green, including the new mapper round-trip tests
  • cargo test -p nuncio-cli — lib + doctest green, including updated JSON rendering assertions
  • Full workspace gate (fmt/clippy/test) green via the local pre-commit hook
## Summary Closes #274 (WS-A2). Unifies every wall-clock instant on the proto boundary to `google.protobuf.Timestamp` and every duration to `google.protobuf.Duration`, replacing the prior mix of unix-seconds and unix-nanoseconds `int64`/`uint64` scalars. **Fields converted (Timestamp):** - `Message.received_at` - `CalendarEvent.start_time` / `end_time` - `CalendarSyncRequest.start_window` / `end_window` - `ListEventsRequest.start_window` / `end_window` - `FilterRule.created_at` / `updated_at` - `FilterExecutionLog.matched_at` - `AuditRecord.timestamp_ns` -> renamed `timestamp` (nanosecond precision preserved losslessly) **Fields converted (Duration):** - `AccountConfig.sync_interval_secs` -> renamed `sync_interval` - `PreviewRuleResponse.execution_time_us` -> renamed `execution_time` **Implementation:** - Added `prost-types` as a workspace dependency (only to `nuncio-proto`, since it re-exports `Timestamp`/`Duration` via a new `nuncio_proto::time` module so downstream crates don't need their own dependency). - `nuncio_proto::time` is the single conversion point between the store's native integer instants (unix-seconds `i64`/`u64`, or unix-nanoseconds for the WORM audit ledger) and the wire types: `timestamp_from_unix_secs`/`timestamp_to_unix_secs`, `timestamp_from_unix_nanos`/`timestamp_to_unix_nanos` (lossless split/join of the audit ledger's sub-second precision), `duration_from_secs`/`duration_to_secs`, `duration_from_micros`/`duration_to_micros`. - `nunciod::grpc`'s mappers now go through these helpers at the proto boundary; core/store models are unchanged (still native seconds/ns integers). - `nuncio-cli`'s `runner.rs` renders `Timestamp` fields as RFC 3339 strings (`format_timestamp`) for both human and `--json` output, and `Duration` fields as their original seconds/microseconds units for output-shape continuity. The audit ledger's `--json` output keeps both a human `timestamp` (RFC 3339) and an exact `timestamp_ns` field. - Regenerated `crates/nuncio-proto/proto/nuncio/v1/descriptor.bin`; `contract_stability::descriptor_matches_committed_golden` passes. - Added round-trip tests: `nuncio_proto::time`'s own unit tests (seconds/nanos/micros round trips, including a negative pre-epoch nanos regression), plus `nunciod::grpc`'s `map_email_to_proto_round_trips_received_at_exactly`, `map_audit_record_to_proto_preserves_nanosecond_precision`, and `account_sync_interval_round_trips_through_duration`. ## Gate output ``` cargo fmt --all -- --check -> clean cargo check-all -> clean (clippy --all-targets --workspace -D warnings) cargo test-all -> 498 passed; 0 failed; 0 ignored (workspace total) ``` ## Test plan - [x] `cargo test -p nuncio-proto` — 32 unit tests incl. `contract_stability::descriptor_matches_committed_golden` - [x] `cargo test -p nunciod` — lib + all e2e suites green, including the new mapper round-trip tests - [x] `cargo test -p nuncio-cli` — lib + doctest green, including updated JSON rendering assertions - [x] Full workspace gate (fmt/clippy/test) green via the local pre-commit hook
Sign in to join this conversation.