Skip to content

feat(role-sync): mirror-mode conflict detection + NATS events (010 PR-4) - #78

Merged
flg77 merged 1 commit into
mainfrom
role-sync/mirror-mode
May 14, 2026
Merged

flg77 merged 1 commit into
mainfrom
role-sync/mirror-mode

Conversation

@flg77

@flg77 flg77 commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary

Fourth PR of proposal 010 — the convergence story. In mirror mode both PR-2 (Go-side file → CRD) and PR-3 (Python-side CRD → file) run simultaneously; this PR adds the small piece that prevents echo loops and surfaces genuine concurrent-write conflicts on NATS.

New module acc/role_sync_conflict.py with ConflictDetector:

  • classify_file_change(role_id, body) returns one of:
    Outcome Meaning
    echo File matches our last CRD-driven write within conflict_window_s → ignore
    applied Genuine operator edit (outside window or no prior record) → propagate
    conflict Concurrent edit detected → LWW resolution + emit event
  • record_our_write(role_id, body) called by the projector after every CRD-driven file write.
  • publish_applied / publish_conflict emit on <events_subject>.{applied,conflict} with payload sufficient for an audit log + PR-5's TUI badge.

Time source + NATS publisher both dependency-injected, so 12 unit tests drive scenarios deterministically with zero time.sleep and zero live NATS.

Status

Detector is inert in production today. Wiring into RoleCRDProjector is deliberately deferred to a tiny follow-up PR so the classifier can be reviewed in isolation. This PR is purely the building block + tests.

Test plan

  • 73/73 green locally (12 new + 19 PR-3 + 42 PR-1)
  • All three classification outcomes covered (echo / applied / conflict)
  • Per-role isolation (record for alpha doesn't affect beta)
  • Publisher exception swallowing (NATS hiccup doesn't break projector)
  • Subject normalisation (acc.role.sync. and acc.role.sync both work)
  • Validation on acc1 — operator's call

Diff size

Area Lines
Production ~240
Tests ~190
Docs ~30
Total ~460

Under PR-4's 500-LOC proposal estimate.

Proposal 010 reference

010 - Bi-directional file-CRD sync for role definitions.md. Status: Signed off 2026-05-14. PR-1 #75 + PR-2 #76 + PR-3 #77 merged.

🤖 Generated with Claude Code

Fourth PR of proposal 010 — the convergence story.  In mirror mode
both PR-2 (file → CRD) and PR-3 (CRD → file) run simultaneously, and
this PR adds the small piece that prevents echo loops and surfaces
genuine concurrent-write conflicts.

New module acc/role_sync_conflict.py with ConflictDetector class:

* classify_file_change(role_id, body) returns one of:
  - "echo"     — matches our last CRD-driven write within the window
  - "applied"  — genuine operator edit (outside window or no prior
                 write recorded)
  - "conflict" — concurrent edit detected; LWW resolution applied

* record_our_write(role_id, body) is called by the CRD projector
  after every file write so the detector can correlate.

* publish_applied / publish_conflict emit NATS events on
  <events_subject>.applied / .conflict.  Payload is JSON with
  role_id, source identifiers, and (for conflicts) the loser snippet
  + RFC3339 timestamp.

* Time source + publisher are both dependency-injected so unit tests
  drive scenarios deterministically (no time.sleep, no live NATS).

* Counters (applied/echo/conflict) accumulate for future /metrics
  integration.

Test coverage: 12 unit tests covering all three classification
outcomes, per-role isolation, record-replacement semantics, the
applied/conflict event surface (including subject normalisation,
publisher absence, publisher exceptions), and counter increments.

Wiring into RoleCRDProjector deliberately deferred to a tiny
follow-up PR so the classifier is reviewed standalone.  The detector
is inert in production builds today.

Total: +458 LOC across 3 files (production ~240 + tests ~190 + docs).
Well under PR-4's 500-LOC budget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant