feat(miner): add local plan-store persistence adapter - #2828
Conversation
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
Codecov Report✅ All modified and coverable lines are covered by tests. 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:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-03 21:41:44 UTC
⏸️ Suggested Action - Manual Review Review summary Nits — 5 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 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.
|
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.
cf0c9f2 to
b5e0fb9
Compare
|
Fixed. The read path now fails closed on the status column too: |
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_resultare 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 withsavePlan(planId, plan)/loadPlan(planId)/listPlans(filter?)/close(plus default-singleton top-level functions andresolvePlanStoreDbPath).savePlanvalidates the plan against theplanDagSchemashape, then persists it with a single atomicINSERT … ON CONFLICTupsert keyed byplanId, recording a derived plan-level status.loadPlanre-validates on read, so a corrupted local row throwscorrupted_plan_rowinstead of silently feeding a malformed plan back intogittensory_plan_status. Returnsnullfor 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
planDagSchemashape fromsrc/mcp/server.ts— a strict{ steps: PlanStep[] }(≤100 steps), each step withid/title/dependsOn/status/attempts/maxAttempts(and optionalactionClass/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-JSlib/*.js+ hand-written.d.tswithnode:sqlite(itsbuildisnode --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 separateplan-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
type(scope): short summaryConventional Commit format.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run typechecknpm run test:coveragelocally — the newtest/unit/miner-plan-store.test.tspasses (whole miner suite green). This change lives entirely inpackages/**, which Codecov does not measure, so it carries nocodecov/patchobligation; 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.jsvianpm run --workspace @jsonbored/gittensory-miner buildnpm audit --audit-level=moderate— this PR adds no dependencies, so dependency-review has nothing new to evaluate.If any required check was skipped, explain why:
packages/gittensory-miner/libplus its test — nosrc/**, UI, API schema, shared D1migrations/, or Cloudflare-binding surface is touched. The SQLite table is a miner-local file, not a hosted-Worker migration.Safety
0o600in a0o700dir, owner-only, and never leaves the machine.UI Evidencesection. — n/a: no visible UI, frontend, docs, or extension change.Notes
Additive and consistent with the package's existing local-store pattern: two new files (
lib/plan-store.js+ itslib/plan-store.d.ts) mirroringlib/run-state.js(path resolution,0o600/0o700perms,:memory:guard,busy_timeout, prepared statements, default-store singleton), one line added to the packagebuildgate, and one new test file. No existing code is modified.