Skip to content

feat(miner): add local plan-store persistence adapter - #2828

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
dhgoal:feat/miner-plan-store
Jul 3, 2026
Merged

feat(miner): add local plan-store persistence adapter#2828
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
dhgoal:feat/miner-plan-store

Conversation

@dhgoal

@dhgoal dhgoal commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a local SQLite persistence adapter for the stateless MCP plan DAG to @jsonbored/gittensory-miner. gittensory_build_plan / gittensory_plan_status / gittensory_record_step_result are deliberately stateless — the caller holds the plan and passes it back each call — so a miner running unattended across process restarts needs somewhere to persist the plan between calls. This is local-only bookkeeping (no plan logic, no network), 100% client-side, mirroring the package's other local stores.

API (lib/plan-store.js):

  • openPlanStore(dbPath?) → a store with savePlan(planId, plan) / loadPlan(planId) / listPlans(filter?) / close (plus default-singleton top-level functions and resolvePlanStoreDbPath).
  • savePlan validates the plan against the planDagSchema shape, then persists it with a single atomic INSERT … ON CONFLICT upsert keyed by planId, recording a derived plan-level status.
  • loadPlan re-validates on read, so a corrupted local row throws corrupted_plan_row instead of silently feeding a malformed plan back into gittensory_plan_status. Returns null for an unknown id.
  • listPlans({ status? }) lists plans, optionally filtered by the derived status (pending/running/completed/failed).

Validation fidelity: the plan is checked against the exact planDagSchema shape from src/mcp/server.ts — a strict { steps: PlanStep[] } (≤100 steps), each step with id/title/dependsOn/status/attempts/maxAttempts (and optional actionClass/lastError) of the right types and bounds, rejecting unknown keys. Since the miner package can't import the hosted Worker's zod schema (it's a zero-dependency plain-JS package), this mirrors it structurally, on both save and load, so the stored plan round-trips exactly.

Closes #2318.

Notes on the issue's suggestions: the sketch named src/plan-store/store.ts, but this package is authored as plain-JS lib/*.js + hand-written .d.ts with node:sqlite (its build is node --check), like every module beside it (run-state, event-ledger, claim-ledger, portfolio-queue, …); I followed that merged convention. It also suggested reusing the claim ledger's DB file "where practical" — I kept a separate plan-store.sqlite3 (same path-resolution/permissions helper), so the two stores stay independent and neither can corrupt the other, which is the cleaner separation in practice.

Scope

Validation

  • git diff --check
  • npm run typecheck
  • npm run test:coverage locally — the new test/unit/miner-plan-store.test.ts passes (whole miner suite green). This change lives entirely in packages/**, which Codecov does not measure, so it carries no codecov/patch obligation; the logic is nonetheless exercised across save/load round-trip, the derived status, upsert-not-duplicate, list-by-status, malformed-plan rejection on save, and corrupted-row rejection on load.
  • node --check lib/plan-store.js via npm run --workspace @jsonbored/gittensory-miner build
  • npm audit --audit-level=moderate — this PR adds no dependencies, so dependency-review has nothing new to evaluate.
  • New behavior has unit tests for new branches, fallback paths, and the round-trip/validation invariants.

If any required check was skipped, explain why:

  • UI/OpenAPI/migration/workers checks are not applicable: this change is one local-persistence module in packages/gittensory-miner/lib plus its test — no src/**, UI, API schema, shared D1 migrations/, or Cloudflare-binding surface is touched. The SQLite table is a miner-local file, not a hosted-Worker migration.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed. The DB is created 0o600 in a 0o700 dir, owner-only, and never leaves the machine.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. — n/a: local-only SQLite persistence, no auth/session/network surface.
  • API/OpenAPI/MCP behavior is updated and tested where needed. — n/a: no API/OpenAPI/MCP surface changed; this only persists/restores the plan JSON, it never talks to the MCP server.
  • UI changes use live API data or real states. — n/a: no UI change.
  • Visible UI changes include a UI Evidence section. — n/a: no visible UI, frontend, docs, or extension change.
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs.

Notes

Additive and consistent with the package's existing local-store pattern: two new files (lib/plan-store.js + its lib/plan-store.d.ts) mirroring lib/run-state.js (path resolution, 0o600/0o700 perms, :memory: guard, busy_timeout, prepared statements, default-store singleton), one line added to the package build gate, and one new test file. No existing code is modified.

@dhgoal
dhgoal requested a review from JSONbored as a code owner July 3, 2026 21:06
@superagent-security

Copy link
Copy Markdown
Contributor

🚨 Contributor flagged. Click here for more info: Superagent Dashboard

@superagent-security superagent-security Bot added the contributor:flagged Contributor flagged for review by trust analysis. label Jul 3, 2026
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.20%. Comparing base (7cfc93c) to head (b5e0fb9).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2828   +/-   ##
=======================================
  Coverage   96.20%   96.20%           
=======================================
  Files         253      253           
  Lines       27781    27781           
  Branches    10093    10093           
=======================================
  Hits        26726    26726           
  Misses        433      433           
  Partials      622      622           
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 3, 2026
@loopover-orb

loopover-orb Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-03 21:41:44 UTC

4 files · 1 AI reviewer · no blockers · readiness 80/100 · CI green · clean

⏸️ Suggested Action - Manual Review

Review summary
This change adds a coherent local SQLite plan-store adapter with path resolution, table creation, atomic upsert, save/read validation, derived status filtering, and matching TypeScript declarations. The visible implementation is internally consistent and the tests exercise the main persistence paths, malformed saves, corrupted stored rows, and legacy invalid statuses, so there are no must-fix correctness defects in the provided diff.

Nits — 5 non-blocking
  • nit: packages/gittensory-miner/lib/plan-store.js:121 tightens file permissions only after SQLite opens or creates the database, leaving a small creation-time window governed by the process umask on multi-user hosts.
  • nit: packages/gittensory-miner/lib/plan-store.js:129 assumes `filter` is a non-null object, so plain JS callers passing `null` get a generic TypeError instead of a store-level validation error.
  • nit: test/unit/miner-plan-store.test.ts:89 has a very long assertion line that is harder to scan than the surrounding tests.
  • packages/gittensory-miner/lib/plan-store.js:121: consider creating the database under a restrictive temporary umask or otherwise ensuring the file is born owner-only before `DatabaseSync` opens it.
  • packages/gittensory-miner/lib/plan-store.js:129: guard `filter` with an object check before reading `filter.status`, matching the explicit validation style used for plan ids and statuses.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #2318
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ❌ 5/25 Preflight is holding this PR: the review lane is unavailable, so it is not ready for automated review.
Contributor workload ✅ 10/10 Author activity: 30 registered-repo PR(s), 15 merged, 1 issue(s).
Contributor context ✅ Confirmed Gittensor contributor dhgoal; Gittensor profile; 30 PR(s), 1 issue(s).
Gate result ✅ Passing No configured blocker found.
Review context
  • Author: dhgoal
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: not available
  • Official Gittensor activity: 30 PR(s), 1 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Await review-lane availability.
  • Refresh registry data or choose a registered active repo.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

Add packages/gittensory-miner/lib/plan-store.js: local SQLite persistence for
the stateless MCP plan DAG so a miner can restore a plan across process
restarts. openPlanStore/savePlan/loadPlan/listPlans; savePlan validates the plan
against the planDagSchema shape and persists it with a single atomic
INSERT...ON CONFLICT upsert, loadPlan re-validates on read so a corrupted row
throws instead of returning a malformed plan, and listPlans filters by a derived
plan status. Local-only, owner-only (0o600), never phones home; mirrors the
run-state/portfolio-queue/event-ledger/claim-ledger pattern.

Closes JSONbored#2318.
@dhgoal
dhgoal force-pushed the feat/miner-plan-store branch from cf0c9f2 to b5e0fb9 Compare July 3, 2026 21:17
@dhgoal

dhgoal commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Fixed. The read path now fails closed on the status column too: rowToRecord validates row.status against PLAN_STATUSES and throws corrupted_plan_row if it's out of vocabulary, so a manually-edited or legacy row can't yield a PlanRecord with an invalid status. Also added a CHECK (status IN (...)) constraint to the table so the DB enforces the same vocabulary on new writes, plus a test that opens a legacy table (no CHECK) with an invalid status and asserts both loadPlan and listPlans throw.

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gittensory approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit eb30c1f into JSONbored:main Jul 3, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor:flagged Contributor flagged for review by trust analysis. gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(miner-foundation): local SQLite persistence adapter for the stateless MCP plan DAG

1 participant