@loopover/miner 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEPLOYMENT.md +156 -0
- package/Dockerfile +38 -0
- package/README.md +269 -0
- package/bin/gittensory-miner-mcp.d.ts +64 -0
- package/bin/gittensory-miner-mcp.js +279 -0
- package/bin/gittensory-miner.js +196 -0
- package/docs/coding-agent-driver.md +140 -0
- package/docs/config-precedence.md +96 -0
- package/docs/cross-repo-discovery-phase1.md +55 -0
- package/docs/discovery-index-contract.md +60 -0
- package/docs/discovery-plane-operator-guide.md +101 -0
- package/docs/env-reference.md +30 -0
- package/docs/fleet-run-manifest.md +50 -0
- package/docs/miner-goal-spec.md +76 -0
- package/docs/miner-selfimprove-calibration.md +105 -0
- package/docs/observability.md +60 -0
- package/docs/operations-runbook.md +209 -0
- package/docs/repo-agnostic-capability-audit.md +129 -0
- package/docs/unattended-scheduling.md +107 -0
- package/expected-engine.version +1 -0
- package/lib/ams-policy.d.ts +20 -0
- package/lib/ams-policy.js +68 -0
- package/lib/attempt-cli.d.ts +99 -0
- package/lib/attempt-cli.js +523 -0
- package/lib/attempt-input-builder.d.ts +24 -0
- package/lib/attempt-input-builder.js +91 -0
- package/lib/attempt-log.d.ts +37 -0
- package/lib/attempt-log.js +177 -0
- package/lib/attempt-runner.d.ts +59 -0
- package/lib/attempt-runner.js +164 -0
- package/lib/attempt-worktree.d.ts +31 -0
- package/lib/attempt-worktree.js +94 -0
- package/lib/calibration-cli.d.ts +1 -0
- package/lib/calibration-cli.js +92 -0
- package/lib/calibration-run.d.ts +161 -0
- package/lib/calibration-run.js +232 -0
- package/lib/calibration-types.d.ts +46 -0
- package/lib/calibration-types.js +74 -0
- package/lib/calibration.d.ts +24 -0
- package/lib/calibration.js +99 -0
- package/lib/ci-poller.d.ts +33 -0
- package/lib/ci-poller.js +234 -0
- package/lib/claim-adjudication.d.ts +21 -0
- package/lib/claim-adjudication.js +36 -0
- package/lib/claim-conflict-resolver.d.ts +29 -0
- package/lib/claim-conflict-resolver.js +101 -0
- package/lib/claim-ledger-cli.d.ts +59 -0
- package/lib/claim-ledger-cli.js +320 -0
- package/lib/claim-ledger-expiry.d.ts +20 -0
- package/lib/claim-ledger-expiry.js +41 -0
- package/lib/claim-ledger.d.ts +63 -0
- package/lib/claim-ledger.js +303 -0
- package/lib/cli-error.d.ts +3 -0
- package/lib/cli-error.js +27 -0
- package/lib/cli.d.ts +3 -0
- package/lib/cli.js +72 -0
- package/lib/coding-agent-construction.d.ts +17 -0
- package/lib/coding-agent-construction.js +103 -0
- package/lib/coding-agent-house-rules.d.ts +24 -0
- package/lib/coding-agent-house-rules.js +64 -0
- package/lib/coding-task-spec.d.ts +44 -0
- package/lib/coding-task-spec.js +183 -0
- package/lib/config-precedence.d.ts +25 -0
- package/lib/config-precedence.js +38 -0
- package/lib/deny-check.d.ts +11 -0
- package/lib/deny-check.js +76 -0
- package/lib/deny-hook-synthesis.d.ts +93 -0
- package/lib/deny-hook-synthesis.js +399 -0
- package/lib/deny-hooks.d.ts +28 -0
- package/lib/deny-hooks.js +155 -0
- package/lib/deployment-docs-audit.d.ts +41 -0
- package/lib/deployment-docs-audit.js +115 -0
- package/lib/discover-cli.d.ts +94 -0
- package/lib/discover-cli.js +284 -0
- package/lib/discovery-throttle.d.ts +8 -0
- package/lib/discovery-throttle.js +33 -0
- package/lib/env-file-indirection.d.ts +4 -0
- package/lib/env-file-indirection.js +45 -0
- package/lib/event-ledger-cli.d.ts +82 -0
- package/lib/event-ledger-cli.js +266 -0
- package/lib/event-ledger.d.ts +37 -0
- package/lib/event-ledger.js +210 -0
- package/lib/execute-local-write.d.ts +14 -0
- package/lib/execute-local-write.js +50 -0
- package/lib/feasibility-cli.d.ts +25 -0
- package/lib/feasibility-cli.js +80 -0
- package/lib/forge-config.d.ts +17 -0
- package/lib/forge-config.js +37 -0
- package/lib/governor-action-mode.d.ts +26 -0
- package/lib/governor-action-mode.js +47 -0
- package/lib/governor-chokepoint-persisted.d.ts +18 -0
- package/lib/governor-chokepoint-persisted.js +46 -0
- package/lib/governor-chokepoint.d.ts +14 -0
- package/lib/governor-chokepoint.js +52 -0
- package/lib/governor-kill-switch.d.ts +26 -0
- package/lib/governor-kill-switch.js +47 -0
- package/lib/governor-ledger-cli.d.ts +32 -0
- package/lib/governor-ledger-cli.js +155 -0
- package/lib/governor-ledger.d.ts +46 -0
- package/lib/governor-ledger.js +203 -0
- package/lib/governor-open-pr.d.ts +21 -0
- package/lib/governor-open-pr.js +27 -0
- package/lib/governor-pause-cli.d.ts +23 -0
- package/lib/governor-pause-cli.js +171 -0
- package/lib/governor-run-halt.d.ts +37 -0
- package/lib/governor-run-halt.js +58 -0
- package/lib/governor-state.d.ts +63 -0
- package/lib/governor-state.js +363 -0
- package/lib/governor-write-rate-limit.d.ts +30 -0
- package/lib/governor-write-rate-limit.js +64 -0
- package/lib/harness-submission-trigger.d.ts +69 -0
- package/lib/harness-submission-trigger.js +138 -0
- package/lib/http-retry.d.ts +12 -0
- package/lib/http-retry.js +52 -0
- package/lib/laptop-init.d.ts +53 -0
- package/lib/laptop-init.js +332 -0
- package/lib/live-issue-snapshot.d.ts +16 -0
- package/lib/live-issue-snapshot.js +117 -0
- package/lib/local-store.d.ts +18 -0
- package/lib/local-store.js +62 -0
- package/lib/logger.d.ts +58 -0
- package/lib/logger.js +167 -0
- package/lib/loop-cli.d.ts +66 -0
- package/lib/loop-cli.js +540 -0
- package/lib/loop-closure.d.ts +35 -0
- package/lib/loop-closure.js +66 -0
- package/lib/loop-reentry.d.ts +51 -0
- package/lib/loop-reentry.js +124 -0
- package/lib/manage-poll.d.ts +79 -0
- package/lib/manage-poll.js +254 -0
- package/lib/manage-status.d.ts +70 -0
- package/lib/manage-status.js +241 -0
- package/lib/metrics-cli.d.ts +9 -0
- package/lib/metrics-cli.js +50 -0
- package/lib/migrate-cli.d.ts +24 -0
- package/lib/migrate-cli.js +111 -0
- package/lib/miner-goal-spec.d.ts +12 -0
- package/lib/miner-goal-spec.js +67 -0
- package/lib/opportunity-fanout.d.ts +86 -0
- package/lib/opportunity-fanout.js +597 -0
- package/lib/opportunity-ranker.d.ts +36 -0
- package/lib/opportunity-ranker.js +118 -0
- package/lib/orb-export.d.ts +54 -0
- package/lib/orb-export.js +206 -0
- package/lib/plan-store-cli.d.ts +37 -0
- package/lib/plan-store-cli.js +151 -0
- package/lib/plan-store.d.ts +51 -0
- package/lib/plan-store.js +231 -0
- package/lib/policy-doc-cache.d.ts +25 -0
- package/lib/policy-doc-cache.js +79 -0
- package/lib/policy-verdict-cache.d.ts +35 -0
- package/lib/policy-verdict-cache.js +106 -0
- package/lib/portfolio-dashboard.d.ts +30 -0
- package/lib/portfolio-dashboard.js +105 -0
- package/lib/portfolio-discovery.d.ts +29 -0
- package/lib/portfolio-discovery.js +100 -0
- package/lib/portfolio-queue-cli.d.ts +87 -0
- package/lib/portfolio-queue-cli.js +509 -0
- package/lib/portfolio-queue-expiry.d.ts +20 -0
- package/lib/portfolio-queue-expiry.js +51 -0
- package/lib/portfolio-queue-manager.d.ts +50 -0
- package/lib/portfolio-queue-manager.js +134 -0
- package/lib/portfolio-queue.d.ts +62 -0
- package/lib/portfolio-queue.js +351 -0
- package/lib/pr-disposition-poller.d.ts +26 -0
- package/lib/pr-disposition-poller.js +163 -0
- package/lib/pr-number-parse.d.ts +4 -0
- package/lib/pr-number-parse.js +23 -0
- package/lib/pr-outcome.d.ts +41 -0
- package/lib/pr-outcome.js +90 -0
- package/lib/prediction-ledger.d.ts +47 -0
- package/lib/prediction-ledger.js +222 -0
- package/lib/pretooluse-hook.d.ts +31 -0
- package/lib/pretooluse-hook.js +93 -0
- package/lib/process-lifecycle.d.ts +31 -0
- package/lib/process-lifecycle.js +106 -0
- package/lib/purge-cli.d.ts +44 -0
- package/lib/purge-cli.js +198 -0
- package/lib/rejection-signal.d.ts +6 -0
- package/lib/rejection-signal.js +101 -0
- package/lib/rejection-state-machine.d.ts +37 -0
- package/lib/rejection-state-machine.js +81 -0
- package/lib/rejection-templates.d.ts +12 -0
- package/lib/rejection-templates.js +71 -0
- package/lib/replay-objective-anchor.d.ts +79 -0
- package/lib/replay-objective-anchor.js +179 -0
- package/lib/replay-snapshot.d.ts +41 -0
- package/lib/replay-snapshot.js +288 -0
- package/lib/replay-task-generation.d.ts +126 -0
- package/lib/replay-task-generation.js +223 -0
- package/lib/repo-clone.d.ts +19 -0
- package/lib/repo-clone.js +102 -0
- package/lib/run-state-cli.d.ts +27 -0
- package/lib/run-state-cli.js +154 -0
- package/lib/run-state.d.ts +37 -0
- package/lib/run-state.js +150 -0
- package/lib/schema-version.d.ts +17 -0
- package/lib/schema-version.js +71 -0
- package/lib/self-review-context.d.ts +28 -0
- package/lib/self-review-context.js +339 -0
- package/lib/slop-assessment.d.ts +3 -0
- package/lib/slop-assessment.js +20 -0
- package/lib/stack-detection.d.ts +41 -0
- package/lib/stack-detection.js +248 -0
- package/lib/status.d.ts +64 -0
- package/lib/status.js +441 -0
- package/lib/store-maintenance.d.ts +31 -0
- package/lib/store-maintenance.js +183 -0
- package/lib/submission-freshness-check.d.ts +32 -0
- package/lib/submission-freshness-check.js +93 -0
- package/lib/update-check.d.ts +36 -0
- package/lib/update-check.js +161 -0
- package/lib/version.d.ts +3 -0
- package/lib/version.js +10 -0
- package/lib/worktree-allocator.d.ts +39 -0
- package/lib/worktree-allocator.js +262 -0
- package/package.json +50 -0
- package/schema/miner-goal-spec.schema.json +111 -0
package/DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Gittensory miner deployment
|
|
2
|
+
|
|
3
|
+
Two form factors for running `@loopover/miner`: **laptop mode** (single machine, zero Docker) and **fleet mode** (containerized workers with a shared data volume). Both are 100% client-side for core operation — the miner never uploads source and never requires a hosted Gittensory callback to boot. Credentials (GitHub tokens, etc.) stay on the operator's machine or in their own secret store; nothing is baked into images.
|
|
4
|
+
|
|
5
|
+
| | Laptop mode | Fleet mode |
|
|
6
|
+
| ---------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
7
|
+
| **Best for** | One contributor machine, local experimentation | Many parallel miner attempts on a host or small cluster |
|
|
8
|
+
| **Dependencies** | Node.js `>=22.13.0` only | Docker (or compatible runtime) + Node image or custom image |
|
|
9
|
+
| **State** | SQLite files under `~/.config/gittensory-miner/` (override with `GITTENSORY_MINER_CONFIG_DIR`) | Same SQLite layout on a mounted `/data` (or `GITTENSORY_MINER_CONFIG_DIR`) volume |
|
|
10
|
+
| **Setup** | `npm install -g @loopover/miner` or workspace build | `docker build` + `docker run` with env + volume (see below) |
|
|
11
|
+
| **Footprint** | One Node process, local disk for ledgers/queues | One container per worker; scale horizontally by adding containers |
|
|
12
|
+
|
|
13
|
+
## Coding-agent provider configuration
|
|
14
|
+
|
|
15
|
+
For provider selection and the CLI-specific model/timeout overrides, see
|
|
16
|
+
[`README.md`](README.md) and the interface-level contract in
|
|
17
|
+
[`docs/coding-agent-driver.md`](docs/coding-agent-driver.md).
|
|
18
|
+
|
|
19
|
+
## Laptop mode walkthrough
|
|
20
|
+
|
|
21
|
+
1. Install Node.js 22.13+ and the package:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install -g @loopover/miner@latest
|
|
25
|
+
# or from a checkout:
|
|
26
|
+
npm install && npm --workspace @loopover/miner run build
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. Inspect what is installed and where local state will live. `status` and `doctor` stay offline; `init --verify-token` is optional and makes one authenticated GitHub call up front:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
gittensory-miner status
|
|
33
|
+
gittensory-miner doctor
|
|
34
|
+
gittensory-miner init --verify-token # optional: validate GITHUB_TOKEN once before attempts
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
3. Expected layout after first use (default paths):
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
~/.config/gittensory-miner/
|
|
41
|
+
laptop-state.sqlite3 # laptop-mode setup state, created by `init`
|
|
42
|
+
portfolio-queue.sqlite3 # prioritized work backlog across tracked repos (#2292)
|
|
43
|
+
claim-ledger.sqlite3 # soft issue claims (#2314)
|
|
44
|
+
plan-store.sqlite3 # persisted MCP plan DAGs (#2318)
|
|
45
|
+
run-state.sqlite3 # per-repo run state (idle/discovering/planning/preparing)
|
|
46
|
+
event-ledger.sqlite3 # append-only miner-loop event audit trail (#2290)
|
|
47
|
+
governor-ledger.sqlite3 # append-only governor allow/deny/throttle decisions (#2328)
|
|
48
|
+
governor-state.sqlite3 # governor cross-attempt counters/state (#5134)
|
|
49
|
+
attempt-log.sqlite3 # per-attempt coding-agent driver event trace (#4294)
|
|
50
|
+
worktree-allocator.sqlite3 # git-worktree-per-attempt allocation bookkeeping (#4297)
|
|
51
|
+
prediction-ledger.sqlite3 # predicted-gate verdicts, for later self-improve scoring (#4263)
|
|
52
|
+
replay-snapshot.sqlite3 # frozen historical-replay target snapshots (#3010)
|
|
53
|
+
policy-doc-cache.sqlite3 # ETag cache for discovery's policy-doc fetches (#4842)
|
|
54
|
+
policy-verdict-cache.sqlite3 # cache of resolved AI-usage-policy verdicts (#4843)
|
|
55
|
+
deny-hook-synthesis.sqlite3 # synthesized PreToolUse deny-hook proposals (#4522)
|
|
56
|
+
orb-export.sqlite3 # opt-in anonymized Orb telemetry export state (#4277)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Not every file appears immediately: `laptop-state` is written by `init`, and each of the others is created
|
|
60
|
+
the first time its subsystem actually runs (an attempt, a discovery pass, a replay, an Orb export, …), so a
|
|
61
|
+
fresh install that has only run `status`/`doctor` will show a subset. All sixteen default into this one
|
|
62
|
+
directory. Override the directory for every store at once with `GITTENSORY_MINER_CONFIG_DIR` or
|
|
63
|
+
`XDG_CONFIG_HOME` (same resolution chain as `@loopover/mcp`); every store except `laptop-state.sqlite3`
|
|
64
|
+
(directory only) also honors its own `GITTENSORY_MINER_<NAME>_DB` path override — e.g.
|
|
65
|
+
`GITTENSORY_MINER_PORTFOLIO_QUEUE_DB` — to relocate an individual file. `doctor`'s `store-integrity:*` checks
|
|
66
|
+
report the persistent stores, so it is the quickest way to confirm what exists and is readable on disk.
|
|
67
|
+
|
|
68
|
+
4. Optional per-repo miner goals: copy [`.gittensory-miner.yml.example`](../../.gittensory-miner.yml.example) to a target repo as `.gittensory-miner.yml`. See [`docs/miner-goal-spec.md`](docs/miner-goal-spec.md).
|
|
69
|
+
|
|
70
|
+
## Fleet mode walkthrough
|
|
71
|
+
|
|
72
|
+
Build the fleet image from the **monorepo root** (the Dockerfile needs the full workspace on disk before `npm ci` — see comments in [`Dockerfile`](Dockerfile)):
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
docker build -f packages/gittensory-miner/Dockerfile -t gittensory-miner:latest .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Run a disposable worker with persistent SQLite state on a mounted volume. Inject secrets at runtime (never bake them into the image):
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
docker run --rm -it \
|
|
82
|
+
-e GITTENSORY_MINER_CONFIG_DIR=/data/miner \
|
|
83
|
+
-e GITHUB_TOKEN \
|
|
84
|
+
-v miner-data:/data/miner \
|
|
85
|
+
gittensory-miner:latest \
|
|
86
|
+
doctor
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The image entrypoint is `gittensory-miner`; pass subcommands after the image name (`status`, `doctor`, `claim`, …).
|
|
90
|
+
|
|
91
|
+
- **`/data/miner` volume** — holds all SQLite state (`claim-ledger.sqlite3`, `plan-store.sqlite3`, etc.) so containers are disposable. Defaults to `GITTENSORY_MINER_CONFIG_DIR=/data/miner` in the image.
|
|
92
|
+
- **`GITHUB_TOKEN`** — supplied by the operator at run time; the image contains no credentials.
|
|
93
|
+
- **Scale** — launch additional containers with the same volume (or partitioned config dirs) for parallel attempts.
|
|
94
|
+
|
|
95
|
+
**Secret-file alternative (`GITHUB_TOKEN_FILE`).** A plain `-e GITHUB_TOKEN` value is visible in plaintext
|
|
96
|
+
via `docker inspect`/`docker compose config` and any full-env dump of the running container. For Docker
|
|
97
|
+
Swarm/Kubernetes-managed secrets (mounted as a file, e.g. at `/run/secrets/github_token`), set
|
|
98
|
+
`GITHUB_TOKEN_FILE` to that mount path instead — the miner reads and trims the file's contents at startup and
|
|
99
|
+
uses it exactly as if `GITHUB_TOKEN` had been set directly:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
docker run --rm -it \
|
|
103
|
+
-e GITTENSORY_MINER_CONFIG_DIR=/data/miner \
|
|
104
|
+
-e GITHUB_TOKEN_FILE=/run/secrets/github_token \
|
|
105
|
+
-v miner-data:/data/miner \
|
|
106
|
+
-v /path/to/your/secret:/run/secrets/github_token:ro \
|
|
107
|
+
gittensory-miner:latest \
|
|
108
|
+
doctor
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If both `GITHUB_TOKEN` and `GITHUB_TOKEN_FILE` are set, the plain `GITHUB_TOKEN` value always wins (same
|
|
112
|
+
precedence rule as ORB's own `src/selfhost/load-file-secrets.ts`). A missing or unreadable `GITHUB_TOKEN_FILE`
|
|
113
|
+
fails the container fast with a clear error naming the file path, rather than silently proceeding with no
|
|
114
|
+
credential. The same `<NAME>_FILE` convention works for any credential the miner reads from a plain env var —
|
|
115
|
+
not only `GITHUB_TOKEN`.
|
|
116
|
+
|
|
117
|
+
The repo-root [`docker-compose.yml`](../../docker-compose.yml) documents the **self-hosted review stack** (the `gittensory` API/orb), not the miner CLI. Miners are clients of that stack (or of github.com directly) and do not require it to run locally.
|
|
118
|
+
|
|
119
|
+
### Docker Compose (fleet mode)
|
|
120
|
+
|
|
121
|
+
Instead of a hand-assembled `docker run`, [`docker-compose.miner.yml`](docker-compose.miner.yml) defines a long-lived `miner` service (built from this package's Dockerfile, `restart: unless-stopped`, state on a named `miner-data` volume). Credentials come from an env file, never inlined:
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
cp .gittensory-miner.env.example .gittensory-miner.env # fill in GITHUB_TOKEN (+ optional provider keys)
|
|
125
|
+
docker compose -f docker-compose.miner.yml up -d --build
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Scaling to N parallel workers.** `docker compose -f docker-compose.miner.yml up -d --scale miner=N` gives every replica the **same** `miner-data` volume — and the miner's SQLite ledgers are **not** safe for concurrent access, so N replicas on one volume will contend/corrupt. To run N **isolated** workers, give each its own state: run N separate compose projects (`docker compose -p miner-1 …`, `-p miner-2 …` — `-p` namespaces the volume) or point each at a distinct `GITTENSORY_MINER_CONFIG_DIR` on its own mount. For built-in isolated horizontal scaling, use the Kubernetes StatefulSet in [`k8s/`](../../k8s/) (per-pod volumes).
|
|
129
|
+
|
|
130
|
+
## Bare-host (systemd, no Docker)
|
|
131
|
+
|
|
132
|
+
To run the miner continuously on a plain Linux host without Docker, supervise `gittensory-miner loop` — the autonomous discover → attempt → manage daemon (#5135) — with systemd. [`systemd/gittensory-miner.service.example`](../../systemd/gittensory-miner.service.example) is a ready-to-adapt persistent unit; its header carries the full install steps:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
npm install -g @loopover/miner
|
|
136
|
+
gittensory-miner init --verify-token # optional: validate GITHUB_TOKEN before discovery/attempt runs
|
|
137
|
+
sudo cp systemd/gittensory-miner.service.example /etc/systemd/system/gittensory-miner.service
|
|
138
|
+
sudo $EDITOR /etc/systemd/system/gittensory-miner.service # set User / WorkingDirectory / ExecStart / secrets
|
|
139
|
+
sudo systemctl daemon-reload
|
|
140
|
+
sudo systemctl enable --now gittensory-miner.service
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Because `loop` is a **long-running daemon that schedules its own cycles**, it is a persistent `Type=simple` service (with `Restart=on-failure`) — **not** a oneshot unit driven by a `.timer`, unlike the periodic `loopover-docker-prune.*.example` hygiene job in [`systemd/`](../../systemd/). Keep `GITHUB_TOKEN` (and any coding-agent credentials) in a root-owned `0600` `EnvironmentFile`, never in the unit file. Follow the loop with `journalctl -u gittensory-miner -f`; `systemctl stop` sends SIGTERM, which the loop handles cleanly at its next kill-switch check.
|
|
144
|
+
|
|
145
|
+
## Invariants
|
|
146
|
+
|
|
147
|
+
- Core miner bookkeeping (claims, plans, queues, ledgers) works offline after install.
|
|
148
|
+
- `gittensory-miner status` and `gittensory-miner doctor` make **no network calls**.
|
|
149
|
+
- Discovery/ranking primitives that touch GitHub only run when explicitly invoked and only perform documented GETs unless a future command says otherwise.
|
|
150
|
+
- Operators own secret injection; images and packages ship without embedded tokens.
|
|
151
|
+
|
|
152
|
+
See [`docs/operations-runbook.md`](docs/operations-runbook.md) for operational scenarios: ledger corruption, two miners on one state dir, and post-upgrade schema migration ([#4875](https://github.com/JSONbored/gittensory/issues/4875)).
|
|
153
|
+
|
|
154
|
+
## Optional hosted discovery plane (opt-in)
|
|
155
|
+
|
|
156
|
+
The Phase 6 **hosted discovery-index** is **off by default** — unlike Orb fleet export (`ORB_AIR_GAP` is the only opt-out). Operators who want cross-fleet metadata queries or soft-claim coordination must opt in explicitly. See [`docs/discovery-plane-operator-guide.md`](docs/discovery-plane-operator-guide.md) ([#4309](https://github.com/JSONbored/gittensory/issues/4309), placeholder until [#4300](https://github.com/JSONbored/gittensory/issues/4300) / [#4301](https://github.com/JSONbored/gittensory/issues/4301) / [#4302](https://github.com/JSONbored/gittensory/issues/4302) ship).
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Fleet-mode image for @loopover/miner (#4295). Build context = monorepo root:
|
|
2
|
+
# docker build -f packages/gittensory-miner/Dockerfile -t gittensory-miner:latest .
|
|
3
|
+
# SECRETS ARE NEVER BAKED: supply operator credentials at `docker run` time only.
|
|
4
|
+
# Persistent SQLite ledgers live on a mounted volume (default GITTENSORY_MINER_CONFIG_DIR=/data/miner).
|
|
5
|
+
|
|
6
|
+
ARG GITTENSORY_VERSION=
|
|
7
|
+
|
|
8
|
+
# --- build: workspace install + engine compile + miner syntax check ----------------------------
|
|
9
|
+
FROM public.ecr.aws/docker/library/node:24-slim AS build
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
# Full source BEFORE `npm ci`: npm workspaces only symlinks packages that already exist on disk.
|
|
12
|
+
# Same ordering fix as the root gittensory-api Dockerfile — @loopover/engine must be
|
|
13
|
+
# present when `npm ci` runs or gittensory-miner's workspace dependency cannot resolve.
|
|
14
|
+
COPY . .
|
|
15
|
+
RUN npm ci --ignore-scripts
|
|
16
|
+
RUN npm --workspace @loopover/engine run build
|
|
17
|
+
RUN npm --workspace @loopover/miner run build
|
|
18
|
+
RUN npm prune --omit=dev --ignore-scripts
|
|
19
|
+
|
|
20
|
+
# --- runtime: non-root CLI image with a mounted config volume -----------------------------------
|
|
21
|
+
FROM public.ecr.aws/docker/library/node:24-slim AS runtime
|
|
22
|
+
WORKDIR /app
|
|
23
|
+
ARG GITTENSORY_VERSION=
|
|
24
|
+
ENV NODE_ENV=production \
|
|
25
|
+
GITTENSORY_MINER_CONFIG_DIR=/data/miner \
|
|
26
|
+
GITTENSORY_MINER_VERSION=${GITTENSORY_VERSION} \
|
|
27
|
+
PATH=/app/node_modules/.bin:$PATH
|
|
28
|
+
COPY --from=build --chown=node:node /app/node_modules ./node_modules
|
|
29
|
+
COPY --from=build --chown=node:node /app/packages/gittensory-miner ./packages/gittensory-miner
|
|
30
|
+
COPY --from=build --chown=node:node /app/packages/gittensory-engine ./packages/gittensory-engine
|
|
31
|
+
RUN mkdir -p /data/miner && chown -R node:node /data
|
|
32
|
+
USER node
|
|
33
|
+
VOLUME ["/data/miner"]
|
|
34
|
+
# No HEALTHCHECK: the miner is a batch/CLI workload (`docker run … gittensory-miner <cmd>`), not a
|
|
35
|
+
# long-running HTTP service — there is no steady-state endpoint to probe unless an operator wraps
|
|
36
|
+
# the container in their own supervising loop.
|
|
37
|
+
ENTRYPOINT ["gittensory-miner"]
|
|
38
|
+
CMD ["doctor"]
|
package/README.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# @loopover/miner
|
|
2
|
+
|
|
3
|
+
Foundation CLI for the local Gittensory miner runtime.
|
|
4
|
+
|
|
5
|
+
This package is the future home of the autonomous discover → analyze → plan → prepare → create → manage miner workflow. In this foundation phase it provides the package scaffold, a minimal CLI surface for `--help` and `--version`, and a non-blocking npm registry version nudge on startup.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
Current scope is intentionally small:
|
|
10
|
+
|
|
11
|
+
- workspace package wiring
|
|
12
|
+
- CLI entry point
|
|
13
|
+
- `--help` and `version` commands
|
|
14
|
+
- startup npm version nudge (override with `--no-update-check` or `GITTENSORY_MINER_NO_UPDATE_CHECK=1`)
|
|
15
|
+
|
|
16
|
+
Environment variables read by the miner are documented in [`docs/env-reference.md`](docs/env-reference.md).
|
|
17
|
+
Regenerate that file with `npm run miner:env-reference` from the repo root after adding or removing env reads.
|
|
18
|
+
|
|
19
|
+
Config precedence (`.gittensory-miner.yml` vs operator env vs CLI flags) is documented in
|
|
20
|
+
[`docs/config-precedence.md`](docs/config-precedence.md).
|
|
21
|
+
|
|
22
|
+
Real miner commands land in follow-up issues.
|
|
23
|
+
|
|
24
|
+
The package also includes the first metadata-only discovery primitive: `fetchCandidateIssues` lists open issue
|
|
25
|
+
metadata across target repos, and `searchCandidateIssues` does the same from a GitHub issue-search query. Both
|
|
26
|
+
paths hard-skip repos whose `AI-USAGE.md` or `CONTRIBUTING.md` explicitly bans AI-generated PRs. They perform
|
|
27
|
+
GitHub GET requests only, never clone source, never upload source, and never write to GitHub.
|
|
28
|
+
|
|
29
|
+
The package also includes a metadata-only ranker: `rankCandidateIssues` composes deterministic engine signals
|
|
30
|
+
(potential, feasibility, lane fit, freshness, dup risk) and returns fan-out candidates sorted by `rankScore`.
|
|
31
|
+
It never clones source and never writes to GitHub.
|
|
32
|
+
|
|
33
|
+
Discovery is per-tenant, not github.com-specific (#4784): `lib/forge-config.js` (`resolveForgeConfig`) holds the
|
|
34
|
+
forge base URL, API version, request headers, repo path, search endpoint/qualifiers, user-agent, and credential env
|
|
35
|
+
var behind one resolver with gittensory's github.com values as the only defaults, so the fan-out targets another
|
|
36
|
+
forge unchanged. `gittensory-miner discover` surfaces `--api-base-url <url>` and `--token-env <VAR>` and forwards a
|
|
37
|
+
tenant goal spec to the ranker, printing `usedDefaultGoalSpec` so a fall-back to the built-in rubric is explicit
|
|
38
|
+
rather than silent. See [`docs/repo-agnostic-capability-audit.md`](docs/repo-agnostic-capability-audit.md) for the
|
|
39
|
+
#4780 audit this executes.
|
|
40
|
+
|
|
41
|
+
The package also includes repo stack auto-detection: `detectRepoStack` (`lib/stack-detection.js`) inspects an
|
|
42
|
+
already-cloned target repo's manifest / lockfile / config files and returns a structured description — language,
|
|
43
|
+
package manager, and the build / test / lint / format commands — for Node (npm/yarn/pnpm/bun), Python
|
|
44
|
+
(pip/poetry/pipenv/uv), Rust, Go, Maven, and Gradle. It is pure (injectable `existsSync` / `readFileSync`), never
|
|
45
|
+
throws, and per its acceptance criteria **fails closed** — a repo with no recognized manifest returns
|
|
46
|
+
`{ detected: false, reason }` and a command that can't be inferred without guessing stays `null`, rather than being
|
|
47
|
+
assumed. Detection only; wiring the description into the attempt prompt is the follow-up ([#4786](https://github.com/JSONbored/gittensory/issues/4786)). (#4785)
|
|
48
|
+
|
|
49
|
+
The package also includes an append-only governor decision ledger: `initGovernorLedger` / `appendGovernorEvent`
|
|
50
|
+
persist structured allow/deny/throttle/kill-switch outcomes in local SQLite for contributor audit. Insert-only —
|
|
51
|
+
no enforcement wiring yet. (#2328)
|
|
52
|
+
|
|
53
|
+
The package also includes a real, persisted governor pause/resume control surface: `gittensory-miner governor
|
|
54
|
+
pause [--reason <text>]` / `governor resume` / `governor status` toggle a `paused`/`reason`/`pausedAt` flag on
|
|
55
|
+
governor-state.js's existing scalar-state row, and `loop-cli.js`'s iteration loop checks it at the same two
|
|
56
|
+
boundaries as the kill switch (before the first cycle and at the top of every subsequent one) — pausing mid-run
|
|
57
|
+
stops the loop before its next cycle claims anything, and resuming (clear the flag, re-invoke `loop`) continues
|
|
58
|
+
from the already-persisted queue/run state exactly where it left off. Distinct from the read-only kill switch
|
|
59
|
+
(env/YAML inputs this package never writes) and the one-way run-halt breaker (no resume path at all) — this is
|
|
60
|
+
the first genuinely operator/governor-writable stop/go control; the autonomous logic that decides *when* to pause
|
|
61
|
+
is separate, later-wave scope. (#4851)
|
|
62
|
+
|
|
63
|
+
The package also includes a local soft-claim ledger: `openClaimLedger` / `claimIssue` / `releaseClaim` /
|
|
64
|
+
`listActiveClaims` persist which issues this miner instance has claimed on this machine. The table is local
|
|
65
|
+
bookkeeping only — duplicate winners are adjudicated elsewhere via `@loopover/engine`. (#2291)
|
|
66
|
+
|
|
67
|
+
The package also includes an append-only event ledger: `initEventLedger` / `appendEvent` / `readEvents` persist
|
|
68
|
+
immutable miner-loop events in local SQLite for contributor audit. Insert-only — rows are never updated or
|
|
69
|
+
deleted. (#2322)
|
|
70
|
+
|
|
71
|
+
The package also records local PR outcomes: `recordPrOutcomeSnapshot` / `readPrOutcomes` write and reduce the
|
|
72
|
+
miner's OWN record of the outcomes of its OWN PRs (merged / closed, with an optional rejection-reason bucket) over
|
|
73
|
+
the append-only event ledger above. This is DISTINCT from the gittensory server's `recordPrOutcome`
|
|
74
|
+
(`src/review/outcomes-wire.ts`), which writes hosted-backend audit rows from the GitHub App's webhook stream — same
|
|
75
|
+
concept name, different codebase layer, no shared code (a laptop-mode miner may have no webhook relay at all). (#4274)
|
|
76
|
+
|
|
77
|
+
The package also includes an append-only prediction ledger: `initPredictionLedger` / `appendPrediction` /
|
|
78
|
+
`readPredictions` persist each predicted-gate verdict (conclusion / pack / readiness score + blocker/warning
|
|
79
|
+
codes, plus the producing `ENGINE_VERSION`) in local SQLite, so a later self-improve pass can score predictions
|
|
80
|
+
against realized outcomes. Insert-only. (#4263)
|
|
81
|
+
|
|
82
|
+
The package also includes the Phase 7 calibration runner: `runHistoricalReplayCalibrationCycle`
|
|
83
|
+
(`lib/calibration-run.js`) scores a completed historical-replay run with the deterministic objective-anchor scorer,
|
|
84
|
+
folds the composite into the engine's `computePhase7CalibrationLoop` combine alongside the existing `pr_outcome`
|
|
85
|
+
signal, and persists the combined snapshot as a `calibration_snapshot` event — queryable with
|
|
86
|
+
`gittensory-miner ledger list --type calibration_snapshot` or `readCalibrationSnapshots` /
|
|
87
|
+
`latestCalibrationSnapshot`. It measures and records only; acting on the metric (autonomy bumps, threshold tuning)
|
|
88
|
+
stays maintainer-only. See [`docs/miner-selfimprove-calibration.md`](docs/miner-selfimprove-calibration.md). (#4248)
|
|
89
|
+
|
|
90
|
+
`gittensory-miner manage status` now also folds each tracked repo's current discover/plan/prepare run state
|
|
91
|
+
(`run-state.js`) alongside its managed PR rows into a "run portfolio" view — `collectRunPortfolio` /
|
|
92
|
+
`renderRunPortfolioTable` — so a repo actively being discovered or planned shows up even with zero PRs yet.
|
|
93
|
+
Additive only: the existing `rows` JSON key and PR table are unchanged; `runPortfolio` is a new key printed
|
|
94
|
+
after the existing table. A real GUI dashboard surface is out of scope here — `apps/gittensory-miner-ui/` is
|
|
95
|
+
Phase 6 of the same roadmap tracker and hasn't been scaffolded yet. (#4279)
|
|
96
|
+
|
|
97
|
+
## Local storage
|
|
98
|
+
|
|
99
|
+
Independent local SQLite stores back the commands above. Each keeps its own file, its own table(s), and (for most stores) its own env-var override — this is a DRY pass over their shared path-resolution/open boilerplate (`local-store.js`), not a merge into one database. (#4272)
|
|
100
|
+
|
|
101
|
+
See [`docs/env-reference.md`](docs/env-reference.md) for the full `GITTENSORY_MINER_*` / `MINER_*` env-var list.
|
|
102
|
+
|
|
103
|
+
| Store | File | Primary table(s) | Module | Env var override |
|
|
104
|
+
| --- | --- | --- | --- | --- |
|
|
105
|
+
| Laptop bootstrap | `laptop-state.sqlite3` | `laptop_meta` | `laptop-init.js` | `GITTENSORY_MINER_CONFIG_DIR` (path only) |
|
|
106
|
+
| Run state | `run-state.sqlite3` | `miner_run_state` | `run-state.js` | `GITTENSORY_MINER_RUN_STATE_DB` |
|
|
107
|
+
| Claim ledger | `claim-ledger.sqlite3` | `miner_claims` | `claim-ledger.js` | `GITTENSORY_MINER_CLAIM_LEDGER_DB` |
|
|
108
|
+
| Portfolio queue | `portfolio-queue.sqlite3` | `miner_portfolio_queue` | `portfolio-queue.js` | `GITTENSORY_MINER_PORTFOLIO_QUEUE_DB` |
|
|
109
|
+
| Event ledger | `event-ledger.sqlite3` | `miner_event_ledger` | `event-ledger.js` | `GITTENSORY_MINER_EVENT_LEDGER_DB` |
|
|
110
|
+
| Plan store | `plan-store.sqlite3` | `miner_plans` | `plan-store.js` | `GITTENSORY_MINER_PLAN_STORE_DB` |
|
|
111
|
+
| Governor ledger | `governor-ledger.sqlite3` | `governor_events` | `governor-ledger.js` | `GITTENSORY_MINER_GOVERNOR_LEDGER_DB` |
|
|
112
|
+
| Governor state | `governor-state.sqlite3` | `governor_scalar_state`, `governor_reputation_history`, `governor_own_submissions` | `governor-state.js` | `GITTENSORY_MINER_GOVERNOR_STATE_DB` |
|
|
113
|
+
| Attempt log | `attempt-log.sqlite3` | `attempt_log_events` | `attempt-log.js` | `GITTENSORY_MINER_ATTEMPT_LOG_DB` |
|
|
114
|
+
| Prediction ledger | `prediction-ledger.sqlite3` | `predictions` | `prediction-ledger.js` | `GITTENSORY_MINER_PREDICTION_LEDGER_DB` |
|
|
115
|
+
| Replay snapshot | `replay-snapshot.sqlite3` | `replay_snapshots` | `replay-snapshot.js` | `GITTENSORY_MINER_REPLAY_SNAPSHOT_DB` |
|
|
116
|
+
| Deny-hook synthesis | `deny-hook-synthesis.sqlite3` | `deny_rule_proposals` | `deny-hook-synthesis.js` | `GITTENSORY_MINER_DENY_HOOK_SYNTHESIS_DB` |
|
|
117
|
+
| Worktree allocator | `worktree-allocator.sqlite3` | `worktree_slots` | `worktree-allocator.js` | `GITTENSORY_MINER_WORKTREE_ALLOCATOR_DB` |
|
|
118
|
+
| Orb export | `orb-export.sqlite3` | `orb_export_meta` | `orb-export.js` | `GITTENSORY_MINER_ORB_EXPORT_DB` |
|
|
119
|
+
| Policy-doc cache | `policy-doc-cache.sqlite3` | `policy_doc_cache` | `policy-doc-cache.js` | `GITTENSORY_MINER_POLICY_DOC_CACHE_DB` |
|
|
120
|
+
| Policy-verdict cache | `policy-verdict-cache.sqlite3` | `policy_verdict_cache` | `policy-verdict-cache.js` | `GITTENSORY_MINER_POLICY_VERDICT_CACHE_DB` |
|
|
121
|
+
|
|
122
|
+
The policy-doc and policy-verdict caches are the only stores above that hold no miner state of their own — both are
|
|
123
|
+
pure optimization, and deleting either file only forces the next run to redo the work it would have skipped. The
|
|
124
|
+
policy-doc cache caches the last-known ETag + body of each target repo's fetched policy docs
|
|
125
|
+
(AI-USAGE.md/CONTRIBUTING.md) so a repeated `discover` revalidates them with a conditional GET (`If-None-Match`)
|
|
126
|
+
instead of re-downloading static content, spending no extra rate-limit budget when GitHub answers
|
|
127
|
+
`304 Not Modified` (#4842). The policy-verdict cache goes one step further: once a repo's deciding doc's ETag is
|
|
128
|
+
confirmed unchanged, it reuses the already-resolved AI-usage-policy verdict instead of re-resolving it from the
|
|
129
|
+
(identical) doc text (#4843).
|
|
130
|
+
|
|
131
|
+
Every store resolves its file the same way: the store-specific env var above, else `GITTENSORY_MINER_CONFIG_DIR`,
|
|
132
|
+
else `XDG_CONFIG_HOME` (falling back to `~/.config`), joined with `gittensory-miner/<file>`. Every store also opens
|
|
133
|
+
its file with `0700`/`0600` permissions and a shared `PRAGMA busy_timeout` so two instances on the same file
|
|
134
|
+
serialize writes instead of racing.
|
|
135
|
+
|
|
136
|
+
Opening a store through `local-store.js` also registers it with the CLI's crash-safety chokepoint
|
|
137
|
+
(`process-lifecycle.js`): the entrypoint calls `installCliSignalHandlers()` once at startup, so a `SIGINT`/`SIGTERM`
|
|
138
|
+
mid-run — or an uncaught exception / unhandled rejection — closes every still-open ledger cleanly and exits with a
|
|
139
|
+
conventional code (130/143 for signals, non-zero for a crash) instead of dying mid-write. A store's normal `close()`
|
|
140
|
+
unregisters itself first, so the happy path never double-closes and a long-running `loop` never accumulates stale
|
|
141
|
+
handles. Cleanup only — no command business logic is affected. (#4826)
|
|
142
|
+
|
|
143
|
+
The "PR portfolio" `manage status` renders is currently a **read-time join**, not a dedicated table:
|
|
144
|
+
`collectManageStatus` reads `portfolio-queue.js` rows (via the `pr:{number}` identifier convention) and joins them
|
|
145
|
+
against `event-ledger.js`'s free-form `manage_pr_update` JSON events at query time, on every read. Decision: keep
|
|
146
|
+
this as a read-time join for now; revisit a dedicated indexed table only if/when PR-portfolio reads become frequent
|
|
147
|
+
enough (e.g. a live-polling dashboard) that the per-read linear event-ledger scan becomes a measured bottleneck.
|
|
148
|
+
|
|
149
|
+
## Install
|
|
150
|
+
|
|
151
|
+
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.
|
|
152
|
+
|
|
153
|
+
See [`docs/cross-repo-discovery-phase1.md`](docs/cross-repo-discovery-phase1.md) for the Phase 1 cross-repo discovery scope (re-scoped from [#1060](https://github.com/JSONbored/gittensory/issues/1060), paper trail for [#2299](https://github.com/JSONbored/gittensory/issues/2299)).
|
|
154
|
+
|
|
155
|
+
See [`docs/discovery-plane-operator-guide.md`](docs/discovery-plane-operator-guide.md) for the optional hosted discovery-index plane (opt-in default OFF; contrasts with Orb's opt-out-only export — [#4309](https://github.com/JSONbored/gittensory/issues/4309)).
|
|
156
|
+
|
|
157
|
+
See [`DEPLOYMENT.md`](DEPLOYMENT.md) for laptop vs fleet deployment.
|
|
158
|
+
|
|
159
|
+
See [`docs/operations-runbook.md`](docs/operations-runbook.md) for SQLite concurrency guarantees, corruption recovery, multi-process collision response, and post-upgrade ledger migration ([#4875](https://github.com/JSONbored/gittensory/issues/4875)).
|
|
160
|
+
|
|
161
|
+
### Laptop-mode quickstart
|
|
162
|
+
|
|
163
|
+
Zero-infra local install — no Docker, Redis, or Postgres required:
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
npm install -g @loopover/miner
|
|
167
|
+
gittensory-miner init
|
|
168
|
+
gittensory-miner doctor
|
|
169
|
+
gittensory-miner status
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`init` creates `~/.config/gittensory-miner/` (or `GITTENSORY_MINER_CONFIG_DIR` / `XDG_CONFIG_HOME` overrides) and a local `laptop-state.sqlite3` bootstrap file. Re-running `init` is idempotent. Pass `--verify-token` to make one authenticated GitHub API call up front and fail fast if `GITHUB_TOKEN` is invalid or missing repository access scopes. `doctor` reports Node, the state directory, SQLite readiness, and whether Docker is installed (informational only). Every local store already applies its own pending schema migrations automatically the moment some other command first opens it, but `migrate` lets an operator proactively bring every EXISTING store file up to date in one pass (e.g. right after upgrading) instead of relying on whichever command happens to touch a given store first; a store file that hasn't been created yet is reported as skipped, not created.
|
|
173
|
+
|
|
174
|
+
From a local checkout:
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
npm install
|
|
178
|
+
npm --workspace @loopover/miner run build
|
|
179
|
+
npm link --workspace @loopover/miner
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Coding-agent driver configuration
|
|
183
|
+
|
|
184
|
+
The production driver is selected by `MINER_CODING_AGENT_PROVIDER`. The value is a comma-separated preference list:
|
|
185
|
+
the first configured name wins, unknown names are skipped, and an empty/unset list leaves production driver
|
|
186
|
+
construction fail-closed. See [`docs/coding-agent-driver.md`](docs/coding-agent-driver.md) for the interface-level
|
|
187
|
+
contract and provider behavior.
|
|
188
|
+
|
|
189
|
+
| Env var | Accepted values | Default / behavior |
|
|
190
|
+
| --- | --- | --- |
|
|
191
|
+
| `MINER_CODING_AGENT_PROVIDER` | `noop`, `claude-cli`, `codex-cli`, `agent-sdk` | Unset / empty means no provider is configured; the miner fails closed until a valid provider name is supplied. |
|
|
192
|
+
| `MINER_CODING_AGENT_CLAUDE_MODEL` | Any Claude model string accepted by the local `claude` CLI | Unset means `claude-cli` uses the CLI's own default model. Ignored by `noop`, `codex-cli`, and `agent-sdk`. |
|
|
193
|
+
| `MINER_CODING_AGENT_CODEX_MODEL` | Any Codex model string accepted by the local `codex` CLI | Unset means `codex-cli` uses the CLI's own default model. Ignored by `noop`, `claude-cli`, and `agent-sdk`. |
|
|
194
|
+
| `MINER_CODING_AGENT_TIMEOUT_MS` | Positive integer milliseconds | Unset or invalid falls back to the CLI driver's default wall-clock timeout of `120000` ms. Ignored by `noop` and `agent-sdk`. |
|
|
195
|
+
|
|
196
|
+
### Recognizing a stale or missing coding-agent credential
|
|
197
|
+
|
|
198
|
+
When an attempt fails on a `claude-cli` / `codex-cli` provider, the CLI-subprocess driver folds the CLI's own output into a machine-readable `error` string on the attempt result. The credential/auth failure modes below map that exact string to a symptom and a concrete remediation — mirroring ORB's hosted-side [Recognizing a stale or missing credential](../../apps/gittensory-ui/src/routes/docs.self-hosting-ai-providers.tsx) table. Every string is emitted by [`cli-subprocess-driver.ts`](../gittensory-engine/src/miner/cli-subprocess-driver.ts); nothing here is speculative.
|
|
199
|
+
|
|
200
|
+
| Error string / pattern | Symptom | Remediation |
|
|
201
|
+
| --- | --- | --- |
|
|
202
|
+
| `claude_code_error_<status>` | The `claude` CLI ran but its `--output-format json` envelope reported `is_error: true` (e.g. `claude_code_error_invalid_api_key`) — the OAuth token is missing, rejected, or expired. `gittensory-miner doctor` reports the same condition up front as `not authenticated: set CLAUDE_CODE_OAUTH_TOKEN`. | Regenerate a long-lived token with `claude setup-token` and set `CLAUDE_CODE_OAUTH_TOKEN`, then retry. |
|
|
203
|
+
| `codex_no_auth` | `codex exec` exited non-zero with no structured error in its JSONL stdout and only the `Reading prompt from stdin...` banner on stderr — its `auth.json` credential is missing or expired. The driver appends its own remediation hint (`... auth.json missing or expired -- run codex auth to authenticate`). | Run `codex auth` to re-authenticate; the next attempt reads the refreshed `auth.json` with no further restart. |
|
|
204
|
+
| `<command>_exit_<code>` | Generic non-zero-exit fallback when neither structured parser matched (e.g. `codex_exit_1: ...`, `claude_exit_1: ...`). The driver appends the redacted stderr slice — an auth failure that neither parser recognized still surfaces here. | Read the appended detail; if it points at authentication, re-run `claude setup-token` or `codex auth` for the failing provider, otherwise address the reported error directly. |
|
|
205
|
+
|
|
206
|
+
## Commands
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
gittensory-miner --help
|
|
210
|
+
gittensory-miner help
|
|
211
|
+
gittensory-miner --version
|
|
212
|
+
gittensory-miner version
|
|
213
|
+
gittensory-miner init [--json] [--verify-token]
|
|
214
|
+
gittensory-miner status [--json]
|
|
215
|
+
gittensory-miner doctor [--json]
|
|
216
|
+
gittensory-miner migrate [--json]
|
|
217
|
+
gittensory-miner manage status [--json]
|
|
218
|
+
gittensory-miner manage poll <owner/repo> <pr#> [--branch <name>] [--json]
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## MCP server
|
|
222
|
+
|
|
223
|
+
The package ships a second bin entry, `gittensory-miner-mcp`, a minimal [Model Context Protocol](https://modelcontextprotocol.io) stdio server that any MCP-compatible client can connect to:
|
|
224
|
+
|
|
225
|
+
```sh
|
|
226
|
+
gittensory-miner-mcp
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
It exposes these read-only tools:
|
|
230
|
+
|
|
231
|
+
- `gittensory_miner_ping` (#5153) — a health check returning a static `{ "status": "ok", "tool": "gittensory_miner_ping" }` object. Reads no AMS state, takes no arguments.
|
|
232
|
+
- `gittensory_miner_get_portfolio_dashboard` (#5155) — the per-repo portfolio-queue backlog dashboard: status counts (queued / in_progress / done), totals, and the oldest-queued age. Wraps `collectPortfolioDashboard()` (no new logic) — the same data `gittensory-miner queue dashboard --json` prints locally. Read-only, takes no arguments.
|
|
233
|
+
- `gittensory_miner_list_claims` (#5156) — lists the local claim ledger (repo, issue number, status, claimed-at, note) via `listClaims()`. Optional `repoFullName` / `status` filters pass through to the query. Read-only — exposes no claim/release mutation.
|
|
234
|
+
- `gittensory_miner_get_audit_feed` (#5158) — read-only, metadata-only event-ledger audit feed (`eventType`, `repoFullName`, `outcome`, `actor`, `detail`, `createdAt`). Wraps `collectEventLedgerAuditFeed()` with the same filters as `gittensory-miner ledger list` (`--repo`, `--since`, `--type`). Never returns `payload_json` or other raw ledger columns.
|
|
235
|
+
|
|
236
|
+
- `gittensory_miner_get_run_state` (#5160) — read-only per-repo run-state (`idle` / `discovering` / `planning` / `preparing`) via `getRunState` / `listRunStates`. Pass `repoFullName` for one repo (a null state means none recorded yet), or omit it to list all. The read-only analog of ORB's `gittensory_get_automation_state`; adds no state-set mutation.
|
|
237
|
+
|
|
238
|
+
- `gittensory_miner_list_plans` / `gittensory_miner_get_plan` (#5161) — read-only access to the persisted plan store (`planId`, plan DAG, status, `updatedAt`) via `listPlans` / `loadPlan`; `list_plans` takes an optional `status` filter, `get_plan` takes a `planId` and returns an explicit `{ planId, found: false }` for an unknown id. These read the store-backed AMS plan store — distinct from ORB's stateless `gittensory_plan_status` tool.
|
|
239
|
+
|
|
240
|
+
- `gittensory_miner_get_governor_decisions` (#5159) — read-only projection of the governor decision log (`id`, `ts`, `eventType`, `repoFullName`, `actionClass`, `decision`, `reason`), optionally filtered by `repoFullName`. The projection **excludes the sensitive `payload_json` column by construction** — `governor-ledger.js` reads it with an explicit named-column SELECT, never `SELECT *`.
|
|
241
|
+
|
|
242
|
+
- `gittensory_miner_status` (#5154) — read-only status + doctor diagnostics, returning `{ status, doctor }`: `status` = package/engine versions (and skew), node version, state-dir + config-file paths, and the resolved coding-agent driver (provider name, the model **env-var NAME** never its value, CLI-present boolean); `doctor` = the checks `gittensory-miner doctor` runs (Docker/CLI presence, config validity, …) as `{ name, ok, detail }`. Reuses `collectStatus` / `runDoctorChecks` so it can't drift from the CLI, and returns only names / booleans / paths — never any env-var value, token, or credential.
|
|
243
|
+
|
|
244
|
+
This completes the read-only AMS MCP tool surface (status, portfolio, claims, event-ledger, governor-ledger, run-state, plan-store).
|
|
245
|
+
|
|
246
|
+
### Client config
|
|
247
|
+
|
|
248
|
+
`gittensory-mcp` (ORB's hosted contributor-workflow tools) and `gittensory-miner-mcp` (AMS's own local state-visibility tools above) can run as two separate stdio servers in the same MCP client session — useful for a dual-role operator running both ORB and AMS on the same box. Generate ORB's half with `gittensory-mcp init-client --print claude` (see the [`@loopover/mcp` README](../gittensory-mcp/README.md#client-config)); `gittensory-miner-mcp` takes no flags, so its entry is just the bin name. Combined, a Claude Desktop / Claude Code style config looks like:
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"mcpServers": {
|
|
253
|
+
"gittensory": {
|
|
254
|
+
"command": "gittensory-mcp",
|
|
255
|
+
"args": ["--stdio"]
|
|
256
|
+
},
|
|
257
|
+
"gittensory-miner": {
|
|
258
|
+
"command": "gittensory-miner-mcp",
|
|
259
|
+
"args": []
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`gittensory` exposes ORB's hosted contributor-workflow tools (issue ranking, PR packet prep, decision packs). `gittensory-miner` exposes AMS's own local state-visibility tools listed above (portfolio dashboard, claims, audit feed, run state, plans) — a fully separate, 100% local tool surface with no shared code or network calls between the two. Both follow the same `gittensory_*` tool-naming convention (`gittensory_...` vs. `gittensory_miner_...`), but back onto different stores: ORB's tools read the hosted gittensory backend, AMS's tools read this machine's own local SQLite files (see [Local storage](#local-storage)) — a handful of AMS tools even name the ORB tool they mirror (e.g. `gittensory_miner_get_run_state` is the read-only analog of `gittensory_get_automation_state`) so the relationship is explicit at the point of use, not just here.
|
|
266
|
+
|
|
267
|
+
## Version check
|
|
268
|
+
|
|
269
|
+
On every invocation the CLI starts an async npm registry lookup (5s timeout). When the installed package is behind `@loopover/miner@latest`, it prints a one-line upgrade command to stderr without blocking or failing the requested command. Set `GITTENSORY_NPM_REGISTRY_URL` to point at a mirror, same as `@loopover/mcp`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { EventLedger } from "../lib/event-ledger.js";
|
|
3
|
+
|
|
4
|
+
/** The static, non-secret payload the gittensory_miner_ping tool always returns, independent of input. */
|
|
5
|
+
export const MINER_PING_STATUS: { status: "ok"; tool: "gittensory_miner_ping" };
|
|
6
|
+
|
|
7
|
+
export interface MinerMcpServerOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Override the portfolio-queue store opener (defaults to the real on-disk store); injection seam for tests.
|
|
10
|
+
* Typed to the minimal read surface the dashboard tool uses, mirroring runPortfolioDashboard's own seam.
|
|
11
|
+
*/
|
|
12
|
+
initPortfolioQueue?: () => { listQueue(repoFullName?: string | null): unknown[]; close(): void };
|
|
13
|
+
/**
|
|
14
|
+
* Override the claim-ledger opener (defaults to the real on-disk ledger); injection seam for tests. Typed to
|
|
15
|
+
* the minimal read surface the list-claims tool uses.
|
|
16
|
+
*/
|
|
17
|
+
openClaimLedger?: () => {
|
|
18
|
+
listClaims(filter?: { repoFullName?: string | null; status?: string | null }): unknown[];
|
|
19
|
+
close(): void;
|
|
20
|
+
};
|
|
21
|
+
/** Override the clock used for the oldest-queued age (defaults to Date.now()); injection seam for tests. */
|
|
22
|
+
nowMs?: number;
|
|
23
|
+
/** Override the event-ledger opener (defaults to initEventLedger); injection seam for tests. */
|
|
24
|
+
initEventLedger?: () => EventLedger;
|
|
25
|
+
/**
|
|
26
|
+
* Override the run-state store opener (defaults to the real on-disk store); injection seam for tests. Typed to
|
|
27
|
+
* the minimal read surface the run-state tool uses (never setRunState).
|
|
28
|
+
*/
|
|
29
|
+
initRunStateStore?: () => {
|
|
30
|
+
getRunState(repoFullName: string): unknown;
|
|
31
|
+
listRunStates(): unknown[];
|
|
32
|
+
close(): void;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Override the plan-store opener (defaults to the real on-disk store); injection seam for tests. Typed to the
|
|
36
|
+
* minimal read surface the plan tools use (never savePlan).
|
|
37
|
+
*/
|
|
38
|
+
openPlanStore?: () => {
|
|
39
|
+
loadPlan(planId: string): unknown;
|
|
40
|
+
listPlans(filter?: { status?: string | null }): unknown[];
|
|
41
|
+
close(): void;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Override the governor-ledger opener (defaults to the real on-disk ledger); injection seam for tests. Typed
|
|
45
|
+
* to the minimal read surface the decisions tool uses (the payload-excluding readGovernorDecisions).
|
|
46
|
+
*/
|
|
47
|
+
initGovernorLedger?: () => {
|
|
48
|
+
readGovernorDecisions(filter?: { repoFullName?: string | null }): unknown[];
|
|
49
|
+
close(): void;
|
|
50
|
+
};
|
|
51
|
+
/** Override the status reader (defaults to status.js's collectStatus); injection seam for tests. */
|
|
52
|
+
collectStatus?: () => unknown;
|
|
53
|
+
/** Override the doctor-checks reader (defaults to status.js's runDoctorChecks); injection seam for tests. */
|
|
54
|
+
runDoctorChecks?: () => unknown[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build the miner MCP server with its tools registered (gittensory_miner_ping,
|
|
59
|
+
* gittensory_miner_get_portfolio_dashboard, gittensory_miner_list_claims, gittensory_miner_get_audit_feed,
|
|
60
|
+
* gittensory_miner_get_run_state, gittensory_miner_list_plans, gittensory_miner_get_plan,
|
|
61
|
+
* gittensory_miner_get_governor_decisions, gittensory_miner_status). `options` supplies test injection seams;
|
|
62
|
+
* production callers pass nothing.
|
|
63
|
+
*/
|
|
64
|
+
export function createMinerMcpServer(options?: MinerMcpServerOptions): McpServer;
|