fix: P0 council bug fixes (atomic writes, BRAIN_DIR hard-fail, thread-safety) [rebased]#153
fix: P0 council bug fixes (atomic writes, BRAIN_DIR hard-fail, thread-safety) [rebased]#153
Conversation
…rity, thread-safety lock P0-5 atomic writes (rule_graph.json): - New src/gradata/_atomic.py: atomic_write_text() (temp file + fsync + os.replace + dir fsync on POSIX) - RuleGraph.save() now uses atomic_write_text — crash mid-write preserves prior state - Test: test_rule_graph_atomic.py::test_rule_graph_save_preserves_prior_state_when_replace_fails P0-4 BRAIN_DIR hard-fail (no more silent data loss): - exceptions.py: new GradataError base + BrainNotConfiguredError(GradataError) - implicit_feedback hook: raises BrainNotConfiguredError instead of swallowing - hooks/_base.py: hook runner no longer suppresses BrainNotConfiguredError - _doctor.py: reports missing BRAIN_DIR as clear brain_dir failure - Test: test_brain_dir_required.py (3 cases) P0-7 package import integrity: - Test: test_import_integrity.py — subprocess: import gradata; from gradata import Brain, Lesson, LessonState - Plus Brain.init() smoke test in tmp_path P0-6 thread-safety lock (Brain documented NOT thread-safe yet ships daemon + mcp_server): - New src/gradata/_brain_lock.py: acquire_brain_lock(brain_dir) context mgr - POSIX: fcntl.flock LOCK_EX|LOCK_NB on BRAIN_DIR/.brain.lock - Windows: msvcrt.locking - In-process duplicate-lock guard - BrainLockedError in exceptions.py - daemon.py: acquires lock at start, releases in cleanup - mcp_server.py: acquires for run_server() duration - Brain itself does NOT auto-acquire (would break tests) - Test: test_brain_lock.py::test_second_mock_daemon_raises_brain_locked CHANGES.md and REPORT.md committed for posterity. Codex (gpt-5.5) generated all four patches; could not self-commit because .git/ is outside its sandbox writable root.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (17)
📝 WalkthroughSummary
WalkthroughThis PR implements four P0 improvements: atomic writes for Changes
Sequence Diagram(s)sequenceDiagram
participant Daemon as GradataDaemon.start()
participant Lock as acquire_brain_lock()
participant FS as File System
participant Server as HTTP Server Lifecycle
Daemon->>Lock: acquire_brain_lock(brain_dir)
activate Lock
Lock->>FS: Open/create .brain.lock file
Lock->>FS: flock/msvcrt.locking (exclusive, non-blocking)
alt Lock Acquired
Lock->>FS: Truncate & write PID, fsync
Lock-->>Daemon: Return context manager
Daemon->>Server: Start within lock context
Server->>Server: Port selection, bind, workers
Server->>Server: serve_forever()
Server-->>Daemon: Complete/Exception
Daemon->>Lock: Exit context (via finally)
Lock->>FS: Unlock OS lock, close file
deactivate Lock
else Lock Unavailable (BrainLockedError)
Lock-->>Daemon: Raise BrainLockedError
Daemon->>Daemon: Cleanup resources
end
sequenceDiagram
participant Hook as Hook Runner (run_hook)
participant FB as implicit_feedback.main()
participant Resolve as resolve_brain_dir()
participant Signals as Signal Detection
Hook->>FB: Execute hook
activate FB
FB->>Signals: _detect_signals()
alt Signals Found
FB->>Resolve: resolve_brain_dir()
alt BRAIN_DIR Resolved
FB->>FB: Persist feedback
FB-->>Hook: Return None
else BRAIN_DIR Unresolved
FB-->>Hook: Raise BrainNotConfiguredError
Hook->>Hook: Re-raise (no suppression)
end
else No Signals
FB-->>Hook: Return None (early exit)
end
deactivate FB
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Review rate limit: 3/5 reviews remaining, refill in 24 minutes. Comment |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
Clean rebase of #145. Cherry-picked only the meaningful Phase B commit(s) onto current main; drops 41 stale m1 commits already merged via #144.