Skip to content
Closed
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
4 changes: 4 additions & 0 deletions packages/gittensory-miner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ The package also includes an append-only governor decision ledger: `initGovernor
persist structured allow/deny/throttle/kill-switch outcomes in local SQLite for contributor audit. Insert-only —
no enforcement wiring yet. (#2328)

The package also includes a local soft-claim ledger: `openClaimLedger` / `claimIssue` / `releaseClaim` /
`listActiveClaims` persist which issues this miner instance has claimed on this machine. The table is local
bookkeeping only — duplicate winners are adjudicated elsewhere via `@jsonbored/gittensory-engine`. (#2291)

## Install

See [`docs/miner-goal-spec.md`](docs/miner-goal-spec.md) for the `.gittensory-miner.yml` field reference and [`.gittensory-miner.yml.example`](../../.gittensory-miner.yml.example) at the repo root.
Expand Down
16 changes: 16 additions & 0 deletions test/unit/miner-claim-ledger-readme.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";

const readmePath = join(process.cwd(), "packages/gittensory-miner/README.md");

describe("gittensory-miner claim ledger README (#2291)", () => {
it("documents the foundation claim ledger API surface", () => {
const readme = readFileSync(readmePath, "utf8");
expect(readme).toContain("claimIssue");
expect(readme).toContain("releaseClaim");
expect(readme).toContain("listActiveClaims");
expect(readme).toContain("bookkeeping only");
expect(readme).toContain("@jsonbored/gittensory-engine");
});
});
44 changes: 44 additions & 0 deletions test/unit/miner-claim-ledger.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mkdtempSync, readFileSync, rmSync, statSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { DatabaseSync } from "node:sqlite";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
CLAIM_STATUSES,
Expand Down Expand Up @@ -175,6 +176,49 @@
expect(listActiveClaims("o/a")).toEqual([claim]);
expect(listActiveClaims("o/missing")).toEqual([]);
});

it("creates miner_claims with the foundation schema (#3352)", () => {
const ledger = tempLedger();
const db = new DatabaseSync(ledger.dbPath, { readonly: true });

Check failure on line 182 in test/unit/miner-claim-ledger.test.ts

View workflow job for this annotation

GitHub Actions / validate-code

Object literal may only specify known properties, but 'readonly' does not exist in type 'DatabaseSyncOptions'. Did you mean to write 'readOnly'?
type TableColumn = { name: string; notnull: number; dflt_value: string | null; pk: number };
const columns = db.prepare("PRAGMA table_info(miner_claims)").all() as TableColumn[];
expect(columns.map((column) => column.name)).toEqual([
"id",
"repo_full_name",
"issue_number",
"claimed_at",
"status",
"note",
]);
for (const name of ["repo_full_name", "issue_number", "claimed_at", "status"]) {
expect(columns.find((column) => column.name === name)?.notnull).toBe(1);
}
expect(columns.find((column) => column.name === "status")?.dflt_value).toBe("'active'");
expect(columns.find((column) => column.name === "id")?.pk).toBe(1);

const uniqueIndexes = (db.prepare("PRAGMA index_list(miner_claims)").all() as Array<{ name: string; unique: number }>)
.filter((index) => index.unique === 1);
expect(uniqueIndexes.length).toBeGreaterThan(0);
const indexCols = db.prepare(`PRAGMA index_info('${uniqueIndexes[0]!.name}')`).all() as Array<{ name: string }>;
expect(indexCols.map((column) => column.name).sort()).toEqual(["issue_number", "repo_full_name"]);
db.close();

const writable = new DatabaseSync(ledger.dbPath);
expect(() =>
writable.exec(
"INSERT INTO miner_claims (repo_full_name, issue_number, claimed_at, status) VALUES ('o/a', 1, '2026-01-01T00:00:00.000Z', 'bogus')",
),
).toThrow();
writable.close();
});

it("scopes active claims per repo (#3354)", () => {
const ledger = tempLedger();
ledger.claimIssue("o/a", 1);
ledger.claimIssue("o/b", 2);
expect(ledger.listActiveClaims("o/a").map((claim) => claim.issueNumber)).toEqual([1]);
expect(ledger.listActiveClaims("o/b").map((claim) => claim.issueNumber)).toEqual([2]);
});
});

function tempRoot() {
Expand Down
Loading