FAILURE MAP
← Case archive

FA-7996 / Form lifecycle / Open access

Form dirty-field bookkeeping: Removing a field leaves a phantom dirty baseline entry · case 01

Removing a field leaves a phantom dirty baseline entry.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The remove operation uses `None` where the contract requires `initial.pop(name, None)`.

VERIFIED REPAIR

Implement the remove operation as `initial.pop(name, None)`.

Unsuccessful approach: Clearing all baselines dirties unaffected fields.

Case contract

Edits change draft only; reset restores baseline; commit accepts draft; unmount removal clears both; dirty comparison includes additions and removals.

Why this case matters

A deterministic model of form dirty-field bookkeeping; this isolates one interface invariant without requiring a browser.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, current, event, name, value):
    initial, current = initial.copy(), current.copy()
    if event == 'input': current[name] = value
    elif event == 'reset': current = dict(initial)
    elif event == 'commit': initial = dict(current)
    elif event == 'remove': current.pop(name, None); None
    dirty = sorted(k for k in set(initial) | set(current) if initial.get(k, object()) != current.get(k, object()))
    return (initial, current, dirty)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('edit', solve({'a':1}, {'a':1}, 'input', 'a', 2), ({'a': 1}, {'a': 2}, ['a']))
check('reset', solve({'a':1}, {'a':2}, 'reset', None, None), ({'a': 1}, {'a': 1}, []))
check('commit', solve({'a':1}, {'a':2}, 'commit', None, None), ({'a': 2}, {'a': 2}, []))
check('remove', solve({'a':1,'b':2}, {'a':3,'b':2}, 'remove', 'a', None), ({'b': 2}, {'b': 2}, []))
check('new', solve({}, {'new':0}, 'read', None, None), ({}, {'new': 0}, ['new']))
check('removed', solve({'a':1}, {}, 'read', None, None), ({'a': 1}, {}, ['a']))
check('parameterized dirty value', solve({'a':N}, {'a':N}, 'input', 'a', N+1), ({'a':N}, {'a':N+1}, ['a']))
for repetition in range(N):
    check('repeat removed', solve({'a':1}, {}, 'read', None, None), ({'a': 1}, {}, ['a']))
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
edit[{'a': 1}, {'a': 2}, ['a']][{'a': 1}, {'a': 2}, ['a']]Passed
reset[{'a': 1}, {'a': 1}, []][{'a': 1}, {'a': 1}, []]Passed
commit[{'a': 2}, {'a': 2}, []][{'a': 2}, {'a': 2}, []]Passed
remove[{'a': 1, 'b': 2}, {'b': 2}, ['a']][{'b': 2}, {'b': 2}, []]Failed
new[{}, {'new': 0}, ['new']][{}, {'new': 0}, ['new']]Passed
removed[{'a': 1}, {}, ['a']][{'a': 1}, {}, ['a']]Passed
parameterized dirty value[{'a': 1}, {'a': 2}, ['a']][{'a': 1}, {'a': 2}, ['a']]Passed
repeat removed[{'a': 1}, {}, ['a']][{'a': 1}, {}, ['a']]Passed

SHA-256 / 73bacb17e06366f54f26209ca5151f0c82f18eed7e172fc0f46282af835a1475

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, current, event, name, value):
    initial, current = initial.copy(), current.copy()
    if event == 'input': current[name] = value
    elif event == 'reset': current = dict(initial)
    elif event == 'commit': initial = dict(current)
    elif event == 'remove': current.pop(name, None); initial.clear()
    dirty = sorted(k for k in set(initial) | set(current) if initial.get(k, object()) != current.get(k, object()))
    return (initial, current, dirty)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('edit', solve({'a':1}, {'a':1}, 'input', 'a', 2), ({'a': 1}, {'a': 2}, ['a']))
check('reset', solve({'a':1}, {'a':2}, 'reset', None, None), ({'a': 1}, {'a': 1}, []))
check('commit', solve({'a':1}, {'a':2}, 'commit', None, None), ({'a': 2}, {'a': 2}, []))
check('remove', solve({'a':1,'b':2}, {'a':3,'b':2}, 'remove', 'a', None), ({'b': 2}, {'b': 2}, []))
check('new', solve({}, {'new':0}, 'read', None, None), ({}, {'new': 0}, ['new']))
check('removed', solve({'a':1}, {}, 'read', None, None), ({'a': 1}, {}, ['a']))
check('parameterized dirty value', solve({'a':N}, {'a':N}, 'input', 'a', N+1), ({'a':N}, {'a':N+1}, ['a']))
for repetition in range(N):
    check('repeat removed', solve({'a':1}, {}, 'read', None, None), ({'a': 1}, {}, ['a']))
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
edit[{'a': 1}, {'a': 2}, ['a']][{'a': 1}, {'a': 2}, ['a']]Passed
reset[{'a': 1}, {'a': 1}, []][{'a': 1}, {'a': 1}, []]Passed
commit[{'a': 2}, {'a': 2}, []][{'a': 2}, {'a': 2}, []]Passed
remove[{}, {'b': 2}, ['b']][{'b': 2}, {'b': 2}, []]Failed
new[{}, {'new': 0}, ['new']][{}, {'new': 0}, ['new']]Passed
removed[{'a': 1}, {}, ['a']][{'a': 1}, {}, ['a']]Passed
parameterized dirty value[{'a': 1}, {'a': 2}, ['a']][{'a': 1}, {'a': 2}, ['a']]Passed
repeat removed[{'a': 1}, {}, ['a']][{'a': 1}, {}, ['a']]Passed

SHA-256 / 42f9e3ff64c7b25b10a54857897e3c5e07a17820cec92f523de1673be565f15f

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(initial, current, event, name, value):
    initial, current = initial.copy(), current.copy()
    if event == 'input': current[name] = value
    elif event == 'reset': current = dict(initial)
    elif event == 'commit': initial = dict(current)
    elif event == 'remove': current.pop(name, None); initial.pop(name, None)
    dirty = sorted(k for k in set(initial) | set(current) if initial.get(k, object()) != current.get(k, object()))
    return (initial, current, dirty)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('edit', solve({'a':1}, {'a':1}, 'input', 'a', 2), ({'a': 1}, {'a': 2}, ['a']))
check('reset', solve({'a':1}, {'a':2}, 'reset', None, None), ({'a': 1}, {'a': 1}, []))
check('commit', solve({'a':1}, {'a':2}, 'commit', None, None), ({'a': 2}, {'a': 2}, []))
check('remove', solve({'a':1,'b':2}, {'a':3,'b':2}, 'remove', 'a', None), ({'b': 2}, {'b': 2}, []))
check('new', solve({}, {'new':0}, 'read', None, None), ({}, {'new': 0}, ['new']))
check('removed', solve({'a':1}, {}, 'read', None, None), ({'a': 1}, {}, ['a']))
check('parameterized dirty value', solve({'a':N}, {'a':N}, 'input', 'a', N+1), ({'a':N}, {'a':N+1}, ['a']))
for repetition in range(N):
    check('repeat removed', solve({'a':1}, {}, 'read', None, None), ({'a': 1}, {}, ['a']))
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
edit[{'a': 1}, {'a': 2}, ['a']][{'a': 1}, {'a': 2}, ['a']]Passed
reset[{'a': 1}, {'a': 1}, []][{'a': 1}, {'a': 1}, []]Passed
commit[{'a': 2}, {'a': 2}, []][{'a': 2}, {'a': 2}, []]Passed
remove[{'b': 2}, {'b': 2}, []][{'b': 2}, {'b': 2}, []]Passed
new[{}, {'new': 0}, ['new']][{}, {'new': 0}, ['new']]Passed
removed[{'a': 1}, {}, ['a']][{'a': 1}, {}, ['a']]Passed
parameterized dirty value[{'a': 1}, {'a': 2}, ['a']][{'a': 1}, {'a': 2}, ['a']]Passed
repeat removed[{'a': 1}, {}, ['a']][{'a': 1}, {}, ['a']]Passed

SHA-256 / b63e8e7931c4bdb9300dcd6b9f00fae2852e95c78e3e5bd58ac37e930f722a94

Verification & scope

This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:38:17.260822+00:00.

Case digest / 69a918393393a5286b23f51462e0c3dccf62d422afa46ccafb61f3838748c70f