@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.
Files changed (218) hide show
  1. package/DEPLOYMENT.md +156 -0
  2. package/Dockerfile +38 -0
  3. package/README.md +269 -0
  4. package/bin/gittensory-miner-mcp.d.ts +64 -0
  5. package/bin/gittensory-miner-mcp.js +279 -0
  6. package/bin/gittensory-miner.js +196 -0
  7. package/docs/coding-agent-driver.md +140 -0
  8. package/docs/config-precedence.md +96 -0
  9. package/docs/cross-repo-discovery-phase1.md +55 -0
  10. package/docs/discovery-index-contract.md +60 -0
  11. package/docs/discovery-plane-operator-guide.md +101 -0
  12. package/docs/env-reference.md +30 -0
  13. package/docs/fleet-run-manifest.md +50 -0
  14. package/docs/miner-goal-spec.md +76 -0
  15. package/docs/miner-selfimprove-calibration.md +105 -0
  16. package/docs/observability.md +60 -0
  17. package/docs/operations-runbook.md +209 -0
  18. package/docs/repo-agnostic-capability-audit.md +129 -0
  19. package/docs/unattended-scheduling.md +107 -0
  20. package/expected-engine.version +1 -0
  21. package/lib/ams-policy.d.ts +20 -0
  22. package/lib/ams-policy.js +68 -0
  23. package/lib/attempt-cli.d.ts +99 -0
  24. package/lib/attempt-cli.js +523 -0
  25. package/lib/attempt-input-builder.d.ts +24 -0
  26. package/lib/attempt-input-builder.js +91 -0
  27. package/lib/attempt-log.d.ts +37 -0
  28. package/lib/attempt-log.js +177 -0
  29. package/lib/attempt-runner.d.ts +59 -0
  30. package/lib/attempt-runner.js +164 -0
  31. package/lib/attempt-worktree.d.ts +31 -0
  32. package/lib/attempt-worktree.js +94 -0
  33. package/lib/calibration-cli.d.ts +1 -0
  34. package/lib/calibration-cli.js +92 -0
  35. package/lib/calibration-run.d.ts +161 -0
  36. package/lib/calibration-run.js +232 -0
  37. package/lib/calibration-types.d.ts +46 -0
  38. package/lib/calibration-types.js +74 -0
  39. package/lib/calibration.d.ts +24 -0
  40. package/lib/calibration.js +99 -0
  41. package/lib/ci-poller.d.ts +33 -0
  42. package/lib/ci-poller.js +234 -0
  43. package/lib/claim-adjudication.d.ts +21 -0
  44. package/lib/claim-adjudication.js +36 -0
  45. package/lib/claim-conflict-resolver.d.ts +29 -0
  46. package/lib/claim-conflict-resolver.js +101 -0
  47. package/lib/claim-ledger-cli.d.ts +59 -0
  48. package/lib/claim-ledger-cli.js +320 -0
  49. package/lib/claim-ledger-expiry.d.ts +20 -0
  50. package/lib/claim-ledger-expiry.js +41 -0
  51. package/lib/claim-ledger.d.ts +63 -0
  52. package/lib/claim-ledger.js +303 -0
  53. package/lib/cli-error.d.ts +3 -0
  54. package/lib/cli-error.js +27 -0
  55. package/lib/cli.d.ts +3 -0
  56. package/lib/cli.js +72 -0
  57. package/lib/coding-agent-construction.d.ts +17 -0
  58. package/lib/coding-agent-construction.js +103 -0
  59. package/lib/coding-agent-house-rules.d.ts +24 -0
  60. package/lib/coding-agent-house-rules.js +64 -0
  61. package/lib/coding-task-spec.d.ts +44 -0
  62. package/lib/coding-task-spec.js +183 -0
  63. package/lib/config-precedence.d.ts +25 -0
  64. package/lib/config-precedence.js +38 -0
  65. package/lib/deny-check.d.ts +11 -0
  66. package/lib/deny-check.js +76 -0
  67. package/lib/deny-hook-synthesis.d.ts +93 -0
  68. package/lib/deny-hook-synthesis.js +399 -0
  69. package/lib/deny-hooks.d.ts +28 -0
  70. package/lib/deny-hooks.js +155 -0
  71. package/lib/deployment-docs-audit.d.ts +41 -0
  72. package/lib/deployment-docs-audit.js +115 -0
  73. package/lib/discover-cli.d.ts +94 -0
  74. package/lib/discover-cli.js +284 -0
  75. package/lib/discovery-throttle.d.ts +8 -0
  76. package/lib/discovery-throttle.js +33 -0
  77. package/lib/env-file-indirection.d.ts +4 -0
  78. package/lib/env-file-indirection.js +45 -0
  79. package/lib/event-ledger-cli.d.ts +82 -0
  80. package/lib/event-ledger-cli.js +266 -0
  81. package/lib/event-ledger.d.ts +37 -0
  82. package/lib/event-ledger.js +210 -0
  83. package/lib/execute-local-write.d.ts +14 -0
  84. package/lib/execute-local-write.js +50 -0
  85. package/lib/feasibility-cli.d.ts +25 -0
  86. package/lib/feasibility-cli.js +80 -0
  87. package/lib/forge-config.d.ts +17 -0
  88. package/lib/forge-config.js +37 -0
  89. package/lib/governor-action-mode.d.ts +26 -0
  90. package/lib/governor-action-mode.js +47 -0
  91. package/lib/governor-chokepoint-persisted.d.ts +18 -0
  92. package/lib/governor-chokepoint-persisted.js +46 -0
  93. package/lib/governor-chokepoint.d.ts +14 -0
  94. package/lib/governor-chokepoint.js +52 -0
  95. package/lib/governor-kill-switch.d.ts +26 -0
  96. package/lib/governor-kill-switch.js +47 -0
  97. package/lib/governor-ledger-cli.d.ts +32 -0
  98. package/lib/governor-ledger-cli.js +155 -0
  99. package/lib/governor-ledger.d.ts +46 -0
  100. package/lib/governor-ledger.js +203 -0
  101. package/lib/governor-open-pr.d.ts +21 -0
  102. package/lib/governor-open-pr.js +27 -0
  103. package/lib/governor-pause-cli.d.ts +23 -0
  104. package/lib/governor-pause-cli.js +171 -0
  105. package/lib/governor-run-halt.d.ts +37 -0
  106. package/lib/governor-run-halt.js +58 -0
  107. package/lib/governor-state.d.ts +63 -0
  108. package/lib/governor-state.js +363 -0
  109. package/lib/governor-write-rate-limit.d.ts +30 -0
  110. package/lib/governor-write-rate-limit.js +64 -0
  111. package/lib/harness-submission-trigger.d.ts +69 -0
  112. package/lib/harness-submission-trigger.js +138 -0
  113. package/lib/http-retry.d.ts +12 -0
  114. package/lib/http-retry.js +52 -0
  115. package/lib/laptop-init.d.ts +53 -0
  116. package/lib/laptop-init.js +332 -0
  117. package/lib/live-issue-snapshot.d.ts +16 -0
  118. package/lib/live-issue-snapshot.js +117 -0
  119. package/lib/local-store.d.ts +18 -0
  120. package/lib/local-store.js +62 -0
  121. package/lib/logger.d.ts +58 -0
  122. package/lib/logger.js +167 -0
  123. package/lib/loop-cli.d.ts +66 -0
  124. package/lib/loop-cli.js +540 -0
  125. package/lib/loop-closure.d.ts +35 -0
  126. package/lib/loop-closure.js +66 -0
  127. package/lib/loop-reentry.d.ts +51 -0
  128. package/lib/loop-reentry.js +124 -0
  129. package/lib/manage-poll.d.ts +79 -0
  130. package/lib/manage-poll.js +254 -0
  131. package/lib/manage-status.d.ts +70 -0
  132. package/lib/manage-status.js +241 -0
  133. package/lib/metrics-cli.d.ts +9 -0
  134. package/lib/metrics-cli.js +50 -0
  135. package/lib/migrate-cli.d.ts +24 -0
  136. package/lib/migrate-cli.js +111 -0
  137. package/lib/miner-goal-spec.d.ts +12 -0
  138. package/lib/miner-goal-spec.js +67 -0
  139. package/lib/opportunity-fanout.d.ts +86 -0
  140. package/lib/opportunity-fanout.js +597 -0
  141. package/lib/opportunity-ranker.d.ts +36 -0
  142. package/lib/opportunity-ranker.js +118 -0
  143. package/lib/orb-export.d.ts +54 -0
  144. package/lib/orb-export.js +206 -0
  145. package/lib/plan-store-cli.d.ts +37 -0
  146. package/lib/plan-store-cli.js +151 -0
  147. package/lib/plan-store.d.ts +51 -0
  148. package/lib/plan-store.js +231 -0
  149. package/lib/policy-doc-cache.d.ts +25 -0
  150. package/lib/policy-doc-cache.js +79 -0
  151. package/lib/policy-verdict-cache.d.ts +35 -0
  152. package/lib/policy-verdict-cache.js +106 -0
  153. package/lib/portfolio-dashboard.d.ts +30 -0
  154. package/lib/portfolio-dashboard.js +105 -0
  155. package/lib/portfolio-discovery.d.ts +29 -0
  156. package/lib/portfolio-discovery.js +100 -0
  157. package/lib/portfolio-queue-cli.d.ts +87 -0
  158. package/lib/portfolio-queue-cli.js +509 -0
  159. package/lib/portfolio-queue-expiry.d.ts +20 -0
  160. package/lib/portfolio-queue-expiry.js +51 -0
  161. package/lib/portfolio-queue-manager.d.ts +50 -0
  162. package/lib/portfolio-queue-manager.js +134 -0
  163. package/lib/portfolio-queue.d.ts +62 -0
  164. package/lib/portfolio-queue.js +351 -0
  165. package/lib/pr-disposition-poller.d.ts +26 -0
  166. package/lib/pr-disposition-poller.js +163 -0
  167. package/lib/pr-number-parse.d.ts +4 -0
  168. package/lib/pr-number-parse.js +23 -0
  169. package/lib/pr-outcome.d.ts +41 -0
  170. package/lib/pr-outcome.js +90 -0
  171. package/lib/prediction-ledger.d.ts +47 -0
  172. package/lib/prediction-ledger.js +222 -0
  173. package/lib/pretooluse-hook.d.ts +31 -0
  174. package/lib/pretooluse-hook.js +93 -0
  175. package/lib/process-lifecycle.d.ts +31 -0
  176. package/lib/process-lifecycle.js +106 -0
  177. package/lib/purge-cli.d.ts +44 -0
  178. package/lib/purge-cli.js +198 -0
  179. package/lib/rejection-signal.d.ts +6 -0
  180. package/lib/rejection-signal.js +101 -0
  181. package/lib/rejection-state-machine.d.ts +37 -0
  182. package/lib/rejection-state-machine.js +81 -0
  183. package/lib/rejection-templates.d.ts +12 -0
  184. package/lib/rejection-templates.js +71 -0
  185. package/lib/replay-objective-anchor.d.ts +79 -0
  186. package/lib/replay-objective-anchor.js +179 -0
  187. package/lib/replay-snapshot.d.ts +41 -0
  188. package/lib/replay-snapshot.js +288 -0
  189. package/lib/replay-task-generation.d.ts +126 -0
  190. package/lib/replay-task-generation.js +223 -0
  191. package/lib/repo-clone.d.ts +19 -0
  192. package/lib/repo-clone.js +102 -0
  193. package/lib/run-state-cli.d.ts +27 -0
  194. package/lib/run-state-cli.js +154 -0
  195. package/lib/run-state.d.ts +37 -0
  196. package/lib/run-state.js +150 -0
  197. package/lib/schema-version.d.ts +17 -0
  198. package/lib/schema-version.js +71 -0
  199. package/lib/self-review-context.d.ts +28 -0
  200. package/lib/self-review-context.js +339 -0
  201. package/lib/slop-assessment.d.ts +3 -0
  202. package/lib/slop-assessment.js +20 -0
  203. package/lib/stack-detection.d.ts +41 -0
  204. package/lib/stack-detection.js +248 -0
  205. package/lib/status.d.ts +64 -0
  206. package/lib/status.js +441 -0
  207. package/lib/store-maintenance.d.ts +31 -0
  208. package/lib/store-maintenance.js +183 -0
  209. package/lib/submission-freshness-check.d.ts +32 -0
  210. package/lib/submission-freshness-check.js +93 -0
  211. package/lib/update-check.d.ts +36 -0
  212. package/lib/update-check.js +161 -0
  213. package/lib/version.d.ts +3 -0
  214. package/lib/version.js +10 -0
  215. package/lib/worktree-allocator.d.ts +39 -0
  216. package/lib/worktree-allocator.js +262 -0
  217. package/package.json +50 -0
  218. package/schema/miner-goal-spec.schema.json +111 -0
@@ -0,0 +1,96 @@
1
+ # Miner config precedence
2
+
3
+ AMS does **not** have a single `config.js` resolver. Configuration is layered by concern across several modules under `packages/gittensory-miner/lib/` and `@loopover/engine`. This document states the order each layer **actually implements today** — not an idealized or corrected order.
4
+
5
+ ## Configuration layers
6
+
7
+ | Layer | Source | Scope | Typical modules |
8
+ | --- | --- | --- | --- |
9
+ | **Per-target-repo file** | `.gittensory-miner.yml` (or `.github/gittensory-miner.yml`, JSON variants) | One cloned target repo | `lib/miner-goal-spec.js`, engine `parseMinerGoalSpecContent` |
10
+ | **Operator env** | `GITTENSORY_MINER_*` / `MINER_*` | This miner process / fleet container | `lib/local-store.js`, `lib/governor-kill-switch.js`, `lib/attempt-cli.js`, … |
11
+ | **CLI flags** | `gittensory-miner <cmd> …` argv | One invocation | `lib/attempt-cli.js`, `lib/discover-cli.js`, `lib/loop-cli.js`, … |
12
+ | **Operator file (not goal spec)** | `~/.config/gittensory-miner/.gittensory-ams.yml` | Operator execution policy | `lib/ams-policy.js` |
13
+
14
+ `.gittensory-miner.yml` is **maintainer-authored in the target repo**. Operator env and CLI flags are **never overridden by a target repo's goal spec** for operator-owned policy (see `lib/ams-policy.js` header).
15
+
16
+ ## `.gittensory-miner.yml` file discovery
17
+
18
+ First existing file wins (engine `MINER_GOAL_SPEC_FILENAMES`):
19
+
20
+ 1. `.gittensory-miner.yml`
21
+ 2. `.github/gittensory-miner.yml`
22
+ 3. `.gittensory-miner.json`
23
+ 4. `.github/gittensory-miner.json`
24
+
25
+ ## Precedence by concern
26
+
27
+ ### Kill switch (halt miner writes)
28
+
29
+ **Sources:** `GITTENSORY_MINER_KILL_SWITCH` (operator env) and `.gittensory-miner.yml` → `killSwitch.paused`.
30
+
31
+ **Order (safest wins, engine `resolveMinerKillSwitch`):**
32
+
33
+ 1. Global env halt → scope `"global"` (always reported even when the repo yml also pauses).
34
+ 2. Else per-repo yml `killSwitch.paused: true` → scope `"repo"`.
35
+ 3. Else → scope `"none"`.
36
+
37
+ There is **no CLI flag** for kill-switch today. `MINER_CODING_AGENT_PAUSED` is a separate axis (coding-agent spawn only) and does not change kill-switch scope.
38
+
39
+ ### Governor live write mode
40
+
41
+ **Sources:** `GITTENSORY_MINER_LIVE_MODE=live` (operator env) and `.gittensory-miner.yml` → `execution.liveModeOptIn: live`.
42
+
43
+ **Order (engine `resolveMinerActionMode`):**
44
+
45
+ 1. Kill switch active → `"paused"` (overrides any live opt-in).
46
+ 2. Else **both** operator env **and** repo yml must equal the exact string `"live"` → `"live"`.
47
+ 3. Else → `"dry_run"`.
48
+
49
+ This is an **AND** requirement, not “last writer wins”. Either side missing or malformed → dry-run.
50
+
51
+ There is **no CLI flag wired to governor live mode** today. `attempt --live` / `loop --live` affect coding-agent spawn mode only (below).
52
+
53
+ ### Coding-agent execution mode (spawn the driver?)
54
+
55
+ **Sources:** `MINER_CODING_AGENT_PAUSED` (operator env) and `attempt|loop --live` (CLI, per invocation).
56
+
57
+ **Order (engine `resolveCodingAgentExecutionMode`, wired in `lib/attempt-cli.js`):**
58
+
59
+ 1. Global env pause (`MINER_CODING_AGENT_PAUSED` truthy) → `"paused"`.
60
+ 2. Else CLI `--live` absent → `agentDryRun: true` → `"dry_run"` (`attempt-cli.js` enforces dry-run default for #2342).
61
+ 3. Else CLI `--live` present → `"live"`.
62
+
63
+ There is **no `.gittensory-miner.yml` field** for coding-agent mode today.
64
+
65
+ ### Discover forge credential env var name
66
+
67
+ **Sources:** `discover --token-env <VAR>` (CLI), programmatic `options.tokenEnv`, forge default (`GITHUB_TOKEN`).
68
+
69
+ **Order (`lib/discover-cli.js`):**
70
+
71
+ 1. CLI `--token-env`
72
+ 2. Else programmatic `options.tokenEnv`
73
+ 3. Else `resolveForgeConfig(...).tokenEnvVar` (default `GITHUB_TOKEN`)
74
+
75
+ There is **no `.gittensory-miner.yml` forge block** today; `--api-base-url` follows the same CLI → programmatic → default shape for the API host.
76
+
77
+ ### Local SQLite store paths
78
+
79
+ **Sources:** per-store `GITTENSORY_MINER_*_DB` env var, then `GITTENSORY_MINER_CONFIG_DIR`, then XDG default (`lib/local-store.js`).
80
+
81
+ Explicit per-store env **wins** over config dir; config dir **wins** over XDG. No CLI or goal-spec override.
82
+
83
+ ## Known gaps / inconsistencies
84
+
85
+ - **No unified precedence** across yml + env + CLI for a single knob — each concern owns its resolver.
86
+ - **Live execution** spans two independent gates: coding-agent `--live` (spawn) vs governor env+yml (writes). Both must allow live for a full live open-pr attempt.
87
+ - **Forge tenant overrides** (`--api-base-url`, `--token-env`) are CLI/programmatic only; `.gittensory-miner.yml` cannot set them yet.
88
+ - **Operator AMS policy** (`.gittensory-ams.yml`) is separate from per-repo goal spec; goal spec never overrides operator policy.
89
+
90
+ If a future change adds yml or CLI for a setting documented here as env-only, update this file and extend `test/unit/miner-config-precedence.test.ts`.
91
+
92
+ ## See also
93
+
94
+ - [`miner-goal-spec.md`](miner-goal-spec.md) — goal-spec field reference
95
+ - [`env-reference.md`](env-reference.md) — generated operator env list
96
+ - ORB `.loopover.yml` (or legacy `.gittensory.yml`, #4773) precedence (`yml > DB > defaults`) in the main app — analogous documentation style, different runtime
@@ -0,0 +1,55 @@
1
+ # Cross-repo discovery — Phase 1 scope (re-scoped from #1060)
2
+
3
+ This document is the in-repo paper trail for [#2299](https://github.com/JSONbored/gittensory/issues/2299). It records the metadata-only, client-driven discovery model that supersedes the original [#1060](https://github.com/JSONbored/gittensory/issues/1060) wording (registered-repo aggregation and any retired global data layer). Maintainers should reopen #1060 and point its body at this file so the miner backlog has one canonical entry point.
4
+
5
+ Part of [#1058](https://github.com/JSONbored/gittensory/issues/1058) (close-the-loop epic). Builds on / supersedes the intent of [#816](https://github.com/JSONbored/gittensory/issues/816).
6
+
7
+ ## Re-scoped discovery model
8
+
9
+ Cross-repo opportunity discovery in Phase 1 is:
10
+
11
+ - **Metadata-only** — GitHub search/listing APIs only; never source clone or upload.
12
+ - **Client-driven** — the contributor supplies the repo list or search scope; no dependency on a Gittensory-registered-repos table or any retired central global data layer.
13
+ - **Deterministic** — reuse existing per-repo signals; no new ML model and no raw reward/score/wallet/hotkey exposure.
14
+ - **Read-only (`wave: "1"`)** — every deliverable in this Phase 1 batch performs GET/list/search only; no GitHub writes, no autonomous claiming, no PR submission.
15
+
16
+ ## Existing signal sources the ranker composes
17
+
18
+ The cross-repo ranker is a **new join over existing signals**, not a new scoring model:
19
+
20
+ | Source | Role |
21
+ |--------|------|
22
+ | [`src/services/issue-quality.ts`](../../../src/services/issue-quality.ts) | Loads or computes per-repo `IssueQualityReport` snapshots (actionable / needs-proof / stale / duplicate-prone / solved). |
23
+ | [`src/signals/reward-risk.ts`](../../../src/signals/reward-risk.ts) → [`packages/gittensory-engine/src/reward-risk.ts`](../../../packages/gittensory-engine/src/reward-risk.ts) | Opportunity factors (`competitionFactor`, `freshnessFactor`) for repo-level ranking; backend shim delegates to the engine implementation. |
24
+ | [`src/services/decision-pack.ts`](../../../src/services/decision-pack.ts) | Existing per-miner, per-repo opportunity pack (reactive baseline the Phase 1 tools extend). |
25
+
26
+ Hosted and stdio MCP surfaces expose the composed shortlist via `gittensory_find_opportunities` (#2308 / #2309).
27
+
28
+ ## Phase 1 issue batch
29
+
30
+ | Issue | Title |
31
+ |-------|-------|
32
+ | [#2299](https://github.com/JSONbored/gittensory/issues/2299) | Scope correction note (this document) |
33
+ | [#2300](https://github.com/JSONbored/gittensory/issues/2300) | `.gittensory-miner.yml` MinerGoalSpec schema doc |
34
+ | [#2301](https://github.com/JSONbored/gittensory/issues/2301) | MinerGoalSpec parser with safe-default fallback |
35
+ | [#2302](https://github.com/JSONbored/gittensory/issues/2302) | Pure ranker — `potential × feasibility × laneFit × freshness × (1 − dupRisk)` |
36
+ | [#2303](https://github.com/JSONbored/gittensory/issues/2303) | Ranker invariant test suite |
37
+ | [#2304](https://github.com/JSONbored/gittensory/issues/2304) | Goal model — MinerGoalSpec → ranker weights |
38
+ | [#2305](https://github.com/JSONbored/gittensory/issues/2305) | Per-repo AI-policy map (CONTRIBUTING / AI-USAGE hard-skip) |
39
+ | [#2306](https://github.com/JSONbored/gittensory/issues/2306) | AI-policy fixture corpus + integration test |
40
+ | [#2307](https://github.com/JSONbored/gittensory/issues/2307) | Cross-repo GitHub search/listing fan-out (metadata-only) |
41
+ | [#2308](https://github.com/JSONbored/gittensory/issues/2308) | Hosted `gittensory_find_opportunities` MCP tool |
42
+ | [#2309](https://github.com/JSONbored/gittensory/issues/2309) | Stdio `gittensory_find_opportunities` in `@loopover/mcp` |
43
+ | [#2310](https://github.com/JSONbored/gittensory/issues/2310) | Miner package `test:miner-pack` / `build:miner` parity checks |
44
+ | [#2311](https://github.com/JSONbored/gittensory/issues/2311) | End-to-end Phase 1 discovery pipeline fixture test |
45
+
46
+ ## Target feature issue (#1060)
47
+
48
+ [#1060 — proactive cross-repo opportunity discovery](https://github.com/JSONbored/gittensory/issues/1060) remains the umbrella feature issue. It should be **reopened** with this Phase 1 scope (metadata-only fan-out + deterministic ranker + MCP tool) replacing the older “aggregate across all registered repos” deliverable list. Proactive alerting and notification filters are follow-on work outside this read-only Phase 1 batch.
49
+
50
+ ## Acceptance (Phase 1)
51
+
52
+ - A miner can ask for a deterministic, metadata-only, cross-repo ranked shortlist via MCP (`gittensory_find_opportunities`).
53
+ - AI-PR-banned repos are hard-skipped upstream and never appear in output.
54
+ - No raw scores, rewards, hotkeys, wallet data, or source contents cross the public boundary.
55
+ - All Phase 1 implementation issues stay read-only on GitHub (GET/search/list only).
@@ -0,0 +1,60 @@
1
+ # Discovery-index API contract
2
+
3
+ The **discovery-index contract** is the typed request/response shape a miner uses to query the *optional* hosted
4
+ discovery-index service. It is defined in `@loopover/engine`
5
+ (`packages/gittensory-engine/src/discovery-index-contract.ts`) so both sides — this repo's future server
6
+ implementation ([#4250](https://github.com/JSONbored/gittensory/issues/4250), maintainer-only, explicitly blocked
7
+ on this contract) and any client — build against one shape.
8
+
9
+ This is **schema/shape only**: no server, no deployed endpoint, no client HTTP implementation (those are #4250 and
10
+ the sibling soft-claim-coordination issue, respectively).
11
+
12
+ ## Why
13
+
14
+ The plane mitigates the rate-limit incident already fixed once for the review stack
15
+ ([#1936](https://github.com/JSONbored/gittensory/issues/1936)): *one* shared GitHub-metadata crawler across the
16
+ miner fleet instead of every miner instance independently hammering the same repos' search/listing endpoints. The
17
+ existing single-instance client pipeline (`packages/gittensory-miner/lib/opportunity-fanout.js` +
18
+ `opportunity-ranker.js`) is what a hosted version must stay compatible with, so the response candidate shape is
19
+ field-for-field compatible with `opportunity-ranker.js`'s `normalizeCandidate` — a miner can swap a local fan-out
20
+ for a hosted query without the ranker changing.
21
+
22
+ ## Boundary (Phase 1)
23
+
24
+ The contract stays inside the Phase 1 discovery boundary
25
+ ([`cross-repo-discovery-phase1.md`](./cross-repo-discovery-phase1.md)): metadata-only, GET/list/search-only, and
26
+ **no raw scores, rewards, wallet/hotkey data, or source contents** cross the public boundary. This is enforced in
27
+ code, not just documented: `discoveryIndexBoundaryViolations` lists any forbidden field on a raw object, and
28
+ `normalizeDiscoveryIndexCandidate` **rejects** (returns `null`) — rather than silently trimming — any candidate
29
+ carrying one, so a misbehaving server cannot smuggle economic/identity/source data past the contract.
30
+ `DISCOVERY_INDEX_FORBIDDEN_FIELDS` is the fragment list (`score`, `reward`, `wallet`, `hotkey`, `coldkey`,
31
+ `mnemonic`, `payout`, `ranking`, `rawtrust`, `trustscore`, `sourcecontent`, `diff`, `patch`).
32
+
33
+ ## Request — `DiscoveryIndexQuery`
34
+
35
+ Every field is optional; a malformed value degrades to a documented default with a warning rather than throwing
36
+ (`normalizeDiscoveryIndexRequest`).
37
+
38
+ - **`repos`** — canonical `owner/repo` targets. Non-string / non-`owner/repo` entries are skipped; deduplicated;
39
+ capped at 200. Default: `[]`.
40
+ - **`orgs`** — bare `owner` (org/user) targets. Entries containing `/` are skipped; deduplicated; capped at 200.
41
+ Default: `[]`.
42
+ - **`searchTerms`** — free-text GitHub issue-search terms. Blank entries skipped; deduplicated; capped at 200.
43
+ Default: `[]`.
44
+ - **`limit`** — page size, floored and clamped to `[1, 200]`; a non-numeric value warns and falls back. Default:
45
+ `50`.
46
+ - **`cursor`** — opaque forward pagination cursor from a previous response's `nextCursor`; a blank/non-string value
47
+ becomes `null`. Default: `null`.
48
+
49
+ ## Response — `DiscoveryIndexResponse`
50
+
51
+ - **`candidates`** — a list of `DiscoveryIndexCandidate`. `normalizeDiscoveryIndexResponse` keeps only valid,
52
+ public-safe entries and drops invalid or boundary-violating ones with a warning.
53
+ - **`nextCursor`** — forward cursor for the next page, or `null` when the result set is exhausted.
54
+ - **`contractVersion`** — `DISCOVERY_INDEX_CONTRACT_VERSION` (currently `1`).
55
+
56
+ ### `DiscoveryIndexCandidate`
57
+
58
+ Metadata-only, field-for-field compatible with `opportunity-ranker.js` `normalizeCandidate`:
59
+ `owner`, `repo`, `repoFullName`, `issueNumber`, `title`, `labels`, `commentsCount`, `createdAt`, `updatedAt`,
60
+ `htmlUrl`, `aiPolicyAllowed`, `aiPolicySource` (`"AI-USAGE.md" | "CONTRIBUTING.md" | "none"`).
@@ -0,0 +1,101 @@
1
+ # Hosted discovery plane — operator guide (opt-in)
2
+
3
+ Operator-facing guide for the **optional** Phase 6 hosted discovery-index plane ([#4250](https://github.com/JSONbored/gittensory/issues/4250)). This is the client/miner half of that roadmap item: how a `gittensory-miner` instance opts in, what it may send, and what never leaves the operator's machine.
4
+
5
+ > **Placeholder scope:** the concrete env var names, HTTP paths, and telemetry event types below are **provisional** until the sibling contract/telemetry/client issues land. When those ship, update this file to match the real symbols — do not treat the TBD names as stable API yet.
6
+ >
7
+ > | Issue | What it defines |
8
+ > |-------|-----------------|
9
+ > | [#4300](https://github.com/JSONbored/gittensory/issues/4300) | Public-data-only discovery-index API contract (request/response shapes) |
10
+ > | [#4301](https://github.com/JSONbored/gittensory/issues/4301) | Anonymized telemetry event schema for the optional hosted plane |
11
+ > | [#4302](https://github.com/JSONbored/gittensory/issues/4302) | Client-side soft-claim coordination request builder |
12
+ > | [#4250](https://github.com/JSONbored/gittensory/issues/4250) | Hosted discovery-index server (maintainer-operated counterpart) |
13
+
14
+ Part of the Miner Wave 2 discovery plane ([#2353](https://github.com/JSONbored/gittensory/issues/2353) Phase 6). Distinct from Phase 1's **local-only** metadata fan-out documented in [`cross-repo-discovery-phase1.md`](cross-repo-discovery-phase1.md) — that path never phones home today.
15
+
16
+ ## Default posture: opt-in (not like Orb)
17
+
18
+ Two telemetry/export surfaces exist in Gittensory, and they intentionally use **opposite defaults**:
19
+
20
+ | Surface | Default | Operator action | Precedent |
21
+ |---------|---------|-------------------|-----------|
22
+ | **Orb fleet calibration** (`src/selfhost/orb-collector.ts`) | **ON** once a GitHub App is configured | Opt out only via `ORB_AIR_GAP=true` (air-gapped / send-nothing) | Review-stack self-host contract — export is always on unless air-gapped |
23
+ | **Hosted discovery plane** (this guide) | **OFF** | Opt **in** explicitly before any hosted index query or plane telemetry | Hybrid, self-host-first miner deployment — participation in a shared hosted plane is never assumed |
24
+
25
+ Do **not** copy Orb's wording for this plane. Orb's header comment is explicit: "Export is ALWAYS ON… there is no opt-out flag" aside from `ORB_AIR_GAP`. The discovery plane is the opposite: **no hosted traffic unless the operator turns it on.**
26
+
27
+ ## What the plane is for
28
+
29
+ When enabled, a miner may query a **shared, metadata-only** discovery index instead of every fleet member independently fanning out GitHub search/listing calls against the same repos — mitigating cross-fleet rate-limit pressure (the same class of incident addressed for the review stack in [#1936](https://github.com/JSONbored/gittensory/issues/1936)).
30
+
31
+ The plane:
32
+
33
+ - Serves **public GitHub metadata only** (issue titles, labels, counts, timestamps, URLs — the same class of fields Phase 1 already uses locally).
34
+ - May coordinate **soft claims** across the fleet (server-side dedup is [#4250](https://github.com/JSONbored/gittensory/issues/4250); client request shape is [#4302](https://github.com/JSONbored/gittensory/issues/4302)).
35
+ - Never receives source trees, diffs, tokens, or write credentials.
36
+
37
+ Local discovery (`opportunity-fanout` + `opportunity-ranker`) continues to work with **zero** hosted configuration.
38
+
39
+ ## Opt-in mechanism (names TBD — update when #4300/#4301 ship)
40
+
41
+ Until the contract issue lands, treat these as **documentation placeholders** for the shape operators should expect:
42
+
43
+ | Variable (provisional) | Default | Purpose |
44
+ |------------------------|---------|---------|
45
+ | `GITTENSORY_MINER_DISCOVERY_PLANE` | unset / `false` | Master opt-in. When not truthy (`1`, `true`, `yes`, `on`), the miner must not call the hosted index or emit discovery-plane telemetry. |
46
+ | `GITTENSORY_MINER_DISCOVERY_INDEX_URL` | unset | Hosted index base URL. Required when the plane is enabled; ignored when opt-in is off. |
47
+ | `GITTENSORY_MINER_DISCOVERY_TELEMETRY` | unset / `false` | Separate opt-in for anonymized operational telemetry ([#4301](https://github.com/JSONbored/gittensory/issues/4301)). Plane queries can stay on while telemetry stays off. |
48
+
49
+ **Truthy-string convention** (when implemented): `/^(1|true|yes|on)$/i`, matching other `GITTENSORY_*` flags in this repo.
50
+
51
+ **Operator checklist (enabled plane):**
52
+
53
+ 1. Set `GITTENSORY_MINER_DISCOVERY_PLANE=true` (exact name may change — see [#4300](https://github.com/JSONbored/gittensory/issues/4300)).
54
+ 2. Set `GITTENSORY_MINER_DISCOVERY_INDEX_URL` to the operator-trusted index endpoint ([#4250](https://github.com/JSONbored/gittensory/issues/4250)).
55
+ 3. Optionally set `GITTENSORY_MINER_DISCOVERY_TELEMETRY=true` if you want anonymized operational events for the hosted service — not required for index queries.
56
+ 4. Keep `GITHUB_TOKEN` (or equivalent) on the instance only; never configure tokens intended for the hosted plane to receive.
57
+
58
+ With opt-in off (default), behavior is byte-identical to today: local SQLite ledgers, local fan-out, no hosted calls.
59
+
60
+ ## Contrast with local soft-claims today
61
+
62
+ `packages/gittensory-miner/lib/claim-ledger.js` records soft claims **locally only** ("never uploads, syncs, or phones home"). Fleet-wide coordination before work starts is what [#4302](https://github.com/JSONbored/gittensory/issues/4302) + the hosted index ([#4250](https://github.com/JSONbored/gittensory/issues/4250)) add **on top of** that ledger — only after explicit opt-in.
63
+
64
+ After-the-fact duplicate adjudication (`isDuplicateClusterWinnerByClaim` in `@loopover/engine`) remains separate; it resolves collisions by observing what publicly landed first, not by preventing overlap up front.
65
+
66
+ ## Invariants
67
+
68
+ Mirrors [`DEPLOYMENT.md`](../DEPLOYMENT.md) tone — concrete guarantees for operators:
69
+
70
+ - **Default OFF** — no hosted discovery-index traffic and no discovery-plane telemetry unless the operator opts in.
71
+ - **Metadata-only index queries** — responses are issue/listing metadata compatible with local `normalizeCandidate` shape; no source upload, no clone, no repo archive.
72
+ - **Read-only client posture** — the miner uses GET/list/search semantics toward GitHub directly (Phase 1) and toward the hosted index when enabled; the plane does not grant the miner new GitHub write capability.
73
+ - **Credentials stay local** — GitHub tokens, PATs, and actor-capable secrets are injected at runtime on the operator's machine or secret store; they are **never** included in index or telemetry payloads.
74
+ - **No compensation signals in the plane** — raw reward values, wallet addresses, hotkeys, trust scores, or private rankings never cross this boundary (same public boundary as [`cross-repo-discovery-phase1.md`](cross-repo-discovery-phase1.md) Acceptance).
75
+ - **Telemetry is a second opt-in** — even with the plane enabled, anonymized telemetry ([#4301](https://github.com/JSONbored/gittensory/issues/4301)) remains separately gated.
76
+ - **Anonymized identifiers only** — when telemetry ships, repo/issue correlation uses HMAC-hashed identifiers keyed by a **per-instance dedicated secret** the collector never holds (same posture as `getOrCreateAnonSecret` / `hmacField` in `src/selfhost/orb-collector.ts` — key separation from GitHub App / webhook secrets).
77
+ - **Low-cardinality reason buckets** — any free-text-adjacent telemetry fields use bucketed categories (Orb's `bucketReasonCode` pattern), not raw maintainer or model prose.
78
+ - **Core miner still works offline** — claims, plans, queues, and local ledgers do not require the hosted plane; `gittensory-miner doctor` / `status` remain no-network commands.
79
+
80
+ ### Never included (client → hosted plane)
81
+
82
+ Inventory style matches `src/selfhost/orb-collector.ts:15-17` ("No diffs, no code…") adapted for discovery-plane domain ([#4301](https://github.com/JSONbored/gittensory/issues/4301)):
83
+
84
+ - Source file contents, patches, or diffs
85
+ - Full issue/PR bodies or review comments
86
+ - GitHub tokens, PATs, App private keys, or any actor-capable credential
87
+ - Commit SHAs, branch names tied to unpublished work, or CI log excerpts
88
+ - Operator login identities, emails, or hostnames usable as PII
89
+ - Raw gate reasons, model transcripts, or free-text maintainer notes
90
+ - Reward amounts, wallet addresses, hotkeys, trust scores, or private rankings
91
+
92
+ ### Never retained by the hosted service (server-side — [#4250](https://github.com/JSONbored/gittensory/issues/4250))
93
+
94
+ The server operating doc (maintainer-only) will restate the same boundary: **never holds source or actor-capable credentials.** This client guide does not define server retention policy; see #4250 deliverables.
95
+
96
+ ## Related docs
97
+
98
+ - [`cross-repo-discovery-phase1.md`](cross-repo-discovery-phase1.md) — local, metadata-only Phase 1 discovery (no hosted plane).
99
+ - [`operations-runbook.md`](operations-runbook.md) — SQLite concurrency, corruption recovery, multi-process collisions, post-upgrade migration ([#4875](https://github.com/JSONbored/gittensory/issues/4875)).
100
+ - [`miner-goal-spec.md`](miner-goal-spec.md) — per-repo `.gittensory-miner.yml` targeting policy.
101
+ - [`../DEPLOYMENT.md`](../DEPLOYMENT.md) — laptop vs fleet deployment and core miner invariants.
@@ -0,0 +1,30 @@
1
+ # gittensory-miner environment variable reference
2
+
3
+ Generated by `npm run miner:env-reference`. Do not edit manually.
4
+
5
+ | Name | First reference | Default |
6
+ | --- | --- | --- |
7
+ | `GITTENSORY_MINER_AMS_POLICY_PATH` | `lib/ams-policy.js` | (none) |
8
+ | `GITTENSORY_MINER_ATTEMPT_LOG_DB` | `lib/attempt-log.js` | (none) |
9
+ | `GITTENSORY_MINER_CLAIM_LEDGER_DB` | `lib/claim-ledger.js` | (none) |
10
+ | `GITTENSORY_MINER_CONFIG_DIR` | `lib/deny-hook-synthesis.js` | `""` |
11
+ | `GITTENSORY_MINER_DENY_HOOK_SYNTHESIS_DB` | `lib/deny-hook-synthesis.js` | `""` |
12
+ | `GITTENSORY_MINER_EVENT_LEDGER_DB` | `lib/event-ledger.js` | (none) |
13
+ | `GITTENSORY_MINER_GOVERNOR_LEDGER_DB` | `lib/governor-ledger.js` | `""` |
14
+ | `GITTENSORY_MINER_GOVERNOR_STATE_DB` | `lib/governor-state.js` | (none) |
15
+ | `GITTENSORY_MINER_KILL_SWITCH` | `lib/config-precedence.js` | `""` |
16
+ | `GITTENSORY_MINER_LOG_LEVEL` | `lib/logger.js` | `""` |
17
+ | `GITTENSORY_MINER_NO_UPDATE_CHECK` | `lib/update-check.js` | `""` |
18
+ | `GITTENSORY_MINER_ORB_EXPORT_DB` | `lib/orb-export.js` | `""` |
19
+ | `GITTENSORY_MINER_PLAN_STORE_DB` | `lib/plan-store.js` | `""` |
20
+ | `GITTENSORY_MINER_POLICY_DOC_CACHE_DB` | `lib/policy-doc-cache.js` | (none) |
21
+ | `GITTENSORY_MINER_POLICY_VERDICT_CACHE_DB` | `lib/policy-verdict-cache.js` | (none) |
22
+ | `GITTENSORY_MINER_PORTFOLIO_QUEUE_DB` | `lib/portfolio-queue.js` | (none) |
23
+ | `GITTENSORY_MINER_PREDICTION_LEDGER_DB` | `lib/prediction-ledger.js` | `""` |
24
+ | `GITTENSORY_MINER_REPLAY_SNAPSHOT_DB` | `lib/replay-snapshot.js` | (none) |
25
+ | `GITTENSORY_MINER_REPO_CLONE_DIR` | `lib/repo-clone.js` | `""` |
26
+ | `GITTENSORY_MINER_RUN_STATE_DB` | `lib/run-state.js` | (none) |
27
+ | `GITTENSORY_MINER_VERSION` | `lib/version.js` | `""` |
28
+ | `GITTENSORY_MINER_WORKTREE_ALLOCATOR_DB` | `lib/worktree-allocator.js` | `""` |
29
+ | `GITTENSORY_MINER_WORKTREE_DIR` | `lib/worktree-allocator.js` | `""` |
30
+ | `MINER_CODING_AGENT_PROVIDER` | `lib/laptop-init.js` | (none) |
@@ -0,0 +1,50 @@
1
+ # Fleet run-manifest
2
+
3
+ The **fleet run-manifest** is the top-level config a *fleet operator* authors to run the miner across many repos
4
+ at once: it declares which repos are in scope for a fleet run and how a finite worktree/concurrency budget is
5
+ split between them. It is parsed by `parseFleetRunManifestContent` / `parseFleetRunManifest` in
6
+ `@loopover/engine` (`packages/gittensory-engine/src/fleet-run-manifest.ts`).
7
+
8
+ It is **not** the same file as `.gittensory-miner.yml` (see [`miner-goal-spec.md`](./miner-goal-spec.md)) — the
9
+ naming is easy to conflate. Same tolerant-parser convention (every field optional, unknown keys ignored, a
10
+ malformed field degrades to a documented default with a warning rather than throwing), but the opposite author
11
+ and the opposite direction of intent:
12
+
13
+ | | `.gittensory-miner.yml` (goal spec) | fleet run-manifest |
14
+ |---|---|---|
15
+ | **Author** | a target repo's maintainer | the miner (fleet) operator |
16
+ | **Lives in** | the target repo | the operator's fleet-run config |
17
+ | **Direction** | how this one repo wants to be approached | which repos to work across, and how to split the budget |
18
+ | **Scope** | one repo | many repos in one run |
19
+ | **Key fields** | `minerEnabled`, `wantedPaths`, `blockedPaths`, `preferredLabels`, `blockedLabels`, `maxConcurrentClaims`, `issueDiscoveryPolicy` | `repos` (each `owner/repo` + `maxConcurrentWorktrees`), `totalConcurrentWorktrees` |
20
+
21
+ ## Schema
22
+
23
+ Every field is optional; unknown keys are ignored; a malformed field falls back to a documented default with a
24
+ warning rather than hard-failing the run.
25
+
26
+ - **`repos`** — a list of target repos. Each entry is either a bare `"owner/repo"` string (uses the default
27
+ per-repo budget) or a `{ repoFullName, maxConcurrentWorktrees }` mapping. Invalid or duplicate entries are
28
+ skipped with a warning. `repoFullName` is a canonical `owner/repo`, compatible with `opportunity-fanout.js`'s
29
+ target list. Default: `[]`.
30
+ - **`repos[].maxConcurrentWorktrees`** — max concurrent worktrees (in-flight attempts) for that repo. A positive
31
+ integer (floored; sub-1 falls back to the default). Default: `1`.
32
+ - **`totalConcurrentWorktrees`** — total concurrent worktrees across the whole fleet, regardless of per-repo
33
+ budgets. A positive integer. Default: `1`.
34
+
35
+ ## Wiring
36
+
37
+ This module produces only the parsed, typed manifest. Driving the fleet concurrency allocator from it is the
38
+ allocator's concern (sibling `feat(miner-concurrency): add git-worktree-per-attempt allocator` issue), and the
39
+ cross-repo `portfolio-queue.js` backlog reads the same repo list — both *consume* this manifest; neither wiring
40
+ lives here.
41
+
42
+ ## Example (`fleet-run.yml`)
43
+
44
+ ```yaml
45
+ totalConcurrentWorktrees: 4
46
+ repos:
47
+ - owner/repo-a
48
+ - repoFullName: owner/repo-b
49
+ maxConcurrentWorktrees: 2
50
+ ```
@@ -0,0 +1,76 @@
1
+ # MinerGoalSpec (`.gittensory-miner.yml`)
2
+
3
+ Per-repo configuration telling an autonomous Gittensory miner what to look for and how to behave when targeting a repo. Parsed by `@loopover/engine` (`parseMinerGoalSpec` / `parseMinerGoalSpecContent`); this document is the field reference. Machine-readable shape: [`../schema/miner-goal-spec.schema.json`](../schema/miner-goal-spec.schema.json). Copy [`.gittensory-miner.yml.example`](../../../.gittensory-miner.yml.example) to `.gittensory-miner.yml` and edit.
4
+
5
+ Discovery order (first match wins):
6
+
7
+ - `.gittensory-miner.yml`
8
+ - `.github/gittensory-miner.yml`
9
+ - `.gittensory-miner.json`
10
+ - `.github/gittensory-miner.json`
11
+
12
+ Every field is optional. Unknown keys are ignored; a malformed field falls back to its documented default with a warning — a broken file never hard-fails the miner.
13
+
14
+ ## Relationship to `.gittensory.yml`
15
+
16
+ | File | Actor | Purpose |
17
+ |------|-------|---------|
18
+ | `.loopover.yml` (or legacy `.gittensory.yml`, dual-read indefinitely — #4773) | Review stack | How a maintainer's repo **reviews** incoming PRs (focus manifest, gate, scoring knobs). |
19
+ | `.gittensory-miner.yml` | Miner runtime | How a miner **searches for and prioritizes** work in a target repo. Unrelated naming concern — not affected by the review-config rebrand above. |
20
+
21
+ They are read by different components and do not conflict. A miner should still treat a target repo's public `.loopover.yml`/`.gittensory.yml` `wantedPaths` / `blockedPaths` as a hard floor when both files exist.
22
+
23
+ ## Fields
24
+
25
+ ### `minerEnabled` (boolean, default: `true`)
26
+
27
+ Explicit opt-out: a public repo with no file remains minable. Set `false` to halt all miner targeting.
28
+
29
+ ### `wantedPaths` (string list, default: `[]`)
30
+
31
+ Work areas the maintainer wants a miner to focus on. Glob list. Empty means no preference.
32
+
33
+ ### `blockedPaths` (string list, default: `[]`)
34
+
35
+ Paths off-limits to a miner; candidates touching one should be skipped. Glob list. Mirrors `.gittensory.yml` `blockedPaths` semantics.
36
+
37
+ ### `preferredLabels` (string list, default: `[]`)
38
+
39
+ Issue labels a miner should favor. Empty means no preference.
40
+
41
+ ### `blockedLabels` (string list, default: `[]`)
42
+
43
+ Issue labels a miner must skip.
44
+
45
+ ### `maxConcurrentClaims` (integer `>= 1`, default: `1`)
46
+
47
+ Maximum issues one miner may hold claimed on this repo at once.
48
+
49
+ ### `issueDiscoveryPolicy` (`encouraged` | `neutral` | `discouraged`, default: `neutral`)
50
+
51
+ How strongly this repo encourages a miner to open discovery issues.
52
+
53
+ ### `feasibilityGate` (object, default: `{ enabled: true, suppressedReasons: [] }`)
54
+
55
+ Per-repo tuning for the feasibility gate (`buildFeasibilityVerdict`) a miner consults before starting work. This is config-parsing surface only — a caller wiring the gate into a decision flow is responsible for reading and applying this policy.
56
+
57
+ - `enabled` (boolean, default: `true`) — whether the feasibility gate is consulted at all before a miner starts work.
58
+ - `suppressedReasons` (string list, default: `[]`) — specific avoid/raise reason codes (e.g. `duplicate_cluster_high`) this repo wants ignored.
59
+
60
+ ### `selfPlagiarism` (object, default: `{ similarityThreshold: 0.85 }`)
61
+
62
+ Per-repo tuning for the Governor self-plagiarism throttle consulted before `open_pr` (#2345). Compares a prospective PR's diff fingerprint against the miner's own recent submission history.
63
+
64
+ - `similarityThreshold` (number in `[0, 1]`, default: `0.85`) — Jaccard similarity at/above which two fingerprints read as near-duplicates across repos.
65
+
66
+ ### `killSwitch` (object, default: `{ paused: false }`)
67
+
68
+ Per-repo kill-switch consulted by the Governor chokepoint before every write action (#2341). Distinct from `minerEnabled`: `minerEnabled` is a discovery-time opt-out (a miner never even considers the repo), while `killSwitch.paused` is a runtime halt of an already-in-flight queue — un-pausing resumes exactly where the queue left off. A separate, operator-controlled GLOBAL kill-switch (env var `GITTENSORY_MINER_KILL_SWITCH`) halts every repo at once and always wins over this per-repo flag.
69
+
70
+ - `paused` (boolean, default: `false`) — halts all miner WRITE actions for this repo without deregistering it from targeting/discovery.
71
+
72
+ ### `execution` (object, default: `{ liveModeOptIn: null }`)
73
+
74
+ Per-repo dry-run/live execution opt-in consulted by the Governor chokepoint (#2342). A freshly-configured miner always defaults to dry-run (observe/log only, never execute a write) — this field is the only per-repo path to live mode, and it alone is not sufficient: the miner's own operator must also separately opt in globally (env var `GITTENSORY_MINER_LIVE_MODE=live`) before writes actually execute. A repo that wants to guarantee it never receives live automated writes, regardless of any operator's global setting, should use `killSwitch.paused: true` instead — the kill-switch always takes precedence over any live-mode opt-in.
75
+
76
+ - `liveModeOptIn` (string or `null`, default: `null`) — must equal EXACTLY the literal `"live"` to opt in. Any other value (a typo, `"yes"`, `"on"`, or a boolean `true` from a malformed file) is treated as not opted in — deliberately not a boolean flag, so a fat-fingered config can never accidentally enable live writes.
@@ -0,0 +1,105 @@
1
+ # Miner self-improve — calibration, value-weighting, and the anti-farming boundary
2
+
3
+ Phase 7's **contributor-safe** half — the drift detector, the calibration dashboard, the prediction ledger, the
4
+ metrics exporter, the extension panel, and the confusion-matrix regression tests — is deliberately scoped to
5
+ **read-only measurement and detection**. The half that actually *acts* on calibration data (bumps autonomy levels,
6
+ tightens gate thresholds, adjusts per-contributor trust) is **maintainer-only** and lives behind a separate
7
+ milestone.
8
+
9
+ This note is for a contributor building against the contributor-safe tools. The single most important thing to
10
+ understand is **where the line is and why** — because the natural instinct ("just wire the dashboard's accuracy
11
+ number into an autonomy-level bump") is precisely the maintainer-only side of the phase, and a contributor-safe tool
12
+ that could do it would defeat the safety it exists to provide.
13
+
14
+ ## What calibration measures
15
+
16
+ The engine contract is `Phase7CalibrationLoopResult`
17
+ ([`packages/gittensory-engine/src/phase7-calibration-loop.ts`](../../gittensory-engine/src/phase7-calibration-loop.ts)):
18
+ it combines the **historical-replay** composite score with the passive **`pr_outcome`** signal and tracks accuracy
19
+ against the documented **`DOCUMENTED_CALIBRATION_BASELINE = 0.62`** (the 62% self-review baseline from the Phase 7
20
+ roadmap). The engine owns the deterministic combine / freshness / threshold / hold-reason logic; the miner runtime
21
+ owns scheduling replay runs and persisting ledger rows.
22
+
23
+ The ground truth underneath is `computeGateEval`'s confusion matrix (`src/review/parity.ts`, `GateEvalRow`): per
24
+ project, how many predictions were `wouldMerge` vs `wouldClose`, and how each resolved against the human decision —
25
+ `mergeConfirmed` (predicted merge, human merged) vs **`mergeFalse` (predicted merge, human *closed* — the dangerous
26
+ error)**, and symmetrically for close. `mergePrecision` / `closePrecision` are the headline accuracy numbers a
27
+ dashboard renders.
28
+
29
+ ## The runner: wiring the replay scorer into the combine (#4248)
30
+
31
+ `computePhase7CalibrationLoop` is a **pure combine contract** — by design it cannot schedule replay runs or read
32
+ ledgers (the miner depends on the engine, not the reverse), so it waits for an external caller to feed it a real
33
+ `historical_replay` composite. #3014 (PR #3225) shipped only that engine side; a 2026-07-08 audit found the closed
34
+ issue claimed the two halves were "wired" when in fact **no miner-side runner ever called the replay scorer
35
+ (`computeObjectiveAnchor`, #3012) and passed its result into the combine** — two finished pieces, never connected.
36
+
37
+ [`lib/calibration-run.js`](../lib/calibration-run.js) (#4248) is that missing runner.
38
+ `runHistoricalReplayCalibrationCycle` scores a completed replay run with the deterministic objective-anchor scorer,
39
+ reduces the per-task scores to one composite `[0, 1]`, folds it into the `HistoricalReplayCalibrationInput` shape the
40
+ engine expects, calls `computePhase7CalibrationLoop` with that **plus the existing `pr_outcome` signal**, and
41
+ persists the combined snapshot as a `calibration_snapshot` event on the local append-only event ledger (the same
42
+ typed-event-over-`event-ledger.js` pattern as `pr-outcome.js`). The persisted metric is queryable with
43
+ `gittensory-miner ledger list --type calibration_snapshot` and via `readCalibrationSnapshots` /
44
+ `latestCalibrationSnapshot`.
45
+
46
+ Consistent with the boundary below, the runner is **read/measure-only**: it produces and persists the tracked
47
+ metric but never acts on it (no autonomy bump, no threshold tune). Acting on the combined accuracy — the
48
+ calibration-gated circuit-breaker — remains maintainer-only (#2352).
49
+
50
+ ## Value-weighting: durable correctness, not volume
51
+
52
+ Raw merge/close precision is not the real objective, and a contributor reading a dashboard number should understand
53
+ why. `GateEvalRow` also carries `weightedMergeConfirmed` / `weightedCloseConfirmed`: a prediction's credit is
54
+ **discounted by `REVERSAL_DISCOUNT_WEIGHT` (currently `0` — full discount)** when its outcome was later reversed (a
55
+ merge a human undid, or a bot-closed PR a contributor reopened). The denominator (`wouldMerge`) is unchanged — only
56
+ the *credit* for a call that didn't hold up is removed. So the weighted precision measures **durable** correctness:
57
+ a prediction that "won" on the day but was reverted a week later earns nothing. This is maintainer-owned prior work
58
+ (#2348) that the contributor-safe tools *display*; they never recompute or re-weight it.
59
+
60
+ ## The maintainer-only line, and why it's drawn there
61
+
62
+ Three maintainer-only concepts sit just past the boundary. A contributor-safe tool must never read or write any of
63
+ them:
64
+
65
+ - **Personalized tuning (#2349).** Per-actor confidence adjustment based on calibration history. A contributor-facing
66
+ surface that could *see or influence its own personalized-trust score* would defeat the entire point of it — so the
67
+ contributor-safe tools are, by construction, blind to it.
68
+ - **Fleet anti-farming (#2350).** The whole phase is designed around gaming vectors from day one: inflating
69
+ merge-precision by only ever opening trivially-safe PRs, or farming duplicate-issue-claim elections. **Every
70
+ contributor-safe tool in this batch is read-only / local-only *because* a write-capable version of any of them
71
+ would be a farming vector** — the restriction is the design, not an arbitrary limitation. (The anonymization posture
72
+ for any telemetry that does leave an instance mirrors `src/selfhost/orb-collector.ts`'s HMAC-with-local-secret
73
+ approach.)
74
+ - **Calibration-gated circuit-breaking (#2352).** `src/review/auto-tune.ts` already holds a self-tightening safety
75
+ breaker: when merge precision drops below a floor over a real sample, it **disables its own auto-merge (hold-only)
76
+ and alerts a human — it only ever makes itself MORE cautious, never loosens**, and a human clears it once fixed.
77
+ That breaker is the eventual consumer of miner-sourced calibration signal (via `computeGateEval`'s `source`
78
+ scoping), but the *enforcement* is maintainer-only. The contributor-safe tools only ever **produce read-only signal**
79
+ for that maintainer-owned system to act on.
80
+
81
+ ## What this phase does NOT do
82
+
83
+ From any contributor-safe tool in this batch:
84
+
85
+ - **No autonomy-level changes** — measuring accuracy never bumps an autonomy level (that gate is fail-closed and
86
+ maintainer-owned).
87
+ - **No gate-threshold changes** — the dashboard/exporter/ledger surface numbers; they never tune the gate.
88
+ - **No writes to maintainer-owned calibration or circuit-breaker state** — no touching personalized-trust scores, the
89
+ auto-tune hold-only flag, or any autonomy configuration.
90
+ - **No de-anonymizing or write-capable telemetry** — local-only or HMAC-anonymized, read-only.
91
+
92
+ ## The neighborhood
93
+
94
+ - Contributor-safe (this batch): drift detector, calibration dashboard + extension panel, prediction ledger, metrics
95
+ exporter, confusion-matrix regression tests — all read-only measurement.
96
+ - Maintainer-only (cited for context, **not** to be reopened or reimplemented here): #2348 (value-weighted
97
+ calibration, merged), #2349 (personalized tuning), #2350 (fleet anti-farming), #2352 (wiring calibration accuracy
98
+ into the live auto-tune breaker).
99
+
100
+ ## References
101
+
102
+ - [`packages/gittensory-engine/src/phase7-calibration-loop.ts`](../../gittensory-engine/src/phase7-calibration-loop.ts) — the calibration-loop contract + `DOCUMENTED_CALIBRATION_BASELINE`.
103
+ - `src/review/parity.ts` — `GateEvalRow` (the confusion matrix) and `REVERSAL_DISCOUNT_WEIGHT` (value-weighting).
104
+ - `src/review/auto-tune.ts` — the existing hold-only accuracy circuit-breaker (#2352's eventual consumer).
105
+ - `src/selfhost/orb-collector.ts` — the anonymized HMAC-with-local-secret telemetry pattern.