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 intoMay 21, 2026
Conversation
…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
There was a problem hiding this comment.
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 msin the F# ZetaId library and updatesZetaObservation.Timestamptoint64<ms>. - Updates the codec’s timestamp bounds-checking and pack/unpack conversions to correctly strip/apply the
msmeasure 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 |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds F# units-of-measure to the ZetaId F# implementation's
Timestampfield. Mika V9.3 minimal delta (~16 lines) against the PR #4548 baseline. Preserves zero-dep discipline by duplicating themsmeasure locally rather than referencingsrc/Core/Units.fs.What changes
src/Core.FSharp.ZetaId/Types.fs[<Measure>] type msat top; changeTimestamp: int64→Timestamp: int64<ms>src/Core.FSharp.ZetaId/Codec.fsMaxTimestamp : int64<ms> = 281474976710655L<ms>(removes[<Literal>]— measure values can't be F# literals); update timestamp range-check +int64strip at bit-pack time +LanguagePrimitives.Int64WithMeasure<ms>wrap at unpack timetests/Tests.FSharp/ZetaId/CrossVerifyTests.fsv.TimestampwithLanguagePrimitives.Int64WithMeasure<ms>intoObservationTotal: 16 lines insertions + 8 deletions across 3 files.
Why
Compile-time safety. With
int64<ms>typing onTimestamp, the F# compiler catches:int64(unitless) whereint64<ms>is expected — compile errorint64<ms>withint64<seconds>or other unit types — compile errorThis is the F#-idiomatic version of the C# implementation's
long Timestampfield. C# can't express this without custom type wrappers; F# gets it for free via units of measure.Why duplicate the
msmeasure locallysrc/Core/Units.fsalready defines[<Measure>] type ms. Importing it would addZeta.Coreas a ProjectReference toZeta.Core.FSharp.ZetaId.fsproj, breaking the zero-external-dependencies discipline that mirrors the C# implementation. The 4-line[<Measure>] type msdefinition 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
BitLayout.fsuntouched — TopDown + BottomUp cross-check discipline preservedEmpirical verification
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:
Zeta.Corebreaks separate-project pattern)msmeasure duplicated locally + Location names match C# canonicalThe 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-linemsmeasure vs cross-project dependency: chose accidental-complexity-stays-accidental (local definition) over essential-complexity (dependency)Composes with substrate
src/Core/Units.fs— the canonicalmsmeasure this PR duplicates locally for zero-dep disciplinememory/persona/amara/conversations/*— the 4-archive Amara cascade that produced the substrate-engineering momentumTest plan
dotnet buildsucceeds 0 warnings 0 errors with measure-typed Timestampdotnet testcross-verify 12/12 still passes (measure-units don't change canonical hex)ace94f2abase; PR fix(post-merge): F# ZetaId Pack-time revalidation + fsharp-output.json rename + Crdt path + Aaron→human-maintainer per Copilot+Codex on PR #4548+#4549 #4551 included)