feat(scroll): P6.7 block provenance (agent change-bar) — P6 CLOSE - #73
Merged
Merged
Conversation
…r + lastAuthor CRDT field
…it/merge follow content
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Render which blocks the native agent authored — the last piece of P6, so this closes the phase.
What
lastAuthorCRDT field ('agent' | 'human') on the blockY.Map. It lives IN the CRDT (not a Yjs transactionorigin, which does not survive network sync) so every peer sees who wrote each block.blockAuthor()reads + validates it (garbage off the wire → undefined);setBlockAuthor()stamps idempotently;setBlockText(...author?)stamps in the SAME transaction (no per-keystroke double render);blockViewOf()is the shared builder and OMITSauthorwhen unset (unstampedBlockViewstays{id,type,text}).data-author+ an absolutely-positioned::afterin the sheet's left gutter. Zero reflow (the P6.1 invariant): no padding/border on the block, and::after(not::before) so it never collides with the:empty::beforestrut that keeps an empty block from collapsing.Gate
simplifier → 4 parallel inspectors (one lens each) → adjudicator PASS. The inspectors surfaced two execution-proven blockers, both fixed with independently-reproduced teeth:
{id,type,text-delta}, so asetBlockAuthor-only change (the shape the driver emits when its actor no-ops) into a live reader's band committed unchecked. FIX: the guard signature now includeslastAuthor— an author stamp into a guarded band is refused like any edit. (Regression tests: author-only into a guarded band → refused; author-only on a cold block → commits.)Plus: marker
::before→::after(an empty agent block otherwise lost its anti-collapse strut → reflow),blockAuthorwire-validation, and a per-block provenance tie in the milestone e2e (a partial-stamp regression now fails, not just total removal).tsc -b --noEmit= 0.Debt (logged, non-blocking)
Editor.tsxlayer has no regression test (the model-primitive tests pass on the buggy commit too, since the bug was the editor stamping after the primitive). No React/Editor render harness in-repo; the fix is read-verified andpreventDefaultguarantees noonEditpath fires on split/merge.::afterno-reflow invariant is inspection-verified only (no CSS harness).targetregardless of which blocks a future richer actor touches (latent today; the one actor touches only target). Stamp per-touched-block when richer actors land.data-authoris unasserted in any e2e (no agent-in-browser harness); the attribute pass-through is trivial and model + persistence are covered.mergeIntoPreviouscan't represent a mixed-authorship survivor (it keeps its own author).Closes #72.
🤖 Generated with Claude Code
https://claude.ai/code/session_01X7WHDHurYHVibsjwvevzFr