Skip to content

polynomial: canonicalize sparse capability inputs - #920

Merged
morluto merged 11 commits into
morluto:mainfrom
kaoru0822-kitauji:agent/canonical-input-pr1
Aug 10, 2026
Merged

polynomial: canonicalize sparse capability inputs#920
morluto merged 11 commits into
morluto:mainfrom
kaoru0822-kitauji:agent/canonical-input-pr1

Conversation

@kaoru0822-kitauji

@kaoru0822-kitauji kaoru0822-kitauji commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • canonicalize bounded sparse polynomial inputs at existing capability request boundaries
  • combine duplicate exponent vectors exactly, remove cancellations and zero terms, and impose deterministic order
  • preserve strict stored polynomial contracts, canonical artifact identity, and independent checker authority
  • preflight structural and cross-field invariants for every canonicalizing request before bounded duplicate arithmetic
  • cap duplicate inputs at 256 coefficient digits and 64 terms per exponent vector
  • preregister and record a bounded host-local mechanism study

Review hardening

The final review pass keeps support-dependent operation semantics on the exact canonical request, so an out-of-budget monomial that cancels does not reject a cheap canonical polynomial. A Pydantic validation context preserves structural and cross-field checks for every canonicalizing request before coefficient accumulation. Duplicate accumulation is bounded before arithmetic by both coefficient size and group cardinality.

Validation

  • upstream base: 0052a5bf78f63f5539be13da6493abb395c5026d
  • PR head: 334efd0717a31408a66d12dc71015bba6f4e52d8
  • focused polynomial contract, evaluation, inverse, identity, and interval lane: 65 passed
  • make check: 856 unit tests plus lint, format, complexity, and type gates passed
  • make test-plan BASE=origin/main: passed; selected the suite fallback because the frozen benchmark config has no exact ownership mapping
  • make test-changed BASE=origin/main: unit, domain, composition, storage, process, MCP, end-to-end, static, build, and documentation lanes passed
  • component lane: inherited macOS timeout-marker races in test_carcara_timeout_fails_closed and/or test_drat_timeout_fails_closed; the branch changes neither checker nor test, and DRAT also failed in isolation
  • make docs-linkcheck and git diff --check: passed

Evidence boundary

Both frozen conditions completed all 9 scheduled slots. Upstream produced 6 accepted and 3 rejected outcomes. Canonicalization produced 6 accepted, 1 rejected, and 2 preserved inconclusive outcomes: one external interruption and one reasoning-protocol ambiguity. Neither was rerun.

Canonicalization removed both observed representation failures and directly enabled a duplicate/zero/reordered polynomial-map candidate to reach COMPLETED/VERIFIED, but aggregate acceptance remained 6/9. This is host-local mechanism evidence, not a causal benchmark claim. No model rollout was rerun during review closeout.

Typed recovery remains isolated from this product change in #928.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bdcdd9712f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/jacobian/polynomials/_support.py Outdated
) -> RequestModel:
try:
return model.model_validate(payload)
return model.model_validate(_canonicalize_sparse_polynomial_inputs(payload))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate the full request before combining terms

For a schema-valid request that fails only a model-level cross-field rule—for example, an inverse map whose variables differ from target_variables—this call canonicalizes every sparse leaf and performs arbitrary-precision Fraction additions before PolynomialMapInverseVerifyRequest.model_validate can reject the mismatch. Consequently, an invalid request can consume the full bounded input's exact-arithmetic cost; validate a concrete noncanonical request model, including its cross-field invariants, before combining terms and producing the canonical request.

AGENTS.md reference: AGENTS.md:L155-L157

Useful? React with 👍 / 👎.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment thread src/jacobian/polynomials/_support.py Outdated
) -> RequestModel:
try:
return model.model_validate(payload)
return model.model_validate(_canonicalize_sparse_polynomial_inputs(payload))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Existing polynomial identity guidance for duplicate terms is now unreachable and its test fails

Sparse polynomial inputs are now silently combined and cleaned up (_canonicalize_sparse_polynomial_inputs at src/jacobian/polynomials/_support.py:558) before the request is checked, so a request with duplicate cancelling terms that used to be rejected with recovery guidance is now accepted and answered as a normal result.
Impact: A previously guaranteed rejection path disappears for all polynomial capabilities and the existing check that guards it now fails.

Mechanism: canonicalization removes the duplicate-exponent rejection exercised by an existing runtime test

tests/boundary/providers/sympy/runtime/test_polynomial_identity.py:203-228 invokes polynomial.identity.verify with left containing x and -x (duplicate exponent vectors) and asserts ERROR with INVALID_POLYNOMIAL_IDENTITY_REQUEST plus the hint "Combine duplicate exponent vectors". After the change, _canonical_sparse_polynomial (src/jacobian/polynomials/_support.py:91-113) merges the two terms to zero and drops them, producing {"terms": []}, which validates cleanly and matches right, so the capability now returns a successful identity result. The PR updates tests/composition/runtime/test_polynomial_map_inverse_verify.py and tests/unit/contracts/test_polynomial_contracts.py but leaves this contradicting test (and the recovery hint it documents) unchanged.

Prompt for agents
The new boundary canonicalization in src/jacobian/polynomials/_support.py (_canonicalize_sparse_polynomial_inputs applied inside _validate_request) makes duplicate/cancelling/zero sparse terms acceptable for every polynomial capability. tests/boundary/providers/sympy/runtime/test_polynomial_identity.py::test_polynomial_identity_duplicate_terms_return_actionable_recovery still asserts that such input is rejected with INVALID_POLYNOMIAL_IDENTITY_REQUEST and a 'Combine duplicate exponent vectors' hint. Decide whether the identity capability should keep rejecting noncanonical input (in which case the canonicalization should not apply to it) or whether the test and any documentation describing the duplicate-term recovery guidance should be updated to the new accepted-and-normalized behavior. Also check reference docs/capability descriptors that still advertise the old rejection.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +96 to +100
for term in parsed.terms:
coefficients[term.exponents] = (
coefficients.get(term.exponents, Fraction())
+ term.coefficient.as_fraction()
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Crafted polynomial requests can make the server perform extremely heavy exact arithmetic before any size budget applies

Coefficients of repeated monomials are added together (_canonical_sparse_polynomial at src/jacobian/polynomials/_support.py:96-100) before the request's own coefficient-size limits are checked, so a request full of huge repeated terms forces very expensive exact arithmetic on input that is ultimately rejected.
Impact: A single crafted request can tie up a worker for a long time instead of being rejected quickly.

Mechanism: exact Fraction accumulation over up to 4,096 terms with 32,768-digit components

_SparsePolynomialInput (src/jacobian/polynomials/_support.py:82-88) permits MAX_POLYNOMIAL_TERMS = 4,096 terms, and each coefficient is a CanonicalRational whose components may hold up to 32,768 digits (src/jacobian/contracts/exact.py:24, 79-86). When many of those terms share the same exponent vector, the canonicalizer performs repeated Fraction additions whose common denominator grows multiplicatively (potentially hundreds of millions of digits), then formats the result. Previously duplicate exponent vectors were rejected immediately by SparseRationalPolynomial.require_unique_canonical_term_order (src/jacobian/contracts/polynomials.py:117-128), and per-operation coefficient digit budgets such as require_bounded_rational(..., max_digits=256) were applied only after model validation, so this arithmetic now happens strictly before any operation budget check.

Prompt for agents
In src/jacobian/polynomials/_support.py, _canonical_sparse_polynomial accumulates exact Fractions across up to MAX_POLYNOMIAL_TERMS (4,096) coefficients whose numerator/denominator may each hold up to 32,768 digits, and it runs before the request model's operation budgets (e.g. require_bounded_rational with max_digits=256) are applied. Consider bounding the input coefficient digit size (or the number of duplicate exponent vectors) inside _SparsePolynomialInputTerm/_SparsePolynomialInput so oversized inputs are rejected before any big-integer accumulation occurs.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread src/jacobian/polynomials/_support.py Outdated
Comment on lines +116 to +128
def _canonicalize_sparse_polynomial_inputs(value: object) -> object:
"""Canonicalize exact sparse leaves while retaining the request structure."""

if isinstance(value, Mapping):
if "terms" in value and set(value) == {"terms"}:
return _canonical_sparse_polynomial(value).model_dump(mode="json")
return {
key: _canonicalize_sparse_polynomial_inputs(item)
for key, item in value.items()
}
if isinstance(value, (list, tuple)):
return [_canonicalize_sparse_polynomial_inputs(item) for item in value]
return value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Normalization silently rewrites the candidate object submitted for verification

For polynomial.map.inverse.verify the inverse_map is the candidate under examination, and it is now rewritten by the boundary before the authoritative model is built. Mathematically this is equivalence-preserving (like terms combined exactly, exact zeros dropped), and the independent checker still runs on the stored canonical artifact, so checker authority is not weakened. Reviewers should nonetheless be aware that the artifact URI an agent receives is now derived from the canonicalized form, so two distinct submitted encodings deduplicate to a single artifact identity — worth confirming against evidence-binding expectations in docs/reference before release.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +40 to +63
| Condition | Exact accepted | Rejected | Inconclusive | False certification |
| --- | ---: | ---: | ---: | ---: |
| Upstream | 6/9 | 3/9 | 0/9 | 0 |
| Canonicalization | 6/9 | 1/9 | 2/9 | 0 |

The primary accepted count did not improve. Canonicalization did remove both
observed `INVALID_POLYNOMIAL_MAP_INVERSE_REQUEST` representation failures. It
also enabled two successful exact inverse-checker calls instead of one. In
`symbolic-coordination-semantic-equivalence-01-r03`, the agent passed duplicate,
cancelling, zero, and reordered sparse terms directly to
`polynomial.map.inverse.verify`; the boundary stored canonical maps and the
independent checker returned `VERIFIED`. Generic outer-request shape failures
remained: three `INVALID_REQUEST` calls were observed in each condition.

One canonicalization rollout, task 2 repetition 2, was interrupted externally
after it began. It was preserved as `INCONCLUSIVE/INFRASTRUCTURE_FAILURE`; the
model was not rerun. Task 3 repetition 1 produced a mathematically correct
terminal object, but its reasoning run IDs were ambiguous, so the finalizer
correctly recorded `INCONCLUSIVE/REASONING_PROTOCOL_INCOMPLETE`. These outcomes
explain why the raw 6/9 acceptance count must not be restated as 6/7 without an
explicit complete-case qualifier. Six canonicalization trajectories also
reported noncanonical trajectory-value extraction errors in the observational
runner; terminal verification and raw traces remain available, but those
secondary extracted values are not evidence.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Preregistration document records numbers that differ from the PR description

The checked-in study write-up reports a completed 9-rollout canonicalization arm (6/9 accepted, 1 rejected, 2 inconclusive), while the PR description states the treatment arm was stopped early after 4 runs (3 accepted, 1 rejected). One of the two accounts is stale; since this document is checked into docs/reference/ as evidence, the mismatch should be reconciled before merge.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1fcfe25a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +115 to +117
for term in parsed.terms:
if term.coefficient.num != "0":
representatives.setdefault(term.exponents, term)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply operation limits after cancelling duplicate terms

When duplicate terms cancel an otherwise out-of-budget monomial, this structural representative still retains the first nonzero term. For example, an evaluation map containing x^33 - x^33 is rejected by the representative's degree-32 operation check even though the canonical map is zero and requires no high-degree computation. This makes semantically equivalent sparse encodings diverge; the preflight should enforce structural and cross-field invariants without applying operation limits to supports that canonicalization removes.

Useful? React with 👍 / 👎.

Comment on lines +135 to +139
for term in parsed.terms:
coefficients[term.exponents] = (
coefficients.get(term.exponents, Fraction())
+ term.coefficient.as_fraction()
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound the aggregate duplicate-term denominator

For a schema-valid group of up to 4,096 terms sharing an exponent and having pairwise-coprime 256-digit denominators, the per-term check passes while this loop constructs a Fraction whose denominator approaches one million digits; only afterward does CanonicalRational.from_fraction reject it against the 32,768-digit contract limit. Because this arithmetic runs synchronously before any capability timeout, a bounded request can still consume disproportionate CPU and memory, so impose a group/result-size budget before performing the additions.

AGENTS.md reference: AGENTS.md:L16-L17

Useful? React with 👍 / 👎.

@kaoru0822-kitauji
kaoru0822-kitauji force-pushed the agent/canonical-input-pr1 branch 3 times, most recently from 647107c to d447278 Compare August 9, 2026 13:46
@kaoru0822-kitauji
kaoru0822-kitauji force-pushed the agent/canonical-input-pr1 branch from d447278 to 3b242f6 Compare August 9, 2026 13:49

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b242f6487

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +640 to +643
canonical_payload = _normalize_sparse_polynomial_inputs(
payload,
normalize=_canonical_sparse_polynomial,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preflight all request models before accumulating terms

Although inverse verification now has a structural preflight, the other _validate_request call sites still canonicalize before validating the complete request. For example, a schema-valid evaluation whose point dimension mismatches its map can include the full bounded set of duplicate groups, causing synchronous exact Fraction accumulation before PolynomialEvaluationRequest rejects the cross-field mismatch; add equivalent structural preflight validation for the remaining request models while deferring operation budgets until after cancellation. This is fresh evidence beyond the previously reported inverse-map case because the new guard is explicitly limited to PolynomialMapInverseVerifyRequest.

AGENTS.md reference: AGENTS.md:L155-L157

Useful? React with 👍 / 👎.

@morluto
morluto merged commit 40d25f2 into morluto:main Aug 10, 2026
54 of 79 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.

2 participants