Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions apps/gittensory-ui/src/routes/docs.maintainer-self-hosting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,74 @@ function MaintainerSelfHosting() {
))}
</div>

<h2>Onboarding simplification proposal (#1574)</h2>
<p>
This section records today&apos;s accurate setup path and the gaps worth closing next — the
issue deliverable for making self-host onboarding as fast as CodeRabbit while keeping robust
per-repo policy in container-private config.
</p>

<h3>Today&apos;s recommended path (verified)</h3>
<ol>
<li>
<code>cp .env.selfhost.example .env</code> — conservative defaults (<code>dry-run</code>,
small <code>GITTENSORY_REVIEW_REPOS</code>).
</li>
<li>
Pull or build the image (<code>INSTALL_AI_CLIS=true</code> by default;{" "}
<code>--build-arg INSTALL_AI_CLIS=false</code> for deterministic-only).
</li>
<li>
One-click GitHub App via <code>/setup</code> + <code>SELFHOST_SETUP_TOKEN</code> (
<code>Checks: write</code> included — re-approve on existing Apps after permission bumps).
</li>
<li>
Mount <code>./gittensory-config</code> and copy{" "}
<code>config/examples/global.gittensory.yml</code> →{" "}
<code>gittensory-config/.gittensory.yml</code> for a centralized private default (per-repo
files deep-merge on top).
</li>
<li>
Add each pilot repo to <code>GITTENSORY_REVIEW_REPOS</code>, watch a PR in{" "}
<code>dry-run</code>, then enable advisory gate mode from the control panel or{" "}
<code>POST /v1/repos/:owner/:repo/activation</code>.
</li>
<li>
Go live by unsetting <code>SELFHOST_DEPLOYMENT_MODE</code>; tune autonomy in private
config when ready.
</li>
</ol>

<h3>Gaps and proposed improvements</h3>
<FeatureRow
items={[
{
title: "Single-command repo onboarding",
description:
"Today: edit .env allowlist, copy YAML templates, sign into the panel, click activate. Proposed: one CLI/API command that adds owner/repo to GITTENSORY_REVIEW_REPOS, seeds gittensory-config/owner__repo/.gittensory.yml from global.gittensory.yml, and POSTs activation — idempotent, dry-run aware.",
},
{
title: "Centralized private default only",
description:
"Most fleets need one gittensory-config/.gittensory.yml (already supported) with optional per-repo overrides — docs now treat that as the default story instead of implying every repo needs its own file.",
},
{
title: "Advisory-by-default on first install",
description:
"The activation endpoint already applies CodeRabbit-style advisory ramp (gate on, deterministic rules advisory, AI off). Proposed: auto-call it on first webhook for a newly installed repo when a SELFHOST_AUTO_ACTIVATE_REPOS flag lists the repo — still overrideable via private config.",
},
{
title: "Clearer activation vocabulary",
description:
"Docs now separate GITTENSORY_REVIEW_REPOS (feature allowlist), gate activation (check + rules), and is_registered (Gittensor registry). Proposed: surface all three in the control-panel repo workspace with plain labels instead of making operators infer from logs.",
},
]}
/>
<Callout variant="note">
None of the proposals above require code changes to adopt today&apos;s path — they describe
UX we can add without weakening the private-config model or env-level kill switches.
</Callout>

<h2>How self-hosting fits with hosted docs</h2>
<p>
The hosted maintainer workflow still applies: review modes, gate settings, safety rules, and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,41 @@ GITHUB_METADATA_CACHE_TTL_SECONDS=600`}
</p>
<CodeBlock filename="self-host env vars" code={SELFHOST_ENV_REFERENCE_MARKDOWN} />

<h2>Repo activation — three layers</h2>
<p>
Self-host docs and logs use &quot;activation&quot; for more than one mechanism. They stack
independently:
</p>
<FeatureRow
items={[
{
title: "Feature allowlist (env)",
description:
"GITTENSORY_REVIEW_REPOS lists which repos run the converged per-PR path (safety, unified comment, grounding, RAG, reputation, …). Empty/unset ⇒ no repo runs those features, regardless of individual GITTENSORY_REVIEW_* flags. Per-repo features: overrides in a private or public .gittensory.yml features: block can force on/off per repo (subject to env kill-switches).",
},
{
title: "Gate activation (DB or private config)",
description:
"Whether the Gittensory check-run and deterministic gate rules run for a repo. Stored in the instance database (control panel, PUT /v1/repos/:owner/:repo/settings, or POST …/activation) and/or in gate.checkMode / gate.enabled in .gittensory.yml. The one-click activation endpoint applies advisory-first defaults: gate on, linked-issue/duplicate/quality rules in advisory mode, AI review still off.",
},
{
title: "Gittensor registration (is_registered)",
description:
"The registry sync (GITTENSOR_REGISTRY_URL) marks repos present in the upstream snapshot with is_registered=1. That flag gates Gittensor-scored mining, evidence graphs, and several maintainer analytics — not basic webhook review. Brokered Orb installs may keep is_registered=0; listConvergenceRepos still pre-indexes GITTENSORY_REVIEW_REPOS for RAG.",
},
]}
/>
<p>
Preview before flipping: <code>GET /v1/repos/:owner/:repo/activation-preview</code> runs the
deterministic advisory engine over recent cached PRs (no AI cost) and returns a{" "}
<code>recommendedAction</code> of <code>enable_advisory</code> when the gate is still off.
</p>

<h2>Per-PR feature flags</h2>
<p>
Most review capabilities need both their own flag and the repo in{" "}
<code>GITTENSORY_REVIEW_REPOS</code>. This gives you a global kill switch and a per-repo
rollout switch.
<code>GITTENSORY_REVIEW_REPOS</code> (unless a per-repo <code>features:</code> override says
otherwise). This gives you a global kill switch and a per-repo rollout switch.
</p>
<CodeBlock
filename=".env"
Expand Down
35 changes: 35 additions & 0 deletions apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,41 @@ volumes:
log for the same subsystem.
</p>

<h2>Re-gate sweeps (agent-regate-sweep)</h2>
<p>
Live PR review is webhook-driven, but open PRs still need periodic re-evaluation — the base
branch moves, duplicate clusters resolve, settings change, and approved PRs can sit unmerged
until CI re-runs. A scheduled sweep (<code>agent-regate-sweep</code>, every ~2 minutes on
the maintenance tick) fans out lightweight <code>agent-regate-pr</code> jobs for the stalest
open PRs per repo (cap <code>SWEEP_MAX_PRS=3</code> by default, REST-budget sized).
</p>
<FeatureRow
items={[
{
title: "What it recomputes",
description:
"Gate verdict + auto-maintain actions for open, non-draft PRs the webhook has not refreshed recently. Does not re-run AI unless the live pipeline would.",
},
{
title: "How candidates are picked",
description:
"Sorted by lastRegatedAt ascending (stalest first), skipping PRs a webhook touched within ~2 minutes. Dry-run suppresses GitHub writes but still advances lastRegatedAt so coverage keeps moving.",
},
{
title: "Backlog-convergence companion",
description:
"A separate backlog-convergence-sweep repairs PRs whose public review surface (comment/check/label) never published for the current head — a case the main sweep can miss when a per-PR job dead-letters after fan-out.",
},
]}
/>
<p>
Log markers: <code>regate_sweep_throttled</code> (sweep temporarily paused),{" "}
<code>regate_sweep_trigger_backlog_deferred</code> (prior regate work still draining —
avoids piling duplicate fan-outs). In metrics, break down deferrals by{" "}
<code>job_type=agent-regate-pr</code> or <code>agent-regate-sweep</code> when GitHub
rate-limit pressure spikes.
</p>

<h2>Routine checks</h2>
<ul>
<li>Queue pending count is not growing without processing.</li>
Expand Down
77 changes: 77 additions & 0 deletions apps/gittensory-ui/src/routes/docs.self-hosting-quickstart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,83 @@ review_context_fetch_failed # REES/RAG/grounding context failure`}
or RAG deliberately.
</p>

<h2>6. Activate your first repo</h2>
<p>
Three separate knobs are easy to conflate — each does something different, and all three
matter for a smooth first rollout:
</p>
<FeatureRow
items={[
{
title: "GITTENSORY_REVIEW_REPOS (env allowlist)",
description:
"Turns on the per-PR converged review path (unified comment, safety, grounding, RAG, etc.) for named repos. Empty means none — even when the global GITTENSORY_REVIEW_* flags are true.",
},
{
title: "Gate activation (DB or private config)",
description:
"Turns on the Gittensory check-run and deterministic gate rules for a repo. One-click via the control panel or POST /v1/repos/:owner/:repo/activation; or set gate.checkMode / gate.enabled in a mounted private .gittensory.yml.",
},
{
title: "is_registered (Gittensor registry)",
description:
"Set automatically when your repo appears in the GITTENSOR_REGISTRY_URL snapshot. Needed for Gittensor-scored mining/evidence features, not for basic PR review on a self-host.",
},
]}
/>
<p>
<strong>Recommended first-repo path today:</strong> add the repo to{" "}
<code>GITTENSORY_REVIEW_REPOS</code>, seed a private global default, then enable advisory
gate mode once webhook delivery works.
</p>
<CodeBlock
filename=".env"
code={`GITTENSORY_REVIEW_REPOS=owner/my-repo
SELFHOST_DEPLOYMENT_MODE=dry-run # keep shadowing until you trust output`}
/>
<p>
Copy the shipped global private default into the compose-mounted config directory (edit your
copy — never commit real policy to a public repo):
</p>
<CodeBlock
lang="bash"
code={`mkdir -p gittensory-config
cp config/examples/global.gittensory.yml gittensory-config/.gittensory.yml
# optional per-repo override:
mkdir -p gittensory-config/owner__my-repo
cp config/examples/global.gittensory.yml gittensory-config/owner__my-repo/.gittensory.yml`}
/>
<p>
Sign in to the control panel (<code>ADMIN_GITHUB_LOGINS</code> must include your GitHub
login), open the repo workspace, preview what Gittensory would have flagged on recent PRs,
then enable advisory mode in one click — the same patch as:
</p>
<CodeBlock
lang="bash"
code={`curl -X POST "https://reviews.example.com/v1/repos/owner/my-repo/activation" \\
-H "Authorization: Bearer <session-or-api-token>" \\
-H "Content-Type: application/json" \\
-d '{}'`}
/>
<Callout variant="note">
That activation endpoint turns on the gate check plus deterministic rules in{" "}
<strong>advisory</strong> mode (non-blocking, no auto-merge) — a CodeRabbit-style ramp. AI
review stays off until you configure it separately. Full semantics in{" "}
<Link to="/docs/self-hosting-configuration">Configuration</Link>.
</Callout>
<Callout variant="warn" title="Checks: write on the GitHub App">
If reviews compute but no <code>Gittensory Orb Review Agent</code> check-run appears, open
your App&apos;s permissions page and confirm <strong>Checks: write</strong> is granted —
<code>checks: read</code> alone 403s the write silently. New permissions also require a
one-time re-approval on each installation; see{" "}
<Link to="/docs/self-hosting-github-app">GitHub App and Orb</Link>.
</Callout>
<p>
When output looks right, switch <code>SELFHOST_DEPLOYMENT_MODE</code> from{" "}
<code>dry-run</code> to unset (live writes). For a shorter future path, see the onboarding
proposal on <Link to="/docs/maintainer-self-hosting">Self-hosted reviews</Link>.
</p>

<h2>Defaults at a glance</h2>
<p>
Nothing below needs a flag to start; everything past the first row needs an explicit{" "}
Expand Down
2 changes: 1 addition & 1 deletion apps/gittensory-ui/src/routes/docs.self-hosting-rag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function SelfHostingRag() {
{
title: "Repo activation",
description:
"GITTENSORY_REVIEW_RAG=true and the repo in GITTENSORY_REVIEW_REPOS, or a private per-repo feature toggle.",
"GITTENSORY_REVIEW_RAG=true, repo in GITTENSORY_REVIEW_REPOS (or features.rag: true in private config). Gittensor is_registered is not required for self-host RAG when the allowlist covers the repo.",
},
{
title: "Vector backend",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ rees_analyzer_config_invalid`}
<h2>RAG returns no context</h2>
<ul>
<li>
Confirm <code>GITTENSORY_REVIEW_RAG=true</code> and repo activation.
Confirm <code>GITTENSORY_REVIEW_RAG=true</code>, the repo is in{" "}
<code>GITTENSORY_REVIEW_REPOS</code> (or has <code>features.rag: true</code> in private
config), and RAG indexing has run — see{" "}
<Link to="/docs/self-hosting-configuration">Configuration</Link> for the three activation
layers (<code>is_registered</code> is only required for Gittensor-scored paths, not RAG on
a self-host allowlist).
</li>
<li>Confirm Qdrant or the vector backend is reachable from the app container.</li>
<li>Confirm the embedding endpoint and model are running.</li>
Expand Down
54 changes: 54 additions & 0 deletions test/unit/docs-selfhost-activation-paths.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";

// Drift guard (#1574): self-host onboarding docs must keep naming the three activation layers, the
// one-click activation routes, Checks:write re-approval, INSTALL_AI_CLIS, and the re-gate sweep job
// types — otherwise operators follow stale instructions after the underlying paths change.

const QUICKSTART = "apps/gittensory-ui/src/routes/docs.self-hosting-quickstart.tsx";
const CONFIG = "apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx";
const OPERATIONS = "apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx";
const GITHUB_APP = "apps/gittensory-ui/src/routes/docs.self-hosting-github-app.tsx";
const MAINTAINER = "apps/gittensory-ui/src/routes/docs.maintainer-self-hosting.tsx";

const quickstart = readFileSync(QUICKSTART, "utf8");
const configuration = readFileSync(CONFIG, "utf8");
const operations = readFileSync(OPERATIONS, "utf8");
const githubApp = readFileSync(GITHUB_APP, "utf8");
const maintainer = readFileSync(MAINTAINER, "utf8");

describe("self-host activation + onboarding docs (#1574)", () => {
it("quickstart documents allowlist, private config seed, activation POST, and Checks: write", () => {
expect(quickstart).toContain("GITTENSORY_REVIEW_REPOS");
expect(quickstart).toContain("config/examples/global.gittensory.yml");
expect(quickstart).toContain("gittensory-config/.gittensory.yml");
expect(quickstart).toContain("/v1/repos/owner/my-repo/activation");
expect(quickstart).toContain("Checks: write");
expect(quickstart).toContain("INSTALL_AI_CLIS");
});

it("configuration separates feature allowlist, gate activation, and is_registered", () => {
expect(configuration).toContain("GITTENSORY_REVIEW_REPOS");
expect(configuration).toContain("is_registered");
expect(configuration).toContain("/v1/repos/:owner/:repo/activation-preview");
expect(configuration).toContain("/v1/repos/:owner/:repo/activation");
});

it("operations documents the periodic re-gate sweep job types", () => {
expect(operations).toContain("agent-regate-sweep");
expect(operations).toContain("agent-regate-pr");
expect(operations).toContain("backlog-convergence-sweep");
expect(operations).toContain("regate_sweep_throttled");
});

it("github-app doc still requires Checks: write at the manifest level", () => {
expect(githubApp).toMatch(/Checks:\s*write/i);
expect(githubApp).toContain("Re-approving a permission bump");
});

it("maintainer index includes the onboarding simplification proposal", () => {
expect(maintainer).toContain("Onboarding simplification proposal");
expect(maintainer).toContain("POST /v1/repos/:owner/:repo/activation");
expect(maintainer).toContain("global.gittensory.yml");
});
});
Loading