Files
ARCADIA-Studio/harness/analysis-source8-20260913/writer-canonical-negate-20260913.md
T

69 lines
7.4 KiB
Markdown

# Canonical writer correction: unary Negate
2026-09-13. Applies to the staged S2 canonical writer. Source contract inspected at `472fe7a4339a23a3339b2b91e8d53d76442f60c4`; the subsequent test-only AsKind correction `a2a28f8d23a46ba1696b6a8410d4d3b5c1808f49` does not change that parser contract. This note records an accepted design/source-review correction, not writer implementation or executable roundtrip evidence. Public source8 admission, parser limits, model types and Exact semantics are unchanged.
## Superseded recommendation
The preserved [r2 design](analysis-source8-repair-design-r2-20260913.md), expression grammar section, recommends: “Canonical unary Negate always prints `-(...)`”. The initial S2 writer plan repeated that rule. **Mandatory grouping of every Negate operand is superseded** by the parent-aware prefix rule below. The r2 document remains byte-identical historical evidence, including its recorded copy SHA256 `3a5b96fd130e3859932ee25544b3ff2f6a21f0774f3b5849e546f9574b9dbf7d`.
The correction preserves the reason for the original recommendation: negative literal nodes and unary operation nodes must remain distinct. It removes only redundant syntax frames that can make an otherwise admitted typed tree unreadable after canonical writing.
## Counterexample and unchanged limits
Use a Number-typed property and a case override whose binding contains 127 Unary Negates around `Number(-1)`. The Expr tree has logical depth 128. An authored binding consisting of `- ` repeated 127 times followed by `ratio(-1, 1)` fits the existing frame profile by source inspection.
The staged declaration parser counts a frame for each Negate, each explicit group and each ratio constructor, alongside declaration/override braces. See [prefix and group parsing](../../crates/oa-core/src/language/analysis_source/syntax.rs), [shared frame cursor](../../crates/oa-core/src/language/analysis_source/cursor.rs) and the [unchanged limit256](../../crates/oa-core/src/language/analysis_source/budget.rs).
| Binding location and spelling | Active-frame calculation | Peak |
| --- | --- | --- |
| Case override, prefix minus without redundant groups | declaration1 + override1 + Negates127 + ratio1 | 130 |
| Case override, superseded mandatory groups | declaration1 + override1 + Negates127 + groups127 + ratio1 | 257: exceeds256 |
| Calculation body, prefix minus without redundant groups | declaration1 + Negates127 + ratio1 | 129 |
| Calculation body, superseded mandatory groups | declaration1 + Negates127 + groups127 + ratio1 | 256 |
A calculation-only writer test therefore misses the case-override failure. These counts describe the current staged declaration parser. Future Project/include caller frames remain inherited context under S4; they must still be counted. This counterexample is not a general resource proof for every depth128 tree, and the correction does not raise the parser ceiling.
## Accepted writer policy
Emit Unary Negate as prefix `- ` and write the operand with unary-precedence parent context:
- Parenthesize a lower-precedence operand, such as a binary expression: `- (1 + 2)`.
- Do not add a redundant group around another unary or an atom/constructor: `- - 1`, `- abs(1)`, `- ratio(-1, 1)`.
- Keep the separating space in canonical prefix output. Existing source with other accepted spacing remains valid.
- Number and Quantity magnitude continue to use [Exact::canonical_literal](../../crates/oa-core/src/analysis_numeric/exact.rs): negative literals remain `ratio(-n, d)`, including denominator1. No leading minus is substituted for a negative literal node.
The required topology discriminators are:
| Canonical binding | Model topology |
| --- | --- |
| `ratio(-1, 1)` | Number(-1) |
| `- 1` | Negate(Number(1)) |
| `- ratio(-1, 1)` | Negate(Number(-1)) |
| `- - 1` | Negate(Negate(Number(1))) |
| `- (1 + 2)` | Negate(Add(Number(1), Number(2))) |
No arithmetic evaluation, folding or reassociation is permitted. All other accepted precedence rules remain: group lower-precedence binary children; group a right binary child of equal precedence; group any comparison child of any comparison parent on either side; preserve ordered list and constructor children and callee-qualified call arguments.
## S3 subtree boundary
A changed subtree must be emitted with parent context derived from its owning binding field and typed child path. It cannot always use top-level expression context.
For example, S2a's operand span in `-(1)` includes `(1)`. Replacing that span with a binary subtree must emit `(1 + 2)`, retaining the necessary group. Replacing it with a negative Exact atom or another unary needs no redundant group; the retained prefix still constructs a Unary node. Adjacent retained/new minus prefixes remain separate tokens in the existing raw scanner. Constructor delimiters outside the selected value span remain owned by the parent.
This is a writer helper contract. S3 still owns original-span selection, comment handling, simultaneous edit composition and complete-model recompile validation. Alias-only updates continue to preserve original spelling and trivia; they do not invoke canonical subtree normalization.
## Required executable acceptance, not yet run
The writer milestone must author and execute these controls before claiming the correction implemented:
1. `negate_override_roundtrip_preserves_depth128_within_frame256`: start with authored complete Number property/case source using 127 prefix Negates around `ratio(-1, 1)`. Parse/lower/admit it; independently inspect exactly 127 Unary nodes and the negative Exact leaf; write the case; reparse/lower and compare the complete model and canonical byte stability. Assert the staged case peak 130 and calculation-body control 129.
2. Source-parser controls for the superseded spelling: the case reaches257 and fails under the unchanged ceiling256, while the corresponding calculation reaches256 and passes. Preserve the distinction when later adding inherited Project/include frames to full-document tests.
3. `all_expr_variants_and_precedence_preserve_exact_trees`: include every topology discriminator above alongside all13 Expr variants and existing binary/comparison grouping controls. Compare typed trees, not evaluated values.
4. `subtree_output_respects_parent_context_and_retained_delimiters`: replace the entire grouped operand span in `-(1)` with an additive subtree, negative Exact atom and another Negate; reparse and compare the intended parent/child topology. Required grouping survives; unnecessary grouping is not forced.
Retain the separate width1024, output-byte/structural-work and cleanup controls. Existing S2b execution proves its own lowerer fixtures, not these future writer cases. No JSON depth-switch or native/performance acceptance follows from this source analysis.
## Decision provenance
The independent critic `/root/canvas_followup_acceptance_review` identified the257-frame counterexample and rechecked the corrected prefix/parent-context policy against the frozen scanner/parser. Parent accepted the correction. The reconciled local writer plan has SHA256 `e6ef6108792896cfb65e825cd19e984bf1c1729cff242653ed7947b4c6e9a803`; its exact correction diff has SHA256 `2e7d9f8627293301c450412466b7d2f27fac574cd4a0d1f09bccb2f36c1904be`; the source-review report including the bounded recheck has SHA256 `8ba73ffa442eeaa03f22f0137112a95aace199dbd7267f62de1737ba3e892ffa`. These identify design/source-review evidence only. The complete rationale and acceptance contract are recorded here so implementation does not depend on local artifacts.