Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .agents/skills/update-lifecycle-docs/references/screenshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,13 @@ or found to be stale. `keep` means the final pixels and live state were manually
reviewed. `replace` and `remove` deliberately fail `bun run check:screenshots`
until the referenced debt is resolved.

| Asset | Docs page | User point | UI route/state | Fixture | Viewport/theme | UI revision | Last verified | Review |
| ----------------------------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------- | ------------------------------------------ | ------------- | ------ |
| `/docs/getting-started/explore-environment/environment-ready.png` | `/docs/getting-started/explore-environment` | Recognize a ready Environment and Service | Environment details, selected Service Summary, `Deployed` / `Ready` | Authorized disposable fixture; incidental identity, hostname, UUID, and timestamp values generalized in DOM; cleanup confirmed | 1440×900 / light | `b1df0cd64b4680fb241364be6cab985a2dce1f4f` | 2026-07-24 | `keep` |
| `/docs/getting-started/onboard-repository/repository-list.png` | `/docs/getting-started/onboard-repository` | Find and select a repository for onboarding | `/onboard`, installed but not-onboarded repository filtered | Authorized disposable fixture; incidental identity, hostname, UUID, and timestamp values generalized in DOM; cleanup confirmed | 1440×900 / light | `b1df0cd64b4680fb241364be6cab985a2dce1f4f` | 2026-07-24 | `keep` |
| `/docs/features/lifecycle-ui/environment-list.png` | `/docs/features/lifecycle-ui` | Find, filter, and select an Environment | `/environments`, one `Torn down` Environment filtered | Authorized disposable fixture; incidental identity, hostname, UUID, and timestamp values generalized in DOM; cleanup confirmed | 1440×900 / light | `b1df0cd64b4680fb241364be6cab985a2dce1f4f` | 2026-07-24 | `keep` |
| Asset | Docs page | User point | UI route/state | Fixture | Viewport/theme | UI revision | Last verified | Review |
| ----------------------------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------ | ------------- | ------ |
| `/docs/getting-started/explore-environment/environment-ready.png` | `/docs/getting-started/explore-environment` | Recognize a ready Environment and Service | Environment details, selected Service Summary, `Deployed` / `Ready` | Authorized disposable fixture; incidental identity, hostname, UUID, and timestamp values generalized in DOM; cleanup confirmed | 1440×900 / light | `b1df0cd64b4680fb241364be6cab985a2dce1f4f` | 2026-07-24 | `keep` |
| `/docs/getting-started/onboard-repository/repository-list.png` | `/docs/getting-started/onboard-repository` | Find and select a repository for onboarding | `/onboard`, installed but not-onboarded repository filtered | Authorized disposable fixture; incidental identity, hostname, UUID, and timestamp values generalized in DOM; cleanup confirmed | 1440×900 / light | `b1df0cd64b4680fb241364be6cab985a2dce1f4f` | 2026-07-24 | `keep` |
| `/docs/features/lifecycle-ui/environment-list.png` | `/docs/features/lifecycle-ui` | Find, filter, and select an Environment | `/environments`, one `Torn down` Environment filtered | Authorized disposable fixture; incidental identity, hostname, UUID, and timestamp values generalized in DOM; cleanup confirmed | 1440×900 / light | `b1df0cd64b4680fb241364be6cab985a2dce1f4f` | 2026-07-24 | `keep` |
| `/docs/releases/keycloak-management-client-capabilities.png` | `/docs/releases` | Configure the management client flows | Keycloak management client, capability configuration | Authorized local non-production realm; component crop contains no hostname, identity, or secret | 1440×900 / dark | Keycloak `26.4.7`; Helm `78b680595d4beda3184124312d61d86ce0cc51e7` | 2026-08-01 | `keep` |
| `/docs/releases/keycloak-management-client-scope.png` | `/docs/releases` | Verify least-privilege scope mappings | Dedicated management client scope with full scope off | Authorized local non-production realm; component crop contains no hostname, identity, or secret | 1440×900 / dark | Keycloak `26.4.7`; Helm `78b680595d4beda3184124312d61d86ce0cc51e7` | 2026-08-01 | `keep` |

The structural check cannot read meaning from rendered pixels. A `keep` status
is a human assertion that the final image contains no secret, personal,
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,4 @@ deployment-issues/

# Local Claude Code state (permission allowlist, session lockfiles)
.claude/
.planning/
10 changes: 10 additions & 0 deletions documentation-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@
"helm-charts": "c0257d1f88b5b30533f490d12f6293fa465b906c",
"lifecycle-opentofu": "b14865912608096379c28ef648f7ed99538b5600"
}
},
"2026-08-01-adopt-lifecycle-mcp-browser-validation": {
"verifiedOn": "2026-08-01",
"sources": {
"lifecycle": "46af94b799ef43c9e88d48212ec6333a52d56e9f",
"lifecycle-ui": "69331dc3876e4aec646a893114739a14755eebd7",
"lifecycle-cli": "3f8600dca8c97a715c7ae01c3a3087ba934fa4e1",
"helm-charts": "78b680595d4beda3184124312d61d86ce0cc51e7",
"lifecycle-opentofu": "b14865912608096379c28ef648f7ed99538b5600"
}
}
}
}
4 changes: 2 additions & 2 deletions documentation-style-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@
"file": "src/pages/docs/features/mcp-server.mdx",
"profile": "asd-ste100",
"reviewedOn": "2026-08-01",
"sha256": "49addadbc408af37d03f1f2e4f8b12e4ca0dcf03c6e4b7b48bb6a3fee963f582"
"sha256": "69ab72bfb6486d0189fc903d4a3d8e170c578be252d39335b7bea8e7c8b0da36"
},
"/docs/features/native-helm-deployment": {
"file": "src/pages/docs/features/native-helm-deployment.mdx",
Expand Down Expand Up @@ -304,7 +304,7 @@
"file": "src/pages/docs/releases/index.mdx",
"profile": "asd-ste100",
"reviewedOn": "2026-08-01",
"sha256": "db4c73e5c8a52b1785a19cb695fcacae932cdbe68a66814750b29af5b0368261"
"sha256": "b8ee17e3117ba7fc2d5badf0b12ecbd2cd8248750ca9e9e9c6f8d36224808722"
},
"/docs/releases/compatibility": {
"file": "src/pages/docs/releases/compatibility.mdx",
Expand Down
9 changes: 8 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import nextra from "nextra";
import { remarkCodeHike, recmaCodeHike } from "codehike/mdx";
import { remarkCodeHikeSearch } from "./src/lib/remark-codehike-search.mjs";

/** @type {import('codehike/mdx').CodeHikeConfig} */
export const chConfig = {
Expand All @@ -26,7 +27,13 @@ export const chConfig = {
}

export const mdxOptions = {
remarkPlugins: [[remarkCodeHike, chConfig]],
remarkPlugins: [
[remarkCodeHike, chConfig],
[
remarkCodeHikeSearch,
{ componentName: chConfig.components.code },
],
],
recmaPlugins: [[recmaCodeHike, chConfig]],
// jsx: true,
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Lifecycle docs",
"main": "index.js",
"scripts": {
"build": "bun run build:prep && bun run build:styles && next build",
"build": "bun run build:prep && bun run build:styles && next build && bun run check:search",
"build:styles": "tailwindcss -i ./src/styles/globals.css -o public/styles.css",
"build:meta": "bun run ./scripts/generateMeta.ts",
"build:llms": "bun run ./scripts/generateLlms.ts",
Expand All @@ -14,6 +14,7 @@
"check:docs": "bun run ./scripts/validateDocs.ts",
"check:llms": "bun run ./scripts/generateLlms.ts --check",
"check:raw": "bun run ./scripts/generateRawMarkdown.ts --check",
"check:search": "bun run ./scripts/validateSearchIndex.ts",
"check:screenshots": "bun run ./scripts/validateDocs.ts --screenshots-only",
"check:styles": "bun run ./scripts/validateSte100.ts",
"clean": "rimraf src/pages/tags src/lib/data",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ This index is generated from the human documentation. Follow the linked page for

## Releases and compatibility

- [Releases](https://uselifecycle.com/docs/releases.md): Find your installed Lifecycle versions and the release information for an upgrade. _(audience: platform-operator, application-developer; last verified: 2026-08-01; baseline: 2026-08-01-lifecycle-mcp-preparation)_
- [Releases](https://uselifecycle.com/docs/releases.md): Find your installed Lifecycle versions and the release information for an upgrade. _(audience: platform-operator, application-developer; last verified: 2026-08-01; baseline: 2026-08-01-adopt-lifecycle-mcp-browser-validation)_
- [Compatibility and deprecation policy](https://uselifecycle.com/docs/releases/compatibility.md): Select compatible Lifecycle components and prepare a safe upgrade or rollback. _(audience: platform-operator, application-developer; last verified: 2026-07-24; baseline: 2026-07-24-comprehensive-audit)_

## Troubleshooting
Expand Down
91 changes: 91 additions & 0 deletions scripts/validateSearchIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Copyright 2026 GoodRx, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import fs from "node:fs";
import path from "node:path";

type SearchPage = {
title: string;
data: Record<string, string>;
};

type SearchIndex = Record<string, SearchPage>;

export type SearchIndexContract = {
route: string;
text: string;
source: string;
};

export const SEARCH_INDEX_CONTRACTS: SearchIndexContract[] = [
{
route: "/docs/features/sites",
text: "a1b2c3d4e5",
source: "src/pages/docs/features/sites.mdx",
},
];

export function validateSearchIndexData(
index: SearchIndex,
contracts: SearchIndexContract[] = SEARCH_INDEX_CONTRACTS,
): string[] {
const issues: string[] = [];

for (const contract of contracts) {
const page = index[contract.route];
if (!page) {
issues.push(
`${contract.route} is missing from the search index (${contract.source})`,
);
continue;
}

const searchableText = [page.title, ...Object.values(page.data)].join("\n");
if (!searchableText.includes(contract.text)) {
issues.push(
`${contract.route} does not index fenced-code text ${JSON.stringify(contract.text)} (${contract.source})`,
);
}
}

return issues;
}

export async function validateSearchIndex(
rootDir = process.cwd(),
): Promise<string[]> {
const indexPath = path.join(
rootDir,
"out/_next/static/chunks/nextra-data-en-US.json",
);
const contents = await fs.promises.readFile(indexPath, "utf8");
const index = JSON.parse(contents) as SearchIndex;
return validateSearchIndexData(index);
}

async function runCli() {
const issues = await validateSearchIndex();
if (issues.length > 0) {
for (const issue of issues) console.error(issue);
process.exit(1);
}

console.log("Search index validation passed.");
}

if (import.meta.main) {
await runCli();
}
3 changes: 3 additions & 0 deletions src/components/codehike/code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import Loader from "@/components/loader";

export const CodeHikeSSR = ({
codeblock,
children: searchIndexText,
handlers = [
callout,
className,
Expand All @@ -58,6 +59,8 @@ export const CodeHikeSSR = ({
classes = "",
...props
}: CodeHikeProps & Omit<CustomPreProps, "code" | "handlers">) => {
// Nextra indexes this MDX child. The rendered code comes from `codeblock`.
void searchIndexText;
const [isClient, setIsClient] = useState(false);
const [theme, setTheme] = useState(initialTheme);

Expand Down
1 change: 1 addition & 0 deletions src/components/codehike/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { HighlightedCode, AnnotationHandler } from "codehike/code";

export type CodeHikeProps = {
codeblock: HighlightedCode;
children?: React.ReactNode;
handlers?: AnnotationHandler[];
theme?: string;
classes?: string;
Expand Down
72 changes: 72 additions & 0 deletions src/lib/remark-codehike-search.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright 2026 GoodRx, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

function propertyName(property) {
if (property?.type !== "Property" || property.computed) return null;
if (property.key?.type === "Identifier") return property.key.name;
if (property.key?.type === "Literal") return property.key.value;
return null;
}

function stringProperty(objectExpression, name) {
if (objectExpression?.type !== "ObjectExpression") return null;

for (const property of objectExpression.properties || []) {
if (propertyName(property) !== name) continue;
if (property.value?.type !== "Literal") return null;
return typeof property.value.value === "string"
? property.value.value
: null;
}

return null;
}

export function codeHikeSource(node, componentName = "Code") {
if (node?.type !== "mdxJsxFlowElement" || node.name !== componentName) {
return null;
}

const attribute = (node.attributes || []).find(
(item) => item?.type === "mdxJsxAttribute" && item.name === "codeblock",
);
const program = attribute?.value?.data?.estree;
const expression = program?.body?.[0]?.expression;

return (
stringProperty(expression, "value") ?? stringProperty(expression, "code")
);
}

function addSearchText(node, componentName) {
const source = codeHikeSource(node, componentName);
if (source !== null) {
node.children = [{ type: "text", value: source }];
}

for (const child of node?.children || []) {
addSearchText(child, componentName);
}
}

/**
* Restores fenced-code text after CodeHike converts Markdown code nodes into
* MDX components. Nextra's later structurizer can then include the text in its
* FlexSearch data.
*/
export function remarkCodeHikeSearch({ componentName = "Code" } = {}) {
return (tree) => addSearchText(tree, componentName);
}
2 changes: 1 addition & 1 deletion src/pages/docs/features/mcp-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ The page shows:
- an enable or disable control
- the **Allow changes** control
- capability and tool information
- one actionable issue when enablement fails
- one issue when enablement fails

Enablement is one bounded request. Lifecycle configures the required Keycloak
sign-in settings and verifies them against the MCP URL.
Expand Down
Loading
Loading