Files
ARCADIA-Studio/harness/text-default-omission-update-policy-review-20260913.md
T

9.0 KiB
Raw Blame History

Compact-only updater policy: independent adversarial review

Reviewer /root/default_omission_review, 2026-09-13. Static source review against 74cc0e09119f9fa9e1287988bdd98c4cbf9db79e in C:/ar-default-9e8r6t; working tree was clean at entry. Parent owns runtime probes, design acceptance, implementation, and Gitea. No production edits, builds, tests, or Gitea operations performed here. Line references below are to this source. Earlier candidate C remains unaccepted.

Decision

The proposed one-condition change is unsafe. Absence from both compact trees proves unchanged default value only if the old and new typed contexts are equivalent. Constructor/declaration changes and an omitted whole composite violate that precondition. A compact-only updater is possible, but it needs typed context/default information or selective completion; it is not equivalent to changing only the record deletion predicate. This conclusion does not depend on preserving field presence after a semantic value changes.

Counterexamples

  1. The existing skipped-operation field already defeats the simple fix. Start with a valid unresolved Exchange occurrence that explicitly authors operation none;. Change its body to Action, preserving occurrence identity. Today's serializer omits operation in before because model.rs:561 has skip_serializing_if; Action does not admit it (model.rs:564–567). Constructor handling renames Exchange to Action and recursively compares payloads (source_project.rs:1268–1289). The proposed predicate retains actual operation none; because it occurs in neither before nor after, leaving an unknown field inside Action. Current required-field/generation details do not rescue this case. Runtime probe should use a MixedLegacy scenario so both unresolved old/new bodies have valid context; assert the target model is independently valid before testing update.

  2. Equal empty payloads conceal obsolete fields. With the proposed compact writer, explicit body { Action { action none; lifeline none; }; }; compacts to Action {}. Change to State with state/lifeline absent, which compacts to State {}. The tag changes, then value returns on equal empty before/after payload records (1191–1192, reached through 1289). Thus action none; remains inside State, which admits only state/lifeline (model.rs:578–580). Merely forcing traversal would not repair it: the proposed absent-both predicate still retains the obsolete action field. Unchanged shared lifeline syntax should survive, but removed constructor-specific fields must not.

  3. Header-selected defaults can change while compact fields remain equal. Start with a stable State under a valid Region and explicit kind State;. Change only its typed body kind to Mode, preserving UUID/name/parent and other values. StateKind maps to separate declaration kinds (model.rs:168–171, 425–429); the factory supplies the declaration-selected state kind (834–838). Candidate compact rendering omits kind from both records, so field trees compare equal. The updater changes the header (source_project.rs:932, 1134–1151) but skips the equal fields (933, 1191–1192). kind State; survives in a Mode declaration; parser rejects header/body disagreement (language.rs:1758–1760). The value is semantically changed even though its compact omission appears unchanged. This is not a request to retain changed-field presence; the updater must either remove or rewrite this stale value.

  4. before=None may mean an omitted old composite, not no old value. An explicitly authored all-default body { Exchange { ... }; }; is absent in compact-before because whole-body omission selects Exchange (model.rs:816–825). Change the occurrence to Action with empty optional payload, which requires an explicit Action tag in compact-after. At the actual body, before is None (source_project.rs:1297–1299). Constructor-special handling requires Some(before) (1271), so it cannot rename the old tag. The proposed predicate retains Exchange, absent from both supplied record maps, then adds Action (1307–1336): two tags in one enum payload. Treating before=None as unconditional replacement prevents the invalid dual tag, but can discard unchanged shared fields' token placement when an omitted composite expands in the same constructor; that needs an explicit preservation decision and tests.

The first witness applies before broad compact generation exists. The other three expose information that compact generation intentionally erases. These are static predictions, not recorded execution results; the parent's bounded runtime gate should confirm them.

Where the smaller rule is sound

Within one unchanged typed context, with canonical omission deterministic and exact, absent-before plus absent-after means the same source default. The rule preserves explicit operation-none/empty-port-anchor/semantic-none syntax on ordinary sibling edits; nondefault-to-default may remove its now-redundant field, and default-to-nondefault updates or inserts normally. This satisfies the clarified user requirement to preserve only unchanged fields. The argument requires:

  • A field has the same validity, meaning, and omission default in both contexts, including admitted source version and lexical context.
  • Whole-field omission does not deprive recursion of the old composite constructor and defaults.
  • Semantic equality remains sensitive to promised floating-point distinctions. A bit-preserving omission predicate is still required.
  • Shared-field identity across constructor switches is explicit; syntactically identical field names alone are insufficient when meanings/defaults differ.

Other audited boundaries

  • Identity/reference edits: Nothing in the current omission inventory elides a live reference because it equals another live reference, apart from lexical parent. IDs remain explicit. Present nondefault aliases still occur in before/after text and reach reference updates (source_project.rs:1215–1225); identity origins used for list keys remain available (852, 863–866, 1378–1381). No additional independent reference counterexample found under those constraints. Any future identity-derived omission defaults require context-aware mapping as well.
  • Lexical parent: Existing comparison is flat and only strips parent when actual syntax is nested and already omits it (910–920). Root/non-root changes with explicit parent therefore remain represented, while structural scopes recover implicit parent from the target model (source_structure.rs:119–129). New children remove their generated parent after choosing lexical scope (216–220). Retain this boundary; do not compact comparison parents relative to lexical owner without revisiting explicit authored-parent updates.
  • Deletion/supersession: Entire declarations are handled structurally (source_project.rs:889–892; source_structure.rs:39–48). The bad absent-both inference matters inside surviving records, especially fields valid only in the retired constructor. Removed list entries still use the existing list correspondence path; reusing an entry with changed constructor inherits the same context problem.
  • before=None: It also occurs for true inserted/mapped-missing fragments and after value-kind changes (1196–1201, 1443). It cannot, by itself, authorize retaining all actual keys. The updater must distinguish a current source default omitted from comparison from a genuinely new context.

Complexity comparison and recommendation

Candidate C (complete comparison plus compact generated fragments) retains old/new typed values and applicable fields directly, so existing recursive equality and constructor transitions have sufficient information. It needs complete extraction including today's serde skips and a compact fragment lookup/rendering path. It can remove or update fields that change to defaults; retaining their presence is not intrinsically required by C.

A compact-only alternative can work if it carries old/new typed-context identities and an omission-default/allowed-field lookup, gates every equality short-circuit on context compatibility, reconstructs the old selected constructor when before=None represents a omitted composite, and prunes obsolete fields on switches while preserving shared unchanged fields. That is effectively a selective complete semantic comparison alongside compact syntax. Its benefit is one generated-text representation; its cost is more context-sensitive logic throughout recursion, including declaration headers and lists. A blanket replace-on-context-change shortcut is simpler but conflicts with the established shared-field/trivia preservation contract (docs/language.rst:1900–1904).

Recommend rejecting only the naive absent-both rule, not asserting that two full rendered strings are mathematically necessary. Prefer C unless a small, explicit context-bearing alternative demonstrates less implementation complexity against the four counterexamples and same-context preservation probes. Do not treat passing the three existing skip-default sibling-edit probes as sufficient acceptance.