Skip to main content

Module chart

Module chart 

Source
Expand description

The deterministic local chart document (mission burndown-scope-analytics-01M3C9J1, WP04 T019–T023).

§What this module is

ChartExport is the portable document form of one folded series: a versioned, local, read-only JSON payload carrying the scope and its period, the counting mode and unit, the ordered daily points, the optional end-date projection, the aggregate coverage tally, the documented formula version and every finding — the document half of data-model.md §6 and of contracts/chart-export.md. It is output, never state (C-001): nothing here opens a store, reads a clock, writes an issue, or creates a second analytics store.

BurndownSeries (+ optional Projection) ──ChartExport::build──▶ versioned local document

§Mirrored, never re-derived (C-008)

The shared values are projected, not recomputed. ChartExport carries a copy of the series’ own scope, mode, unit and formula version, its points, its findings and its CoverageSummary — every number is a shared DTO field, read once (R9). The module does no arithmetic at all: it copies, sorts and names. The one place it could have drifted, a forecast, is a shared value too: ChartProjection mirrors WP03’s Projection rather than drawing a second one.

AnalyticsScope (WP01’s events.rs) and BurndownSeries (WP02’s series.rs) carry no serde derives and are owned by other packages, so the fields this document needs are mirrored into its own serde types (ChartScope, ChartSeries) — the same portable-document precedent crate::capacity::portable and crate::time_tracking::portable follow. The shared leaf types that already derive serde (Mode, Coverage, DailyPoint, CoverageSummary, AnalyticsDiagnostic, ProjectionStatus) are reused verbatim, so the wire vocabulary is the contract’s own and cannot be re-spelled here.

§The marker is read before the shape (T019)

ChartExport::from_json reads the format marker first, so a payload that is not a chart document at all — a tix-capacity, tix-roadmap, tix-health, tix-time or tix-views document, whose fields overlap structurally — is refused by name rather than by whatever shape error its fields happened to raise. A document of a different SCHEMA_VERSION is read, its own fields kept — including the document-level keys this build does not interpret, which travel in ChartExport::unknown — and the mismatch is reported as a VERSION_MISMATCH finding (merged, never appended twice), which is the checked-and-reported third answer between refusing a newer writer and silently dropping its fields.

§Preserved data has its own namespaces (T019)

Data this build does not interpret travels in two channels, and neither can shadow a projected field:

  • the nested extensions object — the channel this build writes, through ChartExport::insert_extension. Because it is its own object, a preserved key named format, series or scope lands inside it and can never duplicate — or be read in place of — the projected field beside it, for this reader or for a lenient one. Flattening an extension bag onto a projected object is what produced a duplicate JSON key (and an unloadable file) in an earlier mission.
  • the flattened catch-all (ChartExport::unknown) — the channel this build only reads. serde delivers a key a newer writer placed directly on the document here, and it is re-emitted verbatim; that is what keeps the version finding’s promise (“its fields are preserved rather than refused or dropped”) true. build always starts it empty and there is no write side, so this build can never emit a second projected key: a reader cannot make it produce a document it then refuses.

§Absent is null, never 0 (C-005, T021)

Every unknown number is null on the wire (a serde Option), never a fabricated zero, and a day with no recorded history keeps its known / coverage_limited / unknown marker beside it. The document states null values plainly, so a consumer cannot mistake “not known” for “nothing happened”.

§Ordering and precision are fixed (T020)

Days are the series’ calendar order, findings are sorted by their own Ord, and both preserved-data channels are BTreeMaps, so key order is normalised. Every value is a whole number (precision): count mode counts whole issues and estimate mode counts whole converted units, because the shared conversion refuses a fractional result rather than rounding it (crate::wip::WipConfig::convert). The only fractional quantity in a document is a projection’s observed_rate, and the projection node documents that beside it. Serialization is therefore a pure function of the input: the same series, mode and period produce identical bytes, and the bytes this build emits re-read as a byte fixpoint (ChartExport::to_json_pretty → ChartExport::from_json → ChartExport::to_json_pretty).

§A closed snapshot is provenance, not a re-read (C-006, T022)

The scope’s snapshot_id is carried into ChartScope::snapshot_id and the fold’s own ClosedSnapshot finding travels with the series’ diagnostics, so a document says which closed view it describes. Nothing is recomputed from current state: an event recorded after the close is not part of the document’s points, exactly as it is not part of the fold.

§A recorded estimate_changed is reported, never replayed

WP03’s decision (units.rs, tracer design-decisions): an estimate change recorded inside the period is reported — its day is coverage-limited and the finding travels in the series’ diagnostics — and is not replayed into the levels. This module inherits that by copying the series verbatim; it never re-values a recorded change, which would be a second formula and would double-count against the stated estimate every other contribution is valued at.

§Remote namespaces stay separate (T022)

tix export --target gitlab|linear mirrors issues to a remote tracker and shells out to glab/linear-cli; this document is a local interchange form and never reaches either tool. Where a remote target is configured, its inability to carry a chart or time-series field is diagnosed explicitly in the document (UNSUPPORTED_REMOTE_FIELD, naming the target and the day counts that stay local), because a silent drop and an invented mapping are both worse than a named refusal. src/sync/** is untouched, and chart is dispatched by name before Tool::from_target is consulted.

§Read-only, on every path

Build, serialize and read back all take values and return values: no file is opened for writing, no History event is appended, and no issue is mutated (C-007).

Structs§

ChartDiagnostic
One document-level finding, in the chart document’s own vocabulary (T021/T022).
ChartExport
Everything a local chart surface knows, in one versioned document (data-model.md §6).
ChartPeriod
The inclusive calendar period a document covers (data-model.md §1).
ChartProjection
The optional read-only end-date forecast, in the document’s own serde shape (data-model.md §5).
ChartScope
The scope a document describes, in the document’s own serde shape (data-model.md §1/§6).
ChartSeries
The ordered daily series, in the document’s own serde shape (data-model.md §4).

Constants§

FORMAT
The document’s format marker. A consumer reads this before treating the payload as a chart document; a document with another marker is refused by name.
SCHEMA_VERSION
The document’s schema version. Bumped when the shape changes incompatibly.
UNSUPPORTED_REMOTE_FIELD
The stable kind spelling of a “a configured remote target cannot carry chart data” finding.
VERSION_MISMATCH
The stable kind spelling of a “different document version” finding.

Functions§

precision_of
The precision of the series values under mode (data-model.md §3: “Values use the selected mode’s precision and unit”, T020).