# MoonBitemporal

MoonBitemporal is an original, pure MoonBit library for facts with **two time axes**: the valid interval when a value applies in the modeled world, and the knowledge instant when the application accepted a claim. It answers both “What do we now believe applied on day X?” and “What did the application believe on day X before a later correction arrived?”

Package namespace: `yyyt0807/moonbitemporal`. Repository: [yyyt0807/MoonBitemporal](https://github.com/yyyt0807/MoonBitemporal).

## Install as a dependency

```sh
moon add yyyt0807/moonbitemporal@0.2.0
```

Import the library in the consuming package's `moon.pkg`:

```text
import {
  "yyyt0807/moonbitemporal" @temporal,
}
```

Repository owner and maintenance commit identity: `yyyt0807`. License: Apache-2.0.
## Why this exists

Pricing, access rules, reference data, calibration metadata and configuration all receive late corrections. Overwriting a record destroys the evidence needed to reproduce an earlier decision. A single timestamp cannot express both a backdated effective date and the later date when the correction became known. MoonBitemporal supplies those semantics without binding a MoonBit application to a particular database, clock, timezone, HTTP server or runtime target.

The core models both business-valid time and knowledge time; interval indexing, calendar arithmetic and application-specific policy decisions remain separate integration concerns.

## Reference comparison and implemented scope

See the [feature matrix against Rust bitemporal-runtime 0.1.0](docs/REFERENCE_COMPARISON.md) for implemented capabilities, semantic differences and explicit exclusions. Shared open-span point/snapshot semantics are checked against actual outputs from the pinned Rust crate. This is an original MoonBit implementation with finite valid intervals, tombstones, atomic batches and portable archive support; it does not include Rust's SQLite adapter, generic payload API or cryptographic receipts. The upstream is MIT OR Apache-2.0; reference scope and fixture provenance are recorded in [third-party notices](THIRD_PARTY_NOTICES.md).

### New in 0.2.0

- `supersession_fragments(index, max_fragments~)` partitions a revision's entire valid span by its immediately prior winners, including gaps, tombstones and same-value reassertions. JSON carries complete source/replacement claim content. It is structural evidence without a digest or signature.
- `capture_decision(id,key,valid_at,known_at)` records the fact used by a business operation. `audit_decisions(decisions,review_known,max_decisions~)` validates the original evidence and returns results in request order. `needs_recompute` detects fact-state changes; `provenance_changed` also detects same-value source changes. Business rules remain in the caller.

The [three complete scenarios](docs/SCENARIOS.md) demonstrate invoice adjustment, access review and calibration correction evidence with executable assertions.

### Established core behavior

- `Span::new(start, end)` is half-open `[start, end)`; `Span::open(start)` has no right bound. Adjacent spans do not overlap.
- A `Change` assigns an opaque string or a tombstone to one key over a valid-time span. Keys are case-sensitive and nonempty.
- `Ledger::commit(known_at, changes)` validates the entire batch, then returns a new ledger. Knowledge instants must increase strictly between batches. Same-key changes in one batch may touch but may not overlap.
- The latest eligible claim whose valid span contains a queried instant wins. A tombstone is a visible withdrawal; a missing claim means nothing was ever known at that point.
- The caller supplies all instants as `Int64`. The library does not infer timezones, units, a system clock or trusted ordering.

```moonbit
let base = try! @temporal.Ledger::new()
let first = try! base.commit(
  10L,
  [@temporal.Change::put("sku/coffee", @temporal.Span::open(0L), "USD 10")],
)
let revised = try! first.commit(
  20L,
  [@temporal.Change::put("sku/coffee", try! @temporal.Span::new(30L, 45L), "USD 9.50")],
)
assert_eq(revised.get("sku/coffee", 35L, 10L), Some("USD 10"))
assert_eq(revised.get("sku/coffee", 35L, 20L), Some("USD 9.50"))
assert_eq(revised.get("sku/coffee", 50L, 20L), Some("USD 10"))
```

`Ledger::observe` returns the winning revision, including a tombstone. `get` returns only the effective value, so use `observe` or `trace` when absence and withdrawal must be distinguished. `timeline` partitions a valid-time window at every source change; `knowledge_timeline` shows how belief about one valid instant evolved. `diff_timeline`, `change_report` and `compare_all_keys` identify affected spans and keys. `coverage` totals assigned, withdrawn and unclaimed duration in a **finite** window. `sample_matrix` provides a bounded valid-time × knowledge-time grid.

October additions: `snapshot_selected` builds a bounded, sorted two-clock view for an explicit key set; `batch_at` retrieves one complete knowledge-time commit by binary search without scanning unrelated keys. See [October features](docs/OCTOBER_FEATURES.md).

## Ingestion, archives and integration

`Ledger::commit` keeps old values immutable and is convenient for short histories. `LedgerBuilder` appends batches in place for larger ingest workloads; `freeze()` returns a detached ledger checkpoint. `commit_with_report` is a dry-run-friendly way to inspect value-level effects before retaining the returned ledger.

`to_json_string` and `from_json_string` exchange a versioned archive. All `Int64` instants are decimal **strings**, so JavaScript consumers do not round values above 2^53. Text import limits input length, nesting and node count, rejects duplicate object members (including escaped equivalent spellings), validates every batch, and enforces a revision ceiling. `from_json(Json)` accepts an already parsed value, so duplicate keys cannot be detected at that entry point. `to_json_since` and `append_json` transfer a chronological tail; `reconcile` compares full archives and rejects a divergent shared prefix. Archive transport, authentication, durable transactions and replay authorization are the caller's responsibility.

The library's source and formats are documented in [architecture](docs/ARCHITECTURE.md), [API and archive contract](docs/API.md), [performance](docs/PERFORMANCE.md), [benchmark evidence](docs/BENCHMARK.md), [testing](docs/TESTING.md) and [security boundaries](docs/SECURITY.md).

## Run from source

Install a current [MoonBit toolchain](https://docs.moonbitlang.com/en/stable/) and run in this directory:

```sh
moon version --all
moon fmt --check
moon check --target all --deny-warn
moon build --target all --deny-warn
moon test --target all --deny-warn
moon run examples/pricing --target wasm-gc
moon run examples/access --target wasm-gc
moon run examples/calibration --target wasm-gc
moon run examples/rollout --target wasm-gc
moon run examples/maintenance --target wasm-gc
moon run examples/decision_review --target wasm-gc
moon run examples/access_review --target wasm-gc
moon run examples/correction_evidence --target wasm-gc
moon run benchmarks/main --target native --release
```

The examples demonstrate invoice replay after a price correction, incident-time access review, corrected telemetry metadata and rule-rollout comparison. They contain assertions and print short verification summaries. CI is configured to run the same build and test gates on Ubuntu and Windows after the repository is pushed. Four backends are checked and tested locally: Wasm, Wasm-GC, JavaScript and Native.

## Boundaries and limitations

This library stores full revision history in memory and performs string-value comparison. It does not persist data, enforce who may submit a correction, coordinate concurrent writers, interpret calendars or parse business-specific payloads. The immutable `Ledger::commit` copies the full history; use `LedgerBuilder` for repeated large ingest. Timeline and report operations enforce caller-configurable output limits, but an application must also bound its own downstream storage and transmission costs. Revision order is defined by the supplied knowledge instants, not by an untrusted machine clock.

## Project status and license

The implementation is original and AI-assisted, not a port or copy of an upstream library. The implementation uses standard valid-time and knowledge-time concepts. The optional Rust validation tool executes the pinned bitemporal-runtime crate; the MoonBit runtime has no Rust dependency and no copied upstream test corpus. Source and generated fixture provenance are documented in [third-party notices](THIRD_PARTY_NOTICES.md). Source code is licensed under [Apache-2.0](LICENSE).

## 十月第二轮：完整批次分页与多键联合覆盖

`Ledger::batches_since(None, max_batches~, max_revisions~)` 从账本起点读取完整原子批次；传 `Some(known_at)` 则严格读取该游标之后的批次。结果的 `next_known_at()` 可继续请求，`has_more()` 区分结束；首批超过修订预算会报错，避免不前进的空页。空游标能包含 Int64 最小时间戳，无须做游标加一。

`joint_assigned_spans(keys, bounds, known_at, max_keys~, max_segments~)` 返回全部指定键同时有值的最大有效区间；不比较值、不执行授权规则，空键集返回整个 bounds。查询键先以有界集合去重再排序，重复键不占额外唯一键预算。运行 `moon run examples/maintenance --target wasm-gc`。详见 [功能与边界](docs/OCTOBER_FEATURES.md)。
