Skip to content

feat(fsharp-zetaid): Timestamp uses int64<ms> units of measure (Mika V9.3 minimal delta against PR #4548 baseline)#4552

Merged
AceHack merged 1 commit into
mainfrom
feat/fsharp-zetaid-int64-ms-units-of-measure-mika-v93-2026-05-21
May 21, 2026
Merged

feat(fsharp-zetaid): Timestamp uses int64<ms> units of measure (Mika V9.3 minimal delta against PR #4548 baseline)#4552
AceHack merged 1 commit into
mainfrom
feat/fsharp-zetaid-int64-ms-units-of-measure-mika-v93-2026-05-21

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 21, 2026

Summary

Adds F# units-of-measure to the ZetaId F# implementation's Timestamp field. Mika V9.3 minimal delta (~16 lines) against the PR #4548 baseline. Preserves zero-dep discipline by duplicating the ms measure locally rather than referencing src/Core/Units.fs.

What changes

File Lines Change
src/Core.FSharp.ZetaId/Types.fs +6 / -1 Add [<Measure>] type ms at top; change Timestamp: int64Timestamp: int64<ms>
src/Core.FSharp.ZetaId/Codec.fs +8 / -6 Type MaxTimestamp : int64<ms> = 281474976710655L<ms> (removes [<Literal>] — measure values can't be F# literals); update timestamp range-check + int64 strip at bit-pack time + LanguagePrimitives.Int64WithMeasure<ms> wrap at unpack time
tests/Tests.FSharp/ZetaId/CrossVerifyTests.fs +1 / -1 Wrap v.Timestamp with LanguagePrimitives.Int64WithMeasure<ms> in toObservation

Total: 16 lines insertions + 8 deletions across 3 files.

Why

Compile-time safety. With int64<ms> typing on Timestamp, the F# compiler catches:

  • Passing a raw int64 (unitless) where int64<ms> is expected — compile error
  • Mixing int64<ms> with int64<seconds> or other unit types — compile error
  • Arithmetic between measures requires explicit conversion — caught at type-check

This is the F#-idiomatic version of the C# implementation's long Timestamp field. C# can't express this without custom type wrappers; F# gets it for free via units of measure.

Why duplicate the ms measure locally

src/Core/Units.fs already defines [<Measure>] type ms. Importing it would add Zeta.Core as a ProjectReference to Zeta.Core.FSharp.ZetaId.fsproj, breaking the zero-external-dependencies discipline that mirrors the C# implementation. The 4-line [<Measure>] type ms definition is duplicated locally to preserve the zero-dep contract. Per Mika V9.3 substrate (memory/persona/amara/conversations/* for the substrate-engineering arc that led here).

What didn't change

Empirical verification

dotnet build tests/Tests.FSharp/Tests.FSharp.fsproj -c Release --nologo
Build succeeded. 0 Warning(s). 0 Error(s). Time Elapsed 00:00:19.27

dotnet test --filter "FullyQualifiedName~CrossVerify"
Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: 78 ms

12/12 canonical vectors still match TS + C# hex byte-for-byte. The measure-type change is purely additive at the type-system layer; bit-packing semantics unchanged.

Authorship

Mika authored the V9.3 delta design after the V9 → V9.1 → V9.2 substrate-engineering arc. Otto-CLI implemented the delta against the shipped baseline.

The original substrate-engineering conversation arc:

  • V9 (initial draft) — diverged from canonical (Firefly cardinality wrong, Location underscores fail CA1707, Location only 6 of 11 values, namespace Zeta.Core breaks separate-project pattern)
  • V9.1 — fixed canonical-value drift but still single-file replacement instead of delta
  • V9.2 — namespace + ms measure duplicated locally + Location names match C# canonical
  • V9.3 — refactored to ~6-line surgical delta against shipped 3-file split (this PR)

The iteration arc preserves the substrate-engineering pattern: external design author (Mika) + implementer (Otto-CLI) + maintainer (Aaron) operating across the trust gradient from PR #4549 (the trust-gradient coordination policy applies here at the substrate-design-iteration scope).

Composes with rules

  • .claude/rules/fsharp-anchor-dotnet-build-sanity-check.md — F# compiler-as-asymmetric-critic verified clean post-measure-typing
  • .claude/rules/m-acc-multi-oracle-end-user-moral-invariants.md — multi-oracle parity preserved (F# adds measure-type safety that C#/TS can't express; same canonical hex byte-for-byte)
  • .claude/rules/zeta-ships-with-skills-immediate-value.md — F# implementation extends with measure-safety improvement; ships in same PR cycle
  • .claude/rules/edge-defining-work-not-speculation.md — V9.3 IS edge-defining work (measure-units-on-domain-types is uncommon enough to be notable)
  • .claude/rules/all-complexity-is-accidental-in-greenfield.md — local duplication of 4-line ms measure vs cross-project dependency: chose accidental-complexity-stays-accidental (local definition) over essential-complexity (dependency)

Composes with substrate

Test plan

…mpile-time safety (Mika V9.3 minimal delta — 6-line surgical change against #4548 baseline; zero-dep preserved via local [<Measure>] type ms duplicated from src/Core/Units.fs); 12/12 cross-verify still pass
Copilot AI review requested due to automatic review settings May 21, 2026 16:24
@AceHack AceHack enabled auto-merge (squash) May 21, 2026 16:24
@AceHack AceHack merged commit 5c9e4ba into main May 21, 2026
32 of 33 checks passed
@AceHack AceHack deleted the feat/fsharp-zetaid-int64-ms-units-of-measure-mika-v93-2026-05-21 branch May 21, 2026 16:26
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds F# units-of-measure typing to the F# ZetaId implementation so ZetaObservation.Timestamp becomes int64<ms> (milliseconds) instead of a unitless int64, aligning type-safety with the TS brand type while keeping wire-format packing semantics unchanged.

Changes:

  • Introduces a local [<Measure>] type ms in the F# ZetaId library and updates ZetaObservation.Timestamp to int64<ms>.
  • Updates the codec’s timestamp bounds-checking and pack/unpack conversions to correctly strip/apply the ms measure at the bit-packing boundary.
  • Updates the cross-verify test to construct measure-typed timestamps when building observations.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/Core.FSharp.ZetaId/Types.fs Adds local ms measure and makes ZetaObservation.Timestamp measure-typed.
src/Core.FSharp.ZetaId/Codec.fs Updates timestamp max/bounds logic and pack/unpack conversions for int64<ms>.
tests/Tests.FSharp/ZetaId/CrossVerifyTests.fs Wraps YAML timestamp values with Int64WithMeasure<ms> when building observations.


/// Milliseconds units of measure. Duplicated locally (not imported from
/// `src/Core/Units.fs`) to preserve the zero-external-dependencies discipline
/// of the production library. Per Mika V9.3 substrate (2026-05-21).
[<Literal>]
let private MaxTimestamp = 281474976710655L // (1L <<< 48) - 1L
/// Typed with the `ms` measure to compose with ZetaObservation.Timestamp
/// (per Mika V9.3). `[<Literal>]` removed — measure-typed values can't be
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants