Skip to content

feat(poiesis-charts): scaffold new crate per B-005 - #4318

Merged
forkwright merged 5 commits into
mainfrom
feat/poiesis-charts-scaffold
May 29, 2026
Merged

feat(poiesis-charts): scaffold new crate per B-005#4318
forkwright merged 5 commits into
mainfrom
feat/poiesis-charts-scaffold

Conversation

@forkwright

@forkwright forkwright commented May 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • Scaffolds the new aletheia-internal poiesis-charts crate per B-005 (poiesis chart subsystem — hand-rolled SVG + Vega-Lite fallback).
  • Lands the parse-don't-validate Chart model, the static render-path decision rule, the deterministic SVG combo emitter that reproduces the offsite slide-3 chart geometry, and the theme-binding seam (Themed/Resolved color modes).
  • Ships per-kind design notes for the eight stubbed emitter arms (bar/column/line/area/scatter/pie/doughnut/stat) so the implementation fan-out is unblocked.

Crate shape

crates/poiesis/charts/
├── Cargo.toml          # poiesis-core + serde + snafu + tracing; insta dev-dep
├── CLAUDE.md
├── docs/design/        # one .md per stubbed kind
└── src/
    ├── lib.rs          # module map + determinism contract + #![deny(missing_docs)]
    ├── error.rs        # Error::{InvalidJson,BadSeriesShape,NakedNumber,VegaRequired,UnresolvedTone,UnresolvedFact,EmitterStub}
    ├── model.rs        # Chart, Series, Axes, FactCite, ChartKind::{render_path,name}
    ├── format.rs       # format_number (only f64→<text> path) + coord (only f64→attr path)
    ├── scale.rs        # Scale + nice() + ticks(); offsite y_left/y_right covered
    ├── theme.rs        # ResolvedTheme + ColorMode + summus stub (deleted when B-002 lands)
    └── render/
        ├── canvas.rs   # Deck (1600×540) + Doc canvases; PlotBox
        ├── kinds.rs    # mod combo;
        ├── kinds/combo.rs  # implemented arm
        └── vega.rs     # cfg(feature = \"charts-vega\") stub

What's implemented vs. stubbed

Arm Status
combo Implemented — renders the offsite navy/teal dual-axis combo. rx=3 columns, r=9 markers, polyline. Themed mode emits var(--tone-*), Resolved emits literal hex.
bar, column, line, area, scatter, pie, doughnut, stat Stubbed (Error::EmitterStub). Per-kind design under docs/design/<kind>.md.
heatmap, boxplot, sankey, candlestick, log/time scales Routed to charts-vega feature. Hard-fail with Error::VegaRequired when disabled.
combo gridlines + tick text Structural <g class=\"gridlines\"> / <g class=\"axes\"> placeholders emitted. Tick math is in scale::ticks but not yet wired into the emitter.
insta golden snapshot of the full offsite SVG Pending — lands with the gridline + tick text follow-up.
Vega-Lite shell-out wiring Pending. render/vega.rs exists as a feature-gated stub.

B-005 acceptance — current state

# Acceptance criterion Status in this PR
1 Reproduce offsite slide-3 chart from schema Geometry + colors + corners + markers present; gridlines + tick text follow-up.
2 Colors come only from theme: summus; zero color literals in spec ResolvedTheme::summus_stub mirrors the offsite palette; tests assert navy/teal route via theme.
3 All data points are Cite(FactId); verify reconciles Point::y: FactCite types it in. Verifier integration lands with B-008.
4 Emitted SVG is deterministic (insta golden) Re-emit-must-be-byte-identical test in place; full byte-golden of the offsite SVG follows the gridline/tick arm.
5 Same SVG embeds in HTML deck (themed) and PPTX (resolved or baked) ColorMode::Themed/Resolved both emit; HTML/PPTX wiring lands with B-003/B-004.
6 Switching theme recolors with zero spec edits ResolvedTheme is the seam; swapping it switches every fill/stroke.
7 Vega-only kind with charts-vega off → hard parse error Chart::validate returns Error::VegaRequired. Tested.

Completion plan for follow-ups

Each stub is tracked in crates/poiesis/charts/docs/design/<kind>.md. The natural ordering:

  1. combo gridlines + tick text + insta golden — closes the determinism leg of acceptance feat(ui): WebUI code review fixes — hljs, emoji, mobile, slash commands #4.
  2. column + line arms — direct extracts from the combo primitives (~70% reuse).
  3. bar + area + scatter — small variations on column/line.
  4. pie + doughnut + stat — share an arc-path primitive.
  5. Vega-Lite wiring in render/vega.rs — under the charts-vega feature.
  6. B-002 wire-up — delete ResolvedTheme::summus_stub and depend on poiesis-theme when it lands.

Gate

  • cargo fmt --check — PASS
  • cargo check --workspace — PASS
  • cargo clippy -p poiesis-charts --all-targets -- -D warnings — PASS
  • cargo test -p poiesis-charts — PASS (30 tests)
  • kanon gate --tier full — PASS (Gate-Passed trailer on HEAD)
  • mcp__kanon__standards_name_check word=charts — free (no collisions)

Naming

charts passes GNOMON L1–L4:

  • L1: lowercase ASCII, pronounceable, grep-friendly, 6 chars.
  • L2: follows the existing poiesis sub-crate register (slides, sheet, text, doc, lint, verify, intake, scaffold, inspect, typst, diff — English domain words rather than Greek roots, deliberately distinct from the aletheia//theatron/ Greek-named cognition substrate).
  • L3: matches what the crate is — the chart subsystem of the report family.
  • L4: every chart kind name (bar, column, line, area, combo, scatter, pie, doughnut, stat, heatmap, boxplot, sankey, candlestick) is the canonical chart-vocabulary word — name-checked free, no Greek strain.

Test plan

  • CI nextest on poiesis-charts
  • Confirm --all-targets clippy still passes under workspace-wide gates
  • Operator review of docs/design/*.md before kicking off the per-kind fan-out

SonarCloud quality gate: spurious SCA finding

The SonarCloud "C Security Rating on New Code" failure on this PR is a single SCA (dependency-analysis) annotation against the new crate's Cargo.toml:

Dependency versions are not predictable if the lock file (Cargo.lock) is missing.

This is a false positive for a cargo-workspace member crate. Cargo workspaces produce one shared Cargo.lock at the workspace root; member crates do not (and should not) carry their own lockfile — see The Cargo Book §Cargo.toml vs Cargo.lock and the Cargo workspace resolver. The repo's authoritative Cargo.lock lives at the workspace root (also touched by this PR for the new member's transitive deps), so every dependency is pinned to an exact version — Sonar's SCA simply does not walk back to the workspace root from a member Cargo.toml.

Operator decision: marked spurious. Suggested durable fix (separate, repo-wide PR) is one of:

  • Add a sonar-project.properties at the repo root with sonar.exclusions=crates/**/Cargo.toml (excludes member manifests from SCA only — root Cargo.toml + Cargo.lock still drive dependency analysis), or
  • Wait for SonarCloud SCA to learn cargo-workspace lockfile inheritance (raised upstream at SonarSource SCA troubleshooting).

Other checks on this PR are green (no AI attribution, gate-attestation with Gate-Passed: kanon 0.1.0, cargo deny, scan). The included refactor commit centralizes the spec-mandated XML namespace constant — orthogonal quality work, not a Sonar fix.

@forkwright

Copy link
Copy Markdown
Owner Author

Triage: the failing condition is C Security Rating on New Code, driven
by a single S5332 ("Using clear-text protocols is security-sensitive")
hotspot on:

  • crates/poiesis/charts/src/render/kinds/combo.rs:127
    xmlns=\"http://www.w3.org/2000/svg\"

That URI is the canonical SVG namespace identifier (matched by string
equality, never fetched), required by every consumer of the emitted SVG.
S5332 fires on the literal regardless of context; marking the hotspot
Safe in SonarCloud once clears the gate.

Unrelated: Security / No AI Attribution were queued behind a dead
actions-runner-aletheia (22:21Z, disk-full). Runner is back up
(23:03:34Z), this PR's backlog is draining. Substrate context in #4319.

@forkwright
forkwright force-pushed the feat/poiesis-charts-scaffold branch 3 times, most recently from 04b6bfa to a8c8eb8 Compare May 29, 2026 05:31
forkwright and others added 5 commits May 29, 2026 14:30
…l + deterministic SVG emitter

New aletheia-internal crate `poiesis-charts` implementing the B-005
acceptance contract foundations:

- Parse-don't-validate `Chart` model with closed `ChartKind`, newtyped
  `FactId`/`FactCite`, and an `Axes` tree whose defaults match the
  offsite slide-3 chart's nice-rounded extents.
- Static render-path decision rule: `ChartKind::render_path()` plus
  axis-scale routing at `Chart::validate`. Vega-Lite kinds and `log`/
  `time` scales hard-fail with `Error::VegaRequired` when the
  `charts-vega` feature is off.
- Hand-rolled SVG combo emitter (`render::kinds::combo`) reproducing
  the offsite navy/teal dual-axis composition, with the determinism
  contract enforced in code: `format::format_number` is the only path
  from `f64` to chart text, `format::coord` is the only path to SVG
  coordinate attributes, group source order is fixed.
- `theme::ResolvedTheme` seam carrying `Themed` (CSS var) and
  `Resolved` (literal hex) color modes — geometry identical between
  them so HTML deck and PPTX-bake outputs match byte-for-byte up to
  fill attribute.
- Per-kind design notes for the eight stubbed arms
  (bar/column/line/area/scatter/pie/doughnut/stat) under
  `crates/poiesis/charts/docs/design/` ready for implementation
  fan-out.
- 30 unit tests; combo emitter has a re-emit-must-be-byte-identical
  determinism test.

Stubbed: per-kind emitter arms beyond `combo`; tick + gridline
rendering inside `combo`; Vega-Lite shell-out wiring. Each is a typed
`Error::EmitterStub` and tracked by name in `render.rs` + the design
docs.

Gate-Passed: kanon 0.1.0
…emitter

Extract the W3C SVG 1.1 XML namespace URI into a single
`SVG_NAMESPACE` constant and reference it from `emit_svg_open`. The
constant carries the spec citation at the declaration site so future
emitters can reuse the same source-of-truth instead of repeating the
literal.

Behavior is unchanged: the emitted combo SVG is byte-identical to
before, verified by the existing `output_is_deterministic_across_two_renders`
test.

Gate-Passed: kanon 0.1.0
Adds a # Examples section to format_number to illustrate the three
most common format modes. Also refreshes _llm/L3-api-index/poiesis-charts.md
and manifest.toml to reflect the updated doc comment.

Gate-Passed: kanon 0.1.0
@forkwright
forkwright force-pushed the feat/poiesis-charts-scaffold branch from e475bdd to 74ee7fd Compare May 29, 2026 16:25
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@forkwright
forkwright merged commit fa2e374 into main May 29, 2026
8 of 9 checks passed
@forkwright
forkwright deleted the feat/poiesis-charts-scaffold branch May 29, 2026 16:28
forkwright pushed a commit that referenced this pull request May 30, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.30.0](v0.29.0...v0.30.0)
(2026-05-30)


### Features

* **aletheia:** faithful import consumer for
[#4163](#4163) (PR3/4)
([#4357](#4357))
([4e49dbe](4e49dbe))
* **aletheia:** populate working_state + typed knowledge in export
([#4163](#4163) PR2/4)
([#4351](#4351))
([702129e](702129e))
* **diaporeia:** wire memory.* MCP tools to organon executor
([#4117](#4117))
([#4352](#4352))
([7f2e243](7f2e243))
* **graphe:** portability raw entry points for
[#4163](#4163) (PR1/4)
([#4349](#4349))
([f267fe5](f267fe5))
* **poiesis-charts:** implement column + bar SVG emitters (B-005)
([#4368](#4368))
([3559002](3559002))
* **poiesis-charts:** implement line + area SVG emitters (B-005)
([#4372](#4372))
([2ca94ac](2ca94ac))
* **poiesis-charts:** implement pie + doughnut SVG emitters (B-005)
([#4371](#4371))
([996dbf3](996dbf3))
* **poiesis-charts:** implement scatter SVG emitter (B-005)
([#4369](#4369))
([6261c8b](6261c8b))
* **poiesis-charts:** implement stat KPI SVG emitter (B-005)
([#4374](#4374))
([1416fa0](1416fa0))
* **poiesis-charts:** implement Vega-Lite shell-out emitter (B-005)
([#4373](#4373))
([e6c9b70](e6c9b70))
* **poiesis-charts:** scaffold new crate per B-005
([#4318](#4318))
([fa2e374](fa2e374))
* **poiesis-core:** land B-001 envelope + factbase + open component
registry ([#4350](#4350))
([0cb2bd3](0cb2bd3))
* **poiesis-doc:** pandoc availability probe + flake.nix pin (B-014)
([#4361](#4361))
([d5efe1d](d5efe1d))
* **poiesis-doc:** scaffold Pandoc backend module, AST serializer, Lua
filter stubs (B-012)
([#4365](#4365))
([23dbe91](23dbe91))
* **poiesis-sheet:** wire B-007 workbook feature flag, error type, and
module declarations
([#4353](#4353))
([f6e574d](f6e574d))
* **poiesis-theme:** B-002 foundation — CSS byte-parity, OOXML
clrScheme+fontScheme, doc-vars, extended summus tokens
([#4370](#4370))
([b0c4319](b0c4319))
* **poiesis-theme:** base PPTX sink (B-002-B)
([#4378](#4378))
([f072bfe](f072bfe))
* **poiesis-theme:** LaTeX template sink (B-002-C)
([#4380](#4380))
([8cc683d](8cc683d))
* **poiesis-theme:** reference.docx sink (B-002-D)
([#4381](#4381))
([520a9b3](520a9b3))
* **poiesis-theme:** Typst template sink (B-002-C)
([#4379](#4379))
([cf48d61](cf48d61))
* **poiesis:** add chromium CDP printer for HTML-to-PDF conversion
([#4377](#4377))
([bd4e29e](bd4e29e))
* **poiesis:** add deck-layout solver and deck HTML/CSS renderer
([#4360](#4360))
([5aa1472](5aa1472))
* **poiesis:** add image-text, timeline, comparison, blank component
packs ([#4367](#4367))
([457efd7](457efd7))
* **poiesis:** add stat, quote, chart, table, image-full component packs
([#4366](#4366))
([4a3df26](4a3df26))
* **poiesis:** add title, section, bullet, two-col component packs
([#4356](#4356))
([dde8451](dde8451))
* **poiesis:** retire poiesis-text; migrate callers to poiesis-doc/typst
(B-013) ([#4354](#4354))
([f13df45](f13df45))


### Documentation

* **instance:** refresh stale v0.13.x version references to v0.29.0+
line ([#4347](#4347))
([01bc922](01bc922))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

1 participant