Skip to content

Commit dbcb7a3

Browse files
authored
docs(miner-ui): cross-reference AMS observability from operations/quickstart/workflow docs (#5191) (#5629)
* feat(ui): add AMS observability callout and tests Introduces the AmsObservabilityCallout component to provide a link to the "Observing your miner" guide, enhancing the user experience for dual-role ORB+AMS operators. The callout is integrated into the miner quickstart, workflow, and self-hosting operations documentation pages. Additionally, comprehensive tests are added to ensure the callout renders correctly and links to the appropriate documentation. This change addresses the need for a unified reference point for AMS observability setup across multiple routes. * fix(routes): restore DocsMinerCodingAgentRoute in routeTree.gen.ts Reintroduces the DocsMinerCodingAgentRoute to the route tree, ensuring it is properly imported and defined. This change updates the route interface and type declarations to include the miner coding agent documentation path, maintaining consistency across the routing structure. * fix(docs): correct wording in miner workflow documentation Updates the wording in the miner workflow documentation to improve clarity and readability. This change ensures that the guidance on handling preflight reports is more precise, enhancing the overall user experience.
1 parent 9bb8ed0 commit dbcb7a3

6 files changed

Lines changed: 132 additions & 16 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Callout } from "@/components/site/primitives";
2+
3+
// AMS (gittensory-miner) observability cross-reference (#5191). A dual-role self-hoster running both ORB (the
4+
// review service) and AMS (the miner) on one box otherwise has no in-app pointer from the operations / quickstart /
5+
// workflow docs to the miner's observability setup. Keeping the callout — and its link target — in one place keeps
6+
// the wording byte-identical across all three routes instead of relying on three hand-copied copies staying in sync.
7+
//
8+
// The target is the in-repo "Observing your miner" guide (landed in #5190): the single AMS observability entry point,
9+
// which itself covers pointing Grafana at the redacted AMS ledger datasources AND loading an AMS dashboard from
10+
// grafana/dashboards/. It is a markdown guide, not an in-app /docs/* route, so a GitHub-blob link is the correct
11+
// target here — the same convention the docs already use for in-repo file references (see docs.self-hosting-configuration.tsx).
12+
export const AMS_OBSERVABILITY_DOC_URL =
13+
"https://github.com/JSONbored/gittensory/blob/main/packages/gittensory-miner/docs/observability.md";
14+
15+
/** A `note` callout pointing a dual-role ORB+AMS operator at the "Observing your miner" observability guide. */
16+
export function AmsObservabilityCallout() {
17+
return (
18+
<Callout variant="note" title="Running the miner on this box too?">
19+
If you also run <strong>AMS</strong> (the <code>gittensory-miner</code>) on this host, see{" "}
20+
<a href={AMS_OBSERVABILITY_DOC_URL} target="_blank" rel="noopener noreferrer">
21+
Observing your miner
22+
</a>{" "}
23+
to point Grafana at the redacted AMS ledger datasources and load its Grafana dashboard —
24+
separate from the ORB review-service observability above.
25+
</Callout>
26+
);
27+
}

apps/gittensory-ui/src/routeTree.gen.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ import { Route as DocsScoreabilityRouteImport } from './routes/docs.scoreability
4343
import { Route as DocsQuickstartRouteImport } from './routes/docs.quickstart'
4444
import { Route as DocsPrivacySecurityRouteImport } from './routes/docs.privacy-security'
4545
import { Route as DocsOwnerChecklistRouteImport } from './routes/docs.owner-checklist'
46-
import { Route as DocsMinerCodingAgentRouteImport } from './routes/docs.miner-coding-agent'
4746
import { Route as DocsMinerWorkflowRouteImport } from './routes/docs.miner-workflow'
4847
import { Route as DocsMinerQuickstartRouteImport } from './routes/docs.miner-quickstart'
48+
import { Route as DocsMinerCodingAgentRouteImport } from './routes/docs.miner-coding-agent'
4949
import { Route as DocsMcpClientsRouteImport } from './routes/docs.mcp-clients'
5050
import { Route as DocsMaintainerWorkflowRouteImport } from './routes/docs.maintainer-workflow'
5151
import { Route as DocsMaintainerSelfHostingRouteImport } from './routes/docs.maintainer-self-hosting'
@@ -252,11 +252,6 @@ const DocsOwnerChecklistRoute = DocsOwnerChecklistRouteImport.update({
252252
path: '/owner-checklist',
253253
getParentRoute: () => DocsRoute,
254254
} as any)
255-
const DocsMinerCodingAgentRoute = DocsMinerCodingAgentRouteImport.update({
256-
id: '/miner-coding-agent',
257-
path: '/miner-coding-agent',
258-
getParentRoute: () => DocsRoute,
259-
} as any)
260255
const DocsMinerWorkflowRoute = DocsMinerWorkflowRouteImport.update({
261256
id: '/miner-workflow',
262257
path: '/miner-workflow',
@@ -267,6 +262,11 @@ const DocsMinerQuickstartRoute = DocsMinerQuickstartRouteImport.update({
267262
path: '/miner-quickstart',
268263
getParentRoute: () => DocsRoute,
269264
} as any)
265+
const DocsMinerCodingAgentRoute = DocsMinerCodingAgentRouteImport.update({
266+
id: '/miner-coding-agent',
267+
path: '/miner-coding-agent',
268+
getParentRoute: () => DocsRoute,
269+
} as any)
270270
const DocsMcpClientsRoute = DocsMcpClientsRouteImport.update({
271271
id: '/mcp-clients',
272272
path: '/mcp-clients',
@@ -556,6 +556,7 @@ export interface FileRoutesById {
556556
'/docs/maintainer-self-hosting': typeof DocsMaintainerSelfHostingRoute
557557
'/docs/maintainer-workflow': typeof DocsMaintainerWorkflowRoute
558558
'/docs/mcp-clients': typeof DocsMcpClientsRoute
559+
'/docs/miner-coding-agent': typeof DocsMinerCodingAgentRoute
559560
'/docs/miner-quickstart': typeof DocsMinerQuickstartRoute
560561
'/docs/miner-workflow': typeof DocsMinerWorkflowRoute
561562
'/docs/owner-checklist': typeof DocsOwnerChecklistRoute
@@ -746,6 +747,7 @@ export interface FileRouteTypes {
746747
| '/docs/maintainer-self-hosting'
747748
| '/docs/maintainer-workflow'
748749
| '/docs/mcp-clients'
750+
| '/docs/miner-coding-agent'
749751
| '/docs/miner-quickstart'
750752
| '/docs/miner-workflow'
751753
| '/docs/owner-checklist'
@@ -1036,20 +1038,20 @@ declare module '@tanstack/react-router' {
10361038
preLoaderRoute: typeof DocsMinerWorkflowRouteImport
10371039
parentRoute: typeof DocsRoute
10381040
}
1039-
'/docs/miner-coding-agent': {
1040-
id: '/docs/miner-coding-agent'
1041-
path: '/miner-coding-agent'
1042-
fullPath: '/docs/miner-coding-agent'
1043-
preLoaderRoute: typeof DocsMinerCodingAgentRouteImport
1044-
parentRoute: typeof DocsRoute
1045-
}
10461041
'/docs/miner-quickstart': {
10471042
id: '/docs/miner-quickstart'
10481043
path: '/miner-quickstart'
10491044
fullPath: '/docs/miner-quickstart'
10501045
preLoaderRoute: typeof DocsMinerQuickstartRouteImport
10511046
parentRoute: typeof DocsRoute
10521047
}
1048+
'/docs/miner-coding-agent': {
1049+
id: '/docs/miner-coding-agent'
1050+
path: '/miner-coding-agent'
1051+
fullPath: '/docs/miner-coding-agent'
1052+
preLoaderRoute: typeof DocsMinerCodingAgentRouteImport
1053+
parentRoute: typeof DocsRoute
1054+
}
10531055
'/docs/mcp-clients': {
10541056
id: '/docs/mcp-clients'
10551057
path: '/mcp-clients'
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { render, screen } from "@testing-library/react";
2+
import { type ReactNode } from "react";
3+
import { describe, expect, it, vi } from "vitest";
4+
5+
import {
6+
AMS_OBSERVABILITY_DOC_URL,
7+
AmsObservabilityCallout,
8+
} from "../components/site/ams-observability-callout";
9+
import { MinerQuickstart } from "./docs.miner-quickstart";
10+
import { MinerWorkflow } from "./docs.miner-workflow";
11+
import { SelfHostingOperations } from "./docs.self-hosting-operations";
12+
13+
vi.mock("@tanstack/react-router", async (importOriginal) => {
14+
const actual = await importOriginal<typeof import("@tanstack/react-router")>();
15+
return {
16+
...actual,
17+
Link: ({ to, children }: { to: string; children: ReactNode }) => <a href={to}>{children}</a>,
18+
};
19+
});
20+
21+
vi.mock("@/components/site/docs-page", () => ({
22+
DocsPage: ({ children, title }: { children: ReactNode; title: string }) => (
23+
<div data-testid="docs-page">
24+
<h1>{title}</h1>
25+
{children}
26+
</div>
27+
),
28+
}));
29+
30+
vi.mock("@/components/site/primitives", () => ({
31+
Callout: ({ children, title }: { children: ReactNode; title?: string }) => (
32+
<section data-testid="callout">
33+
{title ? <strong>{title}</strong> : null}
34+
<div>{children}</div>
35+
</section>
36+
),
37+
CodeBlock: ({ code }: { code: string }) => <pre>{code}</pre>,
38+
FeatureRow: ({ items }: { items: Array<{ title: string; description: string }> }) => (
39+
<dl>
40+
{items.map((item) => (
41+
<div key={item.title}>
42+
<dt>{item.title}</dt>
43+
<dd>{item.description}</dd>
44+
</div>
45+
))}
46+
</dl>
47+
),
48+
}));
49+
50+
vi.mock("@/components/site/workflow-mirror", () => ({
51+
WorkflowMirror: () => <div data-testid="workflow-mirror" />,
52+
}));
53+
54+
// Every route that embeds the shared callout, so a new route add/remove can't silently skip one (#5191).
55+
const ROUTES_WITH_CALLOUT: ReadonlyArray<[string, () => ReactNode]> = [
56+
["/docs/self-hosting-operations", SelfHostingOperations],
57+
["/docs/miner-quickstart", MinerQuickstart],
58+
["/docs/miner-workflow", MinerWorkflow],
59+
];
60+
61+
describe("AMS observability cross-reference callout", () => {
62+
it("renders a link to the Observing your miner guide", () => {
63+
render(<AmsObservabilityCallout />);
64+
const link = screen.getByRole("link", { name: "Observing your miner" });
65+
expect(link.getAttribute("href")).toBe(AMS_OBSERVABILITY_DOC_URL);
66+
});
67+
68+
it("targets a well-formed, non-empty absolute https URL (guards against a blank/copy-paste link)", () => {
69+
expect(AMS_OBSERVABILITY_DOC_URL).toBeTruthy();
70+
const url = new URL(AMS_OBSERVABILITY_DOC_URL);
71+
expect(url.protocol).toBe("https:");
72+
expect(url.hostname).toBe("github.com");
73+
});
74+
75+
it.each(ROUTES_WITH_CALLOUT)("wires the callout into %s", (_path, RouteComponent) => {
76+
const { container } = render(<RouteComponent />);
77+
const link = container.querySelector(`a[href="${AMS_OBSERVABILITY_DOC_URL}"]`);
78+
expect(link).not.toBeNull();
79+
expect(link?.textContent).toBe("Observing your miner");
80+
});
81+
});

apps/gittensory-ui/src/routes/docs.miner-quickstart.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createFileRoute } from "@tanstack/react-router";
22

3+
import { AmsObservabilityCallout } from "@/components/site/ams-observability-callout";
34
import { DocsPage } from "@/components/site/docs-page";
45
import { CodeBlock, Callout } from "@/components/site/primitives";
56
import { Link } from "@tanstack/react-router";
@@ -26,7 +27,7 @@ export const Route = createFileRoute("/docs/miner-quickstart")({
2627
component: MinerQuickstart,
2728
});
2829

29-
function MinerQuickstart() {
30+
export function MinerQuickstart() {
3031
return (
3132
<DocsPage
3233
eyebrow="Get started"
@@ -192,6 +193,7 @@ gittensory-mcp preflight --login your-login --repo owner/repo --base origin/main
192193
page with the <a href="/docs/miner-workflow">miner workflow</a> for the full loop and{" "}
193194
<a href="/docs/privacy-security">privacy &amp; security</a> for the boundary details.
194195
</Callout>
196+
<AmsObservabilityCallout />
195197
</DocsPage>
196198
);
197199
}

apps/gittensory-ui/src/routes/docs.miner-workflow.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createFileRoute } from "@tanstack/react-router";
22
import { Link } from "@tanstack/react-router";
33

4+
import { AmsObservabilityCallout } from "@/components/site/ams-observability-callout";
45
import { DocsPage } from "@/components/site/docs-page";
56
import { CodeBlock, Callout } from "@/components/site/primitives";
67
import { WorkflowMirror, type MirroredStep } from "@/components/site/workflow-mirror";
@@ -25,7 +26,7 @@ export const Route = createFileRoute("/docs/miner-workflow")({
2526
component: MinerWorkflow,
2627
});
2728

28-
function MinerWorkflow() {
29+
export function MinerWorkflow() {
2930
const steps: MirroredStep[] = [
3031
{
3132
title: "Plan",
@@ -136,6 +137,7 @@ function MinerWorkflow() {
136137
commits, prefer cleaning open work over opening more — risk-adjusted priority is part of the
137138
score model.
138139
</Callout>
140+
<AmsObservabilityCallout />
139141
</DocsPage>
140142
);
141143
}

apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createFileRoute, Link } from "@tanstack/react-router";
22

3+
import { AmsObservabilityCallout } from "@/components/site/ams-observability-callout";
34
import { DocsPage } from "@/components/site/docs-page";
45
import { Callout, CodeBlock, FeatureRow } from "@/components/site/primitives";
56

@@ -25,7 +26,7 @@ export const Route = createFileRoute("/docs/self-hosting-operations")({
2526
component: SelfHostingOperations,
2627
});
2728

28-
function SelfHostingOperations() {
29+
export function SelfHostingOperations() {
2930
return (
3031
<DocsPage
3132
eyebrow="Self-hosting"
@@ -96,6 +97,7 @@ selfhost_webhook_enqueue_binding_missing`}
9697
code={`docker compose --profile postgres --profile observability up -d
9798
docker compose --profile postgres --profile observability --profile backup up -d`}
9899
/>
100+
<AmsObservabilityCallout />
99101

100102
<h2>Host clock sync (NTP)</h2>
101103
<Callout variant="warn" title="A single NTP source is a silent single point of failure">

0 commit comments

Comments
 (0)