Skip to content
Closed
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
21 changes: 21 additions & 0 deletions apps/gittensory-ui/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { Route as DocsTroubleshootingRouteImport } from './routes/docs.troublesh
import { Route as DocsScoreabilityRouteImport } from './routes/docs.scoreability'
import { Route as DocsQuickstartRouteImport } from './routes/docs.quickstart'
import { Route as DocsPrivacySecurityRouteImport } from './routes/docs.privacy-security'
import { Route as DocsOwnerChecklistRouteImport } from './routes/docs.owner-checklist'
import { Route as DocsMinerWorkflowRouteImport } from './routes/docs.miner-workflow'
import { Route as DocsMinerQuickstartRouteImport } from './routes/docs.miner-quickstart'
import { Route as DocsMcpClientsRouteImport } from './routes/docs.mcp-clients'
Expand Down Expand Up @@ -140,6 +141,11 @@ const DocsPrivacySecurityRoute = DocsPrivacySecurityRouteImport.update({
path: '/privacy-security',
getParentRoute: () => DocsRoute,
} as any)
const DocsOwnerChecklistRoute = DocsOwnerChecklistRouteImport.update({
id: '/owner-checklist',
path: '/owner-checklist',
getParentRoute: () => DocsRoute,
} as any)
const DocsMinerWorkflowRoute = DocsMinerWorkflowRouteImport.update({
id: '/miner-workflow',
path: '/miner-workflow',
Expand Down Expand Up @@ -285,6 +291,7 @@ export interface FileRoutesByFullPath {
'/docs/mcp-clients': typeof DocsMcpClientsRoute
'/docs/miner-quickstart': typeof DocsMinerQuickstartRoute
'/docs/miner-workflow': typeof DocsMinerWorkflowRoute
'/docs/owner-checklist': typeof DocsOwnerChecklistRoute
'/docs/privacy-security': typeof DocsPrivacySecurityRoute
'/docs/quickstart': typeof DocsQuickstartRoute
'/docs/scoreability': typeof DocsScoreabilityRoute
Expand Down Expand Up @@ -324,6 +331,7 @@ export interface FileRoutesByTo {
'/docs/mcp-clients': typeof DocsMcpClientsRoute
'/docs/miner-quickstart': typeof DocsMinerQuickstartRoute
'/docs/miner-workflow': typeof DocsMinerWorkflowRoute
'/docs/owner-checklist': typeof DocsOwnerChecklistRoute
'/docs/privacy-security': typeof DocsPrivacySecurityRoute
'/docs/quickstart': typeof DocsQuickstartRoute
'/docs/scoreability': typeof DocsScoreabilityRoute
Expand Down Expand Up @@ -367,6 +375,7 @@ export interface FileRoutesById {
'/docs/mcp-clients': typeof DocsMcpClientsRoute
'/docs/miner-quickstart': typeof DocsMinerQuickstartRoute
'/docs/miner-workflow': typeof DocsMinerWorkflowRoute
'/docs/owner-checklist': typeof DocsOwnerChecklistRoute
'/docs/privacy-security': typeof DocsPrivacySecurityRoute
'/docs/quickstart': typeof DocsQuickstartRoute
'/docs/scoreability': typeof DocsScoreabilityRoute
Expand Down Expand Up @@ -411,6 +420,7 @@ export interface FileRouteTypes {
| '/docs/mcp-clients'
| '/docs/miner-quickstart'
| '/docs/miner-workflow'
| '/docs/owner-checklist'
| '/docs/privacy-security'
| '/docs/quickstart'
| '/docs/scoreability'
Expand Down Expand Up @@ -450,6 +460,7 @@ export interface FileRouteTypes {
| '/docs/mcp-clients'
| '/docs/miner-quickstart'
| '/docs/miner-workflow'
| '/docs/owner-checklist'
| '/docs/privacy-security'
| '/docs/quickstart'
| '/docs/scoreability'
Expand Down Expand Up @@ -492,6 +503,7 @@ export interface FileRouteTypes {
| '/docs/mcp-clients'
| '/docs/miner-quickstart'
| '/docs/miner-workflow'
| '/docs/owner-checklist'
| '/docs/privacy-security'
| '/docs/quickstart'
| '/docs/scoreability'
Expand Down Expand Up @@ -643,6 +655,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof DocsPrivacySecurityRouteImport
parentRoute: typeof DocsRoute
}
'/docs/owner-checklist': {
id: '/docs/owner-checklist'
path: '/owner-checklist'
fullPath: '/docs/owner-checklist'
preLoaderRoute: typeof DocsOwnerChecklistRouteImport
parentRoute: typeof DocsRoute
}
'/docs/miner-workflow': {
id: '/docs/miner-workflow'
path: '/miner-workflow'
Expand Down Expand Up @@ -856,6 +875,7 @@ interface DocsRouteChildren {
DocsMcpClientsRoute: typeof DocsMcpClientsRoute
DocsMinerQuickstartRoute: typeof DocsMinerQuickstartRoute
DocsMinerWorkflowRoute: typeof DocsMinerWorkflowRoute
DocsOwnerChecklistRoute: typeof DocsOwnerChecklistRoute
DocsPrivacySecurityRoute: typeof DocsPrivacySecurityRoute
DocsQuickstartRoute: typeof DocsQuickstartRoute
DocsScoreabilityRoute: typeof DocsScoreabilityRoute
Expand All @@ -874,6 +894,7 @@ const DocsRouteChildren: DocsRouteChildren = {
DocsMcpClientsRoute: DocsMcpClientsRoute,
DocsMinerQuickstartRoute: DocsMinerQuickstartRoute,
DocsMinerWorkflowRoute: DocsMinerWorkflowRoute,
DocsOwnerChecklistRoute: DocsOwnerChecklistRoute,
DocsPrivacySecurityRoute: DocsPrivacySecurityRoute,
DocsQuickstartRoute: DocsQuickstartRoute,
DocsScoreabilityRoute: DocsScoreabilityRoute,
Expand Down
1 change: 1 addition & 0 deletions apps/gittensory-ui/src/routes/docs.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const AUDIENCES: Audience[] = [
description: "Registration readiness, label policy, repo settings preview.",
primary: { to: "/docs/beta-onboarding", label: "Beta onboarding" },
links: [
{ to: "/docs/owner-checklist", label: "Onboarding checklist" },
{ to: "/app/owner", label: "Owner console" },
{ to: "/docs/privacy-security", label: "Privacy & security" },
{ to: "/docs/troubleshooting", label: "Troubleshooting" },
Expand Down
161 changes: 161 additions & 0 deletions apps/gittensory-ui/src/routes/docs.owner-checklist.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { createFileRoute, Link } from "@tanstack/react-router";

import { DocsPage } from "@/components/site/docs-page";
import { CodeBlock, Callout } from "@/components/site/primitives";

export const Route = createFileRoute("/docs/owner-checklist")({
head: () => ({
meta: [
{ title: "Repo-owner onboarding checklist — Gittensory docs" },
{
name: "description",
content:
"A pre-flight checklist for repo owners: registration, config quality, labels, issue quality, contribution lanes, validation, maintainer capacity, and the public/private boundary — with honest tradeoffs.",
},
{ property: "og:title", content: "Repo-owner onboarding checklist — Gittensory docs" },
{
property: "og:description",
content:
"A pre-flight checklist for repo owners: registration, config quality, labels, issue quality, contribution lanes, validation, maintainer capacity, and the public/private boundary — with honest tradeoffs.",
},
{ property: "og:url", content: "/docs/owner-checklist" },
],
links: [{ rel: "canonical", href: "/docs/owner-checklist" }],
}),
component: OwnerChecklist,
});

function OwnerChecklist() {
return (
<DocsPage
eyebrow="Repo owners"
title="Repo-owner onboarding checklist"
description="Work through this before you invite Gittensory contribution traffic. It mirrors the readiness report exactly, so each item is something the platform actually checks — and each comes with the honest tradeoff you are opting into."
>
<p>
Gittensory is <strong>quiet by default</strong>: it installs without posting comments or
adding labels until you turn those surfaces on. This checklist is what to confirm first.
Everything owner-only runs through the private API or the{" "}
<Link to="/app/owner">owner console</Link>; readiness is reported as bands and statuses,
never as a raw private number.
</p>
<p>
Start by pulling your readiness report — it returns <code>ready</code> plus a list of{" "}
<code>blockers</code>, and drives every item below. You can also wire a review-only agent
with the <code>repo-owner-intake</code> profile (it explains and drafts setup plans; it
never pushes config, labels issues, or posts comments).
</p>
<CodeBlock
lang="http"
code={`GET /v1/repos/:owner/:repo/registration-readiness
GET /v1/repos/:owner/:repo/gittensor-config-recommendation`}
/>
<CodeBlock
code={`gittensory-mcp init-client --print claude --agent-profile repo-owner-intake`}
/>

<h2>1. Repository registration</h2>
<p>
Confirm the repo is in the current Gittensory registry. If it is not, that is the first{" "}
<code>blocker</code> in the readiness report and nothing else applies yet. Register and
review from the <Link to="/app/owner">owner console</Link>.
</p>

<h2>2. Repo policy &amp; config quality</h2>
<p>
Your policy lives in <code>.gittensory.yml</code> (sections: <code>settings</code>,{" "}
<code>gate</code>, <code>review</code>). The readiness report grades it as{" "}
<code>configQuality</code> (excellent / good / needs_attention / fragile); a{" "}
<strong>fragile</strong> config is a hard blocker. Preview exactly what a given config would
do on a real PR before you commit it:
</p>
<CodeBlock lang="http" code={`POST /v1/repos/:owner/:repo/settings-preview`} />

<h2>3. Labels &amp; trusted pipeline</h2>
<p>
The report checks <code>labelPolicy.trustedPipelineReady</code> and lists missing or unused
registry labels. Configure the labels you actually use before turning on{" "}
<code>labelMultipliers</code>.
</p>
<Callout variant="warn">
Honest tradeoff: enabling trusted-label multipliers before your labels are real and applied
consistently rewards the wrong work. Start without them and add them once the label pipeline
is verified.
</Callout>

<h2>4. Issue quality</h2>
<p>
Clear, reproducible, well-scoped issues are the cheapest way to reduce low-quality PR
pressure: contributors aim at real targets instead of guessing. The issue-quality signal
feeds the contribution lanes below, so tidy your open issues before broadening intake.
</p>

<h2>5. Contribution lanes</h2>
<p>
A repo&apos;s lane is configured (not chosen by contributors) and the config recommendation
endpoint suggests one with its tradeoffs. The lane is one of:
</p>
<ul>
<li>
<code>direct_pr</code> — implementation PRs only (<code>issueDiscoveryShare</code> = 0).
</li>
<li>
<code>issue_discovery</code> — discovery/reporting only (<code>issueDiscoveryShare</code>{" "}
= 1).
</li>
<li>
<code>split</code> — both lanes active (0 &lt; <code>issueDiscoveryShare</code> &lt; 1).
</li>
<li>
<code>inactive</code> — registered but with no current allocation.
</li>
<li>
<code>unknown</code> — not registered or no config yet.
</li>
</ul>
<Callout variant="warn">
Honest tradeoff: the <code>split</code> lane is recommended only when contributor intake is
healthy and config quality is excellent. Adding an issue-discovery slice surfaces more
outside work but adds triage load and duplicate risk — default to <code>direct_pr</code>{" "}
until you have capacity.
</Callout>

<h2>6. Validation expectations &amp; gate readiness</h2>
<p>
Declare your validation commands in <code>.gittensory.yml</code> so contributors know what
&quot;done&quot; means, and so the gate can run. The report reports{" "}
<code>testCoverageHealth</code> as <code>gate_ready</code> or <code>gate_unknown</code>;
gate checks only run when you have explicitly configured them. Until then the gate stays
advisory.
</p>

<h2>7. Maintainer capacity &amp; queue health</h2>
<p>
The report grades <code>queueHealth</code> (low / medium / high / critical) from your open
PR/issue burden, and <code>maintainerCutReadiness</code> tells you whether the repo is calm
enough to reserve a maintainer lane.
</p>
<Callout variant="warn">
Honest tradeoffs: opening more lanes means more triage. A maintainer cut rewards upkeep but
reduces the miner share. Requiring a linked issue improves traceability but deters quick
drive-by PRs. Pick deliberately for the capacity you actually have.
</Callout>

<h2>8. Public/private boundaries</h2>
<p>
Decide what becomes visible: <code>publicSurface</code> (comments + labels),{" "}
<code>commentMode</code>, and <code>publicAudienceMode</code>. Everything that can reach a
public GitHub surface is run through the sanitizer first, so nothing economic or identifying
leaks — no wallet/hotkey, payout, trust-score, ranking, farming, or public-number language,
and no local file paths. Gittensory frames everything as advisory, never as a guaranteed
outcome.
</p>
<Callout variant="safety">
Turning on public comments and labels increases visibility — and the volume of drive-by PRs.
Keep surfaces quiet until items 1–7 are green. See{" "}
<Link to="/docs/privacy-security">privacy &amp; security</Link> for the full boundary and
the <Link to="/docs/beta-onboarding">owner workflow</Link> for the end-to-end setup path.
</Callout>
</DocsPage>
);
}
70 changes: 70 additions & 0 deletions test/unit/docs-owner-checklist.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";

const OWNER_CHECKLIST_PATH = resolve(
import.meta.dirname,
"../../apps/gittensory-ui/src/routes/docs.owner-checklist.tsx",
);

describe("docs owner onboarding checklist page", () => {
const source = readFileSync(OWNER_CHECKLIST_PATH, "utf8");
const normalizedSource = source.replace(/\s+/g, " ");

it("covers every required checklist dimension", () => {
// Acceptance criteria for #254: repo policy, labels, issue quality, validation commands,
// maintainer capacity, contribution lanes, and public/private boundaries.
expect(source).toMatch(/checklist/i);
expect(source).toMatch(/Repository registration/);
expect(source).toMatch(/config quality/i);
expect(source).toMatch(/Labels.*trusted pipeline/i);
expect(source).toMatch(/Issue quality/i);
expect(source).toMatch(/Contribution lanes/);
expect(source).toMatch(/Validation expectations/i);
expect(source).toMatch(/Maintainer capacity/i);
expect(source).toMatch(/Public\/private boundaries/);
});

it("references the real owner-facing endpoints and agent profile", () => {
expect(source).toMatch(/\/v1\/repos\/:owner\/:repo\/registration-readiness/);
expect(source).toMatch(/\/v1\/repos\/:owner\/:repo\/gittensor-config-recommendation/);
expect(source).toMatch(/\/v1\/repos\/:owner\/:repo\/settings-preview/);
expect(source).toMatch(/repo-owner-intake/);
expect(source).toMatch(/\.gittensory\.yml/);
});

it("uses the real contribution-lane taxonomy from the engine", () => {
// Must match ParticipationLane in src/signals/engine.ts.
expect(source).toMatch(/direct_pr/);
expect(source).toMatch(/issue_discovery/);
expect(source).toMatch(/split/);
expect(source).toMatch(/inactive/);
expect(source).toMatch(/unknown/);
});

it("states honest tradeoffs around maintainer burden and low-quality PR pressure", () => {
expect(normalizedSource).toMatch(/honest tradeoff/i);
expect(normalizedSource).toMatch(/low-quality PR pressure/i);
expect(normalizedSource).toMatch(/triage load|more triage/i);
expect(normalizedSource).toMatch(/maintainer (cut|lane)/i);
});

it("links to the owner workflow and the public/private boundary", () => {
expect(source).toMatch(/\/app\/owner/);
expect(source).toMatch(/\/docs\/beta-onboarding/);
expect(source).toMatch(/\/docs\/privacy-security/);
});

it("documents the public/private boundary and quiet-by-default behavior", () => {
expect(normalizedSource).toMatch(/quiet by default/i);
expect(source).toMatch(/publicSurface/);
expect(normalizedSource).toMatch(/run through the sanitizer|through the sanitizer/i);
});

it("avoids reward guarantees, score predictions, and secret material", () => {
expect(source).not.toMatch(/guaranteed (reward|payout|score)/i);
expect(source).not.toMatch(/you will (earn|receive|get)/i);
expect(source).not.toMatch(/predict(s|ed)?\s+your\s+score/i);
expect(source).not.toMatch(/seed phrase|mnemonic|private key/i);
});
});
Loading