oxicode/store/issues/mod.rs
1//! Local issue tracking system — GitHub-style issues stored as markdown files.
2//!
3//! Issues live in `.oxicode/issues/` at the project root (discovered by walking
4//! up from the current directory until `.oxicode/` is found, mirroring
5//! `Settings::find_project_settings`). Each issue is a single markdown file
6//! with a YAML frontmatter block holding structured metadata, followed by a
7//! free-form markdown body:
8//!
9//! ```markdown
10//! ---
11//! id: 12
12//! title: "Fix login bug"
13//! status: open
14//! priority: high
15//! labels: [bug, auth]
16//! assignee: null
17//! created_at: 2026-06-17T10:30:00Z
18//! updated_at: 2026-06-17T14:20:00Z
19//! closed_at: null
20//! sessions: [abc123, def456]
21//! assigned_to: null
22//! github: null
23//! ---
24//!
25//! Free-form markdown body...
26//! ```
27//!
28//! # Design decisions
29//!
30//! - **Why not a `StateStore` port?** Issues are *documents* that humans open
31//! in `$EDITOR`, commit to git, and diff. `StateStore` is an opaque KV/append
32//! blob. Different workload, different storage shape. This mirrors how
33//! `store/session.rs` and `store/settings.rs` coexist with the SDK ports.
34//! - **Optimistic concurrency (content-hash CAS).** Mutations take an optional
35//! `content_hash` captured at the last read. The write is rejected if the
36//! on-disk content has changed since. This is the exact pattern used by the
37//! `edit` tool (`oxicode-agent/src/tools/edit.rs`), so external edits (e.g.
38//! someone editing the file in vim) are detected without any locking.
39//! - **Atomic writes** via temp+rename (same pattern as `store/session.rs`).
40//! - **Assignment is process-liveness based, not time based.** An assigned
41//! issue records the owning session id. Whether that session is still alive
42//! is determined by an OS-held advisory lock on
43//! `.oxicode/issues/.alive/<session_id>` (see [`liveness`]). When the owning
44//! process exits — including `kill -9`, crash, or terminal close — the OS
45//! releases the lock and the assignment becomes stale and reclaimable.
46pub mod error;
47pub mod filter;
48pub mod liveness;
49pub mod serialize;
50pub mod store;
51pub mod types;
52
53// Re-export the public surface so callers can keep using
54// `crate::store::issues::*` exactly as before the directory split.
55pub use error::IssueError;
56pub use filter::IssueFilter;
57pub use serialize::{content_hash, issue_filename, issues_dir, parse_issue, serialize_issue};
58pub use store::{FileIssueStore, IssueSummary};
59pub use types::{Assignment, GithubRef, Issue, IssueMeta, IssuePatch, Priority, Status};