Task: Fix optional<db_timestamp> → db_timestamp for all NOT NULL entity fields

Table of Contents

This page documents a task in the Unify entity timestamp handling story.

Goal

Change every optional<db_timestamp> that maps to a NOT NULL DB column to plain db_timestamp. The optional wrapper let sqlgen silently skip populating the field on a parse failure, which could produce a default-initialised db_timestamp (near-epoch) that then fed through timestamp_to_timepoint to produce the "56 years ago" bug.

Nullable columns (completed_at, last_event_at, etc.) must stay as optional<db_timestamp>.

Status

Field Value
State DONE
Parent story Unify entity timestamp handling
Now Nothing.
Waiting on Nothing.
Next Nothing — task closed.
Last touched 2026-06-04

Acceptance

  • Every optional<db_timestamp> X = "..." (field with a default) is db_timestamp X = "...".
  • Every optional<db_timestamp> X; (no default, truly nullable) remains optional<db_timestamp> X;.
  • Mapper read sites that previously dereferenced *v.X for the now-non-optional fields change to v.X (no dereference).
  • Null-check guards (if (!v.X) throw preceding the *v.X) are removed for non-optional fields.
  • Build green.

Plan

Identification rule

A field should become db_timestamp (not optional) if and only if it has a default value in the struct declaration:

// NOT NULL → becomes db_timestamp:
std::optional<db_timestamp> valid_from = "9999-12-31 23:59:59";

// Nullable → stays optional<db_timestamp>:
std::optional<db_timestamp> completed_at;  // no default

Script approach

Use a Python script on all *_entity.hpp files under projects/:

  1. Match std::optional<db_timestamp> (\w+) = "..."; → replace with db_timestamp \1 = "...";
  2. All other optional<db_timestamp> (without default) → leave unchanged

Mapper read-side fixup

For each field that became non-optional, find the corresponding mapper and:

  1. Remove any null-check guard:

    // Remove this:
    if (!v.valid_from)
        throw std::logic_error("...");
    
  2. Remove the dereference:

    // Before:
    r.recorded_at = timestamp_to_timepoint(*v.valid_from);
    // After:
    r.recorded_at = timestamp_to_timepoint(v.valid_from);
    

Verification

After the change, grep for remaining optional<db_timestamp>. Every hit should be a genuinely nullable field (no default in the struct). Any hit with a default is a missed case.

grep -rn "optional<db_timestamp>" projects/ --include="*_entity.hpp" | grep -v build

PRs

PR Title
#1036 [multi,compass] Timestamp unification mop-up; compass test logging toggle

Review

# Comment summary File Decision Notes
1 valid_from should default to early epoch, not 9999 badge_mapping_entity.hpp Declined Matches currency/badge_definition/host pattern; DB trigger owns the fields on insert.
2 Now field should be 'Nothing.' on DONE tasks task_fix-mapper-helpers.org Fixed in c54d298b1  

Result

Bulk conversion (~100 entity files) landed in PR #1033 on branch feature/unify-entity-timestamps. The mop-up on feature/unify-entity-timestamps-mopup fixed the 7 remaining optional<db_timestamp> fields whose DDL columns are NOT NULL:

  • badge_mapping_entity.hppvalid_from=/=valid_to (with the standard 9999-12-31 23:59:59 defaults, currency pattern).
  • database_info_entity.hpp, workflow_step_entity.hpp, workflow_instance_entity.hpp, workflow_batch_link_entity.hpp, report_input_bundle_entity.hppcreated_at (service_instance pattern, no struct default).

Read-side mappers dropped their null-guard + dereference (database_info_mapper, workflow_step_mapper, workflow_instance_mapper). Two write sites that relied on nullopt → DB current_timestamp default now set the timestamp explicitly via datetime::to_db_string(now()) (report_execution_handler, report_submit_handler).

The 11 remaining optional<db_timestamp> fields were verified nullable in DDL — correctly optional. Full build green.

Emacs 29.1 (Org mode 9.6.6)