From b84505216c86bd5b242995a869d8479d0f260171 Mon Sep 17 00:00:00 2001 From: oktofeesh1 <287075021+oktofeesh1@users.noreply.github.com> Date: Sat, 20 Jun 2026 04:18:30 -0700 Subject: [PATCH] docs(launch): repo-owner onboarding checklist (#254) Adds /docs/owner-checklist, a pre-flight checklist a repo owner works through before inviting Gittensory contribution traffic. It mirrors the real registration-readiness report dimensions, so every item maps to something the platform actually checks, and each carries the honest tradeoff being opted into: - Repository registration, config quality (.gittensory.yml), labels & trusted pipeline, issue quality, contribution lanes, validation/gate readiness, maintainer capacity & queue health, and the public/private boundary. - Cites the real owner endpoints (GET registration-readiness, GET gittensor-config-recommendation, POST settings-preview) and the repo-owner-intake review-only agent profile. - Honest tradeoffs around maintainer burden and low-quality PR pressure (split lane triage cost, maintainer cut vs miner share, linked-issue friction, turning on public surfaces). - Lane taxonomy matches ParticipationLane (direct_pr / issue_discovery / split / inactive / unknown); public/private section names what the sanitizer scrubs. Linked from the docs index (Repo owners). test/unit/docs-owner-checklist.test.ts mirrors the miner-quickstart doc test: asserts every required checklist dimension, the real endpoints/profile, the lane taxonomy, the honest tradeoffs, the owner-workflow links, and the absence of reward/score-prediction/secret copy. Fixes #254 Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/gittensory-ui/src/routeTree.gen.ts | 21 +++ apps/gittensory-ui/src/routes/docs.index.tsx | 1 + .../src/routes/docs.owner-checklist.tsx | 161 ++++++++++++++++++ test/unit/docs-owner-checklist.test.ts | 70 ++++++++ 4 files changed, 253 insertions(+) create mode 100644 apps/gittensory-ui/src/routes/docs.owner-checklist.tsx create mode 100644 test/unit/docs-owner-checklist.test.ts diff --git a/apps/gittensory-ui/src/routeTree.gen.ts b/apps/gittensory-ui/src/routeTree.gen.ts index 4e750aa681..45d1352e2b 100644 --- a/apps/gittensory-ui/src/routeTree.gen.ts +++ b/apps/gittensory-ui/src/routeTree.gen.ts @@ -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' @@ -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', @@ -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 @@ -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 @@ -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 @@ -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' @@ -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' @@ -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' @@ -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' @@ -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 @@ -874,6 +894,7 @@ const DocsRouteChildren: DocsRouteChildren = { DocsMcpClientsRoute: DocsMcpClientsRoute, DocsMinerQuickstartRoute: DocsMinerQuickstartRoute, DocsMinerWorkflowRoute: DocsMinerWorkflowRoute, + DocsOwnerChecklistRoute: DocsOwnerChecklistRoute, DocsPrivacySecurityRoute: DocsPrivacySecurityRoute, DocsQuickstartRoute: DocsQuickstartRoute, DocsScoreabilityRoute: DocsScoreabilityRoute, diff --git a/apps/gittensory-ui/src/routes/docs.index.tsx b/apps/gittensory-ui/src/routes/docs.index.tsx index 479843c813..d03e23dbc7 100644 --- a/apps/gittensory-ui/src/routes/docs.index.tsx +++ b/apps/gittensory-ui/src/routes/docs.index.tsx @@ -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" }, diff --git a/apps/gittensory-ui/src/routes/docs.owner-checklist.tsx b/apps/gittensory-ui/src/routes/docs.owner-checklist.tsx new file mode 100644 index 0000000000..a70ae13db2 --- /dev/null +++ b/apps/gittensory-ui/src/routes/docs.owner-checklist.tsx @@ -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 ( + +

+ Gittensory is quiet by default: 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{" "} + owner console; readiness is reported as bands and statuses, + never as a raw private number. +

+

+ Start by pulling your readiness report — it returns ready plus a list of{" "} + blockers, and drives every item below. You can also wire a review-only agent + with the repo-owner-intake profile (it explains and drafts setup plans; it + never pushes config, labels issues, or posts comments). +

+ + + +

1. Repository registration

+

+ Confirm the repo is in the current Gittensory registry. If it is not, that is the first{" "} + blocker in the readiness report and nothing else applies yet. Register and + review from the owner console. +

+ +

2. Repo policy & config quality

+

+ Your policy lives in .gittensory.yml (sections: settings,{" "} + gate, review). The readiness report grades it as{" "} + configQuality (excellent / good / needs_attention / fragile); a{" "} + fragile config is a hard blocker. Preview exactly what a given config would + do on a real PR before you commit it: +

+ + +

3. Labels & trusted pipeline

+

+ The report checks labelPolicy.trustedPipelineReady and lists missing or unused + registry labels. Configure the labels you actually use before turning on{" "} + labelMultipliers. +

+ + 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. + + +

4. Issue quality

+

+ 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. +

+ +

5. Contribution lanes

+

+ A repo's lane is configured (not chosen by contributors) and the config recommendation + endpoint suggests one with its tradeoffs. The lane is one of: +

+
    +
  • + direct_pr — implementation PRs only (issueDiscoveryShare = 0). +
  • +
  • + issue_discovery — discovery/reporting only (issueDiscoveryShare{" "} + = 1). +
  • +
  • + split — both lanes active (0 < issueDiscoveryShare < 1). +
  • +
  • + inactive — registered but with no current allocation. +
  • +
  • + unknown — not registered or no config yet. +
  • +
+ + Honest tradeoff: the split 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 direct_pr{" "} + until you have capacity. + + +

6. Validation expectations & gate readiness

+

+ Declare your validation commands in .gittensory.yml so contributors know what + "done" means, and so the gate can run. The report reports{" "} + testCoverageHealth as gate_ready or gate_unknown; + gate checks only run when you have explicitly configured them. Until then the gate stays + advisory. +

+ +

7. Maintainer capacity & queue health

+

+ The report grades queueHealth (low / medium / high / critical) from your open + PR/issue burden, and maintainerCutReadiness tells you whether the repo is calm + enough to reserve a maintainer lane. +

+ + 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. + + +

8. Public/private boundaries

+

+ Decide what becomes visible: publicSurface (comments + labels),{" "} + commentMode, and publicAudienceMode. 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. +

+ + 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{" "} + privacy & security for the full boundary and + the owner workflow for the end-to-end setup path. + +
+ ); +} diff --git a/test/unit/docs-owner-checklist.test.ts b/test/unit/docs-owner-checklist.test.ts new file mode 100644 index 0000000000..337a006b58 --- /dev/null +++ b/test/unit/docs-owner-checklist.test.ts @@ -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); + }); +});