fix(reducer): warn (NULL_CLEARED_FIELD) when a null clears a highest_priority field#1854
Conversation
…priority field (#1839) Under the highest_priority reducer a null observation is an explicit tombstone: at the same-or-higher source_priority it wins selection and clears the field. This is by design (only undefined is ignored by the reducer), but the clear was silent — a transient upstream failure that writes null can erase a good historical value with no signal, a data-loss vector for audit-ledger and pipeline use cases. Warn-only fix: emit a non-blocking NULL_CLEARED_FIELD store_warning when an incoming null actually clears a prior non-null value under highest_priority. No change to clearing semantics or snapshot computation. - New pure helper src/services/null_cleared_field_warning.ts (buildNullClearedFieldWarning). Fires only when: incoming value is null, prior snapshot value was non-null, the recomputed snapshot value is now cleared (guards against the typed-field-null-stripped path so the warning never false-positives when the value was actually retained), and the field's merge strategy is highest_priority. - Wired into both store paths after the SOURCE_PRIORITY_IGNORED block: HTTP/actions.ts and MCP/server.ts, each comparing the prior snapshot to the post-reduction snapshot. - Unit test for the helper + HTTP integration test asserting a winning null over a prior non-null value clears the field (unchanged) AND returns a NULL_CLEARED_FIELD warning; plus a no-warning case for clearing an already-empty field. Closes #1839 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
review:pm APPROVE
PM gate criteria satisfied. Ready for merge. |
…red-field tests Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
review:qa APPROVE QA AssessmentPR #1854 implements a warn-only fix for #1839: emit Test Coverage — AdequateUnit tests (
Integration tests (
Agent-Facing Surface CoverageThe warning is emitted through the HTTP
No Functional Surface Changes Requiring New Evals
High-Priority Coverage Domain CheckReducer behavior is covered under Neotoma's observability/correctness domains. The fix:
Regression Risk AssessmentLow. The only production surface is the
Verdict✅ Test coverage is adequate. Unit tests exercise all conditional branches in ✅ No regression risk. Warn-only change with no semantic implications. Ready to merge. 📎 Neotoma: #1839 |
|
review:pm PM Lens ReviewScope alignment: The PR directly addresses issue #1839 (silent null clears under Acceptance criteria verification:
User-visible behavior: On a transient upstream failure that writes Sequencing: Warn-only design means this is safe to merge without gating on any downstream consumer changes (warnings are advisory). The GitHub integration tests pass; ready to ship. No blocking findings. Change is well-scoped, test-complete, and solves the reported gap (silent failure visibility) without expanding scope or breaking existing behavior. |
|
review:ux Design Review: NULL_CLEARED_FIELD WarningUser-Facing SurfaceThe warning is surfaced via the Interaction Flow
Naming & DiscoverabilitySTRENGTH: Error Message & HintSTRENGTH: The message (null_cleared_field_warning.ts:100–108) is actionable. Two concrete remediation paths:
Both are implementable without deep system knowledge. STRENGTH: Scope is precise. Warning fires only when:
This prevents alert fatigue from spurious warnings. Edge Cases & Empty StatesSTRENGTH: Unit tests cover all edge cases:
Integration test exercises the real store path and confirms the field is cleared while the warning fires. Accessibility & Cognitive LoadCONSIDERATION: The message uses technical terminology ("source_priority", "tombstone", "reducer"). This is acceptable for an advanced warning targeting developers who understand merge strategies, but may require context lookup for developers new to Neotoma's highest_priority semantics. Mitigating factor: The warning message includes pointers to the specific action (omit or correct), so developers can resolve the issue even if the background explanation is unfamiliar. DiscoverabilityCONSIDERATION: Developers encounter this warning after sending a clearing null, not before. There's no pre-warning documentation in operator-facing guides that says "highest_priority null clears fields; here's how to avoid that if unintended." Mitigating factor: Warnings are documented in under array type. Agents reading the spec will see Suggestion (not blocking): Consider adding a note to Verdict & Acceptance✅ SIGNED_OFF — The warning surface is clear, naming is discoverable, and the message provides two concrete remediation paths. Developers can debug and resolve null-clears without digging into code. The warn-only approach (no semantic change) is the right choice for a data-loss signal: transparent, non-breaking, actionable. 📎 Neotoma: neotoma#1839 |
|
🤖 Corvus — Ateles swarm, content lens panelist Dogfooding Signal ✅Shippable as a content story. This PR makes a silent failure mode visible — transient upstream failures that write Downstream content opportunity: The null-as-tombstone semantics under Changelog hook: New warning ( No blockers. Ready to merge. |
|
review:content I've posted the review to PR #1854. Here's the comment I submitted: review:content Dogfooding Signal ✅Shippable as a content story. This PR makes a silent failure mode visible — transient upstream failures that write Downstream content opportunity: The null-as-tombstone semantics under Changelog hook: New warning ( No blockers. Ready to merge. Content lens verdict: This PR is not blocking — it's technically sound and demonstrates the observability-first design philosophy that's core to Neotoma's story. I've flagged a downstream explainer post on null-as-tombstone semantics as a future content opportunity, and noted the changelog entry hook for release notes. Posted by the Apis dispatcher on behalf of corvus — the panelist could not post its comment directly. |
|
review:qa APPROVE Coverage AssessmentThe NULL_CLEARED_FIELD warn-only fix ships with solid test coverage across both unit and integration layers. Strengths:
Coverage gaps (non-blocking):
Regression risk: Low
Sign-off: QA coverage is adequate for merge. 📎 Tests: |
|
🤖 Vanellus — Ateles swarm, PR steward Aggregated Panel ReviewPer-Lens VerdictsPM Lens (Pavo) — ✅ APPROVE
QA Lens (Phoenicurus) — ✅ APPROVE
Content Lens (Corvus) — ✅ COMMENT (non-blocking)
CI Status
PR Gate Inheritance
Blocking FindingsNone. Merge Readiness
✅ All conditions met. Approved for merge. 📎 Neotoma: #1839 |
|
🤖 Vanellus — Ateles swarm, PR steward APPROVE Aggregated Review VerdictPanel Roll-up
Blocking FindingsNone identified across all lenses. Non-Blocking Observations
Check StatusRequired (branch protection):
Advisory (monitoring signal):
SummaryPR #1854 implements a warn-only fix for issue #1839: emit Key findings:
Merge readiness: All required checks green; all panel lenses clear or signed-off; no blockers. Ready for operator approval and merge. 📎 Parent issue: neotoma#1839 |
Docs previewPreview URL: https://dev.neotoma.io/pr-1854/ Built from |
The gap (#1839)
Under the
highest_priorityreducer, anullobservation is an explicit tombstone: at the same-or-highersource_priorityit wins selection and clears the field from the snapshot. This is by design — onlyundefinedis dropped/ignored by the reducer;nullis a deliberate clear.The problem reported in #1839 is that the clear is silent. A transient upstream failure that writes
null(an API timeout, a solver returning no value) at same/higher priority can erase a good historical value from the snapshot with nostore_warning, the opposite of whathighest_priorityis meant to protect. There is no signal to make the failure mode discoverable.The fix (warn-only)
Emit a new non-blocking
NULL_CLEARED_FIELDstore_warningwhen an incomingnullactually clears a prior non-null value under ahighest_priorityfield. No change to clearing semantics or snapshot computation —nullremains an explicit tombstone, exactly as before.src/services/null_cleared_field_warning.ts(buildNullClearedFieldWarning), mirroring theSOURCE_PRIORITY_IGNOREDhelper pattern. It returns a warning only when all hold:null(notundefined— omitted fields are ignored and clear nothing),nulltoraw_fragments(where the prior value is in fact retained), so the warning never false-positives, andhighest_priority.SOURCE_PRIORITY_IGNOREDblock, each comparing the prior snapshot to the post-reduction snapshot:src/actions.ts(HTTP/store) andsrc/server.ts(MCPstore). The insertions are minimal and self-contained (one import + one small block each, plus a prior/new snapshot map) to avoid conflicting with parallel work near this area.Tests
tests/unit/null_cleared_field_warning.test.ts— unit-tests the helper across the fire and all no-fire cases (non-null incoming, omitted field, no prior value,last_write, undeclared strategy, falsy-but-non-null prior, and the not-actually-cleared/null-stripped case).tests/integration/store_null_cleared_field_warning.test.ts— exercises the HTTP/storepath end to end: a winningnullover a prior non-null value underhighest_prioritystill clears the field (asserted via the reducer) and returns aNULL_CLEARED_FIELDwarning; anullover an already-empty field emits no warning.All run green locally (11 new tests), alongside the adjacent
SOURCE_PRIORITY_IGNOREDand store-warning suites.type-check,lint, andformat:checkpass with 0 errors. No contract/generated files touched (warn-only helper).Closes #1839
🤖 Generated with Claude Code