Skip to content

Commit bbdede8

Browse files
authored
docs(ui): replace 49 hand-rolled docs.*.tsx thin routes with one dynamic docs.$slug.tsx route (#8182)
Every docs page carried an identical hand-rolled TanStack route file: a loader calling getDocPage, head/og meta duplicating the .mdx frontmatter's own title/description, and the same DocsPage + Suspense/LoadingState body. Adding a page meant touching 5+ files and regenerating the route tree; two tests pinned the route-file count and had to be bumped on every addition. - One dynamic docs.$slug.tsx route replaces all 49 per-page files. title/ description come from frontmatter (already did); eyebrow (the one real per-page datum, previously a hand-copied JSX prop) moves into frontmatter too, via a new required field in source.config.ts's page schema. - docs.index.tsx (a real static page) and docs.fumadocs-spike-api-reference.tsx (a standalone Scalar widget, not DocsPage/MDX at all) stay as their own routes -- audited, neither is boilerplate this replaces. - Every literal `to="/docs/<slug>"` Link across the app (nav sidebar, footer, header dropdown, home page, install flow, agents/miners/maintainers pages) now uses the typed `to="/docs/$slug" params={{ slug }}` form instead -- done properly with real params, not a type-erasing cast, including fixing two pre-existing `as "/docs"` casts in docs-nav.tsx/docs.index.tsx found along the way. - docs.miner-coding-agent.tsx's three exported data arrays (asserted against by a test, unrelated to routing) moved to src/lib/miner-coding-agent-docs-data.ts; their test moved alongside them. - The two route-file-count-pinning tests (docs-source-server-isolation, docs-routes-loading-state) now assert the single dynamic route's shape instead of a per-page count. - Three test-only `Link` mocks (docs.ams-observability-callout, install, install.permissions) updated to interpolate $-params the same way the real router resolves them, so their href assertions stay faithful. Verified in the browser: real docs pages render correctly (title/eyebrow/ description/MDX content), sidebar nav renders + highlights the active page, client-side navigation works, a nonexistent slug renders a real 404, the docs index and the untouched Scalar API route both still work. Closes #8151.
1 parent b95fbc1 commit bbdede8

123 files changed

Lines changed: 584 additions & 3785 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/loopover-ui/content/docs/ai-summaries.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: Optional AI summaries
33
description: A short natural-language summary over the deterministic response. Off by default. Never the source of truth.
4+
eyebrow: Roadmap · exploring
45
---
56

67
## The rule

apps/loopover-ui/content/docs/ams-config-precedence.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: Miner config precedence
33
description: How AMS layers configuration across per-repo goal spec, operator env, CLI flags, and operator policy files -- the order each concern actually implements today.
4+
eyebrow: Maintainers
45
---
56

67
AMS does **not** have a single `config.js` resolver. Configuration is layered by concern across
@@ -87,8 +88,8 @@ First existing file wins (engine `MINER_GOAL_SPEC_FILENAMES`):
8788

8889
<Callout variant="warn">
8990
This is an **AND** requirement, not "last writer wins". Either side missing or malformed →
90-
dry-run. There is also **no CLI flag wired to governor live mode** today —
91-
`attempt --live` / `loop --live` affect coding-agent spawn mode only (below).
91+
dry-run. There is also **no CLI flag wired to governor live mode** today — `attempt --live` /
92+
`loop --live` affect coding-agent spawn mode only (below).
9293
</Callout>
9394

9495
### Coding-agent execution mode (spawn the driver?)
@@ -151,9 +152,9 @@ cached, so a later call retries rather than staying stuck).
151152
requiring auth fail the same way they did before this feature existed).
152153

153154
<Callout variant="note">
154-
This is a distinct concern from "Discover forge credential env var name" above, which resolves
155-
the *name* of an env var to read, not the token *value* itself; `discover --token-env` is
156-
unaffected by this section.
155+
This is a distinct concern from "Discover forge credential env var name" above, which resolves the
156+
*name* of an env var to read, not the token *value* itself; `discover --token-env` is unaffected
157+
by this section.
157158
</Callout>
158159

159160
### Local SQLite store paths

apps/loopover-ui/content/docs/ams-deployment.mdx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: AMS deployment guide
33
description: Deploy @loopover/miner in laptop mode (single machine, zero Docker) or fleet mode (containerized workers) -- both 100% client-side, credentials never baked into images.
4+
eyebrow: Maintainers
45
---
56

67
Two form factors for running `@loopover/miner`: **laptop mode** (single machine, zero Docker) and
@@ -57,9 +58,9 @@ loopover-miner init --interactive # optional: guided prompt for GITHUB_TOKEN
5758
`init --interactive` offers "Authorize with GitHub" (device flow — visit a URL, enter a short
5859
code, no token to copy or paste) as its first option once the miner's GitHub App OAuth client is
5960
configured; the original pasted-PAT prompt stays available as option 2, and is what the wizard
60-
falls back to automatically on any device-flow failure. Unconfigured, the wizard is
61-
byte-identical to the pasted-token-only prompt. Either way, the resulting `GITHUB_TOKEN` acts as
62-
your own GitHub account — there is no separate bot identity.
61+
falls back to automatically on any device-flow failure. Unconfigured, the wizard is byte-identical
62+
to the pasted-token-only prompt. Either way, the resulting `GITHUB_TOKEN` acts as your own GitHub
63+
account — there is no separate bot identity.
6364
</Callout>
6465

6566
### Expected layout after first use
@@ -150,9 +151,9 @@ The image entrypoint is `loopover-miner`; pass subcommands after the image name
150151
config` and any full-env dump of the running container. For Docker Swarm/Kubernetes-managed
151152
secrets (mounted as a file, e.g. at `/run/secrets/github_token`), set `GITHUB_TOKEN_FILE` to that
152153
mount path instead — the miner reads and trims the file's contents at startup and uses it exactly
153-
as if `GITHUB_TOKEN` had been set directly. If both are set, the plain `GITHUB_TOKEN` value
154-
always wins. A missing or unreadable `GITHUB_TOKEN_FILE` fails the container fast with a clear
155-
error naming the file path, rather than silently proceeding with no credential.
154+
as if `GITHUB_TOKEN` had been set directly. If both are set, the plain `GITHUB_TOKEN` value always
155+
wins. A missing or unreadable `GITHUB_TOKEN_FILE` fails the container fast with a clear error
156+
naming the file path, rather than silently proceeding with no credential.
156157
</Callout>
157158

158159
<CodeBlock
@@ -187,10 +188,10 @@ docker compose -f docker-compose.miner.yml up -d --build`}
187188
<Callout variant="warn" title="Scaling to N parallel workers">
188189
`docker compose -f docker-compose.miner.yml up -d --scale miner=N` gives every replica the
189190
**same** `miner-data` volume — and the miner's SQLite ledgers are **not** safe for concurrent
190-
access, so N replicas on one volume will contend/corrupt. To run N **isolated** workers, give
191-
each its own state: run N separate compose projects (`docker compose -p miner-1 …`, `-p miner-2
192-
``-p` namespaces the volume) or point each at a distinct `LOOPOVER_MINER_CONFIG_DIR` on its
193-
own mount. For built-in isolated horizontal scaling, use the Kubernetes StatefulSet in
191+
access, so N replicas on one volume will contend/corrupt. To run N **isolated** workers, give each
192+
its own state: run N separate compose projects (`docker compose -p miner-1 …`, `-p miner-2`
193+
`-p` namespaces the volume) or point each at a distinct `LOOPOVER_MINER_CONFIG_DIR` on its own
194+
mount. For built-in isolated horizontal scaling, use the Kubernetes StatefulSet in
194195
[`k8s/`](https://github.com/JSONbored/loopover/tree/main/k8s) (per-pod volumes).
195196
</Callout>
196197

apps/loopover-ui/content/docs/ams-discovery-plane.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: Hosted discovery plane — operator guide (opt-in)
33
description: How a loopover-miner instance opts into the optional hosted discovery-index plane, what it may send, and what never leaves the operator's machine.
4+
eyebrow: Maintainers
45
---
56

67
Operator-facing guide for the **optional** hosted discovery-index plane. This is the client/miner

apps/loopover-ui/content/docs/ams-env-reference.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: AMS environment variable reference
33
description: Every LOOPOVER_MINER_* / MINER_* env var the miner reads, where it's first referenced in code, and its default -- generated from source, never hand-copied.
4+
eyebrow: Maintainers
45
---
56

67
import { AMS_ENV_REFERENCE_MARKDOWN } from "@/lib/ams-env-reference";

apps/loopover-ui/content/docs/ams-fleet-manifest.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
22
title: Fleet run-manifest
33
description: The top-level config a fleet operator authors to run the miner across many repos at once -- which repos are in scope and how the worktree/concurrency budget is split.
4+
eyebrow: Maintainers
45
---
56

6-
The **fleet run-manifest** is the top-level config a *fleet operator* authors to run the miner
7+
The **fleet run-manifest** is the top-level config a _fleet operator_ authors to run the miner
78
across many repos at once: it declares which repos are in scope for a fleet run and how a finite
89
worktree/concurrency budget is split between them. It is parsed by
910
`parseFleetRunManifestContent` / `parseFleetRunManifest` in `@loopover/engine`
@@ -50,7 +51,7 @@ opposite direction of intent:
5051

5152
This module produces only the parsed, typed manifest. Driving the fleet concurrency allocator
5253
from it is the allocator's own concern, and the cross-repo `portfolio-queue.js` backlog reads the
53-
same repo list — both *consume* this manifest; neither wiring lives here.
54+
same repo list — both _consume_ this manifest; neither wiring lives here.
5455

5556
## Example (`fleet-run.yml`)
5657

apps/loopover-ui/content/docs/ams-goal-spec.mdx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: "MinerGoalSpec (.loopover-miner.yml)"
33
description: Per-repo configuration telling an autonomous LoopOver miner what to look for and how to behave when targeting a repo -- the full field reference.
4+
eyebrow: Maintainers
45
---
56

67
Per-repo configuration telling an autonomous LoopOver miner what to look for and how to behave
@@ -17,8 +18,8 @@ Discovery order (first match wins):
1718
- `.github/loopover-miner.json`
1819

1920
<Callout variant="note">
20-
Every field is optional. Unknown keys are ignored; a malformed field falls back to its
21-
documented default with a warning — a broken file never hard-fails the miner.
21+
Every field is optional. Unknown keys are ignored; a malformed field falls back to its documented
22+
default with a warning — a broken file never hard-fails the miner.
2223
</Callout>
2324

2425
## Relationship to `.loopover.yml`
@@ -113,13 +114,12 @@ Per-repo kill-switch consulted by the Governor chokepoint before every write act
113114
Per-repo dry-run/live execution opt-in consulted by the Governor chokepoint.
114115

115116
<Callout variant="warn">
116-
A freshly-configured miner always defaults to dry-run (observe/log only, never execute a
117-
write) — this field is the only per-repo path to live mode, and it alone is not sufficient: the
118-
miner's own operator must also separately opt in globally (env var
119-
`LOOPOVER_MINER_LIVE_MODE=live`) before writes actually execute. A repo that wants to guarantee
120-
it never receives live automated writes, regardless of any operator's global setting, should use
121-
`killSwitch.paused: true` instead — the kill-switch always takes precedence over any live-mode
122-
opt-in.
117+
A freshly-configured miner always defaults to dry-run (observe/log only, never execute a write) —
118+
this field is the only per-repo path to live mode, and it alone is not sufficient: the miner's own
119+
operator must also separately opt in globally (env var `LOOPOVER_MINER_LIVE_MODE=live`) before
120+
writes actually execute. A repo that wants to guarantee it never receives live automated writes,
121+
regardless of any operator's global setting, should use `killSwitch.paused: true` instead — the
122+
kill-switch always takes precedence over any live-mode opt-in.
123123
</Callout>
124124

125125
- `liveModeOptIn` (string or `null`, default: `null`) — must equal EXACTLY the literal `"live"` to

apps/loopover-ui/content/docs/ams-kill-switch-incident.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: Kill-switch incident runbook
33
description: Detect, activate, and audit a misbehaving Rent-a-Loop miner after the kill-switch stops it — grounded in the shipped #5670 mechanism.
4+
eyebrow: Maintainers
45
---
56

67
Operator runbook for a **misbehaving autonomous miner loop** once the emergency kill-switch has
@@ -17,9 +18,9 @@ been (or must be) engaged. Grounded in the shipped mechanism from #5670 (`packag
1718

1819
Two independent triggers compose into one scope per repo:
1920

20-
| Trigger | How | Scope reported |
21-
| --- | --- | --- |
22-
| Global | `LOOPOVER_MINER_KILL_SWITCH` env var (`1` / `true` / `yes` / `on`) | `"global"` — every repo |
21+
| Trigger | How | Scope reported |
22+
| -------- | -------------------------------------------------------------------------------- | ------------------------- |
23+
| Global | `LOOPOVER_MINER_KILL_SWITCH` env var (`1` / `true` / `yes` / `on`) | `"global"` — every repo |
2324
| Per-repo | `killSwitch.paused: true` in that repo's `.loopover-miner.yml` (`MinerGoalSpec`) | `"repo"` — that repo only |
2425

2526
A global halt **always wins** and is reported as `"global"` even when the per-repo flag is also set.

apps/loopover-ui/content/docs/ams-observability.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: Observing your miner
33
description: Point Grafana at redacted AMS reporting exports to see attempt and prediction history without exposing the miner's live local ledgers.
4+
eyebrow: Maintainers
45
---
56

67
How to point Grafana at redacted miner reporting exports to see attempt and prediction history
@@ -73,9 +74,9 @@ visualize AMS activity, add a dashboard JSON there — or import one at runtime
7374
Ledger` datasources above.
7475

7576
<Callout variant="note">
76-
Panels should query only the redacted reporting schema (e.g. `SELECT * FROM
77-
attempt_log_events`), never a `payload_json`/`reason` column — the exporter drops both, so a
78-
panel referencing them returns no such column.
77+
Panels should query only the redacted reporting schema (e.g. `SELECT * FROM attempt_log_events`),
78+
never a `payload_json`/`reason` column — the exporter drops both, so a panel referencing them
79+
returns no such column.
7980
</Callout>
8081

8182
## Prometheus metrics (opt-in)

apps/loopover-ui/content/docs/ams-operations-runbook.mdx

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: AMS operations runbook
33
description: Recover from SQLite lock contention, ledger corruption, and post-upgrade schema migrations in loopover-miner's local state.
4+
eyebrow: Maintainers
45
---
56

67
Operator-facing runbook for **local SQLite state**: what the concurrency guarantees actually mean,
@@ -45,7 +46,7 @@ Default **5000 ms**; overridable per-test only — production stores always use
4546
{
4647
title: "Two short-lived writers, same file",
4748
description:
48-
"E.g. a CLI command finishing while loop is idle, or Grafana reading while the miner appends. SQLite waits up to 5 seconds for the lock, then proceeds or surfaces \"database is locked\".",
49+
'E.g. a CLI command finishing while loop is idle, or Grafana reading while the miner appends. SQLite waits up to 5 seconds for the lock, then proceeds or surfaces "database is locked".',
4950
},
5051
{
5152
title: "Append-only ledgers",
@@ -67,9 +68,8 @@ Default **5000 ms**; overridable per-test only — production stores always use
6768

6869
<Callout variant="warn">
6970
**Invariant:** one active loop (or one intentional writer set) per state directory. Horizontal
70-
scale = **isolated state dirs** — separate compose projects, separate
71-
`LOOPOVER_MINER_CONFIG_DIR`, or the Kubernetes StatefulSet pattern in the [AMS deployment
72-
guide](/docs/ams-deployment).
71+
scale = **isolated state dirs** — separate compose projects, separate `LOOPOVER_MINER_CONFIG_DIR`,
72+
or the Kubernetes StatefulSet pattern in the [AMS deployment guide](/docs/ams-deployment).
7373
</Callout>
7474

7575
Quick health check — `doctor` includes `laptop-state-sqlite` (file exists + readable) and
@@ -118,9 +118,9 @@ Re-run `loopover-miner doctor`. If locks persist with a single process, see **Le
118118
below.
119119

120120
<Callout variant="note">
121-
Claims are local bookkeeping only. Two miners on different machines claiming the same GitHub
122-
issue is a **fleet coordination** problem (duplicate-cluster adjudication in the engine), not
123-
something SQLite resolves — split state dirs and use operational claim hygiene.
121+
Claims are local bookkeeping only. Two miners on different machines claiming the same GitHub issue
122+
is a **fleet coordination** problem (duplicate-cluster adjudication in the engine), not something
123+
SQLite resolves — split state dirs and use operational claim hygiene.
124124
</Callout>
125125

126126
## Backup and restore
@@ -158,7 +158,7 @@ loopover-miner doctor --json # verify afterward`}
158158
/>
159159

160160
It also removes any leftover `-wal`/`-shm` sidecar files from the live directory after restoring
161-
each store — those hold in-flight writes from *before* the restore, and leaving them in place
161+
each store — those hold in-flight writes from _before_ the restore, and leaving them in place
162162
would let SQLite silently replay stale pre-restore writes back on top of the freshly restored
163163
file on next open.
164164

@@ -184,10 +184,7 @@ Check disk space, permissions (`0600` file, `0700` parent), and backup tools cop
184184
**Remediation.** Stop the miner, then back up the whole state directory (even damaged files help
185185
post-mortems):
186186

187-
<CodeBlock
188-
lang="bash"
189-
code={`cp -a "$STATE_DIR" "\${STATE_DIR}.bak.$(date +%Y%m%d%H%M%S)"`}
190-
/>
187+
<CodeBlock lang="bash" code={`cp -a "$STATE_DIR" "\${STATE_DIR}.bak.$(date +%Y%m%d%H%M%S)"`} />
191188

192189
Choose a recovery tier:
193190

@@ -267,8 +264,8 @@ tarball, pin the previous package version, and file an issue with the failing `u
267264
store filename.
268265

269266
<Callout variant="note" title="Rolling fleet upgrades">
270-
Upgrade and restart **one worker/state dir at a time** so isolated workers never share a
271-
directory mid-migration.
267+
Upgrade and restart **one worker/state dir at a time** so isolated workers never share a directory
268+
mid-migration.
272269
</Callout>
273270

274271
## Related docs

0 commit comments

Comments
 (0)