Skip to content

schema: make serde_json arbitrary_precision opt-in (exact-numbers) - #48

Merged
ymote merged 1 commit into
mainfrom
fix/schema-exact-numbers-opt-in
Sep 29, 2026
Merged

ymote merged 1 commit into
mainfrom
fix/schema-exact-numbers-opt-in

Conversation

@ymote

@ymote ymote commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

Problem

octoscript-schema enabled serde_json/arbitrary_precision on its plain dependency. Cargo unifies features, so every crate that depends on octoscript-schema, directly or through octoscript-capabilities / octoscript-workflow, turned on arbitrary_precision for its whole dependency graph. That changes serde_json's Number representation for every other crate in the build, which breaks untagged or flattened numbers and any code that inspects Number. octos can't take these crates for that reason, and today octos-research uses only octoscript-core.

Fix

  • octoscript-schema: adds a new exact-numbers = ["serde_json/arbitrary_precision"] feature, off by default. The plain serde_json dependency no longer enables the feature.

  • octoscript-capabilities, octoscript-workflow: depend on schema (and workflow on capabilities) with default-features = false. Each exposes an exact-numbers passthrough feature.

  • Leaf binaries keep today's behaviour: octoscript-cli (the octoscript binary), octoscript-media-host and the fuzz crate enable exact-numbers. octoscript-lsp uses schema, capabilities and workflow only as dev-dependencies, so it can't leak the feature to consumers.

  • Why off by default rather than on: arbitrary_precision is global and changes behaviour elsewhere. Only the crate that owns the final graph should turn it on. If the feature were on by default, every consumer would have to remember default-features = false on every path to schema, and one default edge anywhere would turn it back on.

  • Tests: the two tests that need precision beyond f64 are gated on #[cfg(feature = "exact-numbers")]: the existing 40-digit decimal test and a new 30-digit integer bound. New tests pass without the feature:

    • inclusive integer min/max
    • i64::MIN / u64::MAX extremes
    • f64-representable decimals, including 0.1 + 0.2 > 0.3
    • exponents such as 1e21, 1E+21, -1e-7 and 1e2 for integer
    • min > max across representations

    One more test, which runs only without the feature, documents what is lost.

  • Docs: a new "Numeric precision" section in docs/schema-contracts.md, crate-level docs, and a README line.

  • CI: tests the three library crates without the feature, and fails if arbitrary_precision shows up again in their normal cargo tree.

What is lost without exact-numbers

ExactNumber already parses serde_json's shortest round-trip output, so without the feature, bounds stay exact for every integer in the i64/u64 range and every decimal that round-trips through f64 (0.1, 2.5e-3, 1e21). Only literals that need more precision are affected: a 30-digit integer bound, or 0.30000000000000000001 vs 0.30000000000000000002. serde_json rounds these to the nearest f64 at parse time, in both the schema and the value, so they can compare equal. Nothing else about validation changes.

Downstream note: OctoSense's octosense-toolbox depends on octoscript-schema directly. When it bumps its pin it gets the f64 behaviour unless it enables exact-numbers. Its template bounds are small integers (for example 1..720), which are exact either way.

Verification (macOS, local)

  • cargo fmt --check; cargo +nightly fmt --manifest-path fuzz/Cargo.toml --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo clippy -p octoscript-schema -p octoscript-capabilities -p octoscript-workflow --all-targets --no-default-features -- -D warnings
  • cargo test --workspace --all-features and cargo test --workspace: all green
  • cargo test -p octoscript-schema -p octoscript-capabilities -p octoscript-workflow --no-default-features: schema 12, capabilities 127, workflow 97 tests pass
  • cargo test -p octoscript-schema --features exact-numbers: 13 pass
  • cargo test -p octoscript-lsp, -p octoscript-core, -p makepad-script, -p makepad-regex: all pass
  • RUSTFLAGS='--cfg fuzzing' cargo +nightly check --locked --manifest-path fuzz/Cargo.toml: ok
  • cargo tree -e features -i serde_json -p <crate> for every workspace crate: arbitrary_precision appears only for octoscript-cli and octoscript-media-host. It is also absent for workflow and capabilities with all their other features on.
  • Not run here: the Linux-only sandbox steps (cross-target checks, bubblewrap/cgroup ignored tests).

octos proof (throwaway worktree, not a PR)

This used a scratch worktree of octos origin/main (90617dc1). In it, octos-research pointed octoscript-core at this branch by path, added octoscript-capabilities, octoscript-schema and octoscript-workflow with default-features = false, and aligned its makepad-script pin to Octoscript's bf318136.

  • cargo tree -e features -i serde_json -p octos-cli --no-default-features --features api has 0 arbitrary_precision lines, while schema, capabilities and workflow all show in the tree. Control: adding features = ["exact-numbers"] to the schema dependency makes serde_json feature "arbitrary_precision" → octoscript-schema feature "exact-numbers" → octos-research appear.
  • cargo build -p octos-cli --no-default-features --features api: ok.
  • cargo test -p octos-research: ok, 201 tests. This includes a probe test that deserializes an untagged/flattened number, validates a schema and runs a one-step WorkflowEngine plan.

The worktree was deleted afterwards.

🤖 Generated with Claude Code

octoscript-schema enabled serde_json's arbitrary_precision on its plain
dependency. Cargo unifies features, so every consumer of octoscript-schema,
octoscript-capabilities or octoscript-workflow got arbitrary_precision across
its whole graph, which changes serde_json's Number representation and breaks
untagged/flattened numbers elsewhere (octos cannot depend on these crates).

- octoscript-schema: new `exact-numbers` feature, off by default, that turns
  on serde_json/arbitrary_precision. ExactNumber already parses serde_json's
  shortest round-trip output, so without the feature bounds stay exact for
  every i64/u64 integer and every f64-representable decimal; only literals
  past that precision are rounded by serde_json at parse time.
- octoscript-capabilities / octoscript-workflow: depend on schema (and
  capabilities) with default-features = false and expose an `exact-numbers`
  passthrough.
- octoscript-cli and octoscript-media-host (leaf binaries) and the fuzz
  crate enable exact-numbers, so Octoscript's own behaviour is unchanged.
- Tests: the two beyond-f64 tests are gated on exact-numbers; new tests cover
  int and decimal bounds, i64/u64 extremes, exponents and inclusive
  min/max boundaries without the feature, plus one documenting the loss.
- Docs: numeric precision section in docs/schema-contracts.md, crate docs,
  README. CI tests the three library crates without the feature and fails if
  arbitrary_precision reappears in their normal graph.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ymote
ymote merged commit ee6f6e9 into main Sep 29, 2026
8 checks passed
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