Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/settings/actions-permissions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"enabled": true,
"allowed_actions": "selected",
"sha_pinning_required": true
}
7 changes: 7 additions & 0 deletions .github/settings/claude-environment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"wait_timer": 0,
"prevent_self_review": false,
"reviewers": [{"type": "User", "id": 220209011}],
"can_admins_bypass": false,
"deployment_branch_policy": null
}
2 changes: 1 addition & 1 deletion .github/settings/main-ruleset.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Main review and verified checks",
"target": "branch",
"enforcement": "disabled",
"enforcement": "active",
"bypass_actors": [],
"conditions": {"ref_name": {"include": ["refs/heads/main"], "exclude": []}},
"rules": [
Expand Down
1 change: 1 addition & 0 deletions .github/settings/release-environment.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"wait_timer": 0,
"prevent_self_review": false,
"reviewers": [{"type": "User", "id": 220209011}],
"can_admins_bypass": true,
"deployment_branch_policy": {"protected_branches": false, "custom_branch_policies": true}
}
6 changes: 5 additions & 1 deletion .github/settings/selected-actions.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"github_owned_allowed": true,
"verified_allowed": false,
"patterns_allowed": ["SonarSource/sonarqube-scan-action@*"]
"patterns_allowed": [
"SonarSource/sonarqube-scan-action@*",
"anthropics/claude-code-action@*",
"oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6"
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Fixed importing a document whose parent directory is a symlink. Workspace-root symlink rejection is unchanged.
- A tracker that cannot be read now degrades to a package-only view with a warning instead of blanking the dashboard, and tracker writes are disabled while it is unreadable.
- Fixed tracker status changes binding colliding package IDs to the wrong application directory.
- Fixed a failed first tracker status action leaving an unusable database; correcting the package now allows a retry without losing status or history atomicity.
- Quitting with unsaved master resume edits can now be completed via Save, and `restore-cleanup` reports what was actually restored.
- Fixed the SBOM export treating a pending `202` report as a failure and a delivered but invalid report as pending; retries stay bounded.
- CI whitespace and conflict-marker checks now inspect the committed range for the triggering event, including merge resolutions, instead of an always-empty working-tree diff.
Expand Down
4 changes: 3 additions & 1 deletion Sources/NavCenterCore/TrackerStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ public final class TrackerStore {
let existed = SQLiteSupport.exists(dbPath)
try PathSafety.createDirectory(dbPath.deletingLastPathComponent(), inside: repoRoot, label: "tracking directory")
let connection = try SQLiteSupport.Connection(dbPath: dbPath, repoRoot: repoRoot, writable: true, create: !existed)
// Keep a valid empty tracker if the first status action fails. Rolling
// schema creation back with that action leaves a file that cannot retry.
if !existed { try connection.transaction { try connection.createSchema() } }
let result: TrackerStatusUpdateResult = try connection.transaction {
if !existed { try connection.createSchema() }
try connection.validateSchema()
let applicationDir = "applications/" + packageName
let expectedID = trackerID(packageName: packageName)
Expand Down
36 changes: 36 additions & 0 deletions Tests/NavCenterTests/CoreDataIntegrityTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,42 @@ final class CoreDataIntegrityTests: XCTestCase {
XCTAssertEqual(try TrackerStore(repoRoot: root).loadRows().count, 1)
}

func testFailedFirstStatusActionKeepsInitializedTrackerAndCanRetry() throws {
let posting = package.appendingPathComponent("posting.md")
let invalidPosting = Data([0xFF, 0xFE])
try invalidPosting.write(to: posting)
XCTAssertFalse(SQLiteSupport.exists(database))

XCTAssertThrowsError(try status()) { error in
XCTAssertTrue(error.localizedDescription.contains("not UTF-8"), error.localizedDescription)
}

XCTAssertEqual(try Data(contentsOf: posting), invalidPosting)
XCTAssertTrue(try TrackerStore(repoRoot: root).loadRows().isEmpty)
XCTAssertTrue(try TrackerStore.queryRows(repoRoot: root, dbPath: database, sql: "select * from status_events;").isEmpty)
try makePackage(packageName)

let result = try status(.interview)
XCTAssertEqual(result.oldStatus, "")
XCTAssertEqual(result.newStatus, "Interview")
XCTAssertTrue(result.warnings.isEmpty)
XCTAssertEqual(try TrackerStore(repoRoot: root).loadRows().map(\.status), ["Interview"])
let events = try TrackerStore.queryRows(repoRoot: root, dbPath: database, sql: "select * from status_events;")
XCTAssertEqual(events.count, 1)
XCTAssertEqual(events.first?["old_status"] as? String, "")
XCTAssertEqual(events.first?["new_status"] as? String, "Interview")
}

func testEmptyExistingTrackerIsNotInitialized() throws {
try Data().write(to: database)

XCTAssertThrowsError(try status()) { error in
XCTAssertTrue(error.localizedDescription.contains("missing required columns"), error.localizedDescription)
}

XCTAssertEqual(try Data(contentsOf: database), Data())
}

func testStatusChangeRefusesTrackerIDCollisionWithoutMutatingSibling() throws {
let first = "2099-01-01_A-B_Role"
let second = "2099-01-01_A_B_Role"
Expand Down
Loading
Loading