Typed DAV recorder and replay v2 #820
Closed
kayg
wants to merge 40 commits from
feat/dav-recorder-v2 into dev
pull from: feat/dav-recorder-v2
merge into: kayg:dev
kayg:main
kayg:fix/shared-row-swipes
kayg:feat/pass8
kayg:t3/paged-search-reconcile-final
kayg:feat/style-sheet-fixes
kayg:storage/wal-checkpoint-off-save-1051
kayg:feat/contrast
kayg:review/calendar-2
kayg:dev
kayg:feat/collections-db
kayg:fix/instant-keys-1056
kayg:feat/money-round
kayg:perf/stalls-1054
kayg:wip/tiering-v3-cold4
kayg:wip/tiering-v3-cold5
kayg:port/rows-actions
kayg:port/rows-drop
kayg:port/hidden-blocks
kayg:port/rows
kayg:port/port-tables
kayg:port/port-tables-2
kayg:t3/mail-1011-liveness
kayg:port/devices
kayg:port/port-attachments
kayg:port/port-attachments-2
kayg:port/port-attachments-3
kayg:docs/design-consistency
kayg:docs/engineering-standards
kayg:fix/rusqlite-0.41-rustsec-2026-0459
kayg:feat/iphone-fixes
kayg:shared/restore-and-stale-918-919
kayg:fix/phone-top-band-contrast
kayg:feat/notes-approve
kayg:perf/soak-max-gate
kayg:feat/files-s1-identity
kayg:fix/lifecycle-event-id
kayg:feat/quickfix
kayg:wip/tiering-v3-cold
kayg:perf/web-mockup-frame-fixes
kayg:port/contacts-merge
kayg:port/contacts-edit
kayg:test/shared-room-no-deleted-text
kayg:perf/first-paint-credits
kayg:fix/shared-history-after-grant
kayg:fix/phone-swipe-regressions
kayg:feat/contrast-r1
kayg:review/sharing-pr3-review
kayg:feat/tiering-v3-s2-tags
kayg:feat/folder-drop
kayg:fix/deskpills-lazy-share
kayg:feat/sharing-s1-pr3
kayg:tmp/money-round-before
kayg:fix/phone-pane-backdrop-roots
kayg:fix/palette-preload-chips
kayg:fix/invite-defaults
kayg:port/tasks-matrix
kayg:fix/restore-green-gates
kayg:fix/sharing-fast-path-hardening
kayg:t3/photos-s1
kayg:feat/tiering-v3-s1-namespace
kayg:notes-archive-hide
kayg:feat/tasks-actions
kayg:t3/photos-plan-slice-one
kayg:feat/whiteboard-s1
kayg:t3/paged-search-reconcile
kayg:t3/smooth-pane-transitions
kayg:t3/index-position-startup-scan
kayg:fix/private-staging-759b-r2
kayg:feat/dav-transport
kayg:feat/imap-notes-fetch
kayg:feat/imap-notes-projection
kayg:feat/imap-notes-readonly
kayg:feat/imap-notes-auth
kayg:feat/imap-notes-server-foundation
kayg:docs/undo-block-ids-plan
kayg:fix/undo-768
kayg:proto/block-identity-v7d
kayg:issue/carddav-read-perf-fix-r2
kayg:issue/carddav-read-r2
kayg:docs/imap-766-plan-v2
kayg:docs/imap-notes-plan
kayg:docs/quota-plan-2
kayg:fix/parallel-test-flakes
kayg:feat/contacts-companion-pure
kayg:docs/calendar-backend-v2-plan
kayg:feat/calendar-ical-converter
kayg:docs/tasks-writes-v2-plan
kayg:docs/carddav-writes-v2-plan
kayg:docs/contacts-import-v2-plan
kayg:issue/610-pm-midnight
kayg:feat/calendar-v2-s1-containment
kayg:docs/dav-recorder-v2-plan
kayg:plan/previews-865
kayg:plan/save-log-durable-entry
kayg:fix/e2e-data-on-ram
kayg:feat/appwindow3
kayg:feat/appwindow2
kayg:feat/appwindow
kayg:feat/money-p1
kayg:feat/keys-sheet
kayg:review/calendar-2-themes
kayg:feat/smooth-cursor
kayg:fix/float-style
kayg:review/calendar-2-float
kayg:release/2026-11-06-hotfix
kayg:archive/port/rows-drop-code
kayg:archive/port/rows-actions
kayg:archive/port/rows
kayg:scratch/tasks-894-ui-gates
kayg:review/calendar-2-night-wip
kayg:release/2026-11-05
kayg:archive/feat/tiering-v3-s1-namespace
kayg:archive/flaky/rows-drop-fixed
kayg:archive/flaky/rows-actions
kayg:archive/feat/tiering-v3-reader
kayg:archive/feat/tiering-v3-roots
kayg:archive/flaky/rows
kayg:sim/bm15-final
kayg:sim/bm15-fix
kayg:sim/bm15-bis-932
kayg:sim/bm15-bis-909
kayg:sim/bm15
kayg:sim/bm15-bis-887
kayg:perf/tiering-option-b
kayg:port/style-3
kayg:port/style-2
kayg:port/style
kayg:fix/sw-lazy-precache
kayg:fix/vim-w-waits-for-save
kayg:fix/signout-test-races
kayg:fp-scratch/all
kayg:sim/bm15-first-paint
kayg:archive/bm15/rows-drop
kayg:archive/bm15/rows-actions
kayg:fix/palette-snippet-contrast
kayg:archive/bm15/rows
kayg:archive/bm15/hidden
kayg:archive/plan/save-log-durable-entry
kayg:mock/money-fix2
kayg:archive/fix/e2e-data-on-ram
kayg:mock/appearance-all
kayg:mock/pill-press
kayg:archive/docs/design-consistency
kayg:mock/auto-swipes
kayg:mock/mail-actions
kayg:archive/feat/mail-p1
kayg:mock/money-static
kayg:archive/review/standards-917
kayg:archive/docs/engineering-standards
kayg:perf/tiering-761-fix
kayg:mock/type-audit
kayg:mock/third-pane
kayg:feat/contacts-import-v2-s1
kayg:perf/executor-hot-path
kayg:archive/port/hidden-blocks
kayg:archive/port/devices
kayg:scratch/executor-on-894
kayg:scratch/executor-spans2
kayg:scratch/executor-spans
kayg:train/2026-11-03-bm14c
kayg:archive/port/rows-drop
kayg:feat/tiering-761-d-scale
kayg:feat/tiering-761-c-index
kayg:feat/tiering-761-b-reader
kayg:archive/review/sharing-pr3-review-9fec3d1f
kayg:train/2026-11-03-bm14b
kayg:perf/ports-lazy-budgets
kayg:feat/tiering-761-a-migration
kayg:train/2026-11-03-bm14
kayg:train/2026-11-03-bm12
kayg:archive/review/sharing-pr3-review
kayg:archive/feat/sharing-s1-pr3
kayg:train/2026-11-03-bm11
kayg:archive/feat/imap-notes-fetch
kayg:train/2026-11-03-bm10
kayg:archive/review/sharing-pr3-review-code
kayg:archive/plan/previews-865
kayg:train/2026-11-02-bm9
kayg:archive/port/port-attachments-3
kayg:archive/port/contacts-merge
kayg:fix/motion-audit-859
kayg:train/2026-11-02-bm8
kayg:archive/scratch/bm8-prep
kayg:archive/port/port-pickers
kayg:archive/port/port-tables-2
kayg:archive/port/port-tables
kayg:archive/feat/sharing-s1-pr3-code
kayg:archive/scratch/bm7-fixed
kayg:train/2026-11-02-bm7
kayg:archive/fix/parallel-test-flakes-code-minipc01
kayg:train/2026-11-02-bm6
kayg:docs/imap-766-readonly-slice
kayg:feat/dav-recorder-v3
kayg:research/788-diagnosis
kayg:archive/fix/parallel-test-flakes
kayg:archive/perf/pr792-cold-index-edit
kayg:docs/sharing-slice-1
kayg:archive/docs/imap-766-plan-v2
kayg:archive/docs/dav-recorder-v2-plan
kayg:spike/imap-766-s0-cases
kayg:archive/fix/undo-768
kayg:feat/dav-recorder
kayg:archive/docs/contacts-import-v2-plan
kayg:archive/feat/calendar-ical-converter
kayg:feat/contacts-import
kayg:perf/cold-growth-on-764
kayg:perf/cold-growth-on-tiering
kayg:archive/docs/tasks-writes-v2-plan
kayg:archive/docs/calendar-backend-v2-plan
kayg:archive/feat/contacts-companion-pure
kayg:feat/tasks-set-status
kayg:feat/tasks-move-reorder
kayg:archive/docs/carddav-writes-v2-plan
kayg:archive/feat/calendar-v2-s1-containment
kayg:feat/tasks-delete-promote-duplicate
kayg:archive/feat/dav-transport
kayg:archive/fix/carddav-rebuild-r1
kayg:archive/fix/carddav-retention-r1
kayg:feat/carddav-writes
kayg:perf/quota-on-tiering
kayg:perf/quota-simple
kayg:perf/cold-growth-on-quota-stack
kayg:feat/calendar-identity
kayg:feat/calendar-event-parser
kayg:feat/calendar-props-maps
kayg:port/glass-when-ring
kayg:feat/tiering-761-s1
kayg:wip/10by32-01-impl-759b-scratch-20261030
kayg:wip/6by32-01-bm3-webkit-pause-20261030
kayg:wip/10by32-01-bm3-chromium-pause-20261030
kayg:wip/6by32-01-tier-761-s1-pause-20261030
kayg:spike/imap-766-s0
kayg:wip/6by32-02-calternal-pause-20261030
kayg:wip/8by64-01-review-688-pause-20261030
kayg:wip/6by32-01-verify-744-pause-20261030
kayg:wip/6by32-01-verify-742-pause-20261030
kayg:fix/private-staging-759b
kayg:wip/6by32-01-review-748-pause-20261030
kayg:wip/6by32-01-review-733c-pause-20261030
kayg:wip/6by32-01-review-733b-pause-20261030
kayg:wip/6by32-01-perf-idle-8-pause-20261030
kayg:wip/6by32-01-perf767-base-pause-20261030
kayg:wip/6by32-01-perf767b-767-pause-20261030
kayg:wip/6by32-01-perf767b-764-pause-20261030
kayg:wip/6by32-01-perf767-767-pause-20261030
kayg:wip/6by32-01-perf767-764-pause-20261030
kayg:wip/6by32-01-lists-b-pause-20261030
kayg:wip/6by32-01-ids-verify5-pause-20261030
kayg:wip/6by32-01-ids-verify4-pause-20261030
kayg:review/calendar-month
kayg:wip/10by32-02-undo-768-20261030
kayg:test/ids-verify3
kayg:wip/10by32-02-bm3-chromium-20261030
kayg:wip/10by32-01-review-753-20261030
kayg:wip/10by32-01-perf-idle-2-20261030
kayg:wip/10by32-01-bm2-webkit2-20261030
kayg:plan/759b-staging
kayg:train/2026-10-30-bm2
kayg:review/758b-tests
kayg:review/758-tests
kayg:review/764d-tests
kayg:review/767b-tests
kayg:review/preview-2026-10-29
kayg:review/767-tests
kayg:docs/tiering-731-plan
kayg:wip/c6b02-when2
kayg:wip/c6b02-tasks-create-fix5
kayg:wip/c6b02-settings-frost-mock
kayg:wip/c6b02-settings-frost-base
kayg:wip/c6b02-settings-frost
kayg:wip/c6b02-ring
kayg:wip/c6b02-review-733d
kayg:wip/c6b02-bm1-full-a
kayg:wip/compute02-when-field
kayg:wip/compute02-webkit-lists-repro
kayg:wip/compute02-train-bl2-dev
kayg:wip/compute02-dev-green-pw164
kayg:docs/deploy-plan
kayg:review/764c-tests
kayg:review/764b-tests
kayg:review/748g-tests
kayg:review/764-tests
kayg:review/759b-tests
kayg:fix/storage-owner-liveness-642
kayg:fix/storage-fix-7
kayg:wip/compute02-storage-fix-7
kayg:review/759-tests
kayg:feat/tiering-731
kayg:wip/tiering-731-first-attempt
kayg:wip/compute02-storage-fix-1
kayg:review/754g-tests
kayg:perf/idle-honest-2
kayg:review/754f-tests
kayg:review/754e-tests
kayg:review/755-tests
kayg:review/754d-tests
kayg:review/754c-tests
kayg:review/754b-tests
kayg:review/754-tests
kayg:fix/storage-private-tmp-642-dev
kayg:wip/compute02-verify-750
kayg:test/ids-verify5
kayg:test/ids-verify4
kayg:fix/storage-exact-file-quarantine-642
kayg:wip/compute02-verify-740
kayg:perf/one-flush-save
kayg:review/calendar-static
kayg:wip/compute02-verify-737
kayg:review/indexing-ring
kayg:review/when-field-2
kayg:wip/compute02-review-733
kayg:proto/block-identity-2-review
kayg:proto/block-identity-review
kayg:wip/c6b02-tc-design
kayg:review/when-field
kayg:issue/tasks-create-ids
kayg:issue/tasks-create
kayg:review/combined
kayg:wip/compute02-review-combined
kayg:review/task-polish
kayg:review/top-buttons
kayg:review/q1-q5
kayg:train/2026-10-26-bl2
kayg:archive/wt-398h
kayg:archive/train-bm-proptest-seed
kayg:archive/integ325-293
kayg:archive/integ325-ci
kayg:archive/client-auth-review-baseline
kayg:archive/wt-sim-4853
kayg:archive/wt-sim-4811
kayg:archive/wt-448m
kayg:archive/wt-398a-allocator
kayg:issue/calendar-adr
kayg:archive/old/train/2026-10-23bk2
kayg:archive/old/train/2026-10-23bk
kayg:issue/469-mockup-9a
kayg:issue/smart-tasks-mockup
kayg:issue/469-menus
kayg:issue/652-signout-flake
kayg:issue/652-history-fix7
kayg:issue/652-history-fix6
kayg:archive/old/issue/spring-check-perf
kayg:issue/spring-check-perf
kayg:issue/659-rename-fast
kayg:archive/old/train/2026-10-21ar
kayg:issue/search-hardening-3
kayg:issue/652-history-fix5
kayg:issue/660-port-sheets-glass
kayg:issue/652-history-fix4
kayg:issue/652-history-fix3
kayg:issue/652-history-fix2
kayg:issue/652-history-flake
kayg:train/2026-10-21an
kayg:issue/settings-admin-port
kayg:issue/search-port
kayg:train/2026-10-20am
kayg:issue/storage-split-fix2
kayg:issue/storage-split
kayg:archive/old/issue/folder-ids-fix
kayg:train/2026-10-20al
kayg:archive/old/issue/folder-ids
kayg:train/2026-10-20ak
kayg:issue/tables-mockup
kayg:train/2026-10-19aj
kayg:train/2026-10-19ai
kayg:issue/469-merge-fixes
kayg:issue/sidebar-band
kayg:archive/old/issue/608-settings-spec
kayg:train/2026-10-19ah
kayg:issue/swipe-offscreen
kayg:train/2026-10-19ag
kayg:archive/old/issue/590-nlp-adr
kayg:archive/old/issue/587-event-after-write-2
kayg:train/2026-10-19af
kayg:archive/old/issue/587-event-after-write
kayg:issue/469-pill-room-c
kayg:issue/469-pill-room
kayg:train/2026-10-19ae
kayg:issue/469-narrow-handle
kayg:issue/397-ios-swipe-2
kayg:archive/old/issue/adrs-photos-files-tasks
kayg:issue/469-desk-fixes
kayg:archive/old/issue/auto-update-adr
kayg:issue/469-filter-gap
kayg:issue/469-port-1b
kayg:issue/442-opacity-flake
kayg:archive/397-ios-swipe-2
kayg:issue/327-web-fast-3
kayg:archive/old/issue/469-port-1
kayg:issue/469-port-1
kayg:issue/327-web-fast-2
kayg:archive/old/issue/327-web-fast
kayg:archive/old/issue/425-stt-bench-vq1
kayg:archive/old/issue/445-no-bleed
kayg:archive/old/issue/400-swipe-mockup-2
kayg:issue/426-client-review
kayg:issue/426-client-2
kayg:issue/426-client
kayg:archive/old/issue/476-safe-area-lab
kayg:archive/445-top-light-2
kayg:archive/old/issue/445-top-light
kayg:issue/471-blocks-mockup
kayg:archive/old/issue/448-links-app-2
kayg:archive/old/issue/448-links-app
kayg:archive/old/issue/398-cold-links
kayg:issue/434-442-small-2
kayg:issue/434-442-small
kayg:archive/old/issue/398-unload
kayg:archive/old/issue/445-bottom-band
kayg:issue/445-bottom-band
kayg:issue/351-rem-3
kayg:archive/312-views-mockup
kayg:issue/351-rem-2
kayg:issue/351-rem
kayg:archive/old/issue/327-first-note
kayg:issue/395-width-phone
kayg:archive/old/issue/327-typing
kayg:issue/396-ios-edges
kayg:issue/397-ios-swipe
kayg:archive/398-allocator
kayg:archive/old/issue/327-notes-index
kayg:issue/345-347-contrast-empty-2
kayg:archive/old/issue/44-arenas
kayg:issue/44-arenas
kayg:archive/integ325
kayg:archive/old/issue/327-perf-harness
kayg:archive/old/issue/44-thp
kayg:issue/44-thp
kayg:archive/old/issue/285-sticky-pills
kayg:archive/old/issue/44-trim-3
kayg:issue/44-trim-3
kayg:archive/old/issue/165-cards
kayg:archive/old/issue/165-picker-2
kayg:archive/old/issue/212-multi-select
No reviewers
Labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
kayg/calternal!820
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/dav-recorder-v2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements the approved v2 plan in #818 and supersedes #789.
What changed
Gates on dev-02
cargo fmt --all --checkpassed.cargo clippy -j4 -p calternal-dav-replay --locked -- -D warningspassed.cargo test -j4 -p calternal-dav-replay --lockedpassed.cargo run -j4 -p calternal-dav-replay --locked -- check crates/dav-replay/fixturesreturnsPENDING(exit 3) for contacts-crud and contacts-initial-sync.The real-capture replay gate remains PENDING because a real Mac capture is blocked on Mac GUI permission. No Mac capture was run.
Deferred minor:
main.rsmay say partial output was removed even when cleanup itself fails.Independent review, round 1
Head:
565b50e3. Read the approved #818 plan and all three #789 review rounds.Verdict: fix first.
P0
P1
Initial response data can establish an incorrect expected value, including duplicate UIDs.
schema.rs:1473–1495puts UID and all other vCard values into genericCardValueslots;wire.rs:50–98,1121,1404learns unseeded values without enforcing distinct UID identities. Independent HTTP replay probes changed only FN from Test One to Test Two, or collapsed two distinct UIDs into one: both reportedok. During initial sync, an incorrect contact or duplicate identity can therefore become the replay binding and pass. Require independently seeded expectations for synthetic contact content, give UID its own correlation rules, and reject distinct identity slots collapsing to one value. Preserve privacy through target seed data rather than storing captured values.Broken live hrefs are repaired by normalization and request rendering.
schema.rs:687–708drops an optional/davprefix and mergescontactswithaddressbook;wire.rs:209always reconstructs canonical target paths. With principal/resource bindings seeded, changing only a response href from/dav/u/contacts/a.vcfto/u/contacts/a.vcf, or to/dav/u/addressbook/a.vcf, still reportedok. If those alternate paths are not served, Apple follows the advertised URL and gets a failure while replay silently uses its reconstructed URL. Validate target response hrefs against the actual target routes and follow/check the advertised target href. Source-to-target route translation must not erase errors in the target's own response.Ordinary DAV capability responses stop recording.
schema.rs:434–531neither models nor explicitly ignoresDAVandAllow. A normal OPTIONS response containing these headers producesunknown header in response; the writer aborts/removes the session. The documented Apple-to-Radicale capture can therefore stop during capability discovery before producing a fixture. Add typed capability/method-list support and a recorder regression using a representative OPTIONS exchange.P2
Protocol values are treated as arbitrary XML text.
schema.rs:1178–1199placeholders DAV content lengths and falls back to generic text forgetcontenttype. Independent HTTP replay probes changing only length123 → 999999or media typetext/vcard → text/calendarboth reportedok. Preserve media types as protocol enums; validate advertised lengths against the relevant target representation or an explicit derived expectation. Rebinding any first-seen number does not check correctness.ETag syntax and strength are lost inside an opaque binding.
schema.rs:579–587andwire.rs:947accept an arbitrary nonempty ETag slot. Changing only"one"toW/"one"reportedok. Weak versus strong changes If-Match semantics, even when later opaque-value correlations remain consistent. Parse and preserve ETag strength and syntax, with a mutation regression.P3
main.rs:137replaces every recorder error with “partial output was removed”, including cleanup failures and startup failures where an existing directory was deliberately retained. Preserve the recorder's error and report removal only after successful cleanup. Otherwise someone may rely on the message while partial files remain.Checks and limits
~/Developer/Worktrees/review820-r1:nix develop -c bash -c '~/bin/slot cargo test -j4 -p calternal-dav-replay --locked -- --nocapture'passed, including all 38 committed tests and the initial seven probes.nix develop -c bash -c '~/bin/slot cargo test -j4 -p calternal-dav-replay --test review_r1_probe --locked -- --nocapture': 10/10 probe assertions passed. Seven response mutations were accepted incorrectly; numeric 412→204 and XML-shape mutations were rejected; the remaining probe confirmed capability-header rejection. Probe assertions deliberately assert these observed outcomes, so a green probe suite does not mean replay caught all mutations.required-scenarios.json; no captured personal data or credentials are committed there. Typed serialization omits auth payloads/cookies and replaces variable content, with the randomized no-verbatim tests passing.nix develop -c bash -c '~/bin/slot cargo run -j4 -p calternal-dav-replay --locked -- check crates/dav-replay/fixtures'reports PENDING for contacts-crud and contacts-initial-sync. No real Mac capture was run. No code committed or pushed; probe source/logs remain on the VM.Round 1 findings → fixes → tests
Dev-02 gates: fmt, clippy with -D warnings, and crate tests pass. The real-capture replay gate remains PENDING until a Mac GUI capture is available.
Independent review, round 2
Head:
3721d48e8c951819e0fc258c20feb58bf3fd1200. Reviewed against #818, #issuecomment-72055 and #issuecomment-72112.Verdict: fix first. The contact/UID and route fixes work in independently seeded HTTP replay probes. The capability-header fix is incomplete for the documented Radicale capture source.
Status and mutation probes
Each replay case first passed an unchanged baseline with all required synthetic content/identity seeds. Then only one server response was changed. Multi-exchange probes retained the other responses.
Response mutations: 12 caught / 16 tried. The two capability failures are separate recorder compatibility probes, not mutations that replay ought to reject.
Findings
P0: None found.
P1: Capability support still stops the documented Radicale capture.
schema.rs:628–647excludes extended-mkcol, andMethod::parseexcludes MKCALENDAR. Radicale's DAV_HEADERS includes extended-mkcol; OPTIONS builds Allow from its implemented methods, including MKCALENDAR. Independent recorder probes changed only one header at a time from the accepted baseline and confirmed both failures and cleanup. A Contacts capture can encounter these advertised values even if it never creates a calendar. Model these known protocol values and test representative source headers; retain rejection of unknown values.P2: Timestamp typing validates layout but accepts impossible dates/times.
schema.rs:1341–1395,1713–1722checks digit positions rather than date/time ranges or valid weekday names. All three impossible timestamp mutations above passed end-to-end replay. Validate calendar dates, clock ranges and the HTTP-date grammar before assigning a timestamp slot. Legitimate target timestamp variation can remain supported.P2: DAV content-type typing discards charset semantics.
schema.rs:582–608,1317–1322accepts UTF-8 and US-ASCII but maps both to the same MediaType. Changing only the declaration therefore passes replay. These encodings differ for non-ASCII contact content. Preserve the charset in the typed value, or explicitly validate its compatibility with the target representation. This is new to XML getcontenttype typing; the earlier cross-family media-type mismatch is now caught.P3, carried forward:
main.rs:137still replaces recorder errors with “partial output was removed” regardless of cleanup outcome. This remains the acknowledged round-1 minor finding.Test quality: Several committed round-1 mutation tests use empty TargetBindings, so contact/UID and content-length cases can fail for missing seeds before reaching the mutation. Add passing seeded baselines and check the intended rejection reason. The independent probes above supply those seeds.
Verification and limits
/home/cua/Developer/Worktrees/review820-r2. dev-01's queued job was stopped before moving verification to an available dev-02 slot.CARGO_TARGET_DIR=/home/cua/Developer/Worktrees/dav-recorder-v2/target nix develop -c bash -c '~/bin/slot cargo test -j4 -p calternal-dav-replay --locked -- --nocapture': 52 tests passed, including the 48 committed tests and four local probe tests.cargo test -j4 -p calternal-dav-replay --test review_r2_probe --locked -- --nocapturethrough the same Nix/slot setup: 2 passed, with the same 12/16 detection result.~/review820-r2-final.log,~/review820-r2-flow.log,tests/review_r2_probe.rsand appended recorder probes intests/record.rson dev-02.Round 3 status: finding → fix → test
DAVandAllowvalues are stored raw, with no closed-value parser that can rejectextended-mkcolorMKCALENDAR. The recorder mutation test covers each header independently; the replay mutation test rejects a changed capability value.replay_rejects_invalid_timestampscovers impossible values in headers, XML properties, and cardREV.replay_rejects_content_type_charset_changesrejectsutf-8→us-ascii.tokenorsessionare represented by placeholders.replay_requires_unknown_headers_to_match_exactly,sensitive_header_values_are_not_serialized, andheaders_of_preserves_non_utf8_octets_reversiblycover these paths.Dev-02 gates:
nix develop -c bash -c "cargo fmt --all --check"— pass.nix develop -c /home/cua/bin/slot cargo clippy -j4 -p calternal-dav-replay --locked -- -D warnings— pass.nix develop -c /home/cua/bin/slot cargo test -j4 -p calternal-dav-replay --locked— pass, 55 tests.nix develop -c /home/cua/bin/slot cargo run -j4 -p calternal-dav-replay --locked -- check crates/dav-replay/fixtures— PENDING (exit 3); real captures remain blocked on Mac GUI permission.Independent review, round 3 (final)
Head:
58e503944542a8a84b7e9a6a4389433a877fa099. Reviewed #818, #issuecomment-72055, #issuecomment-72126 and the simplified design in #issuecomment-72178. Verdict: fix first. This is the final review round; no further round is proposed.Status
Each replay mutation first passed an unchanged baseline with the required synthetic identity/content bindings. Requests for the ETag change and stale-write cases retain PUT/If-Match. The harness includes HTTP-generated Date and Content-Length headers so strict comparison reaches the intended mutation.
/dav; change contacts to addressbookX-Token-Required: truechanged tofalse/davtest/contacts//.well-known/carddavLocation: /rejected as empty URI path segment/davtest/{urn:ietf:params:xml:ns:carddav}no-uid-conflictaborts captureReplay total: 23 caught / 25 meaningful differences tried. Round-2 set: 16/16, additions: 7/9. Two equivalent controls are excluded from that denominator. Recording/privacy probes are separate. A green probe suite asserts these observed outcomes, including misses; it does not mean every difference was caught.
New findings
P0: None found.
P1 — Credentials and session material are persisted by the raw-header fallback.
schema.rs:543–566redacts a few names and the substrings token/session, but leavesX-Apple-ID-Account-Web-Auth,X-Apple-Password,WWW-AuthenticateandAuthentication-Inforaw. Serialization probes retained Apple credentials, Digest nonce and nextnonce values. More importantly, full recorder probes produced successfully loaded/saved fixture files containing the exact synthetic sentinel from an Apple auth header and a 401 Digest challenge.Link: <https://user:password@host/...>likewise saves URL credentials verbatim. HTTP Location userinfo is removed by URI templating, and Set-Cookie is redacted, but this protection does not cover URL-bearing raw headers. HTTPS Content-Location aborts instead. Expand the explicit sensitive field rules and parse/redact credentials and challenge/session scalars inside structured values; preserve nonsecret structure. Add persisted-fixture sentinel checks for each supported path. The current claim that fixtures cannot contain credentials is false.P1 — Standard source responses still terminate recording, beyond the repaired capability headers.
schema.rs:1002–1025, XML allowlisting, andparse_uri/parse_routestill reject ordinary responses. With an actual disposable Radicale 3.8.3 server, OPTIONS now works, but well-known discovery returnsLocation: /and aborts; an ordinary initial 401 with a text/plain body aborts; authenticated depth-0 PROPFIND returns valid DAV/Radicale XML and aborts. The response includes standard principal-collection-set, report capability properties, root hrefs and a Radicale extension. Separately, the own-server response shape emitted bycarddav-writes-r2(crates/carddav/src/write.rs:90–95throughcrates/dav/src/serve.rs:915–923) is a 403 CardDAV no-uid-conflict; feeding that exact shape through the recorder returns 502/unknown XML element. The recorder currently models no-uid-conflict only in the DAV namespace and only at 409 (schema.rs:445,1134). Standard HTTPS redirects also fail atschema.rs:660–661. Define safe representations for these normal discovery/authentication/protocol responses without persisting arbitrary bodies. Fixing DAV/Allow alone does not make the documented capture path usable.P2 — Whole-value redaction hides protocol differences.
wire.rs:971–996learns the entire nonempty redacted value as an opaque slot. ChangingSet-Cookie: sid=one; HttpOnly; Securetosid=two; HttpOnlypasses replay, hiding loss of Secure alongside legitimate session-ID variation. The substring rule also makes an unrelated boolean such as X-Token-Required opaque: true to false passes. Preserve and compare cookie names/attributes and other nonsecret structure; narrowly identify actual secret scalars. Authorization scheme changes are already caught.P2 — Valid legacy HTTP dates abort.
schema.rs:1352accepts only the 29-character IMF-fixdate layout. A recorder probe with valid obsolete HTTP-dateSunday, 06-Nov-94 08:49:37 GMTreturns 502/invalid HTTP Date header and removes the fixture. HTTP recipients must accept the obsolete date forms; normalize valid forms while retaining the new impossible-date checks.P3, carried forward: The inaccurate unconditional “partial output was removed” message remains; this is the acknowledged earlier finding, not a new one.
Verification and limits
All verification ran on dev-02, detached worktree
/home/cua/Developer/Worktrees/review820-r3; the control host remained read-only apart from the explicitly requested Forgejo comment. No code committed or pushed.CARGO_TARGET_DIR=/home/cua/Developer/Worktrees/dav-recorder-v2/target nix develop -c bash -c '~/bin/slot cargo test -j4 -p calternal-dav-replay --locked -- --nocapture': 58 passed, comprising 55 committed tests and 3 independent probe tests.cargo test -j4 -p calternal-dav-replay --test record --locked review_r3_standard_and_privacy -- --nocapture: 1 passed, six observed cases.python3 ~/review820-r3-live.pylaunches disposable servers, invokes the same Nix/slot setup withcargo test -j4 -p calternal-dav-replay --test record --locked review_r3_live_servers -- --nocapture, then stops only its own child processes: 1 passed, five observed cases.~/review820-r3-all.log,~/review820-r3-record.log,~/review820-r3-live.log,tests/review_r3_probe.rsand appendedtests/record.rsprobes on dev-02.carddav-r2binary is not DAV-mounted. Its live well-known request returned an HTML 200, which also aborts capture, but this is not evidence of mounted own-DAV compatibility. The own-DAV 403 test above is a source-derived wire reproduction, not a full authenticated own-server capture. That compatibility remains unproven and already has a concrete schema mismatch.Paused at
58e503944542a8a84b7e9a6a4389433a877fa099under the three-round rule, pending review and approval of plan v3 in #818. No round 4 or further recorder patches are proposed.Plan v3 separates stock Nix mitmproxy raw capture from offline allowlist export, and retains the typed replay/comparison work and strengthened mutation tests from this PR. It covers every P1 from rounds 1–3, including standard source responses, Apple/Digest/Link credential persistence, independently seeded contact/UID checks, target href correctness, and the source-derived own-DAV CardDAV no-uid-conflict 403 mismatch. The mounted own-server gate and real Mac capture remain unproven.
Design only: no implementation code or fixtures changed; no build, test or capture run. The v3 description supersedes the older v2 plan file on #818’s branch.
Superseded as the v3 work destination by #822, following approved #818 (revision 72227 and review 72220). #820 remains paused at
58e5039445; no further v2 recorder patches or review round are proposed. The v3 task explicitly requires stopping for a material size overrun: source inspection estimates 340–550 incremental Rust changed lines versus 100–180 for closed header representations and the dependent replay normalization/rendering, URI origins, omitted error bodies and recursive loader/slot checks. #822 records that pre-implementation estimate and all gates as NOT RUN (real Apple capture PENDING). It is a blocked draft, not a completed working replacement; no implementation or captures were produced.Closing: superseded by #822 (named in the PR body or comments). Branch kept.
Pull request closed