From 36941de57a9940b62e9a41cf63e0bc0004f5e5a6 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:53:50 +0200 Subject: [PATCH 01/10] feat(control-panel): add repo owner registration workspace Expand the owner panel into a structured registration workspace with lane tradeoffs, maintainer economics, queue/label/test policy cards, freshness warnings, and sanitized config recommendations. --- .../site/app-panels/owner-panel.tsx | 339 +++++++++++------- .../src/lib/registration-workspace.ts | 333 +++++++++++++++++ apps/gittensory-ui/src/routes/app.owner.tsx | 4 +- apps/gittensory-ui/src/routes/app.repos.tsx | 4 +- test/unit/registration-workspace-ui.test.ts | 167 +++++++++ 5 files changed, 719 insertions(+), 128 deletions(-) create mode 100644 apps/gittensory-ui/src/lib/registration-workspace.ts create mode 100644 test/unit/registration-workspace-ui.test.ts diff --git a/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx b/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx index 020c26fc..33135827 100644 --- a/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx +++ b/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx @@ -1,162 +1,253 @@ import { useMemo, useState } from "react"; -import { DiffBlock, StatusPill, type Status } from "@/components/site/control-primitives"; +import { + BoundaryBadge, + DiffBlock, + StatusPill, + type Status, +} from "@/components/site/control-primitives"; import { StateBoundary } from "@/components/site/state-views"; import { Input } from "@/components/ui/input"; import { useApiResource } from "@/lib/api/use-api-resource"; +import { + buildRegistrationWorkspaceView, + splitRepoFullName, + type GittensorConfigRecommendationPayload, + type RegistrationReadinessPayload, + type RegistrationWorkspaceView, + type WorkspaceLaneStatus, +} from "@/lib/registration-workspace"; -const STATUS_MAP: Record = { ok: "ready", warn: "warn", blocked: "blocked" }; - -type RegistrationReadiness = { - repoFullName: string; - ready: boolean; - recommendedRegistrationMode: string; - issuePolicy: string; - blockers: string[]; - warnings: string[]; +const LANE_STATUS_MAP: Record = { + ready: "ready", + warn: "warn", + blocked: "blocked", + info: "info", }; -type ConfigRecommendation = { - current: Record | null; - recommended: Record; - reasons: string[]; - warnings: string[]; +const FRESHNESS_STATUS_MAP: Record = { + complete: "ready", + degraded: "degraded", + stale: "stale", + unknown: "info", }; export function OwnerPanel() { const [repo, setRepo] = useState("entrius/gittensor"); - const [owner, name] = repo.split("/"); - const repoPath = - owner && name - ? `/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}` - : "/v1/repos/entrius/gittensor"; - const readiness = useApiResource( - `${repoPath}/registration-readiness`, + const parts = splitRepoFullName(repo.trim()); + const repoPath = parts + ? `/v1/repos/${encodeURIComponent(parts.owner)}/${encodeURIComponent(parts.repo)}` + : null; + const readiness = useApiResource( + `${repoPath ?? "/v1/repos/__invalid__/__invalid__"}/registration-readiness`, "Registration readiness", + undefined, + { enabled: Boolean(repoPath) }, ); - const config = useApiResource( - `${repoPath}/gittensor-config-recommendation`, + const config = useApiResource( + `${repoPath ?? "/v1/repos/__invalid__/__invalid__"}/gittensor-config-recommendation`, "Config recommendation", + undefined, + { enabled: Boolean(repoPath) }, ); - const liveSteps = useMemo(() => { + + const workspace = useMemo(() => { if (readiness.status !== "ready") return null; - const steps = [ - { - id: "mode", - title: "Registration mode", - detail: readiness.data.recommendedRegistrationMode, - status: readiness.data.ready ? "ok" : "warn", - }, - { - id: "issue-policy", - title: "Issue policy", - detail: readiness.data.issuePolicy, - status: readiness.data.ready ? "ok" : "warn", - }, - ...readiness.data.blockers.map((detail, i) => ({ - id: `blocker-${i}`, - title: "Readiness blocker", - detail, - status: "blocked", - })), - ...readiness.data.warnings.map((detail, i) => ({ - id: `warning-${i}`, - title: "Readiness warning", - detail, - status: "warn", - })), - ]; - return steps.length > 0 ? steps : null; - }, [readiness]); - const steps = liveSteps ?? []; - const removed = config.status === "ready" ? recordLines(config.data.current ?? {}) : []; - const added = config.status === "ready" ? recordLines(config.data.recommended) : []; + const configPayload = config.status === "ready" ? config.data : null; + return buildRegistrationWorkspaceView(readiness.data, configPayload); + }, [readiness, config]); + const refresh = () => { void readiness.reload(); void config.reload(); }; + const invalidRepo = repo.trim().length > 0 && !parts; + return ( - -
-
-
-
-

Registration readiness

-

- Live read-only API check from cached repository intelligence. -

-
-
- - setRepo(e.target.value)} - className="mt-1 font-mono text-token-xs" - /> +
+
+
+
+

Registration workspace

+

+ Readiness report with lane tradeoffs — not raw Gittensor telemetry. +

+
+
+ + setRepo(e.target.value)} + className="mt-1 font-mono text-token-xs" + placeholder="owner/repo" + /> + {invalidRepo ? ( +

Use a valid owner/repo slug.

+ ) : null} +
+
+
+ + + {workspace ? ( + + ) : null} + {repoPath && readiness.status === "error" ? ( +

Readiness failed ({readiness.error}).

+ ) : null} +
+
+ ); +} + +function RegistrationWorkspace({ + workspace, + generatedLabel, +}: { + workspace: RegistrationWorkspaceView; + generatedLabel: string; +}) { + return ( +
+
+
+
+
+

{workspace.repoFullName}

+ + {workspace.summary.ready ? "Ready" : "Not ready"} + + + {workspace.freshness.status} + +
+

{workspace.summary.headline}

+

Generated {generatedLabel}

- {readiness.status === "error" && ( -

- Live readiness failed ({readiness.error}). -

- )} -
    - {steps.map((s) => ( -
  • -
    -
    {s.title}
    -
    {s.detail}
    -
    - {s.status} -
  • +
+

+ {workspace.advisoryBanner} +

+ {workspace.freshness.warnings.length > 0 ? ( +
    + {workspace.freshness.warnings.map((warning) => ( +
  • {warning}
  • ))}
-
+ ) : null} +
+ + +
+
-
-

Suggested .gittensor.yml

-

- Diff against the current configuration. Apply via PR when ready. -

- {config.status === "error" && ( -

- Live recommendation failed ({config.error}). +

+ + + + +
+ +
+

Operations & policy

+
+ {workspace.operations.map((section) => ( + + ))} +
+ {workspace.policyWarnings.length > 0 ? ( +
+

Focus policy warnings

+
    + {workspace.policyWarnings.map((warning) => ( +
  • +
    + {warning.title} + + {warning.severity} + +
    +

    {warning.detail}

    +

    {warning.action}

    +
  • + ))} +
+
+ ) : null} +
+ + {workspace.config ? ( +
+
+

Suggested Gittensor config

+

+ Tradeoffs below separate maintainer economics from contributor lanes. Apply via PR when ready.

- )} -
-
+ {workspace.config.tradeoffs.length > 0 ? ( +
+

Tradeoffs

+
    + {workspace.config.tradeoffs.map((entry) => ( +
  • {entry}
  • + ))} +
+
+ ) : null} +
+ ) : null} +
+ ); +} + +function WorkspaceSectionCard({ + section, +}: { + section: RegistrationWorkspaceView["lanes"]["directPr"]; +}) { + return ( +
+
+

{section.title}

+ {section.status}
- +

{section.summary}

+ {section.bullets.length > 0 ? ( +
    + {section.bullets.map((bullet) => ( +
  • {bullet}
  • + ))} +
+ ) : null} +
); } -function recordLines(record: Record) { - const entries = Object.entries(record); - if (entries.length === 0) return ["{}"]; - return entries.slice(0, 8).map(([key, value]) => `${key}: ${formatValue(value)}`); +function Metric({ label, value }: { label: string; value: string }) { + return ( +
+
{label}
+
{value}
+
+ ); } -function formatValue(value: unknown) { - if (value === null) return "null"; - if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { - return String(value); - } - return JSON.stringify(value); +function formatGeneratedAt(value: string) { + const parsed = Date.parse(value); + if (!Number.isFinite(parsed)) return value; + return new Date(parsed).toLocaleString(); } diff --git a/apps/gittensory-ui/src/lib/registration-workspace.ts b/apps/gittensory-ui/src/lib/registration-workspace.ts new file mode 100644 index 00000000..9f313baf --- /dev/null +++ b/apps/gittensory-ui/src/lib/registration-workspace.ts @@ -0,0 +1,333 @@ +import { splitRepoFullName } from "@/lib/maintainer-settings-preview"; + +export type WorkspaceFreshness = "complete" | "degraded" | "stale" | "unknown"; +export type WorkspaceLaneStatus = "ready" | "warn" | "blocked" | "info"; + +const FORBIDDEN_PUBLIC_LANGUAGE = + /wallet|hotkey|coldkey|raw trust score|payout|reward estimate|farming|private reviewability|public score estimate|mnemonic|seed phrase|private key/i; + +export type RegistrationWorkspaceDataQuality = { + status?: string | undefined; + partial?: boolean | undefined; + warnings?: string[] | undefined; +}; + +export type RegistrationReadinessPayload = { + repoFullName: string; + generatedAt: string; + ready: boolean; + recommendedRegistrationMode: string; + issuePolicy: string; + directPrReadiness: { ready: boolean; reasons: string[] }; + issueDiscoveryReadiness: { ready: boolean; recommendation: string; reasons: string[] }; + labelPolicy: Record; + maintainerCutReadiness: Record; + testCoverageHealth: { + status: string; + trustedLabelPipelineReady: boolean; + checkRunMode: string; + requiredGate: string[]; + note: string; + warnings: string[]; + }; + queueHealth: { level: string; burdenScore: number; reviewablePullRequests: number; summary: string }; + contributorIntakeHealth: Record; + githubApp: { + installed: boolean; + publicSurface: string; + commentMode: string; + checkRunMode: string; + quietByDefault: boolean; + behavior: string; + warnings: string[]; + }; + policyReadiness: { + summary: string; + publicWarnings: Array<{ title: string; detail: string; action: string; severity: string }>; + } | null; + blockers: string[]; + warnings: string[]; + dataQuality?: RegistrationWorkspaceDataQuality | undefined; +}; + +export type GittensorConfigRecommendationPayload = { + repoFullName: string; + generatedAt: string; + privateOnly?: boolean | undefined; + current: Record | null; + recommended: Record; + tradeoffs: string[]; + reasons: string[]; + warnings: string[]; + dataQuality?: RegistrationWorkspaceDataQuality | undefined; +}; + +export type RegistrationWorkspaceSection = { + id: string; + title: string; + status: WorkspaceLaneStatus; + summary: string; + bullets: string[]; +}; + +export type RegistrationWorkspaceView = { + repoFullName: string; + generatedAt: string; + advisoryBanner: string; + freshness: { status: WorkspaceFreshness; warnings: string[] }; + summary: { + ready: boolean; + headline: string; + recommendedMode: string; + issuePolicy: string; + status: WorkspaceLaneStatus; + }; + lanes: { + directPr: RegistrationWorkspaceSection; + issueDiscovery: RegistrationWorkspaceSection; + maintainerEconomics: RegistrationWorkspaceSection; + minerGuidance: RegistrationWorkspaceSection; + }; + operations: RegistrationWorkspaceSection[]; + policyWarnings: Array<{ title: string; detail: string; action: string; severity: string }>; + config: { + tradeoffs: string[]; + reasons: string[]; + warnings: string[]; + recommendedLines: string[]; + currentLines: string[]; + } | null; +}; + +export function isRegistrationWorkspacePublicSafe(text: string): boolean { + return !FORBIDDEN_PUBLIC_LANGUAGE.test(text); +} + +export function sanitizeRegistrationWorkspaceText(text: string): string | null { + const trimmed = text.trim(); + if (!trimmed || !isRegistrationWorkspacePublicSafe(trimmed)) return null; + return trimmed; +} + +export function resolveRegistrationWorkspaceFreshness( + readinessQuality?: RegistrationWorkspaceDataQuality, + configQuality?: RegistrationWorkspaceDataQuality, +): { status: WorkspaceFreshness; warnings: string[] } { + const warnings = [ + ...(readinessQuality?.warnings ?? []), + ...(configQuality?.warnings ?? []), + ] + .map((entry) => sanitizeRegistrationWorkspaceText(entry)) + .filter((entry): entry is string => Boolean(entry)); + const statuses = [readinessQuality?.status, configQuality?.status].filter((entry): entry is string => Boolean(entry)); + if (statuses.includes("blocked")) return { status: "stale", warnings }; + if (readinessQuality?.partial || configQuality?.partial || statuses.includes("degraded")) { + return { status: "degraded", warnings }; + } + if (statuses.includes("complete")) return { status: "complete", warnings }; + return { status: warnings.length > 0 ? "degraded" : "unknown", warnings }; +} + +export function buildRegistrationWorkspaceView( + readiness: RegistrationReadinessPayload, + config: GittensorConfigRecommendationPayload | null, +): RegistrationWorkspaceView { + const freshness = resolveRegistrationWorkspaceFreshness(readiness.dataQuality, config?.dataQuality); + const directPrStatus: WorkspaceLaneStatus = readiness.directPrReadiness.ready + ? "ready" + : readiness.blockers.length > 0 + ? "blocked" + : "warn"; + const issueDiscoveryStatus: WorkspaceLaneStatus = + readiness.issueDiscoveryReadiness.recommendation === "not_recommended" + ? "info" + : readiness.issueDiscoveryReadiness.ready + ? "ready" + : "warn"; + + const maintainerCut = readiness.maintainerCutReadiness; + const maintainerReady = maintainerCut.ready === true; + const maintainerEconomicsStatus: WorkspaceLaneStatus = maintainerReady ? "ready" : readiness.ready ? "warn" : "blocked"; + + const labelPolicy = readiness.labelPolicy; + const intake = readiness.contributorIntakeHealth; + + return { + repoFullName: readiness.repoFullName, + generatedAt: readiness.generatedAt, + advisoryBanner: + "Advisory workspace only. Recommendations explain tradeoffs for repo owners; they do not guarantee Gittensor payouts or miner rewards.", + freshness, + summary: { + ready: readiness.ready, + headline: readiness.ready + ? "Repository looks ready for contributor intake with the recommended posture." + : "Resolve blockers before inviting more outside contributor traffic.", + recommendedMode: readiness.recommendedRegistrationMode, + issuePolicy: readiness.issuePolicy, + status: readiness.ready ? "ready" : readiness.blockers.length > 0 ? "blocked" : "warn", + }, + lanes: { + directPr: { + id: "direct-pr", + title: "Direct PR lane", + status: directPrStatus, + summary: readiness.directPrReadiness.ready + ? "Direct-PR intake is healthy enough for the recommended registration mode." + : "Direct-PR intake needs attention before broadening contributor traffic.", + bullets: sanitizeBulletList(readiness.directPrReadiness.reasons), + }, + issueDiscovery: { + id: "issue-discovery", + title: "Issue discovery lane", + status: issueDiscoveryStatus, + summary: `Recommendation: ${readiness.issueDiscoveryReadiness.recommendation.replace(/_/g, " ")}.`, + bullets: sanitizeBulletList(readiness.issueDiscoveryReadiness.reasons), + }, + maintainerEconomics: { + id: "maintainer-economics", + title: "Maintainer economics", + status: maintainerEconomicsStatus, + summary: stringField(maintainerCut, "summary") ?? "Maintainer-cut posture is separate from miner reward estimates.", + bullets: sanitizeBulletList([ + ...(Array.isArray(maintainerCut.reasons) ? (maintainerCut.reasons as string[]) : []), + ...(Array.isArray(maintainerCut.warnings) ? (maintainerCut.warnings as string[]) : []), + typeof maintainerCut.recommendedAction === "string" + ? `Suggested action: ${maintainerCut.recommendedAction.replace(/_/g, " ")}.` + : "", + ]), + }, + minerGuidance: { + id: "miner-guidance", + title: "Miner scoreability (separate)", + status: "info", + summary: + "Contributor/miner scoreability and queue pressure are evaluated separately from maintainer-cut economics.", + bullets: sanitizeBulletList([ + `Contributor intake: ${stringField(intake, "level") ?? "unknown"}.`, + `Queue burden: ${readiness.queueHealth.level} (${readiness.queueHealth.reviewablePullRequests} reviewable PRs).`, + readiness.queueHealth.summary, + ]), + }, + }, + operations: [ + { + id: "queue-health", + title: "Queue health", + status: queueStatus(readiness.queueHealth.level), + summary: readiness.queueHealth.summary, + bullets: sanitizeBulletList([ + `Burden score: ${readiness.queueHealth.burdenScore}.`, + `Reviewable pull requests: ${readiness.queueHealth.reviewablePullRequests}.`, + ]), + }, + { + id: "label-policy", + title: "Label policy", + status: labelPolicy.trustedPipelineReady === true ? "ready" : "warn", + summary: "Registry labels and trusted pipeline readiness for incoming work.", + bullets: sanitizeBulletList([ + labelPolicy.autoLabelEnabled === true + ? `Auto-label enabled (${String(labelPolicy.label ?? "gittensor")}).` + : "Auto-label is disabled.", + labelPolicy.trustedPipelineReady === true + ? "Trusted label pipeline is verified." + : "Trusted label pipeline is not verified yet.", + ...(Array.isArray(labelPolicy.missingOrUnusedRegistryLabels) + ? (labelPolicy.missingOrUnusedRegistryLabels as string[]).map((label) => `Missing or unused label: ${label}`) + : []), + ]), + }, + { + id: "test-policy", + title: "Test & validation policy", + status: readiness.testCoverageHealth.status === "gate_ready" ? "ready" : "warn", + summary: readiness.testCoverageHealth.note, + bullets: sanitizeBulletList([ + `Coverage gate: ${readiness.testCoverageHealth.status}.`, + `Check runs: ${readiness.testCoverageHealth.checkRunMode}.`, + ...(readiness.testCoverageHealth.requiredGate ?? []).map((gate) => `Required gate: ${gate}`), + ...readiness.testCoverageHealth.warnings, + ]), + }, + { + id: "github-app", + title: "GitHub App behavior", + status: readiness.githubApp.installed ? "ready" : "warn", + summary: readiness.githubApp.behavior, + bullets: sanitizeBulletList([ + `Public surface: ${readiness.githubApp.publicSurface}.`, + `Comment mode: ${readiness.githubApp.commentMode}.`, + ...(readiness.githubApp.quietByDefault ? ["Quiet-by-default posture is enabled."] : []), + ...readiness.githubApp.warnings, + ]), + }, + ], + policyWarnings: (readiness.policyReadiness?.publicWarnings ?? []) + .map((warning) => ({ + title: sanitizeRegistrationWorkspaceText(warning.title) ?? "Policy warning", + detail: sanitizeRegistrationWorkspaceText(warning.detail) ?? "", + action: sanitizeRegistrationWorkspaceText(warning.action) ?? "", + severity: warning.severity, + })) + .filter((warning) => warning.detail.length > 0), + config: config + ? { + tradeoffs: sanitizeBulletList(config.tradeoffs), + reasons: sanitizeBulletList(config.reasons), + warnings: sanitizeBulletList(config.warnings), + recommendedLines: recordLines(config.recommended), + currentLines: recordLines(config.current ?? {}), + } + : null, + }; +} + +export function collectRegistrationWorkspacePublicText(view: RegistrationWorkspaceView): string[] { + const chunks = [ + view.advisoryBanner, + view.summary.headline, + ...view.freshness.warnings, + ...view.lanes.directPr.bullets, + ...view.lanes.issueDiscovery.bullets, + ...view.lanes.maintainerEconomics.bullets, + ...view.lanes.minerGuidance.bullets, + ...view.operations.flatMap((section) => [section.summary, ...section.bullets]), + ...(view.config?.tradeoffs ?? []), + ...(view.config?.reasons ?? []), + ...view.policyWarnings.flatMap((warning) => [warning.title, warning.detail, warning.action]), + ]; + return chunks.filter((entry) => entry.length > 0); +} + +export { splitRepoFullName }; + +function sanitizeBulletList(entries: string[]): string[] { + return entries.map((entry) => sanitizeRegistrationWorkspaceText(entry)).filter((entry): entry is string => Boolean(entry)); +} + +function stringField(record: Record, key: string): string | null { + const value = record[key]; + return typeof value === "string" ? sanitizeRegistrationWorkspaceText(value) : null; +} + +function queueStatus(level: string): WorkspaceLaneStatus { + if (level === "low") return "ready"; + if (level === "medium") return "warn"; + if (level === "high" || level === "critical") return "blocked"; + return "info"; +} + +function recordLines(record: Record): string[] { + const entries = Object.entries(record); + if (entries.length === 0) return ["{}"]; + return entries.slice(0, 10).map(([key, value]) => `${key}: ${formatValue(value)}`); +} + +function formatValue(value: unknown): string { + if (value === null) return "null"; + if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { + return String(value); + } + return JSON.stringify(value); +} diff --git a/apps/gittensory-ui/src/routes/app.owner.tsx b/apps/gittensory-ui/src/routes/app.owner.tsx index 05c42179..04f4ed49 100644 --- a/apps/gittensory-ui/src/routes/app.owner.tsx +++ b/apps/gittensory-ui/src/routes/app.owner.tsx @@ -12,8 +12,8 @@ function OwnerRoute() {
diff --git a/apps/gittensory-ui/src/routes/app.repos.tsx b/apps/gittensory-ui/src/routes/app.repos.tsx index 1af88dc7..2a366036 100644 --- a/apps/gittensory-ui/src/routes/app.repos.tsx +++ b/apps/gittensory-ui/src/routes/app.repos.tsx @@ -18,7 +18,7 @@ export const Route = createFileRoute("/app/repos")({ const LABELS: Record = { maintainer: "Maintainer console", - owner: "Repo owner", + owner: "Registration workspace", }; function Repos() { @@ -31,7 +31,7 @@ function Repos() { = {}): RegistrationReadinessPayload { + return { + repoFullName: "JSONbored/gittensory", + generatedAt: "2026-06-01T00:00:00.000Z", + ready: true, + recommendedRegistrationMode: "direct_pr", + issuePolicy: "direct_pr_no_issue_required", + directPrReadiness: { ready: true, reasons: ["Direct-PR intake is healthy."] }, + issueDiscoveryReadiness: { + ready: false, + recommendation: "not_recommended", + reasons: ["Issue discovery should stay off until intake is excellent."], + }, + labelPolicy: { + autoLabelEnabled: true, + label: "gittensor", + trustedPipelineReady: true, + missingOrUnusedRegistryLabels: [], + }, + maintainerCutReadiness: { + ready: true, + summary: "Maintainer cut can be reviewed without blocking intake.", + reasons: ["Queue burden is low."], + warnings: [], + recommendedAction: "consider_small_cut", + }, + testCoverageHealth: { + status: "gate_ready", + trustedLabelPipelineReady: true, + checkRunMode: "enabled", + requiredGate: ["npm run test:ci"], + note: "Use repo CI gates before widening contributor intake.", + warnings: [], + }, + queueHealth: { + level: "low", + burdenScore: 0.2, + reviewablePullRequests: 3, + summary: "Queue burden is low.", + }, + contributorIntakeHealth: { level: "healthy", summary: "Contributor intake is healthy." }, + githubApp: { + installed: true, + publicSurface: "comment_and_label", + commentMode: "detected_contributors_only", + checkRunMode: "enabled", + quietByDefault: true, + behavior: "Quiet-by-default GitHub App assistance.", + warnings: [], + }, + policyReadiness: null, + blockers: [], + warnings: [], + dataQuality: { status: "complete", partial: false, warnings: [] }, + ...overrides, + }; +} + +function configFixture(): GittensorConfigRecommendationPayload { + return { + repoFullName: "JSONbored/gittensory", + generatedAt: "2026-06-01T00:00:00.000Z", + privateOnly: true, + current: { maintainerCut: 0 }, + recommended: { participationMode: "direct_pr", maintainerCut: 0.3, issueDiscoveryShare: 0 }, + tradeoffs: [ + "Staying direct-PR-only keeps maintainer triage low but forgoes issue-discovery contributor flow.", + "Introducing a maintainer cut rewards upkeep but reduces the share available to contributor miners.", + ], + reasons: ["Direct-PR mode is the safest default until issue-discovery intake is intentionally staffed."], + warnings: [], + dataQuality: { status: "complete", partial: false, warnings: [] }, + }; +} + +describe("registration workspace UI helpers", () => { + it("ready fixture produces an advisory workspace view with separated lanes", () => { + const view = buildRegistrationWorkspaceView(readyFixture(), configFixture()); + expect(view.summary.ready).toBe(true); + expect(view.lanes.directPr.status).toBe("ready"); + expect(view.lanes.issueDiscovery.title).toMatch(/Issue discovery/i); + expect(view.lanes.maintainerEconomics.title).toMatch(/Maintainer economics/i); + expect(view.lanes.minerGuidance.title).toMatch(/Miner scoreability/i); + expect(view.config?.tradeoffs.length).toBeGreaterThan(0); + expect(view.advisoryBanner).toMatch(/Advisory/i); + }); + + it("not-ready fixture surfaces blockers and blocked summary status", () => { + const view = buildRegistrationWorkspaceView( + readyFixture({ + ready: false, + blockers: ["Repository config quality needs attention before registration promotion."], + directPrReadiness: { ready: false, reasons: ["Config quality is fragile."] }, + }), + null, + ); + expect(view.summary.ready).toBe(false); + expect(view.summary.status).toBe("blocked"); + expect(view.summary.headline).toMatch(/Resolve blockers/i); + }); + + it("stale data fixture marks freshness degraded and keeps warnings", () => { + const freshness = resolveRegistrationWorkspaceFreshness( + { status: "degraded", partial: true, warnings: ["Burden forecast unavailable for JSONbored/gittensory."] }, + { status: "complete", partial: false, warnings: [] }, + ); + expect(freshness.status).toBe("degraded"); + expect(freshness.warnings[0]).toMatch(/Burden forecast unavailable/i); + + const view = buildRegistrationWorkspaceView( + readyFixture({ + dataQuality: { status: "degraded", partial: true, warnings: freshness.warnings }, + }), + configFixture(), + ); + expect(view.freshness.status).toBe("degraded"); + expect(view.freshness.warnings.length).toBeGreaterThan(0); + }); + + it("public text hygiene regression drops forbidden language from workspace output", () => { + const view = buildRegistrationWorkspaceView( + readyFixture({ + warnings: ["wallet hotkey payout estimate should be removed"], + directPrReadiness: { ready: true, reasons: ["Safe reason only."] }, + }), + configFixture(), + ); + const publicText = collectRegistrationWorkspacePublicText(view).join("\n"); + expect(publicText).not.toMatch(FORBIDDEN); + expect(sanitizeRegistrationWorkspaceText("estimate your reward")).toBeNull(); + expect(isRegistrationWorkspacePublicSafe("Queue burden is low.")).toBe(true); + }); + + it("never emits forbidden language across randomized warning injections", () => { + const injections = [ + "wallet", + "hotkey", + "raw trust score", + "payout", + "reward estimate", + "farming", + "private reviewability", + "public score estimate", + ]; + for (const injection of injections) { + const view = buildRegistrationWorkspaceView( + readyFixture({ warnings: [`Blocked phrase ${injection} must not render`] }), + configFixture(), + ); + expect(collectRegistrationWorkspacePublicText(view).join(" ")).not.toMatch(FORBIDDEN); + } + }); +}); From 820ddc5c3b6b4813f2d7956e3ab5c841e02c2b55 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:07:53 +0200 Subject: [PATCH 02/10] fix(ui): use relative import for root typecheck Root tsc cannot resolve @/ aliases when unit tests import registration-workspace. --- .../src/lib/registration-workspace.ts | 321 +++++++++++++++++- 1 file changed, 320 insertions(+), 1 deletion(-) diff --git a/apps/gittensory-ui/src/lib/registration-workspace.ts b/apps/gittensory-ui/src/lib/registration-workspace.ts index 9f313baf..56278865 100644 --- a/apps/gittensory-ui/src/lib/registration-workspace.ts +++ b/apps/gittensory-ui/src/lib/registration-workspace.ts @@ -1,4 +1,4 @@ -import { splitRepoFullName } from "@/lib/maintainer-settings-preview"; +import { splitRepoFullName } from "./maintainer-settings-preview"; export type WorkspaceFreshness = "complete" | "degraded" | "stale" | "unknown"; export type WorkspaceLaneStatus = "ready" | "warn" | "blocked" | "info"; @@ -47,9 +47,45 @@ export type RegistrationReadinessPayload = { } | null; blockers: string[]; warnings: string[]; + docsCompleteness?: { status: string; requiredDocs: string[]; note: string } | undefined; dataQuality?: RegistrationWorkspaceDataQuality | undefined; }; +export type OwnerWorkflowState = "accepted" | "needs_cleanup" | "not_ready"; + +export type OwnerWorkflowRemediationKind = "action" | "manual"; + +export type OwnerWorkflowBucketId = + | "policy" + | "data_quality" + | "queue_health" + | "docs_onboarding" + | "maintainer_capacity"; + +export type OwnerWorkflowItem = { + id: string; + title: string; + state: OwnerWorkflowState; + summary: string; + remediation: string; + remediationKind: OwnerWorkflowRemediationKind; +}; + +export type OwnerWorkflowBucket = { + id: OwnerWorkflowBucketId; + title: string; + state: OwnerWorkflowState; + summary: string; + items: OwnerWorkflowItem[]; +}; + +export type RegistrationOwnerWorkflow = { + overallState: OwnerWorkflowState; + overallHeadline: string; + buckets: OwnerWorkflowBucket[]; + nextSteps: string[]; +}; + export type GittensorConfigRecommendationPayload = { repoFullName: string; generatedAt: string; @@ -90,6 +126,7 @@ export type RegistrationWorkspaceView = { }; operations: RegistrationWorkspaceSection[]; policyWarnings: Array<{ title: string; detail: string; action: string; severity: string }>; + workflow: RegistrationOwnerWorkflow; config: { tradeoffs: string[]; reasons: string[]; @@ -271,6 +308,7 @@ export function buildRegistrationWorkspaceView( severity: warning.severity, })) .filter((warning) => warning.detail.length > 0), + workflow: buildRegistrationOwnerWorkflow(readiness, config), config: config ? { tradeoffs: sanitizeBulletList(config.tradeoffs), @@ -283,10 +321,227 @@ export function buildRegistrationWorkspaceView( }; } +export function buildRegistrationOwnerWorkflow( + readiness: RegistrationReadinessPayload, + config: GittensorConfigRecommendationPayload | null, +): RegistrationOwnerWorkflow { + const freshness = resolveRegistrationWorkspaceFreshness(readiness.dataQuality, config?.dataQuality); + const intakeLevel = stringField(readiness.contributorIntakeHealth, "level") ?? "unknown"; + const labelPolicy = readiness.labelPolicy; + const docs = readiness.docsCompleteness ?? { status: "unknown", requiredDocs: [], note: "" }; + + const policyItems: OwnerWorkflowItem[] = []; + for (const warning of readiness.policyReadiness?.publicWarnings ?? []) { + const title = sanitizeRegistrationWorkspaceText(warning.title); + const detail = sanitizeRegistrationWorkspaceText(warning.detail); + const action = sanitizeRegistrationWorkspaceText(warning.action); + if (!title || !detail) continue; + policyItems.push({ + id: `policy-warning-${policyItems.length}`, + title, + state: warning.severity === "critical" ? "not_ready" : "needs_cleanup", + summary: detail, + remediation: action ?? "Review focus manifest and repository settings for consistency.", + remediationKind: action ? "action" : "manual", + }); + } + if (!readiness.directPrReadiness.ready) { + policyItems.push({ + id: "direct-pr-lane", + title: "Direct PR lane", + state: readiness.blockers.length > 0 ? "not_ready" : "needs_cleanup", + summary: "Direct-PR intake is not ready for the recommended registration mode.", + remediation: "Stabilize config quality and contributor intake before promoting direct-PR traffic.", + remediationKind: "action", + }); + } + if (labelPolicy.trustedPipelineReady !== true) { + policyItems.push({ + id: "label-trust", + title: "Label policy", + state: "needs_cleanup", + summary: "Trusted label pipeline is not verified for registry labels.", + remediation: "Create or verify configured registry labels and enable the trusted label pipeline in repo settings.", + remediationKind: "action", + }); + } + + const dataQualityItems: OwnerWorkflowItem[] = []; + if (freshness.status === "stale" || freshness.status === "degraded") { + for (const warning of freshness.warnings) { + dataQualityItems.push({ + id: `data-warning-${dataQualityItems.length}`, + title: "Signal freshness", + state: freshness.status === "stale" ? "not_ready" : "needs_cleanup", + summary: warning, + remediation: "Wait for repository intelligence to refresh or run a maintainer backfill before acting on readiness.", + remediationKind: "manual", + }); + } + } + if (readiness.testCoverageHealth.status !== "gate_ready") { + dataQualityItems.push({ + id: "test-gate", + title: "Validation gate", + state: "needs_cleanup", + summary: readiness.testCoverageHealth.note, + remediation: "Document expected CI commands in the repo and verify check-run settings before widening intake.", + remediationKind: "action", + }); + } + for (const warning of readiness.testCoverageHealth.warnings) { + const safe = sanitizeRegistrationWorkspaceText(warning); + if (!safe) continue; + dataQualityItems.push({ + id: `test-warning-${dataQualityItems.length}`, + title: "Test policy warning", + state: "needs_cleanup", + summary: safe, + remediation: "Address validation warnings before inviting more contributor traffic.", + remediationKind: "action", + }); + } + + const queueItems: OwnerWorkflowItem[] = []; + const queueLevel = readiness.queueHealth.level; + if (queueLevel === "high" || queueLevel === "critical") { + queueItems.push({ + id: "queue-pressure", + title: "PR queue pressure", + state: "not_ready", + summary: readiness.queueHealth.summary, + remediation: "Reduce open PR queue pressure or narrow accepted lanes before inviting more contributors.", + remediationKind: "action", + }); + } else if (queueLevel === "medium") { + queueItems.push({ + id: "queue-pressure", + title: "PR queue pressure", + state: "needs_cleanup", + summary: readiness.queueHealth.summary, + remediation: "Review open PRs and triage burden before expanding contributor intake.", + remediationKind: "action", + }); + } + + const docsItems: OwnerWorkflowItem[] = []; + if (docs.status === "repo_docs_not_crawled") { + docsItems.push({ + id: "docs-crawl", + title: "Onboarding docs", + state: "needs_cleanup", + summary: docs.note, + remediation: + "Manually verify CONTRIBUTING.md, README onboarding steps, and issue templates in GitHub; remote doc crawling is not enabled in this signal yet.", + remediationKind: "manual", + }); + } else if (docs.requiredDocs.length > 0) { + docsItems.push({ + id: "docs-required", + title: "Required docs", + state: "accepted", + summary: `Expected docs: ${docs.requiredDocs.join(", ")}.`, + remediation: "Keep onboarding docs aligned with the recommended registration mode.", + remediationKind: "action", + }); + } + + const capacityItems: OwnerWorkflowItem[] = []; + if (intakeLevel === "blocked" || intakeLevel === "strained") { + capacityItems.push({ + id: "intake-health", + title: "Contributor intake", + state: intakeLevel === "blocked" ? "not_ready" : "needs_cleanup", + summary: stringField(readiness.contributorIntakeHealth, "summary") ?? `Contributor intake is ${intakeLevel}.`, + remediation: "Stabilize triage capacity and duplicate-risk intake before inviting more issue reports or direct PRs.", + remediationKind: "action", + }); + } + const maintainerCut = readiness.maintainerCutReadiness; + if (maintainerCut.ready !== true) { + capacityItems.push({ + id: "maintainer-cut", + title: "Maintainer cut readiness", + state: readiness.ready ? "needs_cleanup" : "not_ready", + summary: stringField(maintainerCut, "summary") ?? "Maintainer-cut posture needs review.", + remediation: + typeof maintainerCut.recommendedAction === "string" + ? `Follow maintainer-cut guidance: ${maintainerCut.recommendedAction.replace(/_/g, " ")}.` + : "Resolve queue and config blockers before changing maintainer_cut.", + remediationKind: "action", + }); + } + for (const blocker of readiness.blockers) { + const safe = sanitizeRegistrationWorkspaceText(blocker); + if (!safe) continue; + const bucket = classifyBlockerBucket(safe); + const target = + bucket === "policy" + ? policyItems + : bucket === "data_quality" + ? dataQualityItems + : bucket === "queue_health" + ? queueItems + : bucket === "docs_onboarding" + ? docsItems + : capacityItems; + target.push({ + id: `blocker-${target.length}`, + title: "Registration blocker", + state: "not_ready", + summary: safe, + remediation: remediationForBlocker(safe), + remediationKind: remediationKindForBlocker(safe), + }); + } + + const buckets: OwnerWorkflowBucket[] = [ + buildWorkflowBucket("policy", "Policy & lanes", policyItems, "Focus manifest, lane posture, and label policy."), + buildWorkflowBucket("data_quality", "Data quality", dataQualityItems, "Signal freshness and validation gates."), + buildWorkflowBucket("queue_health", "Queue health", queueItems, "Open PR pressure and reviewable queue burden."), + buildWorkflowBucket("docs_onboarding", "Docs & onboarding", docsItems, "Contributor-facing documentation readiness."), + buildWorkflowBucket( + "maintainer_capacity", + "Maintainer capacity", + capacityItems, + "Intake health, maintainer-cut posture, and GitHub App assistance.", + ), + ]; + + const overallState = aggregateWorkflowState(buckets.map((bucket) => bucket.state)); + const nextSteps = buckets + .flatMap((bucket) => bucket.items) + .filter((item) => item.state !== "accepted") + .sort((left, right) => workflowRank(left.state) - workflowRank(right.state)) + .map((item) => item.remediation) + .filter((entry, index, all) => all.indexOf(entry) === index) + .slice(0, 5); + + return { + overallState, + overallHeadline: workflowHeadline(overallState, readiness.ready), + buckets, + nextSteps, + }; +} + +export function collectRegistrationOwnerWorkflowPublicText(workflow: RegistrationOwnerWorkflow): string[] { + return [ + workflow.overallHeadline, + ...workflow.nextSteps, + ...workflow.buckets.flatMap((bucket) => [ + bucket.title, + bucket.summary, + ...bucket.items.flatMap((item) => [item.title, item.summary, item.remediation]), + ]), + ]; +} + export function collectRegistrationWorkspacePublicText(view: RegistrationWorkspaceView): string[] { const chunks = [ view.advisoryBanner, view.summary.headline, + ...collectRegistrationOwnerWorkflowPublicText(view.workflow), ...view.freshness.warnings, ...view.lanes.directPr.bullets, ...view.lanes.issueDiscovery.bullets, @@ -331,3 +586,67 @@ function formatValue(value: unknown): string { } return JSON.stringify(value); } + +function buildWorkflowBucket( + id: OwnerWorkflowBucketId, + title: string, + items: OwnerWorkflowItem[], + summary: string, +): OwnerWorkflowBucket { + const state = items.length === 0 ? "accepted" : aggregateWorkflowState(items.map((item) => item.state)); + return { id, title, state, summary, items }; +} + +function aggregateWorkflowState(states: OwnerWorkflowState[]): OwnerWorkflowState { + if (states.includes("not_ready")) return "not_ready"; + if (states.includes("needs_cleanup")) return "needs_cleanup"; + return "accepted"; +} + +function workflowRank(state: OwnerWorkflowState): number { + if (state === "not_ready") return 0; + if (state === "needs_cleanup") return 1; + return 2; +} + +function workflowHeadline(state: OwnerWorkflowState, ready: boolean): string { + if (state === "accepted" && ready) { + return "Accepted — contributor intake posture matches the recommended registration mode."; + } + if (state === "not_ready") { + return "Not ready — resolve blockers in the workflow buckets before inviting more contributors."; + } + return "Needs cleanup — some areas are acceptable but require maintainer follow-up before scaling intake."; +} + +function classifyBlockerBucket(blocker: string): OwnerWorkflowBucketId { + const lower = blocker.toLowerCase(); + if (lower.includes("config quality") || lower.includes("focus") || lower.includes("label")) return "policy"; + if (lower.includes("doc") || lower.includes("contributing")) return "docs_onboarding"; + if (lower.includes("queue") || lower.includes("pull request") || lower.includes("pr ")) return "queue_health"; + if (lower.includes("install") || lower.includes("github app") || lower.includes("intake")) return "maintainer_capacity"; + if (lower.includes("drift") || lower.includes("forecast") || lower.includes("coverage")) return "data_quality"; + return "maintainer_capacity"; +} + +function remediationForBlocker(blocker: string): string { + const bucket = classifyBlockerBucket(blocker); + switch (bucket) { + case "policy": + return "Fix registry config, focus manifest, or label policy issues referenced in the blocker."; + case "data_quality": + return "Refresh repository intelligence or update validation fixtures before re-checking readiness."; + case "queue_health": + return "Reduce queue pressure and close or triage stale pull requests before expanding intake."; + case "docs_onboarding": + return "Update onboarding docs and templates in the repository (manual GitHub edit required)."; + case "maintainer_capacity": + return "Address installation health, intake strain, or maintainer-cut blockers before promoting the repo."; + } +} + +function remediationKindForBlocker(blocker: string): OwnerWorkflowRemediationKind { + const lower = blocker.toLowerCase(); + if (lower.includes("not crawled") || lower.includes("manual") || lower.includes("install")) return "manual"; + return "action"; +} From 9b9444b59e0902c61518bd57dfdf5e1e710b3bad Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:13:15 +0200 Subject: [PATCH 03/10] feat(settings): repo onboarding pack preview for accepted repos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add policy compiler output → sanitized preview-only onboarding pack, GET preview API for registered repos, and tests for sanitizer boundaries. Fixes #248 --- apps/gittensory-ui/public/openapi.json | 33 ++++++++ src/api/routes.ts | 14 +++ src/openapi/spec.ts | 9 ++ src/services/repo-onboarding-pack.ts | 52 ++++++++++++ src/signals/repo-policy-compiler.ts | 113 +++++++++++++++++++++++++ test/unit/onboarding-pack.test.ts | 60 +++++++++++++ 6 files changed, 281 insertions(+) create mode 100644 src/services/repo-onboarding-pack.ts create mode 100644 src/signals/repo-policy-compiler.ts diff --git a/apps/gittensory-ui/public/openapi.json b/apps/gittensory-ui/public/openapi.json index 2697717d..55daf250 100644 --- a/apps/gittensory-ui/public/openapi.json +++ b/apps/gittensory-ui/public/openapi.json @@ -14020,6 +14020,39 @@ } ] } + }, + "/v1/repos/{owner}/{repo}/onboarding-pack/preview": { + "get": { + "responses": { + "200": { + "description": "Preview-only repo onboarding pack for accepted repositories", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + } + }, + "403": { + "description": "Insufficient role" + }, + "404": { + "description": "Repository is not accepted or preview unavailable" + } + }, + "security": [ + { + "GittensoryBearer": [] + }, + { + "GittensorySessionCookie": [] + } + ] + } } }, "servers": [ diff --git a/src/api/routes.ts b/src/api/routes.ts index 8855b1a6..b4151c1d 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -179,6 +179,7 @@ import { buildPullRequestReviewability, type PullRequestReviewability } from ".. import { buildLocalBranchAnalysis, findCurrentBranchPullRequest } from "../signals/local-branch"; import { MAX_LOCAL_SCORER_WARNING_CHARS, MAX_LOCAL_SCORER_WARNING_COUNT } from "../signals/local-scorer-diagnostics"; import { loadRepoFocusManifest } from "../signals/focus-manifest-loader"; +import { buildRepoOnboardingPackPreviewForRepo } from "../services/repo-onboarding-pack"; import { buildRepoSettingsPreview, type PublicSurfaceSkipReason } from "../signals/settings-preview"; import { buildGittensorConfigRecommendation, buildRegistrationReadiness, type InstallationHealthSummary } from "../signals/registration-readiness"; import { fileUpstreamDriftIssues, loadUpstreamStatus, refreshUpstreamDrift, registryHyperparameterDriftWarningsForRepo } from "../upstream/ruleset"; @@ -1477,6 +1478,19 @@ export function createApp() { return c.json(await buildGittensorConfigRecommendationResponse(c.env, fullName)); }); + app.get("/v1/repos/:owner/:repo/onboarding-pack/preview", async (c) => { + const fullName = `${c.req.param("owner")}/${c.req.param("repo")}`; + const forbidden = await requireAppRole(c, ["maintainer", "owner", "operator"]); + if (forbidden) return forbidden; + const response = await buildRepoOnboardingPackPreviewForRepo(c.env, fullName, { + refreshManifest: c.req.query("refresh") === "true", + }); + if ("error" in response) { + return c.json(response, 404); + } + return c.json(response); + }); + app.get("/v1/repos/:owner/:repo/settings", async (c) => { const fullName = `${c.req.param("owner")}/${c.req.param("repo")}`; return c.json(await getRepositorySettings(c.env, fullName)); diff --git a/src/openapi/spec.ts b/src/openapi/spec.ts index 9b7e109c..b173894d 100644 --- a/src/openapi/spec.ts +++ b/src/openapi/spec.ts @@ -373,6 +373,15 @@ export function buildOpenApiSpec() { 200: { description: "Private Gittensor config recommendation for repo owners", content: { "application/json": { schema: GittensorConfigRecommendationSchema } } }, }, }); + registry.registerPath({ + method: "get", + path: "/v1/repos/{owner}/{repo}/onboarding-pack/preview", + responses: { + 200: { description: "Preview-only repo onboarding pack for accepted repositories", content: { "application/json": { schema: z.record(z.string(), z.unknown()) } } }, + 403: { description: "Insufficient role" }, + 404: { description: "Repository is not accepted or preview unavailable" }, + }, + }); registry.registerPath({ method: "get", path: "/v1/repos/{owner}/{repo}/settings", diff --git a/src/services/repo-onboarding-pack.ts b/src/services/repo-onboarding-pack.ts new file mode 100644 index 00000000..ae956ba0 --- /dev/null +++ b/src/services/repo-onboarding-pack.ts @@ -0,0 +1,52 @@ +import { getRepository } from "../db/repositories"; +import type { FocusManifest } from "../signals/focus-manifest"; +import { loadRepoFocusManifest } from "../signals/focus-manifest-loader"; +import { + buildRepoOnboardingPackPreview, + type RepoOnboardingPackPreview, + type RepoPolicyCompilerOutput, +} from "../signals/onboarding-pack"; +import { compileRepoPolicyCompilerOutput } from "../signals/repo-policy-compiler"; + +export type RepoOnboardingPackPreviewResponse = { + repoFullName: string; + accepted: boolean; + preview: RepoOnboardingPackPreview; + policySource: "policy_compiler"; +}; + +export function buildRepoOnboardingPackPreviewFromManifest( + repoFullName: string, + manifest: FocusManifest, +): { preview: RepoOnboardingPackPreview; policyOutput: RepoPolicyCompilerOutput } { + const policyOutput = compileRepoPolicyCompilerOutput({ repoFullName, manifest }); + const preview = buildRepoOnboardingPackPreview(policyOutput); + return { preview, policyOutput }; +} + +/** + * Build a sanitized onboarding-pack preview for an accepted (registered) repository. + */ +export async function buildRepoOnboardingPackPreviewForRepo( + env: Env, + repoFullName: string, + options: { refreshManifest?: boolean } = {}, +): Promise { + const repo = await getRepository(env, repoFullName); + if (!repo?.isRegistered) { + return { + error: "repo_not_accepted", + repoFullName, + }; + } + + const manifest = await loadRepoFocusManifest(env, repoFullName, { refresh: options.refreshManifest === true }); + const { preview } = buildRepoOnboardingPackPreviewFromManifest(repoFullName, manifest); + + return { + repoFullName, + accepted: true, + preview, + policySource: "policy_compiler", + }; +} diff --git a/src/signals/repo-policy-compiler.ts b/src/signals/repo-policy-compiler.ts new file mode 100644 index 00000000..1fc70cf4 --- /dev/null +++ b/src/signals/repo-policy-compiler.ts @@ -0,0 +1,113 @@ +import { + compileFocusManifestPolicy, + isFocusManifestPublicSafe, + type FocusManifest, + type FocusManifestLanePreference, +} from "./focus-manifest"; +import type { RepoPolicyCompilerOutput, RepoPolicyContributionLane } from "./onboarding-pack"; +import { nowIso } from "../utils/json"; + +export type RepoPolicyCompilerInput = { + repoFullName: string; + manifest: FocusManifest; + generatedAt?: string | undefined; +}; + +/** + * Compile a normalized focus manifest into policy output consumed by onboarding-pack generation (#277 → #248). + */ +export function compileRepoPolicyCompilerOutput(input: RepoPolicyCompilerInput): RepoPolicyCompilerOutput { + const policy = compileFocusManifestPolicy(input.manifest); + const contributionLanes: RepoPolicyContributionLane[] = []; + + if (policy.present) { + contributionLanes.push(buildDirectPrLane(policy.publicSafe.contributionLanes.directPrLane, policy)); + contributionLanes.push(buildIssueDiscoveryLane(policy.publicSafe.contributionLanes.issueDiscoveryLane, policy)); + } + + const publicReadinessWarnings = policy.authenticated.readinessWarnings.filter(isFocusManifestPublicSafe); + const publicParseWarnings = policy.authenticated.parseWarnings.filter(isFocusManifestPublicSafe); + + return { + repoFullName: input.repoFullName, + generatedAt: input.generatedAt ?? nowIso(), + contributionLanes, + labelPolicy: { + preferredLabels: policy.publicSafe.labelExpectations.preferredLabels, + requiredLabels: [], + discouragedLabels: [], + note: labelPolicyNote(policy.publicSafe.labelExpectations.linkedIssuePolicy), + }, + validationExpectations: policy.publicSafe.validationExpectations.testExpectations, + readinessWarnings: [ + ...publicReadinessWarnings, + ...publicParseWarnings, + "Confirm contribution guidance stays previewable before publication.", + "Keep public material separated from maintainer-only context.", + ].filter(isFocusManifestPublicSafe), + maintainerExpectations: [ + "Keep pull requests narrow and tied to accepted repository policy.", + "Shape PR descriptions around maintainer public notes and validation expectations.", + ], + publicOutputBoundaries: [ + "Keep sensitive credentials, account secrets, compensation estimates, private maintainer evidence, and local paths out of public contribution text.", + "Keep the pack as guidance for accepted work, not as automated GitHub action.", + ...input.manifest.publicNotes.filter(isFocusManifestPublicSafe), + ], + privateOwnerContext: policy.authenticated.maintainerContext, + }; +} + +function buildDirectPrLane( + preference: FocusManifestLanePreference, + policy: ReturnType, +): RepoPolicyContributionLane { + return { + id: "direct-pr", + title: laneTitle("Direct pull request lane", preference), + summary: directPrSummary(preference, policy.publicSafe.summary), + preferredPaths: policy.publicSafe.contributionLanes.preferredEntryPaths, + discouragedPaths: policy.publicSafe.discouragedWork.blockedEntryPaths, + validationExpectations: policy.publicSafe.validationExpectations.testExpectations, + publicNotes: policy.publicSafe.entryGuidance, + }; +} + +function buildIssueDiscoveryLane( + preference: FocusManifestLanePreference, + policy: ReturnType, +): RepoPolicyContributionLane { + return { + id: "issue-discovery", + title: laneTitle("Issue discovery lane", preference), + summary: issueDiscoverySummary(preference, policy.publicSafe.summary), + preferredPaths: policy.publicSafe.contributionLanes.preferredEntryPaths, + discouragedPaths: policy.publicSafe.discouragedWork.blockedEntryPaths, + validationExpectations: policy.publicSafe.validationExpectations.testExpectations, + publicNotes: policy.publicSafe.entryGuidance.filter((note) => !note.toLowerCase().includes("direct")), + }; +} + +function laneTitle(base: string, preference: FocusManifestLanePreference): string { + if (preference === "preferred") return `${base} (preferred)`; + if (preference === "discouraged") return `${base} (discouraged)`; + return base; +} + +function directPrSummary(preference: FocusManifestLanePreference, summary: string): string { + if (preference === "discouraged") return "Direct pull requests are discouraged for this repository."; + if (preference === "preferred") return summary; + return "Direct pull requests are accepted when they stay inside maintainer-wanted scope."; +} + +function issueDiscoverySummary(preference: FocusManifestLanePreference, summary: string): string { + if (preference === "discouraged") return "Prefer direct fixes over new issue reports."; + if (preference === "preferred") return summary; + return "Issue discovery is optional; confirm maintainer scope before filing new issues."; +} + +function labelPolicyNote(linkedIssuePolicy: string): string { + if (linkedIssuePolicy === "required") return "Link a tracked issue before opening a pull request."; + if (linkedIssuePolicy === "preferred") return "Link a tracked issue when one exists."; + return "Use labels to explain accepted scope, not to promise outcomes."; +} diff --git a/test/unit/onboarding-pack.test.ts b/test/unit/onboarding-pack.test.ts index c38c32e0..9d582d31 100644 --- a/test/unit/onboarding-pack.test.ts +++ b/test/unit/onboarding-pack.test.ts @@ -1,4 +1,12 @@ import { describe, expect, it } from "vitest"; +import { + buildRepoOnboardingPackPreviewForRepo, + buildRepoOnboardingPackPreviewFromManifest, +} from "../../src/services/repo-onboarding-pack"; +import { createTestEnv } from "../helpers/d1"; +import { upsertRepositoryFromGitHub } from "../../src/db/repositories"; +import { parseFocusManifestContent } from "../../src/signals/focus-manifest"; +import { compileRepoPolicyCompilerOutput } from "../../src/signals/repo-policy-compiler"; import { buildRepoOnboardingPackPreview, isRepoOnboardingPackPublicSafe, @@ -200,6 +208,26 @@ describe("buildRepoOnboardingPackPreview", () => { expect(isRepoOnboardingPackPublicSafe(preview)).toBe(true); }); + it("compiler fixture pipeline matches buildRepoOnboardingPackPreviewFromManifest", () => { + const manifest = parseFocusManifestContent( + JSON.stringify({ + wantedPaths: ["src/"], + testExpectations: ["npm run test:ci"], + publicNotes: ["Stay advisory."], + }), + "repo_file", + ); + const compiled = compileRepoPolicyCompilerOutput({ + repoFullName: "JSONbored/gittensory", + manifest, + }); + const fromCompiler = buildRepoOnboardingPackPreview(compiled); + const fromService = buildRepoOnboardingPackPreviewFromManifest("JSONbored/gittensory", manifest); + expect(fromService.preview.contributionLanes.length).toBe(fromCompiler.contributionLanes.length); + expect(fromService.preview.publication.status).toBe("preview_only"); + expect(isRepoOnboardingPackPublicSafe(fromService.preview)).toBe(true); + }); + it("uses stable defaults when optional policy sections are omitted", () => { const preview = buildRepoOnboardingPackPreview( { @@ -235,3 +263,35 @@ describe("buildRepoOnboardingPackPreview", () => { expect(isRepoOnboardingPackPublicSafe(preview)).toBe(true); }); }); + +describe("buildRepoOnboardingPackPreviewForRepo", () => { + it("returns preview_only pack for accepted registered repos", async () => { + const env = createTestEnv(); + await upsertRepositoryFromGitHub( + env, + { name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }, + 1, + ); + await env.DB.prepare("UPDATE repositories SET is_registered = 1 WHERE full_name = ?") + .bind("JSONbored/gittensory") + .run(); + const response = await buildRepoOnboardingPackPreviewForRepo(env, "JSONbored/gittensory"); + expect("error" in response).toBe(false); + if ("error" in response) return; + expect(response.accepted).toBe(true); + expect(response.preview.previewOnly).toBe(true); + expect(response.preview.publication.allowed).toBe(false); + expect(isRepoOnboardingPackPublicSafe(response.preview)).toBe(true); + }); + + it("rejects onboarding pack preview for unregistered repos", async () => { + const env = createTestEnv(); + await upsertRepositoryFromGitHub( + env, + { name: "unregistered", full_name: "owner/unregistered", private: false, owner: { login: "owner" } }, + 1, + ); + const response = await buildRepoOnboardingPackPreviewForRepo(env, "owner/unregistered"); + expect(response).toMatchObject({ error: "repo_not_accepted", repoFullName: "owner/unregistered" }); + }); +}); From f22ef49917e8d4b5485ff5ab5986963083cb3b0d Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:16:15 +0200 Subject: [PATCH 04/10] fix(ui): keep registration workspace copy within public safety regex Replace payout/reward phrasing in advisory copy and tighten sanitizer so CI forbidden-language tests pass on collected workspace text. --- .../src/lib/registration-workspace.ts | 8 +- test/unit/registration-workspace-ui.test.ts | 80 ++++++++++++++++++- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/apps/gittensory-ui/src/lib/registration-workspace.ts b/apps/gittensory-ui/src/lib/registration-workspace.ts index 56278865..2c2beb56 100644 --- a/apps/gittensory-ui/src/lib/registration-workspace.ts +++ b/apps/gittensory-ui/src/lib/registration-workspace.ts @@ -4,7 +4,7 @@ export type WorkspaceFreshness = "complete" | "degraded" | "stale" | "unknown"; export type WorkspaceLaneStatus = "ready" | "warn" | "blocked" | "info"; const FORBIDDEN_PUBLIC_LANGUAGE = - /wallet|hotkey|coldkey|raw trust score|payout|reward estimate|farming|private reviewability|public score estimate|mnemonic|seed phrase|private key/i; + /wallet|hotkey|coldkey|raw trust score|payout|\breward\b|reward estimate|farming|private reviewability|public score estimate|mnemonic|seed phrase|private key/i; export type RegistrationWorkspaceDataQuality = { status?: string | undefined; @@ -193,7 +193,7 @@ export function buildRegistrationWorkspaceView( repoFullName: readiness.repoFullName, generatedAt: readiness.generatedAt, advisoryBanner: - "Advisory workspace only. Recommendations explain tradeoffs for repo owners; they do not guarantee Gittensor payouts or miner rewards.", + "Advisory workspace only. Recommendations explain tradeoffs for repo owners; they do not guarantee Gittensor incentive outcomes.", freshness, summary: { ready: readiness.ready, @@ -225,7 +225,9 @@ export function buildRegistrationWorkspaceView( id: "maintainer-economics", title: "Maintainer economics", status: maintainerEconomicsStatus, - summary: stringField(maintainerCut, "summary") ?? "Maintainer-cut posture is separate from miner reward estimates.", + summary: + stringField(maintainerCut, "summary") ?? + "Maintainer-cut posture is separate from public contributor incentive guidance.", bullets: sanitizeBulletList([ ...(Array.isArray(maintainerCut.reasons) ? (maintainerCut.reasons as string[]) : []), ...(Array.isArray(maintainerCut.warnings) ? (maintainerCut.warnings as string[]) : []), diff --git a/test/unit/registration-workspace-ui.test.ts b/test/unit/registration-workspace-ui.test.ts index f30f3033..7d7d1e80 100644 --- a/test/unit/registration-workspace-ui.test.ts +++ b/test/unit/registration-workspace-ui.test.ts @@ -1,7 +1,9 @@ import { describe, expect, it } from "vitest"; import { + buildRegistrationOwnerWorkflow, buildRegistrationWorkspaceView, + collectRegistrationOwnerWorkflowPublicText, collectRegistrationWorkspacePublicText, isRegistrationWorkspacePublicSafe, resolveRegistrationWorkspaceFreshness, @@ -10,7 +12,8 @@ import { type RegistrationReadinessPayload, } from "../../apps/gittensory-ui/src/lib/registration-workspace"; -const FORBIDDEN = /wallet|hotkey|raw trust score|payout|reward estimate|farming|private reviewability|public score estimate/i; +const FORBIDDEN = + /wallet|hotkey|raw trust score|payout|\breward\b|reward estimate|farming|private reviewability|public score estimate/i; function readyFixture(overrides: Partial = {}): RegistrationReadinessPayload { return { @@ -65,6 +68,11 @@ function readyFixture(overrides: Partial = {}): Re policyReadiness: null, blockers: [], warnings: [], + docsCompleteness: { + status: "repo_docs_not_crawled", + requiredDocs: ["CONTRIBUTING.md", "README.md"], + note: "Gittensory validates public repo docs locally; remote crawl is not enabled yet.", + }, dataQuality: { status: "complete", partial: false, warnings: [] }, ...overrides, }; @@ -145,6 +153,75 @@ describe("registration workspace UI helpers", () => { expect(isRegistrationWorkspacePublicSafe("Queue burden is low.")).toBe(true); }); + it("guided workflow groups readiness into five buckets with remediation", () => { + const workflow = buildRegistrationOwnerWorkflow(readyFixture(), configFixture()); + expect(workflow.buckets.map((bucket) => bucket.id)).toEqual([ + "policy", + "data_quality", + "queue_health", + "docs_onboarding", + "maintainer_capacity", + ]); + const docs = workflow.buckets.find((bucket) => bucket.id === "docs_onboarding"); + expect(docs?.state).toBe("needs_cleanup"); + expect(docs?.items[0]?.remediationKind).toBe("manual"); + expect(workflow.overallState).toBe("needs_cleanup"); + expect(workflow.nextSteps.length).toBeGreaterThan(0); + }); + + it("blocked readiness maps workflow to not ready with concrete blocker remediation", () => { + const workflow = buildRegistrationOwnerWorkflow( + readyFixture({ + ready: false, + blockers: ["Repository config quality needs attention before registration promotion."], + directPrReadiness: { ready: false, reasons: ["Config quality is fragile."] }, + queueHealth: { + level: "critical", + burdenScore: 0.95, + reviewablePullRequests: 40, + summary: "Queue burden is critical.", + }, + }), + null, + ); + expect(workflow.overallState).toBe("not_ready"); + expect(workflow.buckets.find((bucket) => bucket.id === "queue_health")?.state).toBe("not_ready"); + const policy = workflow.buckets.find((bucket) => bucket.id === "policy"); + expect(policy?.items.some((item) => item.title === "Registration blocker")).toBe(true); + expect(collectRegistrationOwnerWorkflowPublicText(workflow).join(" ")).not.toMatch(FORBIDDEN); + }); + + it("accepted workflow when readiness is ready and buckets are clear", () => { + const workflow = buildRegistrationOwnerWorkflow( + readyFixture({ + docsCompleteness: { + status: "verified", + requiredDocs: ["CONTRIBUTING.md"], + note: "Docs verified locally.", + }, + testCoverageHealth: { + status: "gate_ready", + trustedLabelPipelineReady: true, + checkRunMode: "enabled", + requiredGate: ["npm run test:ci"], + note: "Gates ready.", + warnings: [], + }, + }), + configFixture(), + ); + expect(workflow.overallState).toBe("accepted"); + expect(workflow.buckets.every((bucket) => bucket.state === "accepted")).toBe(true); + const view = buildRegistrationWorkspaceView( + readyFixture({ + ready: true, + docsCompleteness: { status: "verified", requiredDocs: ["CONTRIBUTING.md"], note: "Docs verified locally." }, + }), + configFixture(), + ); + expect(view.workflow.overallState).toBe("accepted"); + }); + it("never emits forbidden language across randomized warning injections", () => { const injections = [ "wallet", @@ -162,6 +239,7 @@ describe("registration workspace UI helpers", () => { configFixture(), ); expect(collectRegistrationWorkspacePublicText(view).join(" ")).not.toMatch(FORBIDDEN); + expect(collectRegistrationOwnerWorkflowPublicText(view.workflow).join(" ")).not.toMatch(FORBIDDEN); } }); }); From 35c4e77af8c412095d7676de25e9d595759d9549 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:35:38 +0200 Subject: [PATCH 05/10] test(ui): raise branch coverage for registration workspace helpers Cover guided-workflow branches, policy compiler lane arms, and sanitizer paths so PR #390 clears the 97% global coverage gate. --- test/unit/registration-workspace-ui.test.ts | 262 ++++++++++++++++++++ test/unit/repo-policy-compiler.test.ts | 74 ++++++ 2 files changed, 336 insertions(+) create mode 100644 test/unit/repo-policy-compiler.test.ts diff --git a/test/unit/registration-workspace-ui.test.ts b/test/unit/registration-workspace-ui.test.ts index 7d7d1e80..b783cc86 100644 --- a/test/unit/registration-workspace-ui.test.ts +++ b/test/unit/registration-workspace-ui.test.ts @@ -8,6 +8,7 @@ import { isRegistrationWorkspacePublicSafe, resolveRegistrationWorkspaceFreshness, sanitizeRegistrationWorkspaceText, + splitRepoFullName, type GittensorConfigRecommendationPayload, type RegistrationReadinessPayload, } from "../../apps/gittensory-ui/src/lib/registration-workspace"; @@ -222,6 +223,267 @@ describe("registration workspace UI helpers", () => { expect(view.workflow.overallState).toBe("accepted"); }); + it("resolveRegistrationWorkspaceFreshness handles blocked, stale, and unknown paths", () => { + expect(resolveRegistrationWorkspaceFreshness({ status: "blocked", partial: false, warnings: [] }).status).toBe( + "stale", + ); + expect( + resolveRegistrationWorkspaceFreshness( + { status: "degraded", partial: true, warnings: ["Signal drift detected."] }, + { status: "complete", partial: false, warnings: [] }, + ).status, + ).toBe("degraded"); + expect(resolveRegistrationWorkspaceFreshness(undefined, undefined).status).toBe("unknown"); + expect( + resolveRegistrationWorkspaceFreshness({ status: "unknown", partial: false, warnings: ["Refresh pending."] }) + .warnings, + ).toEqual(["Refresh pending."]); + }); + + it("workspace view covers lane, operations, and policy-warning branches", () => { + const view = buildRegistrationWorkspaceView( + readyFixture({ + ready: false, + blockers: [], + directPrReadiness: { ready: false, reasons: ["Lane warming up."] }, + issueDiscoveryReadiness: { + ready: true, + recommendation: "preferred", + reasons: ["Issue discovery is staffed."], + }, + maintainerCutReadiness: { + ready: false, + reasons: [], + warnings: ["Review maintainer cut."], + }, + labelPolicy: { + autoLabelEnabled: false, + label: "custom", + trustedPipelineReady: false, + missingOrUnusedRegistryLabels: ["needs-triage"], + }, + queueHealth: { level: "medium", burdenScore: 0.55, reviewablePullRequests: 12, summary: "Moderate queue." }, + testCoverageHealth: { + status: "needs_attention", + trustedLabelPipelineReady: false, + checkRunMode: "disabled", + requiredGate: [], + note: "Gate not ready.", + warnings: ["Missing required check."], + }, + githubApp: { + installed: false, + publicSurface: "off", + commentMode: "off", + checkRunMode: "off", + quietByDefault: false, + behavior: "GitHub App not installed.", + warnings: ["Install the app."], + }, + policyReadiness: { + summary: "Policy drift", + publicWarnings: [ + { + title: "Focus manifest drift", + detail: "Manifest paths no longer match registry scope.", + action: "Refresh focus manifest.", + severity: "critical", + }, + { title: "wallet", detail: "hotkey", action: "payout", severity: "warn" }, + ], + }, + }), + null, + ); + expect(view.summary.status).toBe("warn"); + expect(view.lanes.issueDiscovery.status).toBe("ready"); + expect(view.lanes.maintainerEconomics.status).toBe("blocked"); + expect(view.operations.find((section) => section.id === "queue-health")?.status).toBe("warn"); + expect(view.operations.find((section) => section.id === "label-policy")?.bullets.join(" ")).toMatch( + /not verified/i, + ); + expect(view.policyWarnings).toHaveLength(1); + expect(view.config).toBeNull(); + }); + + it("routes blockers into workflow buckets and remediation helpers", () => { + const workflow = buildRegistrationOwnerWorkflow( + readyFixture({ + ready: false, + blockers: [ + "Repository config quality needs attention before registration promotion.", + "CONTRIBUTING.md onboarding doc is missing.", + "Open pull request queue is overloaded.", + "GitHub App installation is incomplete.", + "Burden forecast drift requires refresh.", + "Generic maintainer capacity blocker.", + "wallet hotkey payout estimate", + ], + queueHealth: { level: "medium", burdenScore: 0.5, reviewablePullRequests: 8, summary: "Queue warming." }, + contributorIntakeHealth: { level: "strained", summary: "Intake is strained." }, + maintainerCutReadiness: { + ready: false, + summary: "Cut not ready.", + reasons: [], + warnings: [], + recommendedAction: "hold_cut", + }, + testCoverageHealth: { + status: "needs_attention", + trustedLabelPipelineReady: false, + checkRunMode: "enabled", + requiredGate: ["npm run test:ci"], + note: "Validation incomplete.", + warnings: ["Trusted label pipeline pending."], + }, + dataQuality: { status: "stale", partial: true, warnings: ["Forecast stale."] }, + labelPolicy: { + autoLabelEnabled: true, + label: "gittensor", + trustedPipelineReady: false, + missingOrUnusedRegistryLabels: [], + }, + }), + configFixture(), + ); + expect(workflow.overallState).toBe("not_ready"); + expect(workflow.buckets.find((bucket) => bucket.id === "policy")?.items.length).toBeGreaterThan(1); + expect( + workflow.buckets + .find((bucket) => bucket.id === "docs_onboarding") + ?.items.some((item) => /CONTRIBUTING|onboarding docs/i.test(item.remediation)), + ).toBe(true); + expect(workflow.buckets.find((bucket) => bucket.id === "queue_health")?.items.length).toBeGreaterThan(0); + expect(workflow.buckets.find((bucket) => bucket.id === "data_quality")?.items.length).toBeGreaterThan(0); + expect(workflow.buckets.find((bucket) => bucket.id === "maintainer_capacity")?.items.length).toBeGreaterThan(1); + expect(workflow.nextSteps.length).toBeGreaterThan(0); + }); + + it("covers remaining workflow and view branch arms for coverage", () => { + const warnDiscovery = buildRegistrationWorkspaceView( + readyFixture({ + issueDiscoveryReadiness: { + ready: false, + recommendation: "allowed", + reasons: ["Staff issue triage first."], + }, + directPrReadiness: { ready: false, reasons: ["Warming up."] }, + blockers: [], + }), + { + ...configFixture(), + current: { maintainerCut: 0.1 }, + recommended: { maintainerCut: 0.2, participationMode: "direct_pr" }, + }, + ); + expect(warnDiscovery.lanes.issueDiscovery.status).toBe("warn"); + expect(warnDiscovery.lanes.directPr.status).toBe("warn"); + expect(warnDiscovery.config?.currentLines[0]).toMatch(/maintainerCut/); + + const workflow = buildRegistrationOwnerWorkflow( + readyFixture({ + policyReadiness: { + summary: "ok", + publicWarnings: [ + { + title: "Label drift", + detail: "Registry label unused.", + action: "", + severity: "warn", + }, + ], + }, + queueHealth: { level: "medium", burdenScore: 0.55, reviewablePullRequests: 12, summary: "Moderate burden." }, + contributorIntakeHealth: { level: "strained", summary: "Intake strained." }, + docsCompleteness: { + status: "verified", + requiredDocs: ["CONTRIBUTING.md"], + note: "Docs verified.", + }, + maintainerCutReadiness: { ready: false, reasons: [], warnings: [] }, + blockers: [], + ready: true, + }), + null, + ); + expect(workflow.buckets.find((bucket) => bucket.id === "policy")?.items[0]?.remediationKind).toBe("manual"); + expect(workflow.overallHeadline).toMatch(/Needs cleanup/i); + expect(workflow.buckets.find((bucket) => bucket.id === "queue_health")?.state).toBe("needs_cleanup"); + + const highQueueWorkflow = buildRegistrationOwnerWorkflow( + readyFixture({ + queueHealth: { level: "high", burdenScore: 0.8, reviewablePullRequests: 20, summary: "High burden." }, + }), + null, + ); + expect(highQueueWorkflow.buckets.find((bucket) => bucket.id === "queue_health")?.state).toBe("not_ready"); + + const criticalQueue = buildRegistrationWorkspaceView( + readyFixture({ queueHealth: { level: "critical", burdenScore: 1, reviewablePullRequests: 99, summary: "Critical." } }), + null, + ); + expect(criticalQueue.operations.find((section) => section.id === "queue-health")?.status).toBe("blocked"); + + const edgeCases = buildRegistrationWorkspaceView( + readyFixture({ + queueHealth: { level: "unknown", burdenScore: 0, reviewablePullRequests: 0, summary: "Unknown queue signal." }, + policyReadiness: { + summary: "ok", + publicWarnings: [ + { title: "Safe title", detail: "", action: "Fix manifest.", severity: "warn" }, + { title: "Actionable", detail: "Detail text.", action: "Do the thing.", severity: "warn" }, + ], + }, + blockers: ["Burden forecast drift requires refresh.", "coverage gate below threshold."], + }), + { + ...configFixture(), + recommended: { participationMode: "direct_pr", maintainerCut: 0.2, nested: { flag: true } }, + }, + ); + expect(edgeCases.operations.find((section) => section.id === "queue-health")?.status).toBe("info"); + expect(edgeCases.policyWarnings).toHaveLength(1); + expect(collectRegistrationWorkspacePublicText(edgeCases).join(" ")).toMatch(/Do the thing/i); + expect(edgeCases.config?.recommendedLines.some((line) => line.includes("nested"))).toBe(true); + + const onlyAccepted = buildRegistrationOwnerWorkflow( + readyFixture({ + docsCompleteness: { status: "verified", requiredDocs: [], note: "ok" }, + queueHealth: { level: "low", burdenScore: 0.1, reviewablePullRequests: 1, summary: "low" }, + contributorIntakeHealth: { level: "healthy", summary: "ok" }, + maintainerCutReadiness: { ready: true, reasons: [], warnings: [] }, + labelPolicy: { autoLabelEnabled: true, label: "gittensor", trustedPipelineReady: true, missingOrUnusedRegistryLabels: [] }, + }), + configFixture(), + ); + expect(onlyAccepted.overallState).toBe("accepted"); + expect(onlyAccepted.nextSteps).toEqual([]); + + const maintainerWarn = buildRegistrationWorkspaceView( + readyFixture({ + ready: true, + maintainerCutReadiness: { ready: false, reasons: ["Review cut."], warnings: [], summary: "Cut pending." }, + }), + configFixture(), + ); + expect(maintainerWarn.lanes.maintainerEconomics.status).toBe("warn"); + + const installBlocker = buildRegistrationOwnerWorkflow( + readyFixture({ blockers: ["GitHub App installation is incomplete for this repository."] }), + null, + ); + expect( + installBlocker.buckets + .find((bucket) => bucket.id === "maintainer_capacity") + ?.items.some((item) => item.remediationKind === "manual"), + ).toBe(true); + }); + + it("splitRepoFullName validates owner/repo slugs for the owner panel", () => { + expect(splitRepoFullName("JSONbored/gittensory")).toEqual({ owner: "JSONbored", repo: "gittensory" }); + expect(splitRepoFullName("bad")).toBeNull(); + }); + it("never emits forbidden language across randomized warning injections", () => { const injections = [ "wallet", diff --git a/test/unit/repo-policy-compiler.test.ts b/test/unit/repo-policy-compiler.test.ts new file mode 100644 index 00000000..7aeb3bd5 --- /dev/null +++ b/test/unit/repo-policy-compiler.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from "vitest"; + +import { parseFocusManifest } from "../../src/signals/focus-manifest"; +import { compileRepoPolicyCompilerOutput } from "../../src/signals/repo-policy-compiler"; + +describe("compileRepoPolicyCompilerOutput", () => { + it("returns empty lanes when manifest is absent", () => { + const output = compileRepoPolicyCompilerOutput({ + repoFullName: "owner/repo", + manifest: parseFocusManifest(null), + generatedAt: "2026-06-01T00:00:00.000Z", + }); + expect(output.contributionLanes).toEqual([]); + expect(output.labelPolicy.note).toMatch(/accepted scope/i); + }); + + it("covers direct-PR and issue-discovery lane preference branches", () => { + const discouraged = compileRepoPolicyCompilerOutput({ + repoFullName: "owner/discouraged", + manifest: parseFocusManifest({ + wantedPaths: ["src/"], + issueDiscoveryPolicy: "discouraged", + }), + generatedAt: "2026-06-01T00:00:00.000Z", + }); + expect(discouraged.contributionLanes[0]?.summary).toMatch(/discouraged/i); + expect(discouraged.contributionLanes[1]?.summary).toMatch(/direct fixes/i); + + const preferred = compileRepoPolicyCompilerOutput({ + repoFullName: "owner/preferred", + manifest: parseFocusManifest({ + wantedPaths: ["src/"], + issueDiscoveryPolicy: "encouraged", + linkedIssuePolicy: "required", + }), + generatedAt: "2026-06-01T00:00:00.000Z", + }); + expect(preferred.contributionLanes[0]?.title).toMatch(/discouraged/i); + expect(preferred.contributionLanes[1]?.title).toMatch(/preferred/i); + expect(preferred.labelPolicy.note).toMatch(/tracked issue before opening/i); + + const linkedPreferred = compileRepoPolicyCompilerOutput({ + repoFullName: "owner/neutral", + manifest: parseFocusManifest({ wantedPaths: ["src/"], linkedIssuePolicy: "preferred" }), + generatedAt: "2026-06-01T00:00:00.000Z", + }); + expect(linkedPreferred.labelPolicy.note).toMatch(/when one exists/i); + + const neutralLanes = compileRepoPolicyCompilerOutput({ + repoFullName: "owner/neutral-lanes", + manifest: parseFocusManifest({ preferredLabels: ["bug"] }), + generatedAt: "2026-06-01T00:00:00.000Z", + }); + expect(neutralLanes.contributionLanes[0]?.summary).toMatch(/accepted when they stay inside/i); + expect(neutralLanes.contributionLanes[1]?.summary).toMatch(/optional/i); + expect(neutralLanes.contributionLanes[0]?.title).toBe("Direct pull request lane"); + expect(neutralLanes.contributionLanes[1]?.title).toBe("Issue discovery lane"); + }); + + it("filters unsafe public notes from boundaries", () => { + const output = compileRepoPolicyCompilerOutput({ + repoFullName: "owner/repo", + manifest: parseFocusManifest({ + wantedPaths: ["src/"], + publicNotes: ["Stay focused.", "wallet hotkey payout"], + }), + generatedAt: "2026-06-01T00:00:00.000Z", + }); + expect(output.publicOutputBoundaries).toEqual( + expect.arrayContaining([expect.stringContaining("Stay focused.")]), + ); + expect(output.publicOutputBoundaries.join(" ")).not.toMatch(/wallet|payout/i); + }); +}); From 59982229766a42f7b15d48d1c3c064a7633aa3ac Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:39:58 +0200 Subject: [PATCH 06/10] test: narrow optional fields in repo-policy-compiler tests --- test/unit/repo-policy-compiler.test.ts | 58 +++++++++++++++++--------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/test/unit/repo-policy-compiler.test.ts b/test/unit/repo-policy-compiler.test.ts index 7aeb3bd5..41617231 100644 --- a/test/unit/repo-policy-compiler.test.ts +++ b/test/unit/repo-policy-compiler.test.ts @@ -1,21 +1,40 @@ import { describe, expect, it } from "vitest"; import { parseFocusManifest } from "../../src/signals/focus-manifest"; -import { compileRepoPolicyCompilerOutput } from "../../src/signals/repo-policy-compiler"; +import { + compileRepoPolicyCompilerOutput, + type RepoPolicyCompilerInput, +} from "../../src/signals/repo-policy-compiler"; +import type { RepoPolicyCompilerOutput } from "../../src/signals/onboarding-pack"; + +function lanes(output: RepoPolicyCompilerOutput, min = 1) { + expect(output.contributionLanes).toBeDefined(); + expect(output.contributionLanes!.length).toBeGreaterThanOrEqual(min); + return output.contributionLanes!; +} + +function labelPolicy(output: RepoPolicyCompilerOutput) { + expect(output.labelPolicy).toBeDefined(); + return output.labelPolicy!; +} + +function compile(input: RepoPolicyCompilerInput) { + return compileRepoPolicyCompilerOutput(input); +} describe("compileRepoPolicyCompilerOutput", () => { it("returns empty lanes when manifest is absent", () => { - const output = compileRepoPolicyCompilerOutput({ + const output = compile({ repoFullName: "owner/repo", manifest: parseFocusManifest(null), generatedAt: "2026-06-01T00:00:00.000Z", }); expect(output.contributionLanes).toEqual([]); - expect(output.labelPolicy.note).toMatch(/accepted scope/i); + expect(labelPolicy(output).note).toMatch(/accepted scope/i); }); it("covers direct-PR and issue-discovery lane preference branches", () => { - const discouraged = compileRepoPolicyCompilerOutput({ + const discouraged = compile({ repoFullName: "owner/discouraged", manifest: parseFocusManifest({ wantedPaths: ["src/"], @@ -23,10 +42,10 @@ describe("compileRepoPolicyCompilerOutput", () => { }), generatedAt: "2026-06-01T00:00:00.000Z", }); - expect(discouraged.contributionLanes[0]?.summary).toMatch(/discouraged/i); - expect(discouraged.contributionLanes[1]?.summary).toMatch(/direct fixes/i); + expect(lanes(discouraged, 2)[0]!.summary).toMatch(/discouraged/i); + expect(lanes(discouraged, 2)[1]!.summary).toMatch(/direct fixes/i); - const preferred = compileRepoPolicyCompilerOutput({ + const preferred = compile({ repoFullName: "owner/preferred", manifest: parseFocusManifest({ wantedPaths: ["src/"], @@ -35,30 +54,31 @@ describe("compileRepoPolicyCompilerOutput", () => { }), generatedAt: "2026-06-01T00:00:00.000Z", }); - expect(preferred.contributionLanes[0]?.title).toMatch(/discouraged/i); - expect(preferred.contributionLanes[1]?.title).toMatch(/preferred/i); - expect(preferred.labelPolicy.note).toMatch(/tracked issue before opening/i); + expect(lanes(preferred, 2)[0]!.title).toMatch(/discouraged/i); + expect(lanes(preferred, 2)[1]!.title).toMatch(/preferred/i); + expect(labelPolicy(preferred).note).toMatch(/tracked issue before opening/i); - const linkedPreferred = compileRepoPolicyCompilerOutput({ + const linkedPreferred = compile({ repoFullName: "owner/neutral", manifest: parseFocusManifest({ wantedPaths: ["src/"], linkedIssuePolicy: "preferred" }), generatedAt: "2026-06-01T00:00:00.000Z", }); - expect(linkedPreferred.labelPolicy.note).toMatch(/when one exists/i); + expect(labelPolicy(linkedPreferred).note).toMatch(/when one exists/i); - const neutralLanes = compileRepoPolicyCompilerOutput({ + const neutralLanes = compile({ repoFullName: "owner/neutral-lanes", manifest: parseFocusManifest({ preferredLabels: ["bug"] }), generatedAt: "2026-06-01T00:00:00.000Z", }); - expect(neutralLanes.contributionLanes[0]?.summary).toMatch(/accepted when they stay inside/i); - expect(neutralLanes.contributionLanes[1]?.summary).toMatch(/optional/i); - expect(neutralLanes.contributionLanes[0]?.title).toBe("Direct pull request lane"); - expect(neutralLanes.contributionLanes[1]?.title).toBe("Issue discovery lane"); + const neutral = lanes(neutralLanes, 2); + expect(neutral[0]!.summary).toMatch(/accepted when they stay inside/i); + expect(neutral[1]!.summary).toMatch(/optional/i); + expect(neutral[0]!.title).toBe("Direct pull request lane"); + expect(neutral[1]!.title).toBe("Issue discovery lane"); }); it("filters unsafe public notes from boundaries", () => { - const output = compileRepoPolicyCompilerOutput({ + const output = compile({ repoFullName: "owner/repo", manifest: parseFocusManifest({ wantedPaths: ["src/"], @@ -69,6 +89,6 @@ describe("compileRepoPolicyCompilerOutput", () => { expect(output.publicOutputBoundaries).toEqual( expect.arrayContaining([expect.stringContaining("Stay focused.")]), ); - expect(output.publicOutputBoundaries.join(" ")).not.toMatch(/wallet|payout/i); + expect(output.publicOutputBoundaries!.join(" ")).not.toMatch(/wallet|payout/i); }); }); From 2819520898d2710794e109213a9c463df80e573f Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:43:07 +0200 Subject: [PATCH 07/10] Revert "feat(settings): repo onboarding pack preview for accepted repos" This reverts commit 9b9444b59e0902c61518bd57dfdf5e1e710b3bad. --- apps/gittensory-ui/public/openapi.json | 33 -------- src/api/routes.ts | 14 --- src/openapi/spec.ts | 9 -- src/services/repo-onboarding-pack.ts | 52 ------------ src/signals/repo-policy-compiler.ts | 113 ------------------------- test/unit/onboarding-pack.test.ts | 60 ------------- 6 files changed, 281 deletions(-) delete mode 100644 src/services/repo-onboarding-pack.ts delete mode 100644 src/signals/repo-policy-compiler.ts diff --git a/apps/gittensory-ui/public/openapi.json b/apps/gittensory-ui/public/openapi.json index 55daf250..2697717d 100644 --- a/apps/gittensory-ui/public/openapi.json +++ b/apps/gittensory-ui/public/openapi.json @@ -14020,39 +14020,6 @@ } ] } - }, - "/v1/repos/{owner}/{repo}/onboarding-pack/preview": { - "get": { - "responses": { - "200": { - "description": "Preview-only repo onboarding pack for accepted repositories", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": { - "nullable": true - } - } - } - } - }, - "403": { - "description": "Insufficient role" - }, - "404": { - "description": "Repository is not accepted or preview unavailable" - } - }, - "security": [ - { - "GittensoryBearer": [] - }, - { - "GittensorySessionCookie": [] - } - ] - } } }, "servers": [ diff --git a/src/api/routes.ts b/src/api/routes.ts index b4151c1d..8855b1a6 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -179,7 +179,6 @@ import { buildPullRequestReviewability, type PullRequestReviewability } from ".. import { buildLocalBranchAnalysis, findCurrentBranchPullRequest } from "../signals/local-branch"; import { MAX_LOCAL_SCORER_WARNING_CHARS, MAX_LOCAL_SCORER_WARNING_COUNT } from "../signals/local-scorer-diagnostics"; import { loadRepoFocusManifest } from "../signals/focus-manifest-loader"; -import { buildRepoOnboardingPackPreviewForRepo } from "../services/repo-onboarding-pack"; import { buildRepoSettingsPreview, type PublicSurfaceSkipReason } from "../signals/settings-preview"; import { buildGittensorConfigRecommendation, buildRegistrationReadiness, type InstallationHealthSummary } from "../signals/registration-readiness"; import { fileUpstreamDriftIssues, loadUpstreamStatus, refreshUpstreamDrift, registryHyperparameterDriftWarningsForRepo } from "../upstream/ruleset"; @@ -1478,19 +1477,6 @@ export function createApp() { return c.json(await buildGittensorConfigRecommendationResponse(c.env, fullName)); }); - app.get("/v1/repos/:owner/:repo/onboarding-pack/preview", async (c) => { - const fullName = `${c.req.param("owner")}/${c.req.param("repo")}`; - const forbidden = await requireAppRole(c, ["maintainer", "owner", "operator"]); - if (forbidden) return forbidden; - const response = await buildRepoOnboardingPackPreviewForRepo(c.env, fullName, { - refreshManifest: c.req.query("refresh") === "true", - }); - if ("error" in response) { - return c.json(response, 404); - } - return c.json(response); - }); - app.get("/v1/repos/:owner/:repo/settings", async (c) => { const fullName = `${c.req.param("owner")}/${c.req.param("repo")}`; return c.json(await getRepositorySettings(c.env, fullName)); diff --git a/src/openapi/spec.ts b/src/openapi/spec.ts index b173894d..9b7e109c 100644 --- a/src/openapi/spec.ts +++ b/src/openapi/spec.ts @@ -373,15 +373,6 @@ export function buildOpenApiSpec() { 200: { description: "Private Gittensor config recommendation for repo owners", content: { "application/json": { schema: GittensorConfigRecommendationSchema } } }, }, }); - registry.registerPath({ - method: "get", - path: "/v1/repos/{owner}/{repo}/onboarding-pack/preview", - responses: { - 200: { description: "Preview-only repo onboarding pack for accepted repositories", content: { "application/json": { schema: z.record(z.string(), z.unknown()) } } }, - 403: { description: "Insufficient role" }, - 404: { description: "Repository is not accepted or preview unavailable" }, - }, - }); registry.registerPath({ method: "get", path: "/v1/repos/{owner}/{repo}/settings", diff --git a/src/services/repo-onboarding-pack.ts b/src/services/repo-onboarding-pack.ts deleted file mode 100644 index ae956ba0..00000000 --- a/src/services/repo-onboarding-pack.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { getRepository } from "../db/repositories"; -import type { FocusManifest } from "../signals/focus-manifest"; -import { loadRepoFocusManifest } from "../signals/focus-manifest-loader"; -import { - buildRepoOnboardingPackPreview, - type RepoOnboardingPackPreview, - type RepoPolicyCompilerOutput, -} from "../signals/onboarding-pack"; -import { compileRepoPolicyCompilerOutput } from "../signals/repo-policy-compiler"; - -export type RepoOnboardingPackPreviewResponse = { - repoFullName: string; - accepted: boolean; - preview: RepoOnboardingPackPreview; - policySource: "policy_compiler"; -}; - -export function buildRepoOnboardingPackPreviewFromManifest( - repoFullName: string, - manifest: FocusManifest, -): { preview: RepoOnboardingPackPreview; policyOutput: RepoPolicyCompilerOutput } { - const policyOutput = compileRepoPolicyCompilerOutput({ repoFullName, manifest }); - const preview = buildRepoOnboardingPackPreview(policyOutput); - return { preview, policyOutput }; -} - -/** - * Build a sanitized onboarding-pack preview for an accepted (registered) repository. - */ -export async function buildRepoOnboardingPackPreviewForRepo( - env: Env, - repoFullName: string, - options: { refreshManifest?: boolean } = {}, -): Promise { - const repo = await getRepository(env, repoFullName); - if (!repo?.isRegistered) { - return { - error: "repo_not_accepted", - repoFullName, - }; - } - - const manifest = await loadRepoFocusManifest(env, repoFullName, { refresh: options.refreshManifest === true }); - const { preview } = buildRepoOnboardingPackPreviewFromManifest(repoFullName, manifest); - - return { - repoFullName, - accepted: true, - preview, - policySource: "policy_compiler", - }; -} diff --git a/src/signals/repo-policy-compiler.ts b/src/signals/repo-policy-compiler.ts deleted file mode 100644 index 1fc70cf4..00000000 --- a/src/signals/repo-policy-compiler.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { - compileFocusManifestPolicy, - isFocusManifestPublicSafe, - type FocusManifest, - type FocusManifestLanePreference, -} from "./focus-manifest"; -import type { RepoPolicyCompilerOutput, RepoPolicyContributionLane } from "./onboarding-pack"; -import { nowIso } from "../utils/json"; - -export type RepoPolicyCompilerInput = { - repoFullName: string; - manifest: FocusManifest; - generatedAt?: string | undefined; -}; - -/** - * Compile a normalized focus manifest into policy output consumed by onboarding-pack generation (#277 → #248). - */ -export function compileRepoPolicyCompilerOutput(input: RepoPolicyCompilerInput): RepoPolicyCompilerOutput { - const policy = compileFocusManifestPolicy(input.manifest); - const contributionLanes: RepoPolicyContributionLane[] = []; - - if (policy.present) { - contributionLanes.push(buildDirectPrLane(policy.publicSafe.contributionLanes.directPrLane, policy)); - contributionLanes.push(buildIssueDiscoveryLane(policy.publicSafe.contributionLanes.issueDiscoveryLane, policy)); - } - - const publicReadinessWarnings = policy.authenticated.readinessWarnings.filter(isFocusManifestPublicSafe); - const publicParseWarnings = policy.authenticated.parseWarnings.filter(isFocusManifestPublicSafe); - - return { - repoFullName: input.repoFullName, - generatedAt: input.generatedAt ?? nowIso(), - contributionLanes, - labelPolicy: { - preferredLabels: policy.publicSafe.labelExpectations.preferredLabels, - requiredLabels: [], - discouragedLabels: [], - note: labelPolicyNote(policy.publicSafe.labelExpectations.linkedIssuePolicy), - }, - validationExpectations: policy.publicSafe.validationExpectations.testExpectations, - readinessWarnings: [ - ...publicReadinessWarnings, - ...publicParseWarnings, - "Confirm contribution guidance stays previewable before publication.", - "Keep public material separated from maintainer-only context.", - ].filter(isFocusManifestPublicSafe), - maintainerExpectations: [ - "Keep pull requests narrow and tied to accepted repository policy.", - "Shape PR descriptions around maintainer public notes and validation expectations.", - ], - publicOutputBoundaries: [ - "Keep sensitive credentials, account secrets, compensation estimates, private maintainer evidence, and local paths out of public contribution text.", - "Keep the pack as guidance for accepted work, not as automated GitHub action.", - ...input.manifest.publicNotes.filter(isFocusManifestPublicSafe), - ], - privateOwnerContext: policy.authenticated.maintainerContext, - }; -} - -function buildDirectPrLane( - preference: FocusManifestLanePreference, - policy: ReturnType, -): RepoPolicyContributionLane { - return { - id: "direct-pr", - title: laneTitle("Direct pull request lane", preference), - summary: directPrSummary(preference, policy.publicSafe.summary), - preferredPaths: policy.publicSafe.contributionLanes.preferredEntryPaths, - discouragedPaths: policy.publicSafe.discouragedWork.blockedEntryPaths, - validationExpectations: policy.publicSafe.validationExpectations.testExpectations, - publicNotes: policy.publicSafe.entryGuidance, - }; -} - -function buildIssueDiscoveryLane( - preference: FocusManifestLanePreference, - policy: ReturnType, -): RepoPolicyContributionLane { - return { - id: "issue-discovery", - title: laneTitle("Issue discovery lane", preference), - summary: issueDiscoverySummary(preference, policy.publicSafe.summary), - preferredPaths: policy.publicSafe.contributionLanes.preferredEntryPaths, - discouragedPaths: policy.publicSafe.discouragedWork.blockedEntryPaths, - validationExpectations: policy.publicSafe.validationExpectations.testExpectations, - publicNotes: policy.publicSafe.entryGuidance.filter((note) => !note.toLowerCase().includes("direct")), - }; -} - -function laneTitle(base: string, preference: FocusManifestLanePreference): string { - if (preference === "preferred") return `${base} (preferred)`; - if (preference === "discouraged") return `${base} (discouraged)`; - return base; -} - -function directPrSummary(preference: FocusManifestLanePreference, summary: string): string { - if (preference === "discouraged") return "Direct pull requests are discouraged for this repository."; - if (preference === "preferred") return summary; - return "Direct pull requests are accepted when they stay inside maintainer-wanted scope."; -} - -function issueDiscoverySummary(preference: FocusManifestLanePreference, summary: string): string { - if (preference === "discouraged") return "Prefer direct fixes over new issue reports."; - if (preference === "preferred") return summary; - return "Issue discovery is optional; confirm maintainer scope before filing new issues."; -} - -function labelPolicyNote(linkedIssuePolicy: string): string { - if (linkedIssuePolicy === "required") return "Link a tracked issue before opening a pull request."; - if (linkedIssuePolicy === "preferred") return "Link a tracked issue when one exists."; - return "Use labels to explain accepted scope, not to promise outcomes."; -} diff --git a/test/unit/onboarding-pack.test.ts b/test/unit/onboarding-pack.test.ts index 9d582d31..c38c32e0 100644 --- a/test/unit/onboarding-pack.test.ts +++ b/test/unit/onboarding-pack.test.ts @@ -1,12 +1,4 @@ import { describe, expect, it } from "vitest"; -import { - buildRepoOnboardingPackPreviewForRepo, - buildRepoOnboardingPackPreviewFromManifest, -} from "../../src/services/repo-onboarding-pack"; -import { createTestEnv } from "../helpers/d1"; -import { upsertRepositoryFromGitHub } from "../../src/db/repositories"; -import { parseFocusManifestContent } from "../../src/signals/focus-manifest"; -import { compileRepoPolicyCompilerOutput } from "../../src/signals/repo-policy-compiler"; import { buildRepoOnboardingPackPreview, isRepoOnboardingPackPublicSafe, @@ -208,26 +200,6 @@ describe("buildRepoOnboardingPackPreview", () => { expect(isRepoOnboardingPackPublicSafe(preview)).toBe(true); }); - it("compiler fixture pipeline matches buildRepoOnboardingPackPreviewFromManifest", () => { - const manifest = parseFocusManifestContent( - JSON.stringify({ - wantedPaths: ["src/"], - testExpectations: ["npm run test:ci"], - publicNotes: ["Stay advisory."], - }), - "repo_file", - ); - const compiled = compileRepoPolicyCompilerOutput({ - repoFullName: "JSONbored/gittensory", - manifest, - }); - const fromCompiler = buildRepoOnboardingPackPreview(compiled); - const fromService = buildRepoOnboardingPackPreviewFromManifest("JSONbored/gittensory", manifest); - expect(fromService.preview.contributionLanes.length).toBe(fromCompiler.contributionLanes.length); - expect(fromService.preview.publication.status).toBe("preview_only"); - expect(isRepoOnboardingPackPublicSafe(fromService.preview)).toBe(true); - }); - it("uses stable defaults when optional policy sections are omitted", () => { const preview = buildRepoOnboardingPackPreview( { @@ -263,35 +235,3 @@ describe("buildRepoOnboardingPackPreview", () => { expect(isRepoOnboardingPackPublicSafe(preview)).toBe(true); }); }); - -describe("buildRepoOnboardingPackPreviewForRepo", () => { - it("returns preview_only pack for accepted registered repos", async () => { - const env = createTestEnv(); - await upsertRepositoryFromGitHub( - env, - { name: "gittensory", full_name: "JSONbored/gittensory", private: false, owner: { login: "JSONbored" } }, - 1, - ); - await env.DB.prepare("UPDATE repositories SET is_registered = 1 WHERE full_name = ?") - .bind("JSONbored/gittensory") - .run(); - const response = await buildRepoOnboardingPackPreviewForRepo(env, "JSONbored/gittensory"); - expect("error" in response).toBe(false); - if ("error" in response) return; - expect(response.accepted).toBe(true); - expect(response.preview.previewOnly).toBe(true); - expect(response.preview.publication.allowed).toBe(false); - expect(isRepoOnboardingPackPublicSafe(response.preview)).toBe(true); - }); - - it("rejects onboarding pack preview for unregistered repos", async () => { - const env = createTestEnv(); - await upsertRepositoryFromGitHub( - env, - { name: "unregistered", full_name: "owner/unregistered", private: false, owner: { login: "owner" } }, - 1, - ); - const response = await buildRepoOnboardingPackPreviewForRepo(env, "owner/unregistered"); - expect(response).toMatchObject({ error: "repo_not_accepted", repoFullName: "owner/unregistered" }); - }); -}); From 39c3950d9e27658ac797d63115e7fa207256a6a3 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:43:07 +0200 Subject: [PATCH 08/10] chore: drop #248 compiler tests from #131 branch --- test/unit/repo-policy-compiler.test.ts | 94 -------------------------- 1 file changed, 94 deletions(-) delete mode 100644 test/unit/repo-policy-compiler.test.ts diff --git a/test/unit/repo-policy-compiler.test.ts b/test/unit/repo-policy-compiler.test.ts deleted file mode 100644 index 41617231..00000000 --- a/test/unit/repo-policy-compiler.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { parseFocusManifest } from "../../src/signals/focus-manifest"; -import { - compileRepoPolicyCompilerOutput, - type RepoPolicyCompilerInput, -} from "../../src/signals/repo-policy-compiler"; -import type { RepoPolicyCompilerOutput } from "../../src/signals/onboarding-pack"; - -function lanes(output: RepoPolicyCompilerOutput, min = 1) { - expect(output.contributionLanes).toBeDefined(); - expect(output.contributionLanes!.length).toBeGreaterThanOrEqual(min); - return output.contributionLanes!; -} - -function labelPolicy(output: RepoPolicyCompilerOutput) { - expect(output.labelPolicy).toBeDefined(); - return output.labelPolicy!; -} - -function compile(input: RepoPolicyCompilerInput) { - return compileRepoPolicyCompilerOutput(input); -} - -describe("compileRepoPolicyCompilerOutput", () => { - it("returns empty lanes when manifest is absent", () => { - const output = compile({ - repoFullName: "owner/repo", - manifest: parseFocusManifest(null), - generatedAt: "2026-06-01T00:00:00.000Z", - }); - expect(output.contributionLanes).toEqual([]); - expect(labelPolicy(output).note).toMatch(/accepted scope/i); - }); - - it("covers direct-PR and issue-discovery lane preference branches", () => { - const discouraged = compile({ - repoFullName: "owner/discouraged", - manifest: parseFocusManifest({ - wantedPaths: ["src/"], - issueDiscoveryPolicy: "discouraged", - }), - generatedAt: "2026-06-01T00:00:00.000Z", - }); - expect(lanes(discouraged, 2)[0]!.summary).toMatch(/discouraged/i); - expect(lanes(discouraged, 2)[1]!.summary).toMatch(/direct fixes/i); - - const preferred = compile({ - repoFullName: "owner/preferred", - manifest: parseFocusManifest({ - wantedPaths: ["src/"], - issueDiscoveryPolicy: "encouraged", - linkedIssuePolicy: "required", - }), - generatedAt: "2026-06-01T00:00:00.000Z", - }); - expect(lanes(preferred, 2)[0]!.title).toMatch(/discouraged/i); - expect(lanes(preferred, 2)[1]!.title).toMatch(/preferred/i); - expect(labelPolicy(preferred).note).toMatch(/tracked issue before opening/i); - - const linkedPreferred = compile({ - repoFullName: "owner/neutral", - manifest: parseFocusManifest({ wantedPaths: ["src/"], linkedIssuePolicy: "preferred" }), - generatedAt: "2026-06-01T00:00:00.000Z", - }); - expect(labelPolicy(linkedPreferred).note).toMatch(/when one exists/i); - - const neutralLanes = compile({ - repoFullName: "owner/neutral-lanes", - manifest: parseFocusManifest({ preferredLabels: ["bug"] }), - generatedAt: "2026-06-01T00:00:00.000Z", - }); - const neutral = lanes(neutralLanes, 2); - expect(neutral[0]!.summary).toMatch(/accepted when they stay inside/i); - expect(neutral[1]!.summary).toMatch(/optional/i); - expect(neutral[0]!.title).toBe("Direct pull request lane"); - expect(neutral[1]!.title).toBe("Issue discovery lane"); - }); - - it("filters unsafe public notes from boundaries", () => { - const output = compile({ - repoFullName: "owner/repo", - manifest: parseFocusManifest({ - wantedPaths: ["src/"], - publicNotes: ["Stay focused.", "wallet hotkey payout"], - }), - generatedAt: "2026-06-01T00:00:00.000Z", - }); - expect(output.publicOutputBoundaries).toEqual( - expect.arrayContaining([expect.stringContaining("Stay focused.")]), - ); - expect(output.publicOutputBoundaries!.join(" ")).not.toMatch(/wallet|payout/i); - }); -}); From 6fb946a29d8380a6d3569e45de3fe69085df1866 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:52:44 +0200 Subject: [PATCH 09/10] fix(ui): pass eslint and reuse focus-manifest public safety guard Apply prettier formatting for PR #390 UI check and delegate workspace sanitizer to isFocusManifestPublicSafe with whitespace normalization. --- .../site/app-panels/owner-panel.tsx | 33 +++-- .../src/lib/registration-workspace.ts | 131 +++++++++++++----- test/unit/registration-workspace-ui.test.ts | 2 +- 3 files changed, 121 insertions(+), 45 deletions(-) diff --git a/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx b/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx index 33135827..f293dc09 100644 --- a/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx +++ b/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx @@ -92,7 +92,9 @@ export function OwnerPanel() { {workspace ? ( - + ) : null} {repoPath && readiness.status === "error" ? ( -

Readiness failed ({readiness.error}).

+

+ Readiness failed ({readiness.error}). +

) : null}
@@ -135,7 +142,9 @@ function RegistrationWorkspace({

{workspace.summary.headline}

-

Generated {generatedLabel}

+

+ Generated {generatedLabel} +

@@ -194,12 +203,15 @@ function RegistrationWorkspace({

Suggested Gittensor config

- Tradeoffs below separate maintainer economics from contributor lanes. Apply via PR when ready. + Tradeoffs below separate maintainer economics from contributor lanes. Apply via PR + when ready.

{workspace.config.tradeoffs.length > 0 ? (
-

Tradeoffs

+

+ Tradeoffs +

    {workspace.config.tradeoffs.map((entry) => (
  • {entry}
  • @@ -207,7 +219,10 @@ function RegistrationWorkspace({
) : null} - + ) : null} @@ -240,7 +255,9 @@ function WorkspaceSectionCard({ function Metric({ label, value }: { label: string; value: string }) { return (
-
{label}
+
+ {label} +
{value}
); diff --git a/apps/gittensory-ui/src/lib/registration-workspace.ts b/apps/gittensory-ui/src/lib/registration-workspace.ts index 2c2beb56..044a303b 100644 --- a/apps/gittensory-ui/src/lib/registration-workspace.ts +++ b/apps/gittensory-ui/src/lib/registration-workspace.ts @@ -1,10 +1,12 @@ +import { isFocusManifestPublicSafe } from "../../../../src/signals/focus-manifest"; import { splitRepoFullName } from "./maintainer-settings-preview"; export type WorkspaceFreshness = "complete" | "degraded" | "stale" | "unknown"; export type WorkspaceLaneStatus = "ready" | "warn" | "blocked" | "info"; -const FORBIDDEN_PUBLIC_LANGUAGE = - /wallet|hotkey|coldkey|raw trust score|payout|\breward\b|reward estimate|farming|private reviewability|public score estimate|mnemonic|seed phrase|private key/i; +function normalizePublicWorkspaceText(text: string): string { + return text.trim().replace(/\s+/g, " "); +} export type RegistrationWorkspaceDataQuality = { status?: string | undefined; @@ -30,7 +32,12 @@ export type RegistrationReadinessPayload = { note: string; warnings: string[]; }; - queueHealth: { level: string; burdenScore: number; reviewablePullRequests: number; summary: string }; + queueHealth: { + level: string; + burdenScore: number; + reviewablePullRequests: number; + summary: string; + }; contributorIntakeHealth: Record; githubApp: { installed: boolean; @@ -137,26 +144,26 @@ export type RegistrationWorkspaceView = { }; export function isRegistrationWorkspacePublicSafe(text: string): boolean { - return !FORBIDDEN_PUBLIC_LANGUAGE.test(text); + const normalized = normalizePublicWorkspaceText(text); + return normalized.length > 0 && isFocusManifestPublicSafe(normalized); } export function sanitizeRegistrationWorkspaceText(text: string): string | null { - const trimmed = text.trim(); - if (!trimmed || !isRegistrationWorkspacePublicSafe(trimmed)) return null; - return trimmed; + const normalized = normalizePublicWorkspaceText(text); + if (!normalized || !isFocusManifestPublicSafe(normalized)) return null; + return normalized; } export function resolveRegistrationWorkspaceFreshness( readinessQuality?: RegistrationWorkspaceDataQuality, configQuality?: RegistrationWorkspaceDataQuality, ): { status: WorkspaceFreshness; warnings: string[] } { - const warnings = [ - ...(readinessQuality?.warnings ?? []), - ...(configQuality?.warnings ?? []), - ] + const warnings = [...(readinessQuality?.warnings ?? []), ...(configQuality?.warnings ?? [])] .map((entry) => sanitizeRegistrationWorkspaceText(entry)) .filter((entry): entry is string => Boolean(entry)); - const statuses = [readinessQuality?.status, configQuality?.status].filter((entry): entry is string => Boolean(entry)); + const statuses = [readinessQuality?.status, configQuality?.status].filter( + (entry): entry is string => Boolean(entry), + ); if (statuses.includes("blocked")) return { status: "stale", warnings }; if (readinessQuality?.partial || configQuality?.partial || statuses.includes("degraded")) { return { status: "degraded", warnings }; @@ -169,7 +176,10 @@ export function buildRegistrationWorkspaceView( readiness: RegistrationReadinessPayload, config: GittensorConfigRecommendationPayload | null, ): RegistrationWorkspaceView { - const freshness = resolveRegistrationWorkspaceFreshness(readiness.dataQuality, config?.dataQuality); + const freshness = resolveRegistrationWorkspaceFreshness( + readiness.dataQuality, + config?.dataQuality, + ); const directPrStatus: WorkspaceLaneStatus = readiness.directPrReadiness.ready ? "ready" : readiness.blockers.length > 0 @@ -184,7 +194,11 @@ export function buildRegistrationWorkspaceView( const maintainerCut = readiness.maintainerCutReadiness; const maintainerReady = maintainerCut.ready === true; - const maintainerEconomicsStatus: WorkspaceLaneStatus = maintainerReady ? "ready" : readiness.ready ? "warn" : "blocked"; + const maintainerEconomicsStatus: WorkspaceLaneStatus = maintainerReady + ? "ready" + : readiness.ready + ? "warn" + : "blocked"; const labelPolicy = readiness.labelPolicy; const intake = readiness.contributorIntakeHealth; @@ -273,7 +287,9 @@ export function buildRegistrationWorkspaceView( ? "Trusted label pipeline is verified." : "Trusted label pipeline is not verified yet.", ...(Array.isArray(labelPolicy.missingOrUnusedRegistryLabels) - ? (labelPolicy.missingOrUnusedRegistryLabels as string[]).map((label) => `Missing or unused label: ${label}`) + ? (labelPolicy.missingOrUnusedRegistryLabels as string[]).map( + (label) => `Missing or unused label: ${label}`, + ) : []), ]), }, @@ -285,7 +301,9 @@ export function buildRegistrationWorkspaceView( bullets: sanitizeBulletList([ `Coverage gate: ${readiness.testCoverageHealth.status}.`, `Check runs: ${readiness.testCoverageHealth.checkRunMode}.`, - ...(readiness.testCoverageHealth.requiredGate ?? []).map((gate) => `Required gate: ${gate}`), + ...(readiness.testCoverageHealth.requiredGate ?? []).map( + (gate) => `Required gate: ${gate}`, + ), ...readiness.testCoverageHealth.warnings, ]), }, @@ -327,7 +345,10 @@ export function buildRegistrationOwnerWorkflow( readiness: RegistrationReadinessPayload, config: GittensorConfigRecommendationPayload | null, ): RegistrationOwnerWorkflow { - const freshness = resolveRegistrationWorkspaceFreshness(readiness.dataQuality, config?.dataQuality); + const freshness = resolveRegistrationWorkspaceFreshness( + readiness.dataQuality, + config?.dataQuality, + ); const intakeLevel = stringField(readiness.contributorIntakeHealth, "level") ?? "unknown"; const labelPolicy = readiness.labelPolicy; const docs = readiness.docsCompleteness ?? { status: "unknown", requiredDocs: [], note: "" }; @@ -353,7 +374,8 @@ export function buildRegistrationOwnerWorkflow( title: "Direct PR lane", state: readiness.blockers.length > 0 ? "not_ready" : "needs_cleanup", summary: "Direct-PR intake is not ready for the recommended registration mode.", - remediation: "Stabilize config quality and contributor intake before promoting direct-PR traffic.", + remediation: + "Stabilize config quality and contributor intake before promoting direct-PR traffic.", remediationKind: "action", }); } @@ -363,7 +385,8 @@ export function buildRegistrationOwnerWorkflow( title: "Label policy", state: "needs_cleanup", summary: "Trusted label pipeline is not verified for registry labels.", - remediation: "Create or verify configured registry labels and enable the trusted label pipeline in repo settings.", + remediation: + "Create or verify configured registry labels and enable the trusted label pipeline in repo settings.", remediationKind: "action", }); } @@ -376,7 +399,8 @@ export function buildRegistrationOwnerWorkflow( title: "Signal freshness", state: freshness.status === "stale" ? "not_ready" : "needs_cleanup", summary: warning, - remediation: "Wait for repository intelligence to refresh or run a maintainer backfill before acting on readiness.", + remediation: + "Wait for repository intelligence to refresh or run a maintainer backfill before acting on readiness.", remediationKind: "manual", }); } @@ -387,7 +411,8 @@ export function buildRegistrationOwnerWorkflow( title: "Validation gate", state: "needs_cleanup", summary: readiness.testCoverageHealth.note, - remediation: "Document expected CI commands in the repo and verify check-run settings before widening intake.", + remediation: + "Document expected CI commands in the repo and verify check-run settings before widening intake.", remediationKind: "action", }); } @@ -412,7 +437,8 @@ export function buildRegistrationOwnerWorkflow( title: "PR queue pressure", state: "not_ready", summary: readiness.queueHealth.summary, - remediation: "Reduce open PR queue pressure or narrow accepted lanes before inviting more contributors.", + remediation: + "Reduce open PR queue pressure or narrow accepted lanes before inviting more contributors.", remediationKind: "action", }); } else if (queueLevel === "medium") { @@ -454,8 +480,11 @@ export function buildRegistrationOwnerWorkflow( id: "intake-health", title: "Contributor intake", state: intakeLevel === "blocked" ? "not_ready" : "needs_cleanup", - summary: stringField(readiness.contributorIntakeHealth, "summary") ?? `Contributor intake is ${intakeLevel}.`, - remediation: "Stabilize triage capacity and duplicate-risk intake before inviting more issue reports or direct PRs.", + summary: + stringField(readiness.contributorIntakeHealth, "summary") ?? + `Contributor intake is ${intakeLevel}.`, + remediation: + "Stabilize triage capacity and duplicate-risk intake before inviting more issue reports or direct PRs.", remediationKind: "action", }); } @@ -498,10 +527,30 @@ export function buildRegistrationOwnerWorkflow( } const buckets: OwnerWorkflowBucket[] = [ - buildWorkflowBucket("policy", "Policy & lanes", policyItems, "Focus manifest, lane posture, and label policy."), - buildWorkflowBucket("data_quality", "Data quality", dataQualityItems, "Signal freshness and validation gates."), - buildWorkflowBucket("queue_health", "Queue health", queueItems, "Open PR pressure and reviewable queue burden."), - buildWorkflowBucket("docs_onboarding", "Docs & onboarding", docsItems, "Contributor-facing documentation readiness."), + buildWorkflowBucket( + "policy", + "Policy & lanes", + policyItems, + "Focus manifest, lane posture, and label policy.", + ), + buildWorkflowBucket( + "data_quality", + "Data quality", + dataQualityItems, + "Signal freshness and validation gates.", + ), + buildWorkflowBucket( + "queue_health", + "Queue health", + queueItems, + "Open PR pressure and reviewable queue burden.", + ), + buildWorkflowBucket( + "docs_onboarding", + "Docs & onboarding", + docsItems, + "Contributor-facing documentation readiness.", + ), buildWorkflowBucket( "maintainer_capacity", "Maintainer capacity", @@ -527,7 +576,9 @@ export function buildRegistrationOwnerWorkflow( }; } -export function collectRegistrationOwnerWorkflowPublicText(workflow: RegistrationOwnerWorkflow): string[] { +export function collectRegistrationOwnerWorkflowPublicText( + workflow: RegistrationOwnerWorkflow, +): string[] { return [ workflow.overallHeadline, ...workflow.nextSteps, @@ -560,7 +611,9 @@ export function collectRegistrationWorkspacePublicText(view: RegistrationWorkspa export { splitRepoFullName }; function sanitizeBulletList(entries: string[]): string[] { - return entries.map((entry) => sanitizeRegistrationWorkspaceText(entry)).filter((entry): entry is string => Boolean(entry)); + return entries + .map((entry) => sanitizeRegistrationWorkspaceText(entry)) + .filter((entry): entry is string => Boolean(entry)); } function stringField(record: Record, key: string): string | null { @@ -595,7 +648,8 @@ function buildWorkflowBucket( items: OwnerWorkflowItem[], summary: string, ): OwnerWorkflowBucket { - const state = items.length === 0 ? "accepted" : aggregateWorkflowState(items.map((item) => item.state)); + const state = + items.length === 0 ? "accepted" : aggregateWorkflowState(items.map((item) => item.state)); return { id, title, state, summary, items }; } @@ -623,11 +677,15 @@ function workflowHeadline(state: OwnerWorkflowState, ready: boolean): string { function classifyBlockerBucket(blocker: string): OwnerWorkflowBucketId { const lower = blocker.toLowerCase(); - if (lower.includes("config quality") || lower.includes("focus") || lower.includes("label")) return "policy"; + if (lower.includes("config quality") || lower.includes("focus") || lower.includes("label")) + return "policy"; if (lower.includes("doc") || lower.includes("contributing")) return "docs_onboarding"; - if (lower.includes("queue") || lower.includes("pull request") || lower.includes("pr ")) return "queue_health"; - if (lower.includes("install") || lower.includes("github app") || lower.includes("intake")) return "maintainer_capacity"; - if (lower.includes("drift") || lower.includes("forecast") || lower.includes("coverage")) return "data_quality"; + if (lower.includes("queue") || lower.includes("pull request") || lower.includes("pr ")) + return "queue_health"; + if (lower.includes("install") || lower.includes("github app") || lower.includes("intake")) + return "maintainer_capacity"; + if (lower.includes("drift") || lower.includes("forecast") || lower.includes("coverage")) + return "data_quality"; return "maintainer_capacity"; } @@ -649,6 +707,7 @@ function remediationForBlocker(blocker: string): string { function remediationKindForBlocker(blocker: string): OwnerWorkflowRemediationKind { const lower = blocker.toLowerCase(); - if (lower.includes("not crawled") || lower.includes("manual") || lower.includes("install")) return "manual"; + if (lower.includes("not crawled") || lower.includes("manual") || lower.includes("install")) + return "manual"; return "action"; } diff --git a/test/unit/registration-workspace-ui.test.ts b/test/unit/registration-workspace-ui.test.ts index b783cc86..2f39cdc3 100644 --- a/test/unit/registration-workspace-ui.test.ts +++ b/test/unit/registration-workspace-ui.test.ts @@ -14,7 +14,7 @@ import { } from "../../apps/gittensory-ui/src/lib/registration-workspace"; const FORBIDDEN = - /wallet|hotkey|raw trust score|payout|\breward\b|reward estimate|farming|private reviewability|public score estimate/i; + /wallet|hotkey|raw trust score|payout|reward|farming|private reviewability|reviewability|public score estimate/i; function readyFixture(overrides: Partial = {}): RegistrationReadinessPayload { return { From 087f3715210f9711b11ba613044adb48817b2895 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:22:39 +0200 Subject: [PATCH 10/10] feat(control-panel): add repo owner registration workspace (#131) Expand the owner panel into a structured registration workspace with lane tradeoffs, maintainer economics, queue/label/test policy cards, freshness warnings, and sanitized config recommendations. Exclude apps/** from root coverage thresholds so UI helper unit tests do not dilute the src gate. Co-authored-by: Cursor --- .../site/app-panels/owner-panel.tsx | 356 ++++++--- .../src/lib/registration-workspace.ts | 713 ++++++++++++++++++ apps/gittensory-ui/src/routes/app.owner.tsx | 4 +- apps/gittensory-ui/src/routes/app.repos.tsx | 4 +- test/unit/registration-workspace-ui.test.ts | 507 +++++++++++++ vitest.config.ts | 2 +- 6 files changed, 1457 insertions(+), 129 deletions(-) create mode 100644 apps/gittensory-ui/src/lib/registration-workspace.ts create mode 100644 test/unit/registration-workspace-ui.test.ts diff --git a/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx b/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx index 020c26fc..f293dc09 100644 --- a/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx +++ b/apps/gittensory-ui/src/components/site/app-panels/owner-panel.tsx @@ -1,162 +1,270 @@ import { useMemo, useState } from "react"; -import { DiffBlock, StatusPill, type Status } from "@/components/site/control-primitives"; +import { + BoundaryBadge, + DiffBlock, + StatusPill, + type Status, +} from "@/components/site/control-primitives"; import { StateBoundary } from "@/components/site/state-views"; import { Input } from "@/components/ui/input"; import { useApiResource } from "@/lib/api/use-api-resource"; +import { + buildRegistrationWorkspaceView, + splitRepoFullName, + type GittensorConfigRecommendationPayload, + type RegistrationReadinessPayload, + type RegistrationWorkspaceView, + type WorkspaceLaneStatus, +} from "@/lib/registration-workspace"; -const STATUS_MAP: Record = { ok: "ready", warn: "warn", blocked: "blocked" }; - -type RegistrationReadiness = { - repoFullName: string; - ready: boolean; - recommendedRegistrationMode: string; - issuePolicy: string; - blockers: string[]; - warnings: string[]; +const LANE_STATUS_MAP: Record = { + ready: "ready", + warn: "warn", + blocked: "blocked", + info: "info", }; -type ConfigRecommendation = { - current: Record | null; - recommended: Record; - reasons: string[]; - warnings: string[]; +const FRESHNESS_STATUS_MAP: Record = { + complete: "ready", + degraded: "degraded", + stale: "stale", + unknown: "info", }; export function OwnerPanel() { const [repo, setRepo] = useState("entrius/gittensor"); - const [owner, name] = repo.split("/"); - const repoPath = - owner && name - ? `/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}` - : "/v1/repos/entrius/gittensor"; - const readiness = useApiResource( - `${repoPath}/registration-readiness`, + const parts = splitRepoFullName(repo.trim()); + const repoPath = parts + ? `/v1/repos/${encodeURIComponent(parts.owner)}/${encodeURIComponent(parts.repo)}` + : null; + const readiness = useApiResource( + `${repoPath ?? "/v1/repos/__invalid__/__invalid__"}/registration-readiness`, "Registration readiness", + undefined, + { enabled: Boolean(repoPath) }, ); - const config = useApiResource( - `${repoPath}/gittensor-config-recommendation`, + const config = useApiResource( + `${repoPath ?? "/v1/repos/__invalid__/__invalid__"}/gittensor-config-recommendation`, "Config recommendation", + undefined, + { enabled: Boolean(repoPath) }, ); - const liveSteps = useMemo(() => { + + const workspace = useMemo(() => { if (readiness.status !== "ready") return null; - const steps = [ - { - id: "mode", - title: "Registration mode", - detail: readiness.data.recommendedRegistrationMode, - status: readiness.data.ready ? "ok" : "warn", - }, - { - id: "issue-policy", - title: "Issue policy", - detail: readiness.data.issuePolicy, - status: readiness.data.ready ? "ok" : "warn", - }, - ...readiness.data.blockers.map((detail, i) => ({ - id: `blocker-${i}`, - title: "Readiness blocker", - detail, - status: "blocked", - })), - ...readiness.data.warnings.map((detail, i) => ({ - id: `warning-${i}`, - title: "Readiness warning", - detail, - status: "warn", - })), - ]; - return steps.length > 0 ? steps : null; - }, [readiness]); - const steps = liveSteps ?? []; - const removed = config.status === "ready" ? recordLines(config.data.current ?? {}) : []; - const added = config.status === "ready" ? recordLines(config.data.recommended) : []; + const configPayload = config.status === "ready" ? config.data : null; + return buildRegistrationWorkspaceView(readiness.data, configPayload); + }, [readiness, config]); + const refresh = () => { void readiness.reload(); void config.reload(); }; + const invalidRepo = repo.trim().length > 0 && !parts; + return ( - -
-
-
-
-

Registration readiness

-

- Live read-only API check from cached repository intelligence. -

-
-
- - setRepo(e.target.value)} - className="mt-1 font-mono text-token-xs" - /> -
+
+
+
+
+

Registration workspace

+

+ Readiness report with lane tradeoffs — not raw Gittensor telemetry. +

- {readiness.status === "error" && ( -

- Live readiness failed ({readiness.error}). +

+ + setRepo(e.target.value)} + className="mt-1 font-mono text-token-xs" + placeholder="owner/repo" + /> + {invalidRepo ? ( +

Use a valid owner/repo slug.

+ ) : null} +
+
+
+ + + {workspace ? ( + + ) : null} + {repoPath && readiness.status === "error" ? ( +

+ Readiness failed ({readiness.error}). +

+ ) : null} +
+
+ ); +} + +function RegistrationWorkspace({ + workspace, + generatedLabel, +}: { + workspace: RegistrationWorkspaceView; + generatedLabel: string; +}) { + return ( +
+
+
+
+
+

{workspace.repoFullName}

+ + {workspace.summary.ready ? "Ready" : "Not ready"} + + + {workspace.freshness.status} + + +
+

{workspace.summary.headline}

+

+ Generated {generatedLabel}

- )} -
    - {steps.map((s) => ( -
  • -
    -
    {s.title}
    -
    {s.detail}
    -
    - {s.status} -
  • +
+
+

+ {workspace.advisoryBanner} +

+ {workspace.freshness.warnings.length > 0 ? ( +
    + {workspace.freshness.warnings.map((warning) => ( +
  • {warning}
  • ))}
-
+ ) : null} +
+ + +
+
-
-

Suggested .gittensor.yml

-

- Diff against the current configuration. Apply via PR when ready. -

- {config.status === "error" && ( -

- Live recommendation failed ({config.error}). +

+ + + + +
+ +
+

Operations & policy

+
+ {workspace.operations.map((section) => ( + + ))} +
+ {workspace.policyWarnings.length > 0 ? ( +
+

Focus policy warnings

+
    + {workspace.policyWarnings.map((warning) => ( +
  • +
    + {warning.title} + + {warning.severity} + +
    +

    {warning.detail}

    +

    {warning.action}

    +
  • + ))} +
+
+ ) : null} +
+ + {workspace.config ? ( +
+
+

Suggested Gittensor config

+

+ Tradeoffs below separate maintainer economics from contributor lanes. Apply via PR + when ready.

- )} -
-
+ {workspace.config.tradeoffs.length > 0 ? ( +
+

+ Tradeoffs +

+
    + {workspace.config.tradeoffs.map((entry) => ( +
  • {entry}
  • + ))} +
+
+ ) : null} +
+ ) : null} +
+ ); +} + +function WorkspaceSectionCard({ + section, +}: { + section: RegistrationWorkspaceView["lanes"]["directPr"]; +}) { + return ( +
+
+

{section.title}

+ {section.status}
- +

{section.summary}

+ {section.bullets.length > 0 ? ( +
    + {section.bullets.map((bullet) => ( +
  • {bullet}
  • + ))} +
+ ) : null} +
); } -function recordLines(record: Record) { - const entries = Object.entries(record); - if (entries.length === 0) return ["{}"]; - return entries.slice(0, 8).map(([key, value]) => `${key}: ${formatValue(value)}`); +function Metric({ label, value }: { label: string; value: string }) { + return ( +
+
+ {label} +
+
{value}
+
+ ); } -function formatValue(value: unknown) { - if (value === null) return "null"; - if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { - return String(value); - } - return JSON.stringify(value); +function formatGeneratedAt(value: string) { + const parsed = Date.parse(value); + if (!Number.isFinite(parsed)) return value; + return new Date(parsed).toLocaleString(); } diff --git a/apps/gittensory-ui/src/lib/registration-workspace.ts b/apps/gittensory-ui/src/lib/registration-workspace.ts new file mode 100644 index 00000000..044a303b --- /dev/null +++ b/apps/gittensory-ui/src/lib/registration-workspace.ts @@ -0,0 +1,713 @@ +import { isFocusManifestPublicSafe } from "../../../../src/signals/focus-manifest"; +import { splitRepoFullName } from "./maintainer-settings-preview"; + +export type WorkspaceFreshness = "complete" | "degraded" | "stale" | "unknown"; +export type WorkspaceLaneStatus = "ready" | "warn" | "blocked" | "info"; + +function normalizePublicWorkspaceText(text: string): string { + return text.trim().replace(/\s+/g, " "); +} + +export type RegistrationWorkspaceDataQuality = { + status?: string | undefined; + partial?: boolean | undefined; + warnings?: string[] | undefined; +}; + +export type RegistrationReadinessPayload = { + repoFullName: string; + generatedAt: string; + ready: boolean; + recommendedRegistrationMode: string; + issuePolicy: string; + directPrReadiness: { ready: boolean; reasons: string[] }; + issueDiscoveryReadiness: { ready: boolean; recommendation: string; reasons: string[] }; + labelPolicy: Record; + maintainerCutReadiness: Record; + testCoverageHealth: { + status: string; + trustedLabelPipelineReady: boolean; + checkRunMode: string; + requiredGate: string[]; + note: string; + warnings: string[]; + }; + queueHealth: { + level: string; + burdenScore: number; + reviewablePullRequests: number; + summary: string; + }; + contributorIntakeHealth: Record; + githubApp: { + installed: boolean; + publicSurface: string; + commentMode: string; + checkRunMode: string; + quietByDefault: boolean; + behavior: string; + warnings: string[]; + }; + policyReadiness: { + summary: string; + publicWarnings: Array<{ title: string; detail: string; action: string; severity: string }>; + } | null; + blockers: string[]; + warnings: string[]; + docsCompleteness?: { status: string; requiredDocs: string[]; note: string } | undefined; + dataQuality?: RegistrationWorkspaceDataQuality | undefined; +}; + +export type OwnerWorkflowState = "accepted" | "needs_cleanup" | "not_ready"; + +export type OwnerWorkflowRemediationKind = "action" | "manual"; + +export type OwnerWorkflowBucketId = + | "policy" + | "data_quality" + | "queue_health" + | "docs_onboarding" + | "maintainer_capacity"; + +export type OwnerWorkflowItem = { + id: string; + title: string; + state: OwnerWorkflowState; + summary: string; + remediation: string; + remediationKind: OwnerWorkflowRemediationKind; +}; + +export type OwnerWorkflowBucket = { + id: OwnerWorkflowBucketId; + title: string; + state: OwnerWorkflowState; + summary: string; + items: OwnerWorkflowItem[]; +}; + +export type RegistrationOwnerWorkflow = { + overallState: OwnerWorkflowState; + overallHeadline: string; + buckets: OwnerWorkflowBucket[]; + nextSteps: string[]; +}; + +export type GittensorConfigRecommendationPayload = { + repoFullName: string; + generatedAt: string; + privateOnly?: boolean | undefined; + current: Record | null; + recommended: Record; + tradeoffs: string[]; + reasons: string[]; + warnings: string[]; + dataQuality?: RegistrationWorkspaceDataQuality | undefined; +}; + +export type RegistrationWorkspaceSection = { + id: string; + title: string; + status: WorkspaceLaneStatus; + summary: string; + bullets: string[]; +}; + +export type RegistrationWorkspaceView = { + repoFullName: string; + generatedAt: string; + advisoryBanner: string; + freshness: { status: WorkspaceFreshness; warnings: string[] }; + summary: { + ready: boolean; + headline: string; + recommendedMode: string; + issuePolicy: string; + status: WorkspaceLaneStatus; + }; + lanes: { + directPr: RegistrationWorkspaceSection; + issueDiscovery: RegistrationWorkspaceSection; + maintainerEconomics: RegistrationWorkspaceSection; + minerGuidance: RegistrationWorkspaceSection; + }; + operations: RegistrationWorkspaceSection[]; + policyWarnings: Array<{ title: string; detail: string; action: string; severity: string }>; + workflow: RegistrationOwnerWorkflow; + config: { + tradeoffs: string[]; + reasons: string[]; + warnings: string[]; + recommendedLines: string[]; + currentLines: string[]; + } | null; +}; + +export function isRegistrationWorkspacePublicSafe(text: string): boolean { + const normalized = normalizePublicWorkspaceText(text); + return normalized.length > 0 && isFocusManifestPublicSafe(normalized); +} + +export function sanitizeRegistrationWorkspaceText(text: string): string | null { + const normalized = normalizePublicWorkspaceText(text); + if (!normalized || !isFocusManifestPublicSafe(normalized)) return null; + return normalized; +} + +export function resolveRegistrationWorkspaceFreshness( + readinessQuality?: RegistrationWorkspaceDataQuality, + configQuality?: RegistrationWorkspaceDataQuality, +): { status: WorkspaceFreshness; warnings: string[] } { + const warnings = [...(readinessQuality?.warnings ?? []), ...(configQuality?.warnings ?? [])] + .map((entry) => sanitizeRegistrationWorkspaceText(entry)) + .filter((entry): entry is string => Boolean(entry)); + const statuses = [readinessQuality?.status, configQuality?.status].filter( + (entry): entry is string => Boolean(entry), + ); + if (statuses.includes("blocked")) return { status: "stale", warnings }; + if (readinessQuality?.partial || configQuality?.partial || statuses.includes("degraded")) { + return { status: "degraded", warnings }; + } + if (statuses.includes("complete")) return { status: "complete", warnings }; + return { status: warnings.length > 0 ? "degraded" : "unknown", warnings }; +} + +export function buildRegistrationWorkspaceView( + readiness: RegistrationReadinessPayload, + config: GittensorConfigRecommendationPayload | null, +): RegistrationWorkspaceView { + const freshness = resolveRegistrationWorkspaceFreshness( + readiness.dataQuality, + config?.dataQuality, + ); + const directPrStatus: WorkspaceLaneStatus = readiness.directPrReadiness.ready + ? "ready" + : readiness.blockers.length > 0 + ? "blocked" + : "warn"; + const issueDiscoveryStatus: WorkspaceLaneStatus = + readiness.issueDiscoveryReadiness.recommendation === "not_recommended" + ? "info" + : readiness.issueDiscoveryReadiness.ready + ? "ready" + : "warn"; + + const maintainerCut = readiness.maintainerCutReadiness; + const maintainerReady = maintainerCut.ready === true; + const maintainerEconomicsStatus: WorkspaceLaneStatus = maintainerReady + ? "ready" + : readiness.ready + ? "warn" + : "blocked"; + + const labelPolicy = readiness.labelPolicy; + const intake = readiness.contributorIntakeHealth; + + return { + repoFullName: readiness.repoFullName, + generatedAt: readiness.generatedAt, + advisoryBanner: + "Advisory workspace only. Recommendations explain tradeoffs for repo owners; they do not guarantee Gittensor incentive outcomes.", + freshness, + summary: { + ready: readiness.ready, + headline: readiness.ready + ? "Repository looks ready for contributor intake with the recommended posture." + : "Resolve blockers before inviting more outside contributor traffic.", + recommendedMode: readiness.recommendedRegistrationMode, + issuePolicy: readiness.issuePolicy, + status: readiness.ready ? "ready" : readiness.blockers.length > 0 ? "blocked" : "warn", + }, + lanes: { + directPr: { + id: "direct-pr", + title: "Direct PR lane", + status: directPrStatus, + summary: readiness.directPrReadiness.ready + ? "Direct-PR intake is healthy enough for the recommended registration mode." + : "Direct-PR intake needs attention before broadening contributor traffic.", + bullets: sanitizeBulletList(readiness.directPrReadiness.reasons), + }, + issueDiscovery: { + id: "issue-discovery", + title: "Issue discovery lane", + status: issueDiscoveryStatus, + summary: `Recommendation: ${readiness.issueDiscoveryReadiness.recommendation.replace(/_/g, " ")}.`, + bullets: sanitizeBulletList(readiness.issueDiscoveryReadiness.reasons), + }, + maintainerEconomics: { + id: "maintainer-economics", + title: "Maintainer economics", + status: maintainerEconomicsStatus, + summary: + stringField(maintainerCut, "summary") ?? + "Maintainer-cut posture is separate from public contributor incentive guidance.", + bullets: sanitizeBulletList([ + ...(Array.isArray(maintainerCut.reasons) ? (maintainerCut.reasons as string[]) : []), + ...(Array.isArray(maintainerCut.warnings) ? (maintainerCut.warnings as string[]) : []), + typeof maintainerCut.recommendedAction === "string" + ? `Suggested action: ${maintainerCut.recommendedAction.replace(/_/g, " ")}.` + : "", + ]), + }, + minerGuidance: { + id: "miner-guidance", + title: "Miner scoreability (separate)", + status: "info", + summary: + "Contributor/miner scoreability and queue pressure are evaluated separately from maintainer-cut economics.", + bullets: sanitizeBulletList([ + `Contributor intake: ${stringField(intake, "level") ?? "unknown"}.`, + `Queue burden: ${readiness.queueHealth.level} (${readiness.queueHealth.reviewablePullRequests} reviewable PRs).`, + readiness.queueHealth.summary, + ]), + }, + }, + operations: [ + { + id: "queue-health", + title: "Queue health", + status: queueStatus(readiness.queueHealth.level), + summary: readiness.queueHealth.summary, + bullets: sanitizeBulletList([ + `Burden score: ${readiness.queueHealth.burdenScore}.`, + `Reviewable pull requests: ${readiness.queueHealth.reviewablePullRequests}.`, + ]), + }, + { + id: "label-policy", + title: "Label policy", + status: labelPolicy.trustedPipelineReady === true ? "ready" : "warn", + summary: "Registry labels and trusted pipeline readiness for incoming work.", + bullets: sanitizeBulletList([ + labelPolicy.autoLabelEnabled === true + ? `Auto-label enabled (${String(labelPolicy.label ?? "gittensor")}).` + : "Auto-label is disabled.", + labelPolicy.trustedPipelineReady === true + ? "Trusted label pipeline is verified." + : "Trusted label pipeline is not verified yet.", + ...(Array.isArray(labelPolicy.missingOrUnusedRegistryLabels) + ? (labelPolicy.missingOrUnusedRegistryLabels as string[]).map( + (label) => `Missing or unused label: ${label}`, + ) + : []), + ]), + }, + { + id: "test-policy", + title: "Test & validation policy", + status: readiness.testCoverageHealth.status === "gate_ready" ? "ready" : "warn", + summary: readiness.testCoverageHealth.note, + bullets: sanitizeBulletList([ + `Coverage gate: ${readiness.testCoverageHealth.status}.`, + `Check runs: ${readiness.testCoverageHealth.checkRunMode}.`, + ...(readiness.testCoverageHealth.requiredGate ?? []).map( + (gate) => `Required gate: ${gate}`, + ), + ...readiness.testCoverageHealth.warnings, + ]), + }, + { + id: "github-app", + title: "GitHub App behavior", + status: readiness.githubApp.installed ? "ready" : "warn", + summary: readiness.githubApp.behavior, + bullets: sanitizeBulletList([ + `Public surface: ${readiness.githubApp.publicSurface}.`, + `Comment mode: ${readiness.githubApp.commentMode}.`, + ...(readiness.githubApp.quietByDefault ? ["Quiet-by-default posture is enabled."] : []), + ...readiness.githubApp.warnings, + ]), + }, + ], + policyWarnings: (readiness.policyReadiness?.publicWarnings ?? []) + .map((warning) => ({ + title: sanitizeRegistrationWorkspaceText(warning.title) ?? "Policy warning", + detail: sanitizeRegistrationWorkspaceText(warning.detail) ?? "", + action: sanitizeRegistrationWorkspaceText(warning.action) ?? "", + severity: warning.severity, + })) + .filter((warning) => warning.detail.length > 0), + workflow: buildRegistrationOwnerWorkflow(readiness, config), + config: config + ? { + tradeoffs: sanitizeBulletList(config.tradeoffs), + reasons: sanitizeBulletList(config.reasons), + warnings: sanitizeBulletList(config.warnings), + recommendedLines: recordLines(config.recommended), + currentLines: recordLines(config.current ?? {}), + } + : null, + }; +} + +export function buildRegistrationOwnerWorkflow( + readiness: RegistrationReadinessPayload, + config: GittensorConfigRecommendationPayload | null, +): RegistrationOwnerWorkflow { + const freshness = resolveRegistrationWorkspaceFreshness( + readiness.dataQuality, + config?.dataQuality, + ); + const intakeLevel = stringField(readiness.contributorIntakeHealth, "level") ?? "unknown"; + const labelPolicy = readiness.labelPolicy; + const docs = readiness.docsCompleteness ?? { status: "unknown", requiredDocs: [], note: "" }; + + const policyItems: OwnerWorkflowItem[] = []; + for (const warning of readiness.policyReadiness?.publicWarnings ?? []) { + const title = sanitizeRegistrationWorkspaceText(warning.title); + const detail = sanitizeRegistrationWorkspaceText(warning.detail); + const action = sanitizeRegistrationWorkspaceText(warning.action); + if (!title || !detail) continue; + policyItems.push({ + id: `policy-warning-${policyItems.length}`, + title, + state: warning.severity === "critical" ? "not_ready" : "needs_cleanup", + summary: detail, + remediation: action ?? "Review focus manifest and repository settings for consistency.", + remediationKind: action ? "action" : "manual", + }); + } + if (!readiness.directPrReadiness.ready) { + policyItems.push({ + id: "direct-pr-lane", + title: "Direct PR lane", + state: readiness.blockers.length > 0 ? "not_ready" : "needs_cleanup", + summary: "Direct-PR intake is not ready for the recommended registration mode.", + remediation: + "Stabilize config quality and contributor intake before promoting direct-PR traffic.", + remediationKind: "action", + }); + } + if (labelPolicy.trustedPipelineReady !== true) { + policyItems.push({ + id: "label-trust", + title: "Label policy", + state: "needs_cleanup", + summary: "Trusted label pipeline is not verified for registry labels.", + remediation: + "Create or verify configured registry labels and enable the trusted label pipeline in repo settings.", + remediationKind: "action", + }); + } + + const dataQualityItems: OwnerWorkflowItem[] = []; + if (freshness.status === "stale" || freshness.status === "degraded") { + for (const warning of freshness.warnings) { + dataQualityItems.push({ + id: `data-warning-${dataQualityItems.length}`, + title: "Signal freshness", + state: freshness.status === "stale" ? "not_ready" : "needs_cleanup", + summary: warning, + remediation: + "Wait for repository intelligence to refresh or run a maintainer backfill before acting on readiness.", + remediationKind: "manual", + }); + } + } + if (readiness.testCoverageHealth.status !== "gate_ready") { + dataQualityItems.push({ + id: "test-gate", + title: "Validation gate", + state: "needs_cleanup", + summary: readiness.testCoverageHealth.note, + remediation: + "Document expected CI commands in the repo and verify check-run settings before widening intake.", + remediationKind: "action", + }); + } + for (const warning of readiness.testCoverageHealth.warnings) { + const safe = sanitizeRegistrationWorkspaceText(warning); + if (!safe) continue; + dataQualityItems.push({ + id: `test-warning-${dataQualityItems.length}`, + title: "Test policy warning", + state: "needs_cleanup", + summary: safe, + remediation: "Address validation warnings before inviting more contributor traffic.", + remediationKind: "action", + }); + } + + const queueItems: OwnerWorkflowItem[] = []; + const queueLevel = readiness.queueHealth.level; + if (queueLevel === "high" || queueLevel === "critical") { + queueItems.push({ + id: "queue-pressure", + title: "PR queue pressure", + state: "not_ready", + summary: readiness.queueHealth.summary, + remediation: + "Reduce open PR queue pressure or narrow accepted lanes before inviting more contributors.", + remediationKind: "action", + }); + } else if (queueLevel === "medium") { + queueItems.push({ + id: "queue-pressure", + title: "PR queue pressure", + state: "needs_cleanup", + summary: readiness.queueHealth.summary, + remediation: "Review open PRs and triage burden before expanding contributor intake.", + remediationKind: "action", + }); + } + + const docsItems: OwnerWorkflowItem[] = []; + if (docs.status === "repo_docs_not_crawled") { + docsItems.push({ + id: "docs-crawl", + title: "Onboarding docs", + state: "needs_cleanup", + summary: docs.note, + remediation: + "Manually verify CONTRIBUTING.md, README onboarding steps, and issue templates in GitHub; remote doc crawling is not enabled in this signal yet.", + remediationKind: "manual", + }); + } else if (docs.requiredDocs.length > 0) { + docsItems.push({ + id: "docs-required", + title: "Required docs", + state: "accepted", + summary: `Expected docs: ${docs.requiredDocs.join(", ")}.`, + remediation: "Keep onboarding docs aligned with the recommended registration mode.", + remediationKind: "action", + }); + } + + const capacityItems: OwnerWorkflowItem[] = []; + if (intakeLevel === "blocked" || intakeLevel === "strained") { + capacityItems.push({ + id: "intake-health", + title: "Contributor intake", + state: intakeLevel === "blocked" ? "not_ready" : "needs_cleanup", + summary: + stringField(readiness.contributorIntakeHealth, "summary") ?? + `Contributor intake is ${intakeLevel}.`, + remediation: + "Stabilize triage capacity and duplicate-risk intake before inviting more issue reports or direct PRs.", + remediationKind: "action", + }); + } + const maintainerCut = readiness.maintainerCutReadiness; + if (maintainerCut.ready !== true) { + capacityItems.push({ + id: "maintainer-cut", + title: "Maintainer cut readiness", + state: readiness.ready ? "needs_cleanup" : "not_ready", + summary: stringField(maintainerCut, "summary") ?? "Maintainer-cut posture needs review.", + remediation: + typeof maintainerCut.recommendedAction === "string" + ? `Follow maintainer-cut guidance: ${maintainerCut.recommendedAction.replace(/_/g, " ")}.` + : "Resolve queue and config blockers before changing maintainer_cut.", + remediationKind: "action", + }); + } + for (const blocker of readiness.blockers) { + const safe = sanitizeRegistrationWorkspaceText(blocker); + if (!safe) continue; + const bucket = classifyBlockerBucket(safe); + const target = + bucket === "policy" + ? policyItems + : bucket === "data_quality" + ? dataQualityItems + : bucket === "queue_health" + ? queueItems + : bucket === "docs_onboarding" + ? docsItems + : capacityItems; + target.push({ + id: `blocker-${target.length}`, + title: "Registration blocker", + state: "not_ready", + summary: safe, + remediation: remediationForBlocker(safe), + remediationKind: remediationKindForBlocker(safe), + }); + } + + const buckets: OwnerWorkflowBucket[] = [ + buildWorkflowBucket( + "policy", + "Policy & lanes", + policyItems, + "Focus manifest, lane posture, and label policy.", + ), + buildWorkflowBucket( + "data_quality", + "Data quality", + dataQualityItems, + "Signal freshness and validation gates.", + ), + buildWorkflowBucket( + "queue_health", + "Queue health", + queueItems, + "Open PR pressure and reviewable queue burden.", + ), + buildWorkflowBucket( + "docs_onboarding", + "Docs & onboarding", + docsItems, + "Contributor-facing documentation readiness.", + ), + buildWorkflowBucket( + "maintainer_capacity", + "Maintainer capacity", + capacityItems, + "Intake health, maintainer-cut posture, and GitHub App assistance.", + ), + ]; + + const overallState = aggregateWorkflowState(buckets.map((bucket) => bucket.state)); + const nextSteps = buckets + .flatMap((bucket) => bucket.items) + .filter((item) => item.state !== "accepted") + .sort((left, right) => workflowRank(left.state) - workflowRank(right.state)) + .map((item) => item.remediation) + .filter((entry, index, all) => all.indexOf(entry) === index) + .slice(0, 5); + + return { + overallState, + overallHeadline: workflowHeadline(overallState, readiness.ready), + buckets, + nextSteps, + }; +} + +export function collectRegistrationOwnerWorkflowPublicText( + workflow: RegistrationOwnerWorkflow, +): string[] { + return [ + workflow.overallHeadline, + ...workflow.nextSteps, + ...workflow.buckets.flatMap((bucket) => [ + bucket.title, + bucket.summary, + ...bucket.items.flatMap((item) => [item.title, item.summary, item.remediation]), + ]), + ]; +} + +export function collectRegistrationWorkspacePublicText(view: RegistrationWorkspaceView): string[] { + const chunks = [ + view.advisoryBanner, + view.summary.headline, + ...collectRegistrationOwnerWorkflowPublicText(view.workflow), + ...view.freshness.warnings, + ...view.lanes.directPr.bullets, + ...view.lanes.issueDiscovery.bullets, + ...view.lanes.maintainerEconomics.bullets, + ...view.lanes.minerGuidance.bullets, + ...view.operations.flatMap((section) => [section.summary, ...section.bullets]), + ...(view.config?.tradeoffs ?? []), + ...(view.config?.reasons ?? []), + ...view.policyWarnings.flatMap((warning) => [warning.title, warning.detail, warning.action]), + ]; + return chunks.filter((entry) => entry.length > 0); +} + +export { splitRepoFullName }; + +function sanitizeBulletList(entries: string[]): string[] { + return entries + .map((entry) => sanitizeRegistrationWorkspaceText(entry)) + .filter((entry): entry is string => Boolean(entry)); +} + +function stringField(record: Record, key: string): string | null { + const value = record[key]; + return typeof value === "string" ? sanitizeRegistrationWorkspaceText(value) : null; +} + +function queueStatus(level: string): WorkspaceLaneStatus { + if (level === "low") return "ready"; + if (level === "medium") return "warn"; + if (level === "high" || level === "critical") return "blocked"; + return "info"; +} + +function recordLines(record: Record): string[] { + const entries = Object.entries(record); + if (entries.length === 0) return ["{}"]; + return entries.slice(0, 10).map(([key, value]) => `${key}: ${formatValue(value)}`); +} + +function formatValue(value: unknown): string { + if (value === null) return "null"; + if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { + return String(value); + } + return JSON.stringify(value); +} + +function buildWorkflowBucket( + id: OwnerWorkflowBucketId, + title: string, + items: OwnerWorkflowItem[], + summary: string, +): OwnerWorkflowBucket { + const state = + items.length === 0 ? "accepted" : aggregateWorkflowState(items.map((item) => item.state)); + return { id, title, state, summary, items }; +} + +function aggregateWorkflowState(states: OwnerWorkflowState[]): OwnerWorkflowState { + if (states.includes("not_ready")) return "not_ready"; + if (states.includes("needs_cleanup")) return "needs_cleanup"; + return "accepted"; +} + +function workflowRank(state: OwnerWorkflowState): number { + if (state === "not_ready") return 0; + if (state === "needs_cleanup") return 1; + return 2; +} + +function workflowHeadline(state: OwnerWorkflowState, ready: boolean): string { + if (state === "accepted" && ready) { + return "Accepted — contributor intake posture matches the recommended registration mode."; + } + if (state === "not_ready") { + return "Not ready — resolve blockers in the workflow buckets before inviting more contributors."; + } + return "Needs cleanup — some areas are acceptable but require maintainer follow-up before scaling intake."; +} + +function classifyBlockerBucket(blocker: string): OwnerWorkflowBucketId { + const lower = blocker.toLowerCase(); + if (lower.includes("config quality") || lower.includes("focus") || lower.includes("label")) + return "policy"; + if (lower.includes("doc") || lower.includes("contributing")) return "docs_onboarding"; + if (lower.includes("queue") || lower.includes("pull request") || lower.includes("pr ")) + return "queue_health"; + if (lower.includes("install") || lower.includes("github app") || lower.includes("intake")) + return "maintainer_capacity"; + if (lower.includes("drift") || lower.includes("forecast") || lower.includes("coverage")) + return "data_quality"; + return "maintainer_capacity"; +} + +function remediationForBlocker(blocker: string): string { + const bucket = classifyBlockerBucket(blocker); + switch (bucket) { + case "policy": + return "Fix registry config, focus manifest, or label policy issues referenced in the blocker."; + case "data_quality": + return "Refresh repository intelligence or update validation fixtures before re-checking readiness."; + case "queue_health": + return "Reduce queue pressure and close or triage stale pull requests before expanding intake."; + case "docs_onboarding": + return "Update onboarding docs and templates in the repository (manual GitHub edit required)."; + case "maintainer_capacity": + return "Address installation health, intake strain, or maintainer-cut blockers before promoting the repo."; + } +} + +function remediationKindForBlocker(blocker: string): OwnerWorkflowRemediationKind { + const lower = blocker.toLowerCase(); + if (lower.includes("not crawled") || lower.includes("manual") || lower.includes("install")) + return "manual"; + return "action"; +} diff --git a/apps/gittensory-ui/src/routes/app.owner.tsx b/apps/gittensory-ui/src/routes/app.owner.tsx index 05c42179..04f4ed49 100644 --- a/apps/gittensory-ui/src/routes/app.owner.tsx +++ b/apps/gittensory-ui/src/routes/app.owner.tsx @@ -12,8 +12,8 @@ function OwnerRoute() {
diff --git a/apps/gittensory-ui/src/routes/app.repos.tsx b/apps/gittensory-ui/src/routes/app.repos.tsx index 1af88dc7..2a366036 100644 --- a/apps/gittensory-ui/src/routes/app.repos.tsx +++ b/apps/gittensory-ui/src/routes/app.repos.tsx @@ -18,7 +18,7 @@ export const Route = createFileRoute("/app/repos")({ const LABELS: Record = { maintainer: "Maintainer console", - owner: "Repo owner", + owner: "Registration workspace", }; function Repos() { @@ -31,7 +31,7 @@ function Repos() { = {}): RegistrationReadinessPayload { + return { + repoFullName: "JSONbored/gittensory", + generatedAt: "2026-06-01T00:00:00.000Z", + ready: true, + recommendedRegistrationMode: "direct_pr", + issuePolicy: "direct_pr_no_issue_required", + directPrReadiness: { ready: true, reasons: ["Direct-PR intake is healthy."] }, + issueDiscoveryReadiness: { + ready: false, + recommendation: "not_recommended", + reasons: ["Issue discovery should stay off until intake is excellent."], + }, + labelPolicy: { + autoLabelEnabled: true, + label: "gittensor", + trustedPipelineReady: true, + missingOrUnusedRegistryLabels: [], + }, + maintainerCutReadiness: { + ready: true, + summary: "Maintainer cut can be reviewed without blocking intake.", + reasons: ["Queue burden is low."], + warnings: [], + recommendedAction: "consider_small_cut", + }, + testCoverageHealth: { + status: "gate_ready", + trustedLabelPipelineReady: true, + checkRunMode: "enabled", + requiredGate: ["npm run test:ci"], + note: "Use repo CI gates before widening contributor intake.", + warnings: [], + }, + queueHealth: { + level: "low", + burdenScore: 0.2, + reviewablePullRequests: 3, + summary: "Queue burden is low.", + }, + contributorIntakeHealth: { level: "healthy", summary: "Contributor intake is healthy." }, + githubApp: { + installed: true, + publicSurface: "comment_and_label", + commentMode: "detected_contributors_only", + checkRunMode: "enabled", + quietByDefault: true, + behavior: "Quiet-by-default GitHub App assistance.", + warnings: [], + }, + policyReadiness: null, + blockers: [], + warnings: [], + docsCompleteness: { + status: "repo_docs_not_crawled", + requiredDocs: ["CONTRIBUTING.md", "README.md"], + note: "Gittensory validates public repo docs locally; remote crawl is not enabled yet.", + }, + dataQuality: { status: "complete", partial: false, warnings: [] }, + ...overrides, + }; +} + +function configFixture(): GittensorConfigRecommendationPayload { + return { + repoFullName: "JSONbored/gittensory", + generatedAt: "2026-06-01T00:00:00.000Z", + privateOnly: true, + current: { maintainerCut: 0 }, + recommended: { participationMode: "direct_pr", maintainerCut: 0.3, issueDiscoveryShare: 0 }, + tradeoffs: [ + "Staying direct-PR-only keeps maintainer triage low but forgoes issue-discovery contributor flow.", + "Introducing a maintainer cut rewards upkeep but reduces the share available to contributor miners.", + ], + reasons: ["Direct-PR mode is the safest default until issue-discovery intake is intentionally staffed."], + warnings: [], + dataQuality: { status: "complete", partial: false, warnings: [] }, + }; +} + +describe("registration workspace UI helpers", () => { + it("ready fixture produces an advisory workspace view with separated lanes", () => { + const view = buildRegistrationWorkspaceView(readyFixture(), configFixture()); + expect(view.summary.ready).toBe(true); + expect(view.lanes.directPr.status).toBe("ready"); + expect(view.lanes.issueDiscovery.title).toMatch(/Issue discovery/i); + expect(view.lanes.maintainerEconomics.title).toMatch(/Maintainer economics/i); + expect(view.lanes.minerGuidance.title).toMatch(/Miner scoreability/i); + expect(view.config?.tradeoffs.length).toBeGreaterThan(0); + expect(view.advisoryBanner).toMatch(/Advisory/i); + }); + + it("not-ready fixture surfaces blockers and blocked summary status", () => { + const view = buildRegistrationWorkspaceView( + readyFixture({ + ready: false, + blockers: ["Repository config quality needs attention before registration promotion."], + directPrReadiness: { ready: false, reasons: ["Config quality is fragile."] }, + }), + null, + ); + expect(view.summary.ready).toBe(false); + expect(view.summary.status).toBe("blocked"); + expect(view.summary.headline).toMatch(/Resolve blockers/i); + }); + + it("stale data fixture marks freshness degraded and keeps warnings", () => { + const freshness = resolveRegistrationWorkspaceFreshness( + { status: "degraded", partial: true, warnings: ["Burden forecast unavailable for JSONbored/gittensory."] }, + { status: "complete", partial: false, warnings: [] }, + ); + expect(freshness.status).toBe("degraded"); + expect(freshness.warnings[0]).toMatch(/Burden forecast unavailable/i); + + const view = buildRegistrationWorkspaceView( + readyFixture({ + dataQuality: { status: "degraded", partial: true, warnings: freshness.warnings }, + }), + configFixture(), + ); + expect(view.freshness.status).toBe("degraded"); + expect(view.freshness.warnings.length).toBeGreaterThan(0); + }); + + it("public text hygiene regression drops forbidden language from workspace output", () => { + const view = buildRegistrationWorkspaceView( + readyFixture({ + warnings: ["wallet hotkey payout estimate should be removed"], + directPrReadiness: { ready: true, reasons: ["Safe reason only."] }, + }), + configFixture(), + ); + const publicText = collectRegistrationWorkspacePublicText(view).join("\n"); + expect(publicText).not.toMatch(FORBIDDEN); + expect(sanitizeRegistrationWorkspaceText("estimate your reward")).toBeNull(); + expect(isRegistrationWorkspacePublicSafe("Queue burden is low.")).toBe(true); + }); + + it("guided workflow groups readiness into five buckets with remediation", () => { + const workflow = buildRegistrationOwnerWorkflow(readyFixture(), configFixture()); + expect(workflow.buckets.map((bucket) => bucket.id)).toEqual([ + "policy", + "data_quality", + "queue_health", + "docs_onboarding", + "maintainer_capacity", + ]); + const docs = workflow.buckets.find((bucket) => bucket.id === "docs_onboarding"); + expect(docs?.state).toBe("needs_cleanup"); + expect(docs?.items[0]?.remediationKind).toBe("manual"); + expect(workflow.overallState).toBe("needs_cleanup"); + expect(workflow.nextSteps.length).toBeGreaterThan(0); + }); + + it("blocked readiness maps workflow to not ready with concrete blocker remediation", () => { + const workflow = buildRegistrationOwnerWorkflow( + readyFixture({ + ready: false, + blockers: ["Repository config quality needs attention before registration promotion."], + directPrReadiness: { ready: false, reasons: ["Config quality is fragile."] }, + queueHealth: { + level: "critical", + burdenScore: 0.95, + reviewablePullRequests: 40, + summary: "Queue burden is critical.", + }, + }), + null, + ); + expect(workflow.overallState).toBe("not_ready"); + expect(workflow.buckets.find((bucket) => bucket.id === "queue_health")?.state).toBe("not_ready"); + const policy = workflow.buckets.find((bucket) => bucket.id === "policy"); + expect(policy?.items.some((item) => item.title === "Registration blocker")).toBe(true); + expect(collectRegistrationOwnerWorkflowPublicText(workflow).join(" ")).not.toMatch(FORBIDDEN); + }); + + it("accepted workflow when readiness is ready and buckets are clear", () => { + const workflow = buildRegistrationOwnerWorkflow( + readyFixture({ + docsCompleteness: { + status: "verified", + requiredDocs: ["CONTRIBUTING.md"], + note: "Docs verified locally.", + }, + testCoverageHealth: { + status: "gate_ready", + trustedLabelPipelineReady: true, + checkRunMode: "enabled", + requiredGate: ["npm run test:ci"], + note: "Gates ready.", + warnings: [], + }, + }), + configFixture(), + ); + expect(workflow.overallState).toBe("accepted"); + expect(workflow.buckets.every((bucket) => bucket.state === "accepted")).toBe(true); + const view = buildRegistrationWorkspaceView( + readyFixture({ + ready: true, + docsCompleteness: { status: "verified", requiredDocs: ["CONTRIBUTING.md"], note: "Docs verified locally." }, + }), + configFixture(), + ); + expect(view.workflow.overallState).toBe("accepted"); + }); + + it("resolveRegistrationWorkspaceFreshness handles blocked, stale, and unknown paths", () => { + expect(resolveRegistrationWorkspaceFreshness({ status: "blocked", partial: false, warnings: [] }).status).toBe( + "stale", + ); + expect( + resolveRegistrationWorkspaceFreshness( + { status: "degraded", partial: true, warnings: ["Signal drift detected."] }, + { status: "complete", partial: false, warnings: [] }, + ).status, + ).toBe("degraded"); + expect(resolveRegistrationWorkspaceFreshness(undefined, undefined).status).toBe("unknown"); + expect( + resolveRegistrationWorkspaceFreshness({ status: "unknown", partial: false, warnings: ["Refresh pending."] }) + .warnings, + ).toEqual(["Refresh pending."]); + }); + + it("workspace view covers lane, operations, and policy-warning branches", () => { + const view = buildRegistrationWorkspaceView( + readyFixture({ + ready: false, + blockers: [], + directPrReadiness: { ready: false, reasons: ["Lane warming up."] }, + issueDiscoveryReadiness: { + ready: true, + recommendation: "preferred", + reasons: ["Issue discovery is staffed."], + }, + maintainerCutReadiness: { + ready: false, + reasons: [], + warnings: ["Review maintainer cut."], + }, + labelPolicy: { + autoLabelEnabled: false, + label: "custom", + trustedPipelineReady: false, + missingOrUnusedRegistryLabels: ["needs-triage"], + }, + queueHealth: { level: "medium", burdenScore: 0.55, reviewablePullRequests: 12, summary: "Moderate queue." }, + testCoverageHealth: { + status: "needs_attention", + trustedLabelPipelineReady: false, + checkRunMode: "disabled", + requiredGate: [], + note: "Gate not ready.", + warnings: ["Missing required check."], + }, + githubApp: { + installed: false, + publicSurface: "off", + commentMode: "off", + checkRunMode: "off", + quietByDefault: false, + behavior: "GitHub App not installed.", + warnings: ["Install the app."], + }, + policyReadiness: { + summary: "Policy drift", + publicWarnings: [ + { + title: "Focus manifest drift", + detail: "Manifest paths no longer match registry scope.", + action: "Refresh focus manifest.", + severity: "critical", + }, + { title: "wallet", detail: "hotkey", action: "payout", severity: "warn" }, + ], + }, + }), + null, + ); + expect(view.summary.status).toBe("warn"); + expect(view.lanes.issueDiscovery.status).toBe("ready"); + expect(view.lanes.maintainerEconomics.status).toBe("blocked"); + expect(view.operations.find((section) => section.id === "queue-health")?.status).toBe("warn"); + expect(view.operations.find((section) => section.id === "label-policy")?.bullets.join(" ")).toMatch( + /not verified/i, + ); + expect(view.policyWarnings).toHaveLength(1); + expect(view.config).toBeNull(); + }); + + it("routes blockers into workflow buckets and remediation helpers", () => { + const workflow = buildRegistrationOwnerWorkflow( + readyFixture({ + ready: false, + blockers: [ + "Repository config quality needs attention before registration promotion.", + "CONTRIBUTING.md onboarding doc is missing.", + "Open pull request queue is overloaded.", + "GitHub App installation is incomplete.", + "Burden forecast drift requires refresh.", + "Generic maintainer capacity blocker.", + "wallet hotkey payout estimate", + ], + queueHealth: { level: "medium", burdenScore: 0.5, reviewablePullRequests: 8, summary: "Queue warming." }, + contributorIntakeHealth: { level: "strained", summary: "Intake is strained." }, + maintainerCutReadiness: { + ready: false, + summary: "Cut not ready.", + reasons: [], + warnings: [], + recommendedAction: "hold_cut", + }, + testCoverageHealth: { + status: "needs_attention", + trustedLabelPipelineReady: false, + checkRunMode: "enabled", + requiredGate: ["npm run test:ci"], + note: "Validation incomplete.", + warnings: ["Trusted label pipeline pending."], + }, + dataQuality: { status: "stale", partial: true, warnings: ["Forecast stale."] }, + labelPolicy: { + autoLabelEnabled: true, + label: "gittensor", + trustedPipelineReady: false, + missingOrUnusedRegistryLabels: [], + }, + }), + configFixture(), + ); + expect(workflow.overallState).toBe("not_ready"); + expect(workflow.buckets.find((bucket) => bucket.id === "policy")?.items.length).toBeGreaterThan(1); + expect( + workflow.buckets + .find((bucket) => bucket.id === "docs_onboarding") + ?.items.some((item) => /CONTRIBUTING|onboarding docs/i.test(item.remediation)), + ).toBe(true); + expect(workflow.buckets.find((bucket) => bucket.id === "queue_health")?.items.length).toBeGreaterThan(0); + expect(workflow.buckets.find((bucket) => bucket.id === "data_quality")?.items.length).toBeGreaterThan(0); + expect(workflow.buckets.find((bucket) => bucket.id === "maintainer_capacity")?.items.length).toBeGreaterThan(1); + expect(workflow.nextSteps.length).toBeGreaterThan(0); + }); + + it("covers remaining workflow and view branch arms for coverage", () => { + const warnDiscovery = buildRegistrationWorkspaceView( + readyFixture({ + issueDiscoveryReadiness: { + ready: false, + recommendation: "allowed", + reasons: ["Staff issue triage first."], + }, + directPrReadiness: { ready: false, reasons: ["Warming up."] }, + blockers: [], + }), + { + ...configFixture(), + current: { maintainerCut: 0.1 }, + recommended: { maintainerCut: 0.2, participationMode: "direct_pr" }, + }, + ); + expect(warnDiscovery.lanes.issueDiscovery.status).toBe("warn"); + expect(warnDiscovery.lanes.directPr.status).toBe("warn"); + expect(warnDiscovery.config?.currentLines[0]).toMatch(/maintainerCut/); + + const workflow = buildRegistrationOwnerWorkflow( + readyFixture({ + policyReadiness: { + summary: "ok", + publicWarnings: [ + { + title: "Label drift", + detail: "Registry label unused.", + action: "", + severity: "warn", + }, + ], + }, + queueHealth: { level: "medium", burdenScore: 0.55, reviewablePullRequests: 12, summary: "Moderate burden." }, + contributorIntakeHealth: { level: "strained", summary: "Intake strained." }, + docsCompleteness: { + status: "verified", + requiredDocs: ["CONTRIBUTING.md"], + note: "Docs verified.", + }, + maintainerCutReadiness: { ready: false, reasons: [], warnings: [] }, + blockers: [], + ready: true, + }), + null, + ); + expect(workflow.buckets.find((bucket) => bucket.id === "policy")?.items[0]?.remediationKind).toBe("manual"); + expect(workflow.overallHeadline).toMatch(/Needs cleanup/i); + expect(workflow.buckets.find((bucket) => bucket.id === "queue_health")?.state).toBe("needs_cleanup"); + + const highQueueWorkflow = buildRegistrationOwnerWorkflow( + readyFixture({ + queueHealth: { level: "high", burdenScore: 0.8, reviewablePullRequests: 20, summary: "High burden." }, + }), + null, + ); + expect(highQueueWorkflow.buckets.find((bucket) => bucket.id === "queue_health")?.state).toBe("not_ready"); + + const criticalQueue = buildRegistrationWorkspaceView( + readyFixture({ queueHealth: { level: "critical", burdenScore: 1, reviewablePullRequests: 99, summary: "Critical." } }), + null, + ); + expect(criticalQueue.operations.find((section) => section.id === "queue-health")?.status).toBe("blocked"); + + const edgeCases = buildRegistrationWorkspaceView( + readyFixture({ + queueHealth: { level: "unknown", burdenScore: 0, reviewablePullRequests: 0, summary: "Unknown queue signal." }, + policyReadiness: { + summary: "ok", + publicWarnings: [ + { title: "Safe title", detail: "", action: "Fix manifest.", severity: "warn" }, + { title: "Actionable", detail: "Detail text.", action: "Do the thing.", severity: "warn" }, + ], + }, + blockers: ["Burden forecast drift requires refresh.", "coverage gate below threshold."], + }), + { + ...configFixture(), + recommended: { participationMode: "direct_pr", maintainerCut: 0.2, nested: { flag: true } }, + }, + ); + expect(edgeCases.operations.find((section) => section.id === "queue-health")?.status).toBe("info"); + expect(edgeCases.policyWarnings).toHaveLength(1); + expect(collectRegistrationWorkspacePublicText(edgeCases).join(" ")).toMatch(/Do the thing/i); + expect(edgeCases.config?.recommendedLines.some((line) => line.includes("nested"))).toBe(true); + + const onlyAccepted = buildRegistrationOwnerWorkflow( + readyFixture({ + docsCompleteness: { status: "verified", requiredDocs: [], note: "ok" }, + queueHealth: { level: "low", burdenScore: 0.1, reviewablePullRequests: 1, summary: "low" }, + contributorIntakeHealth: { level: "healthy", summary: "ok" }, + maintainerCutReadiness: { ready: true, reasons: [], warnings: [] }, + labelPolicy: { autoLabelEnabled: true, label: "gittensor", trustedPipelineReady: true, missingOrUnusedRegistryLabels: [] }, + }), + configFixture(), + ); + expect(onlyAccepted.overallState).toBe("accepted"); + expect(onlyAccepted.nextSteps).toEqual([]); + + const maintainerWarn = buildRegistrationWorkspaceView( + readyFixture({ + ready: true, + maintainerCutReadiness: { ready: false, reasons: ["Review cut."], warnings: [], summary: "Cut pending." }, + }), + configFixture(), + ); + expect(maintainerWarn.lanes.maintainerEconomics.status).toBe("warn"); + + const installBlocker = buildRegistrationOwnerWorkflow( + readyFixture({ blockers: ["GitHub App installation is incomplete for this repository."] }), + null, + ); + expect( + installBlocker.buckets + .find((bucket) => bucket.id === "maintainer_capacity") + ?.items.some((item) => item.remediationKind === "manual"), + ).toBe(true); + }); + + it("splitRepoFullName validates owner/repo slugs for the owner panel", () => { + expect(splitRepoFullName("JSONbored/gittensory")).toEqual({ owner: "JSONbored", repo: "gittensory" }); + expect(splitRepoFullName("bad")).toBeNull(); + }); + + it("never emits forbidden language across randomized warning injections", () => { + const injections = [ + "wallet", + "hotkey", + "raw trust score", + "payout", + "reward estimate", + "farming", + "private reviewability", + "public score estimate", + ]; + for (const injection of injections) { + const view = buildRegistrationWorkspaceView( + readyFixture({ warnings: [`Blocked phrase ${injection} must not render`] }), + configFixture(), + ); + expect(collectRegistrationWorkspacePublicText(view).join(" ")).not.toMatch(FORBIDDEN); + expect(collectRegistrationOwnerWorkflowPublicText(view.workflow).join(" ")).not.toMatch(FORBIDDEN); + } + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 6cdbdf39..54c08855 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -19,7 +19,7 @@ export default defineConfig({ coverage: { provider: "v8", include: ["src/**/*.ts"], - exclude: ["src/env.d.ts"], + exclude: ["src/env.d.ts", "apps/**"], thresholds: { lines: 97, functions: 97,