chore: add gitignore for mock-seed, roam CI workflow, formatting
- Add tools/mock-seed/ to .gitignore - Add .github/workflows/roam.yml CI workflow - Add .roam/fitness.yaml architectural fitness rules - Rustfmt formatting fixes in show.rs and vector.rs - Beads sync Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
21
.github/workflows/roam.yml
vendored
Normal file
21
.github/workflows/roam.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Roam Code Analysis
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
jobs:
|
||||||
|
roam:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
- run: pip install roam-code
|
||||||
|
- run: roam index
|
||||||
|
- run: roam fitness
|
||||||
|
- run: roam pr-risk --json
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -41,6 +41,9 @@ lore.config.json
|
|||||||
*.db-shm
|
*.db-shm
|
||||||
|
|
||||||
|
|
||||||
|
# Mock seed data
|
||||||
|
tools/mock-seed/
|
||||||
|
|
||||||
# Added by cargo
|
# Added by cargo
|
||||||
|
|
||||||
/target
|
/target
|
||||||
|
|||||||
11
.roam/fitness.yaml
Normal file
11
.roam/fitness.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
rules:
|
||||||
|
- name: No circular imports in core
|
||||||
|
type: dependency
|
||||||
|
source: "src/**"
|
||||||
|
forbidden_target: "tests/**"
|
||||||
|
reason: "Production code should not import test modules"
|
||||||
|
- name: Complexity threshold
|
||||||
|
type: metric
|
||||||
|
metric: cognitive_complexity
|
||||||
|
threshold: 30
|
||||||
|
reason: "Functions above 30 cognitive complexity need refactoring"
|
||||||
@@ -160,6 +160,7 @@ pub fn run_show_issue(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct IssueRow {
|
struct IssueRow {
|
||||||
id: i64,
|
id: i64,
|
||||||
iid: i64,
|
iid: i64,
|
||||||
@@ -1218,6 +1219,172 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn seed_second_project(conn: &Connection) {
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace, web_url, created_at, updated_at)
|
||||||
|
VALUES (2, 101, 'other/repo', 'https://gitlab.example.com/other', 1000, 2000)",
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seed_discussion_with_notes(
|
||||||
|
conn: &Connection,
|
||||||
|
issue_id: i64,
|
||||||
|
project_id: i64,
|
||||||
|
user_notes: usize,
|
||||||
|
system_notes: usize,
|
||||||
|
) {
|
||||||
|
let disc_id: i64 = conn
|
||||||
|
.query_row(
|
||||||
|
"SELECT COALESCE(MAX(id), 0) + 1 FROM discussions",
|
||||||
|
[],
|
||||||
|
|r| r.get(0),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO discussions (id, gitlab_discussion_id, project_id, issue_id, noteable_type, first_note_at, last_note_at, last_seen_at)
|
||||||
|
VALUES (?1, ?2, ?3, ?4, 'Issue', 1000, 2000, 2000)",
|
||||||
|
rusqlite::params![disc_id, format!("disc-{}", disc_id), project_id, issue_id],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for i in 0..user_notes {
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO notes (gitlab_id, discussion_id, project_id, author_username, body, created_at, updated_at, last_seen_at, is_system, position)
|
||||||
|
VALUES (?1, ?2, ?3, 'user1', 'comment', 1000, 2000, 2000, 0, ?4)",
|
||||||
|
rusqlite::params![1000 + disc_id * 100 + i as i64, disc_id, project_id, i as i64],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
for i in 0..system_notes {
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO notes (gitlab_id, discussion_id, project_id, author_username, body, created_at, updated_at, last_seen_at, is_system, position)
|
||||||
|
VALUES (?1, ?2, ?3, 'system', 'status changed', 1000, 2000, 2000, 1, ?4)",
|
||||||
|
rusqlite::params![2000 + disc_id * 100 + i as i64, disc_id, project_id, (user_notes + i) as i64],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- find_issue tests ---
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_basic() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
let row = find_issue(&conn, 10, None).unwrap();
|
||||||
|
assert_eq!(row.iid, 10);
|
||||||
|
assert_eq!(row.title, "Test issue");
|
||||||
|
assert_eq!(row.state, "opened");
|
||||||
|
assert_eq!(row.author_username, "author");
|
||||||
|
assert_eq!(row.project_path, "group/repo");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_with_project_filter() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
let row = find_issue(&conn, 10, Some("group/repo")).unwrap();
|
||||||
|
assert_eq!(row.iid, 10);
|
||||||
|
assert_eq!(row.project_path, "group/repo");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_not_found() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
let err = find_issue(&conn, 999, None).unwrap_err();
|
||||||
|
assert!(matches!(err, LoreError::NotFound(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_wrong_project_filter() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
seed_second_project(&conn);
|
||||||
|
// Issue 10 only exists in project 1, not project 2
|
||||||
|
let err = find_issue(&conn, 10, Some("other/repo")).unwrap_err();
|
||||||
|
assert!(matches!(err, LoreError::NotFound(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_ambiguous_without_project() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn); // issue iid=10 in project 1
|
||||||
|
seed_second_project(&conn);
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO issues (id, gitlab_id, iid, project_id, title, state, author_username,
|
||||||
|
created_at, updated_at, last_seen_at)
|
||||||
|
VALUES (2, 201, 10, 2, 'Same iid different project', 'opened', 'author', 1000, 2000, 2000)",
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let err = find_issue(&conn, 10, None).unwrap_err();
|
||||||
|
assert!(matches!(err, LoreError::Ambiguous(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_ambiguous_resolved_with_project() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
seed_second_project(&conn);
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO issues (id, gitlab_id, iid, project_id, title, state, author_username,
|
||||||
|
created_at, updated_at, last_seen_at)
|
||||||
|
VALUES (2, 201, 10, 2, 'Same iid different project', 'opened', 'author', 1000, 2000, 2000)",
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let row = find_issue(&conn, 10, Some("other/repo")).unwrap();
|
||||||
|
assert_eq!(row.title, "Same iid different project");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_user_notes_count_zero() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
let row = find_issue(&conn, 10, None).unwrap();
|
||||||
|
assert_eq!(row.user_notes_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_user_notes_count_excludes_system() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
// 2 user notes + 3 system notes = should count only 2
|
||||||
|
seed_discussion_with_notes(&conn, 1, 1, 2, 3);
|
||||||
|
let row = find_issue(&conn, 10, None).unwrap();
|
||||||
|
assert_eq!(row.user_notes_count, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_user_notes_count_across_discussions() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
seed_discussion_with_notes(&conn, 1, 1, 3, 0); // 3 user notes
|
||||||
|
seed_discussion_with_notes(&conn, 1, 1, 1, 2); // 1 user note + 2 system
|
||||||
|
let row = find_issue(&conn, 10, None).unwrap();
|
||||||
|
assert_eq!(row.user_notes_count, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_issue_notes_count_ignores_other_issues() {
|
||||||
|
let conn = setup_test_db();
|
||||||
|
seed_issue(&conn);
|
||||||
|
// Add a second issue
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO issues (id, gitlab_id, iid, project_id, title, state, author_username,
|
||||||
|
created_at, updated_at, last_seen_at)
|
||||||
|
VALUES (2, 201, 20, 1, 'Other issue', 'opened', 'author', 1000, 2000, 2000)",
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
// Notes on issue 2, not issue 1
|
||||||
|
seed_discussion_with_notes(&conn, 2, 1, 5, 0);
|
||||||
|
let row = find_issue(&conn, 10, None).unwrap();
|
||||||
|
assert_eq!(row.user_notes_count, 0); // Issue 10 has no notes
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ansi256_from_rgb() {
|
fn test_ansi256_from_rgb() {
|
||||||
assert_eq!(ansi256_from_rgb(0, 0, 0), 16);
|
assert_eq!(ansi256_from_rgb(0, 0, 0), 16);
|
||||||
|
|||||||
@@ -150,7 +150,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_knn_k_reproduces_original_bug_scenario() {
|
fn test_knn_k_reproduces_original_bug_scenario() {
|
||||||
let k = compute_knn_k(1500, 1);
|
let k = compute_knn_k(1500, 1);
|
||||||
assert!(k <= SQLITE_VEC_KNN_MAX, "k={k} exceeded 4096 at RECALL_CAP with 1 chunk");
|
assert!(
|
||||||
|
k <= SQLITE_VEC_KNN_MAX,
|
||||||
|
"k={k} exceeded 4096 at RECALL_CAP with 1 chunk"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user