[AZ-508] Consolidate _iso_ts_now into helpers/iso_timestamps

Batch 48 / Cycle 1 (greenfield Step 7). Closes cumulative review
batches 31-33 F2 and 28-30 F3 by replacing the duplicated private
_iso_ts_now() one-liners with a single Layer-1 helper:

  src/gps_denied_onboard/helpers/iso_timestamps.py
  iso_ts_now() -> str

Output format matches the canonical FDR _TS fixture
(YYYY-MM-DDTHH:MM:SS.ffffffZ); no FDR schema change.

Migrated call-sites (3): c7_inference/onnx_trt_ep_runtime,
c7_inference/thermal_publisher, plus the 3 c6_tile_cache callers
that previously imported from the local c6_tile_cache/_timestamp
shim (now deleted, superseded by the Layer-1 helper).

Spec drift resolved (Choose A, user-approved): spec listed 5 call
sites + +00:00 regex; on-disk reality at batch start is 3 sites +
Z-suffix matching every existing helper and the FDR _TS fixture.
Spec preamble + AC-2 regex updated in the task file; documented in
batch_48_cycle1_report.md.

Tests: 9 new AC tests (AC-1..AC-7 + Layer-1 invariant +
public-surface defensive); 216 focused tests pass including the
unmodified AZ-272 FDR schema suite and AZ-270 / AZ-507 layering
lints. Verdict: PASS (no findings).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-13 23:23:22 +03:00
parent f29897cb3a
commit 5441ea2017
15 changed files with 596 additions and 48 deletions
+7
View File
@@ -360,6 +360,13 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- **Owned by**: AZ-264.
- **Consumed by**: c2_vpr, c2_5_rerank, c3_matcher.
### shared/helpers/iso_timestamps
- **File**: `src/gps_denied_onboard/helpers/iso_timestamps.py`
- **Purpose**: Single Layer-1 UTC ISO-8601 timestamp source for FDR record envelopes (`iso_ts_now() -> str`, format `YYYY-MM-DDTHH:MM:SS.ffffffZ` matching the canonical FDR `_TS` fixture). Replaces the duplicated private `_iso_ts_now` one-liners that previously lived inside `c6_tile_cache` (3 modules — already locally consolidated under `_timestamp.py`) and `c7_inference` (2 modules). Stateless free function; stdlib only.
- **Owned by**: AZ-264 (AZ-508 consolidation task).
- **Consumed by**: c6_tile_cache (`cache_budget_enforcer`, `postgres_filesystem_store`, `freshness_gate`), c7_inference (`onnx_trt_ep_runtime`, `thermal_publisher`); future C10/C11 FDR producers should import this helper rather than redefining the one-liner locally.
### shared/frame_source
- **Directory**: `src/gps_denied_onboard/frame_source/`
@@ -2,7 +2,9 @@
**Task**: AZ-508_hygiene_iso_timestamps_consolidation
**Name**: ISO-timestamp helper consolidation
**Description**: Replace five identical private `_iso_ts_now()` one-liners across c6_tile_cache + c7_inference with a single Layer-1 helper `src/gps_denied_onboard/helpers/iso_timestamps.py` exposing `iso_ts_now() -> str`. Closes cumulative review batches 3133 Finding F2 (Low / Maintainability) and the earlier 2830 cumulative review Finding F3 (the 3 c6 copies).
**Description**: Replace the duplicated private `_iso_ts_now()` one-liners across c6_tile_cache + c7_inference with a single Layer-1 helper `src/gps_denied_onboard/helpers/iso_timestamps.py` exposing `iso_ts_now() -> str`. Closes cumulative review batches 3133 Finding F2 (Low / Maintainability) and the earlier 2830 cumulative review Finding F3 (the 3 c6 copies).
> **Spec drift note (Batch 48, 2026-05-13)**: this task was authored before c6 had locally consolidated its three copies into `components/c6_tile_cache/_timestamp.py`, and before `tensorrt_runtime.py` removed its local copy. Actual call-sites needing migration at implementation time: 3 (c6 `_timestamp.py` shim, c7 `onnx_trt_ep_runtime.py`, c7 `thermal_publisher.py`). Also, AC-2 originally specified a `+00:00` regex copied from a misread of `test_az272_fdr_record_schema.py`; the canonical FDR `ts` format on disk is the `Z`-suffix variant (`_TS = "2026-05-11T00:00:00.000000Z"`), produced by all three existing local helpers. AC-2 regex below has been corrected to `Z`; this preserves the AC-5 "FDR schema tests pass unmodified" invariant and the Excluded clause "no schema change".
**Complexity**: 2 points
**Dependencies**: AZ-263_initial_structure, AZ-266_log_module
**Component**: helpers (epic AZ-264 / E-CC-HELPERS)
@@ -73,9 +75,9 @@ When `from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now` is run
Then the import succeeds; the function is callable; the returned value is a `str`
**AC-2: Output format is byte-identical to the previous local helpers**
Given the existing FDR record format that the five local helpers produced
Given the existing FDR record format that the three local helpers produced (`%Y-%m-%dT%H:%M:%S.%fZ`, canonical FDR `_TS` fixture)
When `iso_ts_now()` is called
Then the output matches `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}\+00:00$` and is parseable by `datetime.fromisoformat(...)` into a UTC-aware datetime
Then the output matches `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$` and is parseable by `datetime.fromisoformat(value.replace("Z", "+00:00"))` into a UTC-aware datetime
**AC-3: Monotonicity under normal clock**
Given two `iso_ts_now()` calls separated by at least 1 microsecond
@@ -0,0 +1,222 @@
# Batch 48 / Cycle 1 — Implementation Report
**Date**: 2026-05-13
**Tasks**: AZ-508 — ISO-timestamp helper consolidation (2pt)
**Total complexity**: 2 points
**Result**: PASS (per-batch code review)
**Jira tracker state**: AZ-508 transitioned To Do → In Progress (prior
session) → In Testing (this batch)
## Scope
Hygiene PBI from cumulative reviews batches 2830 (Finding F3) and
batches 3133 (Finding F2). Consolidates the duplicated private
`_iso_ts_now()` one-liners across `c6_tile_cache` and `c7_inference`
into a single Layer-1 helper at
`src/gps_denied_onboard/helpers/iso_timestamps.py` (`iso_ts_now() -> str`,
RFC 3339 UTC microsecond `Z`-suffix). Closes F2 before the upcoming
C2 batches (AZ-339 / AZ-340) and the C4/C3 batches (AZ-358 / AZ-349)
would have added the 8th and 9th copies of the helper.
## Spec Drift Resolution (User-Approved Choose A)
The task spec assumed:
- **5** call-sites needing migration.
- AC-2 format regex `\.\d{6}\+00:00$`.
- "FDR records standardize on `+00:00` per `test_az272_fdr_record_schema.py`".
On-disk reality at Batch 48 start:
- **3** call-sites: c6's three callers had already been locally
consolidated under `src/gps_denied_onboard/components/c6_tile_cache/_timestamp.py`
(export `iso_ts_now`, same `Z`-suffix format); `tensorrt_runtime.py`
carries no local `_iso_ts_now` definition.
- All 3 existing helpers produce `%Y-%m-%dT%H:%M:%S.%fZ` (`Z` suffix).
- The canonical FDR `ts` fixture is `_TS = "2026-05-11T00:00:00.000000Z"`
(also `Z` suffix). The `+00:00` strings in that test file belong to
*other* DTO fields (manifest `generated_at_iso`, `observed_at_iso`),
not the FDR `ts` envelope.
Picking the spec's `+00:00` literally would have silently changed FDR
record `ts` bit-shape — explicitly **Excluded** by the spec ("no schema
change") and would have broken AC-5 (FDR schema tests pass unmodified).
User selected **Choose A**: Z-format helper, byte-identical to existing
3 helpers + FDR `_TS` fixture; AC-2 regex corrected in the spec; drift
documented in this report and in the spec's preamble.
## Files Changed
### Production (new)
- `src/gps_denied_onboard/helpers/iso_timestamps.py`
`iso_ts_now() -> str`. Stateless free function; stdlib `datetime` +
`timezone` only; format
`datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")`.
### Production (modified)
- `src/gps_denied_onboard/helpers/__init__.py` — re-exports `iso_ts_now`
through the helpers package facade (alphabetical insertion in both
the import block and `__all__`).
- `src/gps_denied_onboard/components/c6_tile_cache/cache_budget_enforcer.py`
import path `c6_tile_cache._timestamp``helpers.iso_timestamps`
(one-line edit; call-sites untouched).
- `src/gps_denied_onboard/components/c6_tile_cache/postgres_filesystem_store.py`
same path swap (2 call-sites untouched).
- `src/gps_denied_onboard/components/c6_tile_cache/freshness_gate.py`
same path swap (2 call-sites untouched).
- `src/gps_denied_onboard/components/c7_inference/onnx_trt_ep_runtime.py`
removed local `def _iso_ts_now`; added top-of-file
`from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now as _iso_ts_now`
(preserves the `_iso_ts_now` symbol at 2 call-sites); removed the
now-unused `from datetime import datetime, timezone`.
- `src/gps_denied_onboard/components/c7_inference/thermal_publisher.py`
same pattern (1 call-site preserved); removed the now-unused
`from datetime import datetime, timezone`.
### Production (deleted)
- `src/gps_denied_onboard/components/c6_tile_cache/_timestamp.py`
the c6-internal consolidation shim is no longer needed; the three
c6 callers now import directly from `helpers.iso_timestamps`. The
Layer-1 helper supersedes the Layer-2 component-local one.
### Tests (new)
- `tests/unit/test_az508_iso_timestamps.py` — 9 tests:
- `test_ac1_import_and_call_returns_str` (AC-1)
- `test_ac2_format_matches_canonical_regex` (AC-2 format)
- `test_ac2_fromisoformat_roundtrip_yields_utc_aware_datetime` (AC-2 parseability)
- `test_ac3_two_successive_calls_are_non_decreasing` (AC-3)
- `test_ac4_no_other_iso_ts_now_definition_exists_in_src` (AC-4
AST-walk over `src/`)
- `test_ac4_c6_and_c7_callers_import_from_helpers` (AC-4 explicit
call-site sweep — 5 files)
- `test_ac6_helper_uses_stdlib_only` (AC-6, stdlib whitelist)
- `test_helper_is_layer_1_no_component_imports` (Constraint
§ Layer-1 discipline)
- `test_helper_public_surface_is_minimal` (defensive: `__all__`
is exactly `["iso_ts_now"]`)
### Docs (modified)
- `_docs/02_document/module-layout.md` — appended
`### shared/helpers/iso_timestamps` row to the helpers section.
- `_docs/02_tasks/todo/AZ-508_hygiene_iso_timestamps_consolidation.md`
drift note added to the description; AC-2 regex corrected from
`\+00:00$` to `Z$`.
- `_docs/03_implementation/reviews/batch_48_review.md` (new) —
per-batch code review (PASS).
- `_docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md`
replay-attempt timestamp + reason updated (PyPI shows `gtsam==4.2.1`
but `requires_dist: numpy<2.0.0`; block unchanged).
## Acceptance Criteria Coverage
| AC | Description | Status | Test |
|----|-------------|--------|------|
| AC-1 | Helper exists, importable, callable, returns `str` | PASS | `test_ac1_import_and_call_returns_str` |
| AC-2 | Format regex match + `fromisoformat` round-trip | PASS | `test_ac2_format_matches_canonical_regex`, `test_ac2_fromisoformat_roundtrip_yields_utc_aware_datetime` |
| AC-3 | Lexicographic monotonicity under normal clock | PASS | `test_ac3_two_successive_calls_are_non_decreasing` |
| AC-4 | All call sites migrated; no stray definitions | PASS | `test_ac4_no_other_iso_ts_now_definition_exists_in_src`, `test_ac4_c6_and_c7_callers_import_from_helpers` |
| AC-5 | FDR schema tests pass unmodified | PASS | `tests/unit/test_az272_fdr_record_schema.py` runs unchanged (35 tests pass) |
| AC-6 | No new third-party dependencies | PASS | `test_ac6_helper_uses_stdlib_only` + `pyproject.toml` unchanged |
| AC-7 | AZ-270 layering lint passes | PASS | `tests/unit/test_az270_compose_root.py::test_ac6_only_compose_root_imports_concrete_strategies` |
## Test Results
- **Focused suite** (tests/unit/test_az508_iso_timestamps.py +
test_az272_fdr_record_schema.py + tests/unit/c6_tile_cache/ +
tests/unit/c7_inference/): **216 passed, 53 environment-skipped, 0 failed**
in 7.97s. Environment-gated skips (Docker compose for c6 integration,
CUDA / TensorRT / Jetson hardware for c7) — unchanged from Batch 47.
- **Focused per-helper**: 9/9 new AZ-508 tests PASS in 1.36s.
- **Layering lint**: AZ-270 + AZ-507 lints PASS (no cross-component
import violations introduced).
- **Lint**: `ruff check` clean on every file authored or modified by
this batch. 3 pre-existing ruff findings (B905, RUF022, RUF023) in
`onnx_trt_ep_runtime.py` left untouched per
`coderule.mdc` "fix pre-existing lints only if in the modified area".
## Architectural Decisions
1. **Layer-1 helper supersedes the c6 internal `_timestamp.py`**.
The c6-internal consolidation shipped earlier (cumulative review
2830 F3) was a Layer-2 stop-gap pending the cross-component
consolidation. With the Layer-1 helper now in place, the c6 shim
is redundant and was deleted; the three c6 callers now import
the cross-component helper directly. This collapses two
abstractions into one.
2. **Preserve `_iso_ts_now` symbol at c7 call-sites via import alias**.
The c7 modules each have 12 in-method `_iso_ts_now()` call-sites.
Rather than edit every call-site, the new module-level
`from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now as _iso_ts_now`
keeps the local symbol name unchanged → minimal diff per c7 file
and minimal risk of accidentally missing a call-site. Matches
the task spec's preferred form (Scope § Included bullet 2).
3. **Stdlib-only helper, no Clock dependency**. The helper is wall-clock
metadata about when an FDR record envelope was emitted — explicitly
NOT a payload field that might need an injectable Clock for tests.
The task spec calls this out (Excluded § "Any change to flight_clock
/ wall_clock"), and the new module docstring reiterates it so
future contributors do not route `age_seconds`-style fields through
it.
4. **AST-walk AC-4 test instead of a grep gate**. AC-4 originally
contemplated a CI-level `grep -rn "def _iso_ts_now" src/` gate. The
in-repo test in `test_az508_iso_timestamps.py` uses an AST walk
instead — it catches both `def _iso_ts_now` AND
`def iso_ts_now` defined anywhere outside the helper, with file
paths in the assertion message, and runs as part of the standard
unit suite (no separate CI hook needed). The same test also enforces
the consumer-side import discipline so the regression surface is
"the import or the test breaks", not "a stray definition lingers".
## Carried-over Findings (from prior batches)
- **F1 from cumulative review 4345 / Batch 46 / Batch 47**: closed by
this batch. `_iso_ts_now` is now in exactly one place.
- **F2 from Batch 46 / Batch 47** (spec→impl drift on C7 API names for
AZ-339 / AZ-340 / AZ-358 / AZ-349): **NOT addressed in this batch**;
belongs to a spec-hygiene PBI to be filed before AZ-339 starts.
- **D-CROSS-CVE-1 opencv pin leftover**: replay attempted at Batch 48
bootstrap; PyPI shows `gtsam==4.2.1` (newer than 4.2 referenced in
the leftover) but still pins `numpy<2.0.0`. Block unchanged;
leftover timestamp updated.
## New Findings (per Batch 48 code review)
None.
## Jira Tracker
- AZ-508 was already `In Progress` at Batch 48 start (transitioned in
a prior `/autodev` session per the state file). Read-back via
`getJiraIssue` confirmed `status.name == "In Progress"` before any
source edits. Will transition `In Progress → In Testing` after this
batch's commit lands.
- Task spec archived to `_docs/02_tasks/done/AZ-508_hygiene_iso_timestamps_consolidation.md`
as part of the implement skill Step 13.
## Next
With AZ-508 closed, the carried-over F1 from batches 4347 is resolved
and the next C2 / C4 / C3 batches can ship without re-adding the
helper. Top Batch 49 candidates (per Batch 47's "Next" list, updated):
- **AZ-339** — MegaLoc + MixVPR strategies (5pt). Same skeleton as
UltraVPR (B47) but two strategies; F2 spec-hygiene PBI may want to
ship first.
- **AZ-340** — SelaVPR + EigenPlaces + SALAD strategies (5pt). Closes
the C2 alternative-backbone buffet.
- **AZ-358** — C4 OpenCV/GTSAM pose estimator (5pt). Changes pace from
C2-heavy streak; opens C4.
- **AZ-389** — C5 internal orthorectifier for mid-flight tiles (3pt).
- **Spec-hygiene PBI** for F2 (C7 API drift in AZ-339 / AZ-340 / AZ-358 /
AZ-349 specs) — strongly recommended before AZ-339.
Per autodev orchestrator: Batch 48 is the third batch since the last
cumulative review (batches 4345). The implement skill Step 14.5
triggers a **cumulative review batches 4648** before Batch 49 starts.
@@ -0,0 +1,126 @@
# Code Review Report — Batch 48
**Batch**: 48 (Cycle 1)
**Tasks**: AZ-508 — ISO-timestamp helper consolidation (2 pt)
**Date**: 2026-05-13
**Verdict**: **PASS**
## Findings
_No findings._
## Phase Summary
### Phase 1 — Context Loading
- Task spec: `_docs/02_tasks/todo/AZ-508_hygiene_iso_timestamps_consolidation.md`
- Cumulative review F2 (batches 3133) + F3 (batches 2830) — origin of the
hygiene PBI.
- Changed source files: 1 new helper + 1 helper package re-export +
3 c6 import rewires + 2 c7 local-def → import rewires + 1 deleted
c6 internal shim.
- Changed test files: 1 new (`tests/unit/test_az508_iso_timestamps.py`,
9 tests covering AC-1..AC-7 + Layer-1 invariant).
- Changed docs: `module-layout.md` (new `shared/helpers/iso_timestamps`
entry) + task spec (drift note + corrected AC-2 regex).
### Phase 2 — Spec Compliance (AC-by-AC)
| AC | Verdict | Evidence |
|----|---------|----------|
| AC-1 | PASS | `test_ac1_import_and_call_returns_str` |
| AC-2 | PASS | `test_ac2_format_matches_canonical_regex` + `test_ac2_fromisoformat_roundtrip_yields_utc_aware_datetime` |
| AC-3 | PASS | `test_ac3_two_successive_calls_are_non_decreasing` |
| AC-4 | PASS | `test_ac4_no_other_iso_ts_now_definition_exists_in_src` (AST walk over `src/`) + `test_ac4_c6_and_c7_callers_import_from_helpers` |
| AC-5 | PASS | `tests/unit/test_az272_fdr_record_schema.py` runs unmodified, 100% pass |
| AC-6 | PASS | `test_ac6_helper_uses_stdlib_only` (AST whitelist) — `pyproject.toml` unchanged |
| AC-7 | PASS | `tests/unit/test_az270_compose_root.py::test_ac6_only_compose_root_imports_concrete_strategies` passes |
**Spec drift note**: the task spec originally listed 5 call-sites and a
`+00:00` regex. On-disk reality at implementation time: 3 call-sites
(c6 already locally consolidated under `_timestamp.py`; `tensorrt_runtime.py`
had no local copy) and the canonical FDR `ts` format is `Z`-suffix per
the `_TS = "2026-05-11T00:00:00.000000Z"` fixture. Spec + AC-2 regex
updated in this batch; no behavioral drift from the spec's actual intent
("single Layer-1 helper, byte-identical to existing locals, no FDR
schema change"). User explicitly approved the path via Choose A.
### Phase 3 — Code Quality
- **SRP**: pure stateless free function; no class wrapper (per Constraint
§ "no class wrapping").
- **Error handling**: no exception suppression; stdlib `datetime.now`
cannot raise under normal operation.
- **Naming**: `iso_ts_now` matches the canonical name used by all three
pre-existing local helpers — no naming churn in call sites
(`from … import iso_ts_now as _iso_ts_now` alias preserved in the c7
files).
- **Complexity**: 1 line. n/a.
- **DRY**: net deletion — eliminates duplication (the explicit goal).
- **Test quality**: assertions cover format, parseability, monotonicity,
absence of stray definitions, import-path enforcement, layer-1
invariant, and stdlib-only constraint.
- **Dead code**: removed unused `from datetime import datetime, timezone`
imports from both c7 files after the local `_iso_ts_now` defs were
removed (adjacent-hygiene, permitted by `coderule.mdc`).
### Phase 4 — Security Quick-Scan
No SQL, no `subprocess`, no `eval` / `exec`, no secrets, no untrusted
input handling. Helper is stdlib-only and side-effect-free. n/a.
### Phase 5 — Performance Scan
`datetime.now(timezone.utc).strftime(...)` is the same call all three
previous local helpers made; zero runtime delta. No new allocations on
the hot path.
### Phase 6 — Cross-Task Consistency
Single task in batch — no inter-task interface concerns. Helper aligns
with the existing 8-helper convention (single-file Layer-1 modules under
`src/gps_denied_onboard/helpers/`, re-exported from `helpers/__init__.py`).
### Phase 7 — Architecture Compliance
- **Layer direction**: helper sits at Layer 1 (Foundation / shared);
consumers in c6 (Layer 2 Infrastructure) and c7 (Layer 2
Infrastructure) import strictly downward. ✓
- **Public API respect**: consumers import via
`gps_denied_onboard.helpers.iso_timestamps` (or the `helpers` package
facade after the `__init__.py` re-export). Internal modules of c6/c7
are not imported across components. ✓
- **No new cyclic deps**: the helper imports only stdlib; no possible
cycle. ✓
- **Duplicate symbols**: eliminated by design — the AC-4 AST walk
asserts zero other `iso_ts_now` / `_iso_ts_now` definitions remain
anywhere under `src/`. ✓
- **Cross-cutting concern home**: moved from per-component locals to
the canonical `helpers/` directory. ✓
- **AZ-507 layering lint**: `test_ac6_only_compose_root_imports_concrete_strategies`
passes — helper imports are allowed from components. ✓
- **AZ-270 lint**: passes. ✓
## Pre-existing Findings (NOT introduced by this batch)
The following ruff findings remain in
`src/gps_denied_onboard/components/c7_inference/onnx_trt_ep_runtime.py`
but are pre-existing (verified by re-running ruff against `HEAD` before
this batch's edits):
- **B905** at line 547 — `zip()` without `strict=`.
- **RUF022** at lines 8898 — `__all__` not sorted.
- **RUF023** at lines 127134 — `__slots__` not sorted.
Per `coderule.mdc` "Pre-existing lint errors should only be fixed if
they're in the modified area" — left untouched. Candidate for a future
c7 hygiene PBI.
## Verdict Justification
- Critical findings: 0
- High findings: 0
- Medium findings: 0
- Low findings: 0
**PASS** — proceed to commit per implement skill Step 11.
+2 -2
View File
@@ -8,9 +8,9 @@ status: in_progress
sub_step:
phase: 7
name: batch-loop
detail: "batch 48 — AZ-508 (ISO timestamp helper consolidation)"
detail: "cumulative review batches 46-48 due before next batch"
retry_count: 0
cycle: 1
tracker: jira
last_completed_batch: 47
last_completed_batch: 48
last_cumulative_review: batches_43-45
@@ -1,6 +1,9 @@
# D-CROSS-CVE-1 opencv-python pin deferred — gtsam/numpy ABI block
**Recorded**: 2026-05-11T02:55+03:00 (Europe/Kyiv)
**Last replay attempt**: 2026-05-13T23:09+03:00 (Europe/Kyiv) — PyPI shows
`gtsam==4.2.1` as the latest release; `requires_dist: numpy<2.0.0,>=1.11.0`.
Replay condition (numpy>=2 wheels) still NOT met. Leftover remains open.
**Status**: deferred-non-user (replay when upstream gtsam wheels target numpy>=2)
## What is blocked
@@ -1,21 +0,0 @@
"""RFC 3339 UTC timestamp helper shared inside the c6_tile_cache component.
Single source for the FDR record envelope ``ts`` field across
``postgres_filesystem_store``, ``freshness_gate``, and
``cache_budget_enforcer`` — formerly a triplicate ``_iso_ts_now`` per
module (`cumulative_review_batches_28-30` F3). The format is wall-clock
metadata about WHEN the FDR record was emitted; producers that need a
Clock-driven payload field (e.g., ``age_seconds`` derived from an
injected clock) MUST NOT route through this helper.
"""
from __future__ import annotations
from datetime import datetime, timezone
__all__ = ["iso_ts_now"]
def iso_ts_now() -> str:
"""Return an RFC 3339 UTC timestamp with microsecond precision and a ``Z`` suffix."""
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
@@ -34,7 +34,6 @@ from dataclasses import dataclass
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Final
from gps_denied_onboard.components.c6_tile_cache._timestamp import iso_ts_now
from gps_denied_onboard.components.c6_tile_cache._types import (
TileId,
TileMetadata,
@@ -50,6 +49,7 @@ from gps_denied_onboard.components.c6_tile_cache.interface import (
TileStore,
)
from gps_denied_onboard.fdr_client.records import CURRENT_SCHEMA_VERSION, FdrRecord
from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now
if TYPE_CHECKING:
from gps_denied_onboard.components.c6_tile_cache._tile_pixel_handle import (
@@ -47,7 +47,6 @@ from typing import TYPE_CHECKING, Any, Final
import psycopg
from psycopg_pool import ConnectionPool
from gps_denied_onboard.components.c6_tile_cache._timestamp import iso_ts_now
from gps_denied_onboard.components.c6_tile_cache._types import (
Bbox,
FreshnessLabel,
@@ -61,6 +60,7 @@ from gps_denied_onboard.components.c6_tile_cache.errors import (
)
from gps_denied_onboard.config.schema import ConfigError
from gps_denied_onboard.fdr_client.records import CURRENT_SCHEMA_VERSION, FdrRecord
from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now
if TYPE_CHECKING:
from gps_denied_onboard.clock.interface import Clock
@@ -48,7 +48,6 @@ from psycopg_pool import ConnectionPool
from gps_denied_onboard.components.c6_tile_cache._tile_pixel_handle import (
TilePixelHandle,
)
from gps_denied_onboard.components.c6_tile_cache._timestamp import iso_ts_now
from gps_denied_onboard.components.c6_tile_cache._types import (
Bbox,
FreshnessLabel,
@@ -76,6 +75,7 @@ from gps_denied_onboard.fdr_client.records import (
CURRENT_SCHEMA_VERSION,
FdrRecord,
)
from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now
from gps_denied_onboard.helpers.sha256_sidecar import (
SIDECAR_SUFFIX,
Sha256Sidecar,
@@ -46,7 +46,6 @@ from __future__ import annotations
import hashlib
import os
from collections.abc import Callable
from datetime import datetime, timezone
from pathlib import Path
from typing import TYPE_CHECKING, Any, Final, Literal
@@ -79,6 +78,7 @@ from gps_denied_onboard.fdr_client.records import (
CURRENT_SCHEMA_VERSION,
FdrRecord,
)
from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now as _iso_ts_now
from gps_denied_onboard.logging import get_logger
if TYPE_CHECKING:
@@ -166,18 +166,6 @@ def _sha256_of_file(path: Path) -> str:
return digest.hexdigest()
def _iso_ts_now() -> str:
"""RFC 3339 UTC timestamp with microsecond precision and a ``Z`` suffix.
Mirrors :func:`components.c6_tile_cache._timestamp.iso_ts_now` —
consolidation into ``helpers.iso_timestamp`` is intentionally
deferred to the next cross-component hygiene pass (peer imports
between c6 and c7 would violate layer-2 horizontal-import etiquette
documented in ``module-layout.md``).
"""
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
# ----------------------------------------------------------------------
# Runtime.
@@ -53,7 +53,6 @@ from __future__ import annotations
import threading
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Protocol, runtime_checkable
from gps_denied_onboard._types.thermal import ThermalState
@@ -65,6 +64,7 @@ from gps_denied_onboard.fdr_client.records import (
CURRENT_SCHEMA_VERSION,
FdrRecord,
)
from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now as _iso_ts_now
from gps_denied_onboard.logging import get_logger
if TYPE_CHECKING:
@@ -340,11 +340,6 @@ def _default_safe_snapshot(measured_at_ns: int) -> ThermalState:
)
def _iso_ts_now() -> str:
"""RFC 3339 UTC timestamp with microsecond precision and ``Z`` suffix."""
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
class _JtopSource:
"""``jtop`` (jetson-stats) thermal source — Tier-2 production path.
@@ -22,6 +22,7 @@ from gps_denied_onboard.helpers.imu_preintegrator import (
ImuPreintegrator,
make_imu_preintegrator,
)
from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now
from gps_denied_onboard.helpers.lightglue_runtime import (
LightGlueConcurrentAccessError,
LightGlueRuntime,
@@ -83,6 +84,7 @@ __all__ = [
"adjoint",
"exp_map",
"is_valid_rotation",
"iso_ts_now",
"log_map",
"make_imu_preintegrator",
"matrix_to_se3",
@@ -0,0 +1,33 @@
"""UTC ISO-8601 timestamp helper (E-CC-HELPERS / AZ-264 / AZ-508).
Single Layer-1 source for the wall-clock string used in the FDR record
``ts`` envelope across c6_tile_cache and c7_inference. Consolidates what
used to be a private ``_iso_ts_now`` one-liner repeated per module.
The output format is locked to RFC 3339 / ISO 8601 UTC with microsecond
precision and a ``Z`` suffix, matching the canonical FDR ``ts`` fixture
in ``tests/unit/test_az272_fdr_record_schema.py`` (``_TS =
"2026-05-11T00:00:00.000000Z"``) and the format produced by the three
existing local helpers this module replaces. Changing the format would
alter FDR record bit-shape and is explicitly out of scope for AZ-508.
Producers that need a Clock-injected payload field (e.g.
``age_seconds`` derived from an injected wall-clock for testability)
MUST NOT route through this helper — it is purely metadata about WHEN
the FDR record envelope itself was emitted.
"""
from __future__ import annotations
from datetime import datetime, timezone
__all__ = ["iso_ts_now"]
def iso_ts_now() -> str:
"""Return an RFC 3339 UTC timestamp with microsecond precision and a ``Z`` suffix.
Format: ``YYYY-MM-DDTHH:MM:SS.ffffffZ`` (fixed-width, lexicographically
monotonic under a non-decreasing wall clock).
"""
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
+191
View File
@@ -0,0 +1,191 @@
"""AC tests for AZ-508: ISO-timestamp helper consolidation.
Verifies the `iso_timestamps` helper exposed at
`gps_denied_onboard.helpers.iso_timestamps.iso_ts_now` — the single
Layer-1 source for FDR record envelope timestamps that replaced the
duplicated `_iso_ts_now` one-liners in c6_tile_cache and c7_inference.
Output contract (matches the canonical FDR `_TS` fixture in
`tests/unit/test_az272_fdr_record_schema.py`):
YYYY-MM-DDTHH:MM:SS.ffffffZ (UTC, microsecond precision, ``Z`` suffix)
"""
from __future__ import annotations
import ast
import re
import time
from datetime import datetime, timezone
from pathlib import Path
import pytest
from gps_denied_onboard.helpers import iso_ts_now
from gps_denied_onboard.helpers.iso_timestamps import iso_ts_now as iso_ts_now_direct
_TS_REGEX: re.Pattern[str] = re.compile(
r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$"
)
_REPO_ROOT: Path = Path(__file__).resolve().parents[2]
_HELPER_PATH: Path = (
_REPO_ROOT / "src" / "gps_denied_onboard" / "helpers" / "iso_timestamps.py"
)
_C6_DIR: Path = (
_REPO_ROOT / "src" / "gps_denied_onboard" / "components" / "c6_tile_cache"
)
_C7_DIR: Path = (
_REPO_ROOT / "src" / "gps_denied_onboard" / "components" / "c7_inference"
)
def test_ac1_import_and_call_returns_str() -> None:
# Act
value = iso_ts_now()
# Assert
assert isinstance(value, str)
assert value, "iso_ts_now() returned an empty string"
# Both the package-level and module-level imports must resolve to the
# same callable so consumers can reach it either way.
assert iso_ts_now is iso_ts_now_direct
def test_ac2_format_matches_canonical_regex() -> None:
# Act
value = iso_ts_now()
# Assert
assert _TS_REGEX.fullmatch(value), (
f"{value!r} does not match the canonical FDR ts format "
f"YYYY-MM-DDTHH:MM:SS.ffffffZ"
)
def test_ac2_fromisoformat_roundtrip_yields_utc_aware_datetime() -> None:
# Arrange
value = iso_ts_now()
iso_with_offset = value.replace("Z", "+00:00")
# Act
parsed = datetime.fromisoformat(iso_with_offset)
# Assert
assert parsed.tzinfo is not None
assert parsed.utcoffset() == timezone.utc.utcoffset(parsed)
def test_ac3_two_successive_calls_are_non_decreasing() -> None:
# Arrange / Act
a = iso_ts_now()
time.sleep(0.000_005)
b = iso_ts_now()
# Assert (lexicographic comparison is correct for the fixed-width format)
assert b >= a, f"expected {b!r} >= {a!r}"
def test_ac4_no_other_iso_ts_now_definition_exists_in_src() -> None:
"""AC-4: a `def _iso_ts_now` / `def iso_ts_now` MUST exist only inside
`helpers/iso_timestamps.py`. Any other definition under `src/` means a
consumer slipped a copy back in.
"""
# Arrange
src_root = _REPO_ROOT / "src"
offenders: list[tuple[Path, str]] = []
# Act
for path in src_root.rglob("*.py"):
if path == _HELPER_PATH:
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8"))
except SyntaxError:
continue
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and node.name in {
"iso_ts_now",
"_iso_ts_now",
}:
offenders.append((path.relative_to(_REPO_ROOT), node.name))
# Assert
assert offenders == [], (
f"Found stray `iso_ts_now` definitions outside the helper: {offenders}"
)
def test_ac4_c6_and_c7_callers_import_from_helpers() -> None:
"""The 3 migrated call-sites must import from `helpers.iso_timestamps`
(directly or via the `helpers` package facade) so future hygiene
cycles can rely on the single source of truth.
"""
# Arrange
callers = [
_C6_DIR / "cache_budget_enforcer.py",
_C6_DIR / "postgres_filesystem_store.py",
_C6_DIR / "freshness_gate.py",
_C7_DIR / "onnx_trt_ep_runtime.py",
_C7_DIR / "thermal_publisher.py",
]
expected_token = "gps_denied_onboard.helpers.iso_timestamps"
# Act / Assert
for caller in callers:
text = caller.read_text(encoding="utf-8")
assert expected_token in text, (
f"{caller.relative_to(_REPO_ROOT)} does not import "
f"`iso_ts_now` from `{expected_token}`"
)
assert "def _iso_ts_now" not in text, (
f"{caller.relative_to(_REPO_ROOT)} still defines a local "
"_iso_ts_now (consolidation incomplete)"
)
def test_ac6_helper_uses_stdlib_only() -> None:
"""AC-6: no third-party imports inside the helper module."""
# Arrange
tree = ast.parse(_HELPER_PATH.read_text(encoding="utf-8"))
allowed_stdlib = {"datetime", "__future__"}
# Act
imports: list[str] = []
for node in ast.walk(tree):
if isinstance(node, ast.Import):
imports.extend(alias.name for alias in node.names)
elif isinstance(node, ast.ImportFrom):
if node.module is not None:
imports.append(node.module)
# Assert
for name in imports:
top = name.split(".")[0]
assert top in allowed_stdlib, (
f"helpers/iso_timestamps.py imports `{name}`; only stdlib "
f"({sorted(allowed_stdlib)}) is allowed by AC-6"
)
def test_helper_is_layer_1_no_component_imports() -> None:
"""Layer-1 discipline: the helper MUST NOT import from any component.
(Constraint § Layer-1 discipline in the AZ-508 task spec.)
"""
# Arrange
text = _HELPER_PATH.read_text(encoding="utf-8")
# Assert
assert "gps_denied_onboard.components" not in text, (
"helpers/iso_timestamps.py imports from a component — Layer-1 "
"discipline violated"
)
@pytest.mark.parametrize("expected_field", ["iso_ts_now"])
def test_helper_public_surface_is_minimal(expected_field: str) -> None:
"""Defensive: only ``iso_ts_now`` is re-exported from the module."""
# Arrange
import gps_denied_onboard.helpers.iso_timestamps as module
# Assert
assert expected_field in module.__all__
assert module.__all__ == ["iso_ts_now"]