notes: nested map edits in the property writer (calendar step 2) #781
Closed
kayg
wants to merge 7 commits from
feat/calendar-props-maps into dev
pull from: feat/calendar-props-maps
merge into: kayg:dev
kayg:main
kayg:feat/style-sheet-fixes
kayg:storage/wal-checkpoint-off-save-1051
kayg:t3/paged-search-reconcile-final
kayg:feat/pass8
kayg:review/calendar-2
kayg:fix/shared-row-swipes
kayg:feat/contrast
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:feat/dav-recorder-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: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!781
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/calendar-props-maps"
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?
Failure → effect → detection → recovery → test: unsafe input → explicit refusal with no note writes → boundary validation → edit the note or enable the completed UI contract later → awkward YAML / hidden syntax / stored replay / cross-folder transfer regressions. Gates: fmt, four-crate clippy with -D warnings, events/notes/tasks/calternal tests on dev-01.
Plan (ADR 0067 build order, step 2)
Add safe nested map edits to the existing property writer (
crates/notes/src/props.rs). Steps 3 (event parser) and 4 (identity/promotion) stack on this branch. Needed later byevents(journal map: marker -> fields) andsource(connected resource map).API
Changegainsparents: Vec<String>andChange::under(&["events", id]):Change::text("start", v).under(&["events", id])isevents.<id>.start. Top-level changes are unchanged (parentsempty), so rooms (EditKind::Props), tasks and the merge path need no other change.props::get_at(text, path)andprops::keys_at(text, path).props/map.rs(about 330 lines);props.rsonly got ascanrefactor (one scanner for any indent;entriescalls it with indent 0, same behaviour) andrender_in(list indent relative to a nesting depth).Sizes and big-O
Failure table
pickfinds two; alsovalidate(serde_yaml) rejects any duplicate in the blockBroken{line}, nothing written, person fixes in source modeduplicate_keys_refuse[..],{..}, block scalar-malformed_maps_never_writescanerrorsmalformed_maps_never_write<<merge in the blockrefuse_aliases(ignores quoted text, comments, block scalars)malformed_maps_never_write,aliases_in_text_and_block_scalars_are_fineverify: new block parsed by serde_yaml must equal the old parse with only this change madeBroken, nothing writtennested_edits_match_serde_yaml(5000 random trees)adds_last_and_keeps_every_other_byte, property testevents:removes_a_pair_and_an_emptied_mapsame_valueadds_last...Gate tests
unrelated YAML bytes survive:adds_last_and_keeps_every_other_byte,nested_edits_match_serde_yaml.duplicate keys refuse safely:duplicate_keys_refuse.malformed maps never write:malformed_maps_never_write.Decisions (no questions asked)
{}), and sequences of maps (log:list of outcome maps) are not edited by path in this step: refused, nothing written. Replacing/removing such a key whole still works through the old top-level path. (Not needed by steps 3-4.)Newis not extended with a map variant; maps are built pair by pair, so the writer never needs to serialise whole maps.blockneeds---\n); unchanged.Results
All run on calternal-dev-01-compute02 inside the
infra/nix-devshellflake (flake files not committed):cargo test -p calternal-notes --lib props: 24 passed (9 new inprops::map_tests).cargo clippy -p calternal-notes -p calternal-tasks --all-targets -j4 -- -D warnings: clean.cargo fmtapplied.cargo test -p calternal-notes -p calternal-tasks --no-fail-fast -j4: notes 435 passed, tasks 28 passed. One earlier run had a single failure inroom::save::...::independent_652_real_syncclient_joining_snapshot_is_not_saved(EOF while parsing, unrelated to props, VM shared with other jobs); it passed on rerun. Not investigated further.Plan was thought through before coding; the PR text was written once the gate was green.
Resume verification (2026-10-31)
Head remains
cd8e5cbdc2688c255b6cdbdbfa31a53fb5187e6d; no implementation change was needed. Fresh checks on dev-01 in the Nix shell, with every heavy command through~/bin/slot:cargo fmt --all -- --check: passed.cargo clippy -p calternal-notes -p calternal-tasks --all-targets -j4 -- -D warnings: passed.cargo test -p calternal-notes -p calternal-tasks --no-fail-fast -j4: notes 435 passed / 6 ignored, tasks 28 passed. All nested-map gates passed.Raw log:
calternal-dev-01-compute02:~/cal-resume-s2.log. No PRs merged.Independent review, round 1
Verdict: fix first. Reviewed
cd8e5cbdc2688c255b6cdbdbfa31a53fb5187e6d, ADRs 0067/0068/0056/0066, the implementation and the cited tests. Severity follows the requested rule: silent note-content loss is P0. Findings below are source-level traces; I have not rerun the full gate suites.P0 — CRLF/BOM frontmatter is treated as absent, allowing a second frontmatter block.
crates/notes/src/props/map.rs:36, throughcrates/notes/src/props.rs:365and:394. For---\r\ntitle: Keep\r\n---\r\nbody,changed(text, &[Change::text("uid", "new").under(&["source"])])accepts validation, seesold_len == 0, and prepends a fresh LF block. The original properties become body text. A UTF-8 BOM before an otherwise valid block takes the same path; even malformed CRLF/BOM YAML passes the absent-block check. The PR's statement that CRLF is unreadable does not mean writes are refused. Fix: distinguish absent frontmatter from unsupported/malformed frontmatter before building edits; preserve the complete existing encoding/line endings or returnBroken, with no edit. Add CRLF and BOM golden/refusal cases, including malformed and duplicate-key variants.P0 — Replacing a leaf deletes an untouched indented comment.
crates/notes/src/props/map.rs:118and the new scanner incrates/notes/src/props.rs:250. For---\nm:\n a: old\n # keep this comment\n b: other\n---\nbody, settingm.atonewreplaces the whole entry span, including the comment. The YAML equality verifier accepts this because comments do not affect parsed values. Fix: preserve comment lines independently of the replaced value, or refuse a replacement whose continuation lines cannot be retained safely. Test this exact case for replace/remove and comments between nested maps.Failure-table audit (coverage inspected, not a new passing-test claim):
duplicate_keys_refusecovers top, inner and unrelated duplicates for LF blocks. CRLF/BOM bypass remains finding 1.malformed_maps_never_write.other: {x: &a yes}are not covered;starts_aliasdoes not recognize a value after a flow-map colon. Expand refusals if the contract is “any anchor anywhere means no write.”map::expected, so it is not an independent oracle for errors in that helper.Mergeability: Forgejo currently reports
mergeable: false, but on dev-01git merge-tree --write-tree origin/dev cd8e5cbdexits 0 againstdev=2f304154054ec68b7855bb1f2bd24b1fb15a386d(tree40626d4e458dbcf9c10386842a66339b40c86765). No Git content conflict was reproduced. #783/#785 stack on this PR; stacking explains cumulative diffs, not this false server flag. The precise Forgejo-side cause is unproven. No code pushed or merged.Independent reproduction addendum: a standalone Rust probe on dev-01, inside the Nix shell and compiled through
~/bin/slot, reproduced both P0 scenarios. CRLF and BOM inputs returnedOkwith a fresh LF frontmatter prepended; replacingm.areturnedOkwith# keep this commentdeleted. Probe:/tmp/cal-independent-r1/repro.rs, binary/tmp/cal-independent-r1/repro. It linked the VM's existing cached Notes library (no fresh full-suite build); the relevant props/map implementation is unchanged between that library's branch source and the reviewed stack. The parser modules in the probe were copied directly from the reviewed source. This is a focused reproduction, not a fresh full gate run.Round 1 implementation fixes
Head
f92341a6. No merge. #783 is being rebased onto this head, followed by #785.review_envelopes_refuse_before_any_editcovers valid, malformed and duplicate-key input, both nested and top-level writes.review_leaf_comments_are_never_deleteduses the exact reproduction plus a nested-map case.review_flow_aliases_refusecovers both alias-consumer and standalone-anchor forms.expectedAll three new regressions failed on the original implementation in
~/cal-r1-s2-red.log. The first full run exposed the old property generator's assumption that every valid tree can be edited, including commented subtrees; its refusal oracle now checks that exact additional unsupported case. No permissive production fallback was added.Decision: retain the existing line-level splicer; no whole-YAML serialization. CRLF/BOM editing remains unsupported and safely refuses. Unrelated bytes are never demoted to body text. Comment-bearing subtree replacement is unsupported until comment ownership can be retained unambiguously. Work remains O(frontmatter bytes) with no Home scan.
Fresh dev-01 Nix gates, heavy commands through
~/bin/slot, log~/cal-r1-s2-final.log:cargo fmt --all && cargo fmt --all -- --checkcargo clippy -p calternal-notes -p calternal-tasks -p calternal --all-targets -j4 -- -D warningscargo test -p calternal-notes -p calternal-tasks -p calternal --no-fail-fast -j4All passed, including notes 438 passed / 6 ignored and tasks 28 passed. The events crate does not exist at this stack layer; its gates run on #783/#785. Existing ignored tests were not enabled. No further independent review round is claimed.
Independent review, round 2
Verdict: fix first. Reviewed
f92341a658fb0fa892aeb19e3cb9037d82f47569, the round-1 review/fix status, and ADRs 0067/0068/0056/0066. No prior round-2 comment existed when checked. No code pushed or merged.review_envelopes_refuse_before_any_editdirectly checks valid, malformed and duplicate-key variants for the unsupported envelopes and both write routes. It establishes refusal, not editing support.review_leaf_comments_are_never_deletedproves the cited standalone-comment cases refuse. It does not cover inline comments on retained sequence items or child fields; new finding below.review_flow_aliases_refusechecks standalone flow anchors and a consumer. It is not an exhaustive YAML-token oracle.expectedoracleexpected; byte/refusal goldens are independent but narrower.New P0 — Inline continuation comments still disappear silently.
crates/notes/src/props.rs:292,crates/notes/src/props/map.rs:112, andcrates/notes/src/props.rs:501. The new guard recognizes only lines whose trimmed text starts with#. Starting with:Change::list("a", vec!["old".into(), "new".into()]).under(&["m"])returns success witholdandnew, but deletes# keep this commentfrom the unchanged old item. Replacing a subtree containingx: old # keep this commentalso deletes the comment. YAML semantic verification cannot detect either loss. This is a remaining hole exposed by round 2, not a claim that the fix newly created the old loss. Fix: preserve inline comments with their retained source items/fields, or conservatively refuse affected replacements/removals using YAML-aware comment detection that distinguishes quoted/scalar#text. Add independent exact-byte/refusal cases for list append, subtree replacement and removal.Verification: on dev-01, Nix shell, all heavy commands through
~/bin/slot, at stack head771eb102(these props functions are unchanged from this PR):cargo test -p calternal-notes review_ -j4passed 24 tests, including all three new props regressions. A standalone Rust probe linked the current cached stack libraries and observed the successful comment deletion above;/tmp/cal-r2-probe.rs,/tmp/cal-r2-probe.out. This is focused evidence, not a fresh full-suite, perf or exhaustive YAML claim. The test pass does not waive the P0.round 2 fixes for final review
Head
94807faa. Simplification: replace only a simple scalar or block-list target's own lines. Comments inside the target, flow values, anchors/aliases, multiline scalars, subtree replacement and unsupported syntax refuse with “edit this in the note in source mode”. A true no-op keeps the original bytes. Deleting the last leaf keeps the parent lines. No merge.#text remains editable.review_round2_awkward_corpus_only_target_lines_or_refusalindependently checks exact prefix/suffix bytes and the parsed target for a corpus of comments, flow values, anchors, aliases, block scalars and multiline quotes. It does not use the production expected-value helper.Passed on dev-01 in Nix, log
~/cal-r2-s2-final3.log:Notes: 439 passed / 6 existing ignored; tasks: 28 passed; calternal unit/integration suites passed with existing ignored tests. The events crate is introduced by the next stack layer, so it is absent from this layer's command. No build/test ran on the control host; Nix scaffolding was not committed.
Decision: support fewer write forms, keeping lenient reads. Source editing is required for refused forms; this is intentional. The existing generated tests still use their old shared semantic oracle, supplemented by the independent byte/refusal corpus. Final review round 3 is pending; this comment does not claim review approval.
Independent review, round 3 (final)
Verdict: stop and simplify. Reviewed
94807faabbdc6335ba719e5fa4987a5f032a7ab6, round-2 review #70965, fix status #71020, simplification notes and ADRs 0067/0068. No code pushed or merged. Round 3 still finds a new P0 reproduction, so this is the final review round under kayg's rule.nested_edits_match_serde_yamlpasses its default 5,000 cases. Replacement checks exact prefix/suffix, but insertion/deletion checks only line subsequences, and semantic expectations still call productionmap::expected. The alphabetic generated leaves and 14-value independent corpus miss plain scalars containing interior quotes plus comments. These tests do not prove every accepted edit preserves authored comments.New P0: an interior quote in a YAML plain scalar bypasses the comment-loss refusal.
crates/notes/src/props.rs:291,:670. Valid YAML:Change::text("a", "new").under(&["m"])succeeds and replaces the target witha: new, silently deleting# keep. Top-level replacement does the same.a: Bob said "hello # keepalso reproduces it. In YAML, a quote embedded in an already-started plain scalar does not open a quoted scalar;# keepis a real comment.strip_commentopens quote state after a space, misses the hash, andrefuse_comment_lossaccepts it. The serde_yaml postcondition passes because it cannot see comments. Independent VM probes observed success and exact comment loss; the ordinaryold # keepcontrol refuses. This is a new input hole in the surviving comment-loss class, not a regression claim about the earlier exact fixtures.Concrete simpler design / fix: make the editable syntax a strict lexical allowlist independent of the lenient reader: initially refuse any existing target scalar/list item containing
#, and refuse interior quotes in plain scalars. Permit only complete single-line scalars and simple block-list items whose whole lexical form is proven safe; keep all complex/commented targets for source editing. Keep exact splices and serde_yaml as a postcondition, not a comment oracle. Add independent replacement/removal/list tests for both quote types and hashes, asserting exact unchanged input on refusal; generate punctuation-bearing plain scalars rather than only alphabetic leaves. Do not start a fourth patch/review cycle around another quote heuristic.Fresh verification on dev-01, Nix shell, stack head
eb7ffd2d(the implicated writer/guard is unchanged from this PR), heavy commands via~/bin/slot:cargo test -p calternal-events -p calternal-notes -p calternal-tasks review_ -j4: events 21, notes 25, tasks 7 passed; includes independent awkward corpus.cargo test -p calternal-notes nested_edits_match_serde_yaml -j4: 1 passed, default 5,000 generated cases.~/bin/slot rustc --edition=2024 /tmp/cal-r3-props-probe.rs -L dependency=target/debug/deps --extern calternal_notes="$notes_lib" -o /tmp/cal-r3-props-probe; executed separately. Source/output:/tmp/cal-r3-props-probe.rs,/tmp/cal-r3-props-probe.out; test log/tmp/cal-r3-review-tests.log, property log/tmp/cal-r3-property-test.logon dev-01.$notes_libwas the newest builtlibcalternal_notes-*.rlibin that worktree.No fresh full-suite, clippy, performance or browser result is claimed. No additional P1–P3 finding beyond the previously acknowledged oracle limitation.
On hold under the three-round stop-and-simplify rule. The replacement plan is PR #802: #802
Plan: https://git.kayg.org/kayg/calternal/src/branch/docs/calendar-backend-v2-plan/docs/plans/0067-calendar-backend-v2.md
No further patches or fourth review round on this stack. The plan chooses a strict whole-frontmatter write allowlist, the editor's Lezer parser with an explicit backend bridge feasibility gate, and centralized marker writes disabled until full UI hiding. It lists salvage, independent tests and smaller replacement PRs. This comment changes no implementation code and requests no merge.
kayg referenced this pull request2026-11-05 23:21:11 +00:00
housekeeping #1009: needs-decision.
Calendar v1 writer slice was held after three review rounds; #802 chooses the shared note executor approach. Preserve old writer regressions instead of replaying the abandoned mutation path.
original head:
94807faabbdc6335ba719e5fa4987a5f032a7ab6. against devcd7f84432febfc63b788fbb9b9f9950452982d74, git cherry reports 11 unmatched and 0 equivalent patches. the landing audit lists each candidate missing commit and stable patch match inpr-781.md; unmatched patches can include superseded behavior.left open, source branch unchanged. no rebase or new test claim for this decision lane.
Superseded by calendar backend v2 (#802). The branch and original tips are archived under
archive/(see #1009), so nothing is lost; useful regression vectors are noted there for the active lanes. Closing.Pull request closed