Skip to content

Commit 23693c3

Browse files
authored
refactor(miner): extract shared local SQLite store helper (#4523)
Extract the ~15 lines of path-resolution/permission/busy-timeout boilerplate hand-duplicated across run-state.js, claim-ledger.js, portfolio-queue.js, and event-ledger.js into a shared local-store.js (resolveLocalStoreDbPath / normalizeLocalStoreDbPath / openLocalStoreDb). Each store keeps its own file, table, and env var, and public APIs and on-disk layout are unchanged — this is a DRY pass, not a merge. As a side effect of routing through the shared open helper, run-state.js now also sets PRAGMA busy_timeout (the one inconsistency among the four stores), matching the other three's wait-don't-fail behavior under concurrent access. Add a "Local storage" README section documenting all four stores together (file, table, module, env var), and record the decision that the manage-status PR portfolio stays a read-time join for now rather than a dedicated table. Closes #4272
1 parent b069dde commit 23693c3

10 files changed

Lines changed: 291 additions & 105 deletions

File tree

packages/gittensory-miner/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,30 @@ Additive only: the existing `rows` JSON key and PR table are unchanged; `runPort
5454
after the existing table. A real GUI dashboard surface is out of scope here — `apps/gittensory-miner-ui/` is
5555
Phase 6 of the same roadmap tracker and hasn't been scaffolded yet. (#4279)
5656

57+
## Local storage
58+
59+
Four independent local SQLite stores back the commands above. Each keeps its own file, its own table, and its own
60+
env-var override — this is a DRY pass over their shared path-resolution/open boilerplate (`local-store.js`), not a
61+
merge into one database. (#4272)
62+
63+
| Store | File | Table | Module | Env var override |
64+
| --- | --- | --- | --- | --- |
65+
| Run state | `run-state.sqlite3` | `miner_run_state` | `run-state.js` | `GITTENSORY_MINER_RUN_STATE_DB` |
66+
| Claim ledger | `claim-ledger.sqlite3` | `miner_claims` | `claim-ledger.js` | `GITTENSORY_MINER_CLAIM_LEDGER_DB` |
67+
| Portfolio queue | `portfolio-queue.sqlite3` | `miner_portfolio_queue` | `portfolio-queue.js` | `GITTENSORY_MINER_PORTFOLIO_QUEUE_DB` |
68+
| Event ledger | `event-ledger.sqlite3` | `miner_event_ledger` | `event-ledger.js` | `GITTENSORY_MINER_EVENT_LEDGER_DB` |
69+
70+
Every store resolves its file the same way: the store-specific env var above, else `GITTENSORY_MINER_CONFIG_DIR`,
71+
else `XDG_CONFIG_HOME` (falling back to `~/.config`), joined with `gittensory-miner/<file>`. Every store also opens
72+
its file with `0700`/`0600` permissions and a shared `PRAGMA busy_timeout` so two instances on the same file
73+
serialize writes instead of racing.
74+
75+
The "PR portfolio" `manage status` renders is currently a **read-time join**, not a dedicated table:
76+
`collectManageStatus` reads `portfolio-queue.js` rows (via the `pr:{number}` identifier convention) and joins them
77+
against `event-ledger.js`'s free-form `manage_pr_update` JSON events at query time, on every read. Decision: keep
78+
this as a read-time join for now; revisit a dedicated indexed table only if/when PR-portfolio reads become frequent
79+
enough (e.g. a live-polling dashboard) that the per-read linear event-ledger scan becomes a measured bottleneck.
80+
5781
## Install
5882

5983
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.

packages/gittensory-miner/lib/claim-ledger.js

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import { chmodSync, mkdirSync } from "node:fs";
2-
import { homedir } from "node:os";
3-
import { dirname, join } from "node:path";
4-
import { DatabaseSync } from "node:sqlite";
1+
import { normalizeLocalStoreDbPath, openLocalStoreDb, resolveLocalStoreDbPath } from "./local-store.js";
52

63
// The miner's local soft-claim ledger (#2314): a 100% client-side record of "I'm working on issue #N in repo X",
74
// so Phase 2's soft-claim adjudication (sibling issues) has somewhere to persist claims. Schema + CRUD only — no
@@ -15,26 +12,11 @@ const defaultDbFileName = "claim-ledger.sqlite3";
1512
let defaultClaimLedger = null;
1613

1714
export function resolveClaimLedgerDbPath(env = process.env) {
18-
const explicitPath = typeof env.GITTENSORY_MINER_CLAIM_LEDGER_DB === "string"
19-
? env.GITTENSORY_MINER_CLAIM_LEDGER_DB.trim()
20-
: "";
21-
if (explicitPath) return explicitPath;
22-
23-
const explicitConfigDir = typeof env.GITTENSORY_MINER_CONFIG_DIR === "string"
24-
? env.GITTENSORY_MINER_CONFIG_DIR.trim()
25-
: "";
26-
if (explicitConfigDir) return join(explicitConfigDir, defaultDbFileName);
27-
28-
const configHome = typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()
29-
? env.XDG_CONFIG_HOME.trim()
30-
: join(homedir(), ".config");
31-
return join(configHome, "gittensory-miner", defaultDbFileName);
15+
return resolveLocalStoreDbPath(defaultDbFileName, "GITTENSORY_MINER_CLAIM_LEDGER_DB", env);
3216
}
3317

3418
function normalizeDbPath(dbPath) {
35-
const path = (dbPath ?? resolveClaimLedgerDbPath()).trim();
36-
if (!path) throw new Error("invalid_claim_ledger_db_path");
37-
return path;
19+
return normalizeLocalStoreDbPath(dbPath, resolveClaimLedgerDbPath(), "invalid_claim_ledger_db_path");
3820
}
3921

4022
function normalizeRepoFullName(repoFullName) {
@@ -74,10 +56,7 @@ function rowToClaim(row) {
7456
*/
7557
export function openClaimLedger(dbPath = resolveClaimLedgerDbPath()) {
7658
const resolvedPath = normalizeDbPath(dbPath);
77-
mkdirSync(dirname(resolvedPath), { recursive: true, mode: 0o700 });
78-
const db = new DatabaseSync(resolvedPath);
79-
chmodSync(resolvedPath, 0o600);
80-
db.exec("PRAGMA busy_timeout = 5000");
59+
const db = openLocalStoreDb(resolvedPath);
8160
// LOCAL bookkeeping only: this table records which issues this miner instance has soft-claimed on this
8261
// machine. It does NOT adjudicate contested duplicates — sibling miners claiming the same issue are
8362
// resolved elsewhere via `isDuplicateClusterWinnerByClaim` from `@jsonbored/gittensory-engine` (#3355).

packages/gittensory-miner/lib/event-ledger.js

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import { chmodSync, mkdirSync } from "node:fs";
2-
import { homedir } from "node:os";
3-
import { dirname, join } from "node:path";
4-
import { DatabaseSync } from "node:sqlite";
51
import { isDeepStrictEqual } from "node:util";
2+
import { normalizeLocalStoreDbPath, openLocalStoreDb, resolveLocalStoreDbPath } from "./local-store.js";
63

74
// The miner's local, append-only event ledger (#2290): an immutable audit trail of every significant miner-loop
85
// event (discovered_issue, plan_built, plan_step_completed, pr_prepared, … — a small fixed vocabulary for this
@@ -16,26 +13,11 @@ const defaultDbFileName = "event-ledger.sqlite3";
1613
let defaultEventLedger = null;
1714

1815
export function resolveEventLedgerDbPath(env = process.env) {
19-
const explicitPath = typeof env.GITTENSORY_MINER_EVENT_LEDGER_DB === "string"
20-
? env.GITTENSORY_MINER_EVENT_LEDGER_DB.trim()
21-
: "";
22-
if (explicitPath) return explicitPath;
23-
24-
const explicitConfigDir = typeof env.GITTENSORY_MINER_CONFIG_DIR === "string"
25-
? env.GITTENSORY_MINER_CONFIG_DIR.trim()
26-
: "";
27-
if (explicitConfigDir) return join(explicitConfigDir, defaultDbFileName);
28-
29-
const configHome = typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()
30-
? env.XDG_CONFIG_HOME.trim()
31-
: join(homedir(), ".config");
32-
return join(configHome, "gittensory-miner", defaultDbFileName);
16+
return resolveLocalStoreDbPath(defaultDbFileName, "GITTENSORY_MINER_EVENT_LEDGER_DB", env);
3317
}
3418

3519
function normalizeDbPath(dbPath) {
36-
const path = (dbPath ?? resolveEventLedgerDbPath()).trim();
37-
if (!path) throw new Error("invalid_event_ledger_db_path");
38-
return path;
20+
return normalizeLocalStoreDbPath(dbPath, resolveEventLedgerDbPath(), "invalid_event_ledger_db_path");
3921
}
4022

4123
function normalizeEventType(type) {
@@ -108,11 +90,7 @@ function rowToEntry(row) {
10890
*/
10991
export function initEventLedger(dbPath = resolveEventLedgerDbPath()) {
11092
const resolvedPath = normalizeDbPath(dbPath);
111-
mkdirSync(dirname(resolvedPath), { recursive: true, mode: 0o700 });
112-
const db = new DatabaseSync(resolvedPath);
113-
chmodSync(resolvedPath, 0o600);
114-
// Wait (rather than fail) for a concurrent writer's lock so two ledger instances on the same file serialize.
115-
db.exec("PRAGMA busy_timeout = 5000");
93+
const db = openLocalStoreDb(resolvedPath);
11694
// `UNIQUE(seq)` makes the monotonic-ordering guarantee an enforced invariant: a duplicate seq can never persist,
11795
// even if the append path were ever changed.
11896
db.exec(`
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { DatabaseSync } from "node:sqlite";
2+
3+
export function resolveLocalStoreDbPath(
4+
defaultDbFileName: string,
5+
explicitEnvVarName: string,
6+
env?: Record<string, string | undefined>,
7+
): string;
8+
9+
export function normalizeLocalStoreDbPath(
10+
dbPath: string | null | undefined,
11+
resolvedDefault: string,
12+
invalidPathError: string,
13+
): string;
14+
15+
export function openLocalStoreDb(
16+
resolvedPath: string,
17+
options?: { busyTimeoutMs?: number },
18+
): DatabaseSync;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { chmodSync, mkdirSync } from "node:fs";
2+
import { homedir } from "node:os";
3+
import { dirname, join } from "node:path";
4+
import { DatabaseSync } from "node:sqlite";
5+
6+
// Shared path-resolution + DB-open boilerplate for the package's local SQLite stores (#4272). This is a DRY pass
7+
// only, not a merge: run-state.js, claim-ledger.js, portfolio-queue.js, and event-ledger.js each keep their own
8+
// `.sqlite3` file, table, and env var — this module just extracts the ~15 lines each hand-duplicated
9+
// (env-var/config-dir/XDG path resolution, mkdirSync(0o700) + chmodSync(0o600), and `PRAGMA busy_timeout`).
10+
11+
/**
12+
* Resolve a local store's DB path from, in order: an explicit env var, `GITTENSORY_MINER_CONFIG_DIR`,
13+
* `XDG_CONFIG_HOME` (falling back to `~/.config`) — mirroring every store's prior hand-written resolver.
14+
*/
15+
export function resolveLocalStoreDbPath(defaultDbFileName, explicitEnvVarName, env = process.env) {
16+
const explicitPath = typeof env[explicitEnvVarName] === "string" ? env[explicitEnvVarName].trim() : "";
17+
if (explicitPath) return explicitPath;
18+
19+
const explicitConfigDir = typeof env.GITTENSORY_MINER_CONFIG_DIR === "string"
20+
? env.GITTENSORY_MINER_CONFIG_DIR.trim()
21+
: "";
22+
if (explicitConfigDir) return join(explicitConfigDir, defaultDbFileName);
23+
24+
const configHome = typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()
25+
? env.XDG_CONFIG_HOME.trim()
26+
: join(homedir(), ".config");
27+
return join(configHome, "gittensory-miner", defaultDbFileName);
28+
}
29+
30+
/** Trim and validate a caller-supplied (or resolved-default) DB path, throwing `invalidPathError` if it is empty. */
31+
export function normalizeLocalStoreDbPath(dbPath, resolvedDefault, invalidPathError) {
32+
const raw = dbPath ?? resolvedDefault;
33+
if (typeof raw !== "string" || !raw.trim()) throw new Error(invalidPathError);
34+
return raw.trim();
35+
}
36+
37+
/**
38+
* Open (creating parent dirs on first use) a local store's SQLite file with 0700/0600 permissions and a shared
39+
* busy-timeout, so two instances of the same store on one file serialize writes instead of racing. Skips the
40+
* mkdir/chmod steps for the special `:memory:` path, which has no on-disk file. `run-state.js` previously opened
41+
* its DB with no busy-timeout at all (the one inconsistency among the four stores this issue found); folding it
42+
* through this shared helper gives it the same wait-don't-fail behavior the other three already had.
43+
*/
44+
export function openLocalStoreDb(resolvedPath, options = {}) {
45+
const busyTimeoutMs = options.busyTimeoutMs ?? 5000;
46+
const isMemory = resolvedPath === ":memory:";
47+
if (!isMemory) mkdirSync(dirname(resolvedPath), { recursive: true, mode: 0o700 });
48+
const db = new DatabaseSync(resolvedPath);
49+
if (!isMemory) chmodSync(resolvedPath, 0o600);
50+
db.exec(`PRAGMA busy_timeout = ${busyTimeoutMs}`);
51+
return db;
52+
}

packages/gittensory-miner/lib/portfolio-queue.js

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import { chmodSync, mkdirSync } from "node:fs";
2-
import { homedir } from "node:os";
3-
import { dirname, join } from "node:path";
4-
import { DatabaseSync } from "node:sqlite";
1+
import { normalizeLocalStoreDbPath, openLocalStoreDb, resolveLocalStoreDbPath } from "./local-store.js";
52

63
// The miner's local portfolio/queue store (#2292): a 100% client-side, prioritized backlog of candidate work
74
// items across every repo the miner has been pointed at ("what should I look at next, across everything I'm
@@ -15,26 +12,11 @@ const defaultDbFileName = "portfolio-queue.sqlite3";
1512
let defaultPortfolioQueueStore = null;
1613

1714
export function resolvePortfolioQueueDbPath(env = process.env) {
18-
const explicitPath = typeof env.GITTENSORY_MINER_PORTFOLIO_QUEUE_DB === "string"
19-
? env.GITTENSORY_MINER_PORTFOLIO_QUEUE_DB.trim()
20-
: "";
21-
if (explicitPath) return explicitPath;
22-
23-
const explicitConfigDir = typeof env.GITTENSORY_MINER_CONFIG_DIR === "string"
24-
? env.GITTENSORY_MINER_CONFIG_DIR.trim()
25-
: "";
26-
if (explicitConfigDir) return join(explicitConfigDir, defaultDbFileName);
27-
28-
const configHome = typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()
29-
? env.XDG_CONFIG_HOME.trim()
30-
: join(homedir(), ".config");
31-
return join(configHome, "gittensory-miner", defaultDbFileName);
15+
return resolveLocalStoreDbPath(defaultDbFileName, "GITTENSORY_MINER_PORTFOLIO_QUEUE_DB", env);
3216
}
3317

3418
function normalizeDbPath(dbPath) {
35-
const raw = dbPath ?? resolvePortfolioQueueDbPath();
36-
if (typeof raw !== "string" || !raw.trim()) throw new Error("invalid_portfolio_queue_db_path");
37-
return raw.trim();
19+
return normalizeLocalStoreDbPath(dbPath, resolvePortfolioQueueDbPath(), "invalid_portfolio_queue_db_path");
3820
}
3921

4022
function normalizeRepoFullName(repoFullName) {
@@ -78,14 +60,8 @@ function rowToEntry(row) {
7860
*/
7961
export function initPortfolioQueueStore(dbPath = resolvePortfolioQueueDbPath()) {
8062
const resolvedPath = normalizeDbPath(dbPath);
81-
// The store is a persistent local file; the special in-memory path (':memory:') has no file to create or chmod.
82-
if (resolvedPath !== ":memory:") {
83-
mkdirSync(dirname(resolvedPath), { recursive: true, mode: 0o700 });
84-
}
85-
const db = new DatabaseSync(resolvedPath);
86-
if (resolvedPath !== ":memory:") chmodSync(resolvedPath, 0o600);
87-
// Wait (rather than fail) for a concurrent writer's lock so two queue instances on the same file serialize.
88-
db.exec("PRAGMA busy_timeout = 5000");
63+
// openLocalStoreDb skips mkdir/chmod for the special in-memory path (':memory:'), which has no file on disk.
64+
const db = openLocalStoreDb(resolvedPath);
8965
db.exec(`
9066
CREATE TABLE IF NOT EXISTS miner_portfolio_queue (
9167
repo_full_name TEXT NOT NULL,

packages/gittensory-miner/lib/run-state.js

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import { chmodSync, mkdirSync } from "node:fs";
2-
import { homedir } from "node:os";
3-
import { dirname, join } from "node:path";
4-
import { DatabaseSync } from "node:sqlite";
1+
import { normalizeLocalStoreDbPath, openLocalStoreDb, resolveLocalStoreDbPath } from "./local-store.js";
52

63
export const RUN_STATES = Object.freeze(["idle", "discovering", "planning", "preparing"]);
74

@@ -10,26 +7,11 @@ const defaultDbFileName = "run-state.sqlite3";
107
let defaultRunStateStore = null;
118

129
export function resolveRunStateDbPath(env = process.env) {
13-
const explicitPath = typeof env.GITTENSORY_MINER_RUN_STATE_DB === "string"
14-
? env.GITTENSORY_MINER_RUN_STATE_DB.trim()
15-
: "";
16-
if (explicitPath) return explicitPath;
17-
18-
const explicitConfigDir = typeof env.GITTENSORY_MINER_CONFIG_DIR === "string"
19-
? env.GITTENSORY_MINER_CONFIG_DIR.trim()
20-
: "";
21-
if (explicitConfigDir) return join(explicitConfigDir, defaultDbFileName);
22-
23-
const configHome = typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()
24-
? env.XDG_CONFIG_HOME.trim()
25-
: join(homedir(), ".config");
26-
return join(configHome, "gittensory-miner", defaultDbFileName);
10+
return resolveLocalStoreDbPath(defaultDbFileName, "GITTENSORY_MINER_RUN_STATE_DB", env);
2711
}
2812

2913
function normalizeDbPath(dbPath) {
30-
const path = (dbPath ?? resolveRunStateDbPath()).trim();
31-
if (!path) throw new Error("invalid_run_state_db_path");
32-
return path;
14+
return normalizeLocalStoreDbPath(dbPath, resolveRunStateDbPath(), "invalid_run_state_db_path");
3315
}
3416

3517
function normalizeRepoFullName(repoFullName) {
@@ -51,9 +33,7 @@ function normalizeRunState(state) {
5133
*/
5234
export function initRunStateStore(dbPath = resolveRunStateDbPath()) {
5335
const resolvedPath = normalizeDbPath(dbPath);
54-
mkdirSync(dirname(resolvedPath), { recursive: true, mode: 0o700 });
55-
const db = new DatabaseSync(resolvedPath);
56-
chmodSync(resolvedPath, 0o600);
36+
const db = openLocalStoreDb(resolvedPath);
5737
db.exec(`
5838
CREATE TABLE IF NOT EXISTS miner_run_state (
5939
repo_full_name TEXT PRIMARY KEY,

packages/gittensory-miner/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"expected-engine.version"
3333
],
3434
"scripts": {
35-
"build": "node --check bin/gittensory-miner.js && node --check lib/version.js && node --check lib/cli.js && node --check lib/deny-check.js && node --check lib/run-state-cli.js && node --check lib/update-check.js && node --check lib/worktree-allocator.js && node --check lib/opportunity-fanout.js && node --check lib/ci-poller.js && node --check lib/run-state.js && node --check lib/deny-hooks.js && node --check lib/event-ledger.js && node --check lib/event-ledger-cli.js && node --check lib/claim-ledger.js && node --check lib/claim-ledger-expiry.js && node --check lib/claim-ledger-cli.js && node --check lib/claim-adjudication.js && node --check lib/portfolio-queue.js && node --check lib/portfolio-queue-cli.js && node --check lib/portfolio-queue-manager.js && node --check lib/portfolio-discovery.js && node --check lib/opportunity-ranker.js && node --check lib/plan-store.js && node --check lib/plan-store-cli.js && node --check lib/rejection-templates.js && node --check lib/governor-ledger.js && node --check lib/governor-ledger-cli.js && node --check lib/manage-status.js && node --check lib/manage-poll.js && node --check lib/status.js && node --check lib/laptop-init.js && node --check lib/replay-objective-anchor.js && node --check lib/replay-task-generation.js && node --check lib/calibration-types.js && node --check lib/calibration.js"
35+
"build": "node --check bin/gittensory-miner.js && node --check lib/version.js && node --check lib/local-store.js && node --check lib/cli.js && node --check lib/deny-check.js && node --check lib/run-state-cli.js && node --check lib/update-check.js && node --check lib/worktree-allocator.js && node --check lib/opportunity-fanout.js && node --check lib/ci-poller.js && node --check lib/run-state.js && node --check lib/deny-hooks.js && node --check lib/event-ledger.js && node --check lib/event-ledger-cli.js && node --check lib/claim-ledger.js && node --check lib/claim-ledger-expiry.js && node --check lib/claim-ledger-cli.js && node --check lib/claim-adjudication.js && node --check lib/portfolio-queue.js && node --check lib/portfolio-queue-cli.js && node --check lib/portfolio-queue-manager.js && node --check lib/portfolio-discovery.js && node --check lib/opportunity-ranker.js && node --check lib/plan-store.js && node --check lib/plan-store-cli.js && node --check lib/rejection-templates.js && node --check lib/governor-ledger.js && node --check lib/governor-ledger-cli.js && node --check lib/manage-status.js && node --check lib/manage-poll.js && node --check lib/status.js && node --check lib/laptop-init.js && node --check lib/replay-objective-anchor.js && node --check lib/replay-task-generation.js && node --check lib/calibration-types.js && node --check lib/calibration.js"
3636
},
3737
"dependencies": {
3838
"@jsonbored/gittensory-engine": ">=0.1.0 <1.0.0"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { readFileSync } from "node:fs";
2+
import { join } from "node:path";
3+
import { describe, expect, it } from "vitest";
4+
5+
const readmePath = join(process.cwd(), "packages/gittensory-miner/README.md");
6+
7+
describe("gittensory-miner local storage README (#4272)", () => {
8+
it("documents all four local stores together with their file/table/module/env-var", () => {
9+
const readme = readFileSync(readmePath, "utf8");
10+
expect(readme).toContain("## Local storage");
11+
expect(readme).toContain("run-state.sqlite3");
12+
expect(readme).toContain("miner_run_state");
13+
expect(readme).toContain("claim-ledger.sqlite3");
14+
expect(readme).toContain("miner_claims");
15+
expect(readme).toContain("portfolio-queue.sqlite3");
16+
expect(readme).toContain("miner_portfolio_queue");
17+
expect(readme).toContain("event-ledger.sqlite3");
18+
expect(readme).toContain("miner_event_ledger");
19+
expect(readme).toContain("GITTENSORY_MINER_RUN_STATE_DB");
20+
expect(readme).toContain("GITTENSORY_MINER_CLAIM_LEDGER_DB");
21+
expect(readme).toContain("GITTENSORY_MINER_PORTFOLIO_QUEUE_DB");
22+
expect(readme).toContain("GITTENSORY_MINER_EVENT_LEDGER_DB");
23+
});
24+
25+
it("documents the PR-portfolio read-time-join decision", () => {
26+
const readme = readFileSync(readmePath, "utf8");
27+
expect(readme).toContain("read-time join");
28+
expect(readme).toContain("manage_pr_update");
29+
});
30+
});

0 commit comments

Comments
 (0)