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,