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
76 changes: 63 additions & 13 deletions docs/RELEASE_0.0.18_EVIDENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Last updated: 2026-04-13

## Scope

Tracks the `0.0.18` release-prep gates for the repo state after the GitHub-published `v0.0.17` release, including version alignment, changelog/docs refresh, packaging outputs, website-sync readiness, and release/distribution handoff evidence for GitHub release, npm, and Chrome extension distribution.
Tracks the `0.0.18` release-prep and post-merge CI-repair gates for the repo state after the GitHub-published `v0.0.17` release, including version alignment, changelog/docs refresh, packaging outputs, website-sync readiness, and release/distribution handoff evidence for GitHub release, npm, and Chrome extension distribution.

## Baseline comparison

Expand All @@ -29,11 +29,11 @@ Tracks the `0.0.18` release-prep gates for the repo state after the GitHub-publi

## Current repo note

- Active prep branch: `codex/release-0-0-18`
- Source branch for eventual tag/publish: merged `main`
- Prep branch base: `origin/main`
- `origin/main` at start of prep: `4a80e25269dfe92ccaf300aa8026a3234189aac5`
- Local version authority is now `package.json` at `0.0.18`; extension version owners must stay synced via `npm run extension:sync`.
- Active CI-repair branch: `codex/release-0-0-18-fix`
- Source branch for release rerun: merged `main`
- Fix branch base: `origin/main` at `f001dac9cd7fdcc60ce3a6384f76985d3e7c039e`
- Release tag already pushed: `v0.0.18`
- Local version authority is `package.json` at `0.0.18`; extension version owners stay synced via `npm run extension:sync`.
- Release-version sweep confirmed `0.0.18` across active version owners and current-cycle docs. `tsconfig.json` and `eslint.config.js` contain no release-version strings, so they were reviewed and left unchanged.

## Mandatory release gates
Expand All @@ -45,15 +45,15 @@ Tracks the `0.0.18` release-prep gates for the repo state after the GitHub-publi
- [x] `node scripts/audit-zombie-files.mjs`
- Result: `{"ok":true,"scanned":915,"flagged":[]}`
- [x] `node scripts/docs-drift-check.mjs`
- Result: passed; current generated source counts confirmed as `64` CLI commands, `57` tools, `59` `/ops`, and `35` `/canvas`
- Result: passed; current generated source counts confirmed as `72` CLI commands, `65` tools, `59` `/ops`, and `35` `/canvas`
- [x] `node scripts/chrome-store-compliance-check.mjs`
- Result: passed
- [x] `./skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh`
- Result: passed
- [x] `npx opendevbrowser --help`
- Result: generated successfully; output length `528` lines
- Result: generated successfully; output length `590` lines
- [x] `npx opendevbrowser help`
- Result: generated successfully; output length `528` lines and matched `--help` via `cmp`
- Result: generated successfully; output length `590` lines and matched `--help` via `cmp`
- [x] `npm run lint`
- Result: passed
- [x] `npm run typecheck`
Expand All @@ -65,7 +65,25 @@ Tracks the `0.0.18` release-prep gates for the repo state after the GitHub-publi
- [x] `npm run test:release-gate`
- Result: all `5` release-gate groups passed
- [x] `npm run test`
- Result: passed on the rebased `origin/main` + `0.0.18` candidate; coverage remained above the required `97%` branch threshold
- Result: passed on the post-merge CI-repair branch from `origin/main`; coverage remained above the required `97%` branch threshold

## Post-merge CI repair summary

- `scripts/cli-onboarding-smoke.mjs`
- `loadQuickStartGuide` now accepts an injected `SkillLoaderCtor`, so tests no longer require a prebuilt `dist/skills/skill-loader.js`.
- `tests/daemon-autostart.test.ts`
- Linux temp-dir fixture now lives under `tmpdir()` instead of `resolve(tmpdir(), "..")`.
- `tests/desktop-runtime-audit.test.ts`, `tests/desktop-runtime-permission.test.ts`
- macOS-only `/usr/sbin/screencapture` assumptions are now injected through `statImpl`.
- `tests/system-chrome-cookies.test.ts`
- direct SQLite assertion now uses the explicit Darwin path in the test helper, and the Linux-fragile wrapper warning expectation was removed.
- `scripts/login-fixture-live-probe.mjs`
- direct-execution guard prevents `main()` from running on import during tests.
- `src/providers/workflows.ts`
- refreshed generic retailer titles such as `Amazon.com` are now treated as marketplace chrome instead of overwriting a valid product title.
- Supporting regression coverage was added in:
- `tests/cli-onboarding-smoke-script.test.ts`
- `tests/providers-product-video-workflow.test.ts`

## Packaging gates

Expand Down Expand Up @@ -113,20 +131,52 @@ Tracks the `0.0.18` release-prep gates for the repo state after the GitHub-publi

## External release workflow evidence

- [ ] Release workflow run URL
- [x] Release workflow failure evidence
- Run: `https://github.com/freshtechbro/opendevbrowser/actions/runs/24322225760`
- Workflow: `Public Release`
- Head: `v0.0.18` -> `f001dac9cd7fdcc60ce3a6384f76985d3e7c039e`
- Result: failed during the pre-publish `npm run test` gate; the CI-only owner failures patched on `codex/release-0-0-18-fix` came from this run
- [ ] GitHub release URL
- Current state: `gh release view v0.0.18` returned `release not found`
- [ ] npm publish verification
- [ ] Chrome Web Store publish status
- [ ] Private website sync dispatch evidence
- [x] Chrome Web Store publish blocker evidence
- Run: `https://github.com/freshtechbro/opendevbrowser/actions/runs/24322237855`
- Workflow: `Chrome Store Publish`
- Result: failed before upload because repository secrets `CWS_CLIENT_ID`, `CWS_CLIENT_SECRET`, `CWS_REFRESH_TOKEN`, and `CWS_EXTENSION_ID` are absent
- Secret inventory check: `gh secret list` currently returns only `PRIVATE_REPO_DISPATCH_TOKEN`
- [x] Private website sync dispatch evidence
- Run: `https://github.com/freshtechbro/opendevbrowser/actions/runs/24322215822`
- Workflow: `Dispatch Private Website Sync`
- Head: `main` -> `f001dac9cd7fdcc60ce3a6384f76985d3e7c039e`
- Result: completed successfully

## Notes

- This ledger is the active `0.0.18` proof record and should be updated as gates complete.
- `docs/RELEASE_0.0.17_EVIDENCE.md` remains historical and should not be rewritten during `0.0.18` prep.
- The current local release-gate proof set on `codex/release-0-0-18-fix` is green:
- `npm run version:check`
- `node scripts/docs-drift-check.mjs`
- `node scripts/chrome-store-compliance-check.mjs`
- `./skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh`
- `npm run lint`
- `npm run typecheck`
- `npm run build`
- `npm run extension:build`
- `npx opendevbrowser --help`
- `npx opendevbrowser help`
- `npm run test`
- Post-fix blocker closure for this release:
- `feature.canvas.managed_headless` cleared after widening preview navigation fallback from `data:text/html` timeout into `setContent`/document-write recovery.
- `feature.canvas.managed_headed` cleared after increasing managed-headed daemon launch budget to `60000ms`.
- `feature.cli.smoke` cleared after removing the harness-only `review` timeout override of `15000ms`.
- Post-merge CI-only blocker closure for this release:
- onboarding smoke no longer depends on built `dist`
- daemon autostart test uses a Linux-safe tmp fixture
- desktop runtime tests no longer assume a macOS-only binary exists in CI
- system Chrome cookie tests no longer assert Darwin-only behavior from Linux
- login-fixture probe no longer exits the process when imported in tests
- product-video title refresh now resists generic marketplace chrome
- Both strict live-gate scripts currently return non-zero when `env_limited` or `skipped` lanes remain, even with `0` true `fail` results. Release readiness should therefore be read from the recorded counts and scenario details, not the raw process exit code alone.
- Final rebased candidate status:
- provider direct proof still contains honest `env_limited` lanes but no true failures
Expand Down
10 changes: 6 additions & 4 deletions scripts/cli-onboarding-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,17 @@ function buildIsolatedSkillEnv(tempRoot, configDir) {
export async function loadQuickStartGuide(
rootDir = ROOT,
metadata = ONBOARDING_METADATA,
envOverrides = {}
envOverrides = {},
SkillLoaderCtor
) {
const moduleUrl = pathToFileURL(path.join(ROOT, "dist", "skills", "skill-loader.js")).href;
const { SkillLoader } = await import(moduleUrl);
const LoaderClass = SkillLoaderCtor ?? (
await import(pathToFileURL(path.join(rootDir, "dist", "skills", "skill-loader.js")).href)
).SkillLoader;
const overrides = Object.fromEntries(
ISOLATED_SKILL_ENV_KEYS.map((key) => [key, envOverrides[key]])
);
return withTemporaryEnv(overrides, async () => {
const loader = new SkillLoader(rootDir);
const loader = new LoaderClass(rootDir);
return loader.loadSkill(metadata.skillName, metadata.skillTopic);
});
}
Expand Down
17 changes: 12 additions & 5 deletions scripts/login-fixture-live-probe.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node
import http from "node:http";
import { URLSearchParams } from "node:url";
import { URLSearchParams, pathToFileURL } from "node:url";
import {
defaultArtifactPath,
finalizeReport,
Expand Down Expand Up @@ -670,7 +670,14 @@ async function main() {
}
}

main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
const isDirectExecution = () => {
const entrypoint = process.argv[1];
return typeof entrypoint === "string" && import.meta.url === pathToFileURL(entrypoint).href;
};

if (isDirectExecution()) {
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
}
11 changes: 11 additions & 0 deletions src/providers/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,17 @@ const isMarketplaceTitleChrome = (title: string, productUrl: string): boolean =>
const cleaned = normalizePlainText(title);
if (!cleaned) return true;
try {
const canonicalHostBrand = normalizePlainText(inferBrandFromUrl(productUrl))
.replace(/\.com\b/gi, "")
.trim()
.toLowerCase();
const canonicalTitle = cleaned
.replace(/\.com\b/gi, "")
.trim()
.toLowerCase();
if (canonicalHostBrand && canonicalTitle === canonicalHostBrand) {
return true;
}
const host = new URL(productUrl).hostname.toLowerCase();
if (host.includes("walmart.")) {
return WALMART_TITLE_CHROME_RE.test(cleaned);
Expand Down
3 changes: 2 additions & 1 deletion tests/cli-onboarding-smoke-script.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe("cli-onboarding-smoke script", () => {

it("loads the bundled quick-start guide inside isolated compatibility homes", async () => {
const isolatedRoot = await mkdtemp(join(os.tmpdir(), "odb-isolated-skill-home-"));
const { SkillLoader } = await import("../src/skills/skill-loader");
const guide = await loadQuickStartGuide(process.cwd(), onboardingMetadata, {
CODEX_HOME: isolatedRoot,
OPENCODE_CONFIG_DIR: join(isolatedRoot, "config"),
Expand All @@ -49,7 +50,7 @@ describe("cli-onboarding-smoke script", () => {
AMPCLI_HOME: join(isolatedRoot, ".amp"),
AMP_CLI_HOME: join(isolatedRoot, ".amp"),
AMP_HOME: join(isolatedRoot, ".amp")
});
}, SkillLoader);
expect(guide).toContain("## Quick Start");
expect(guide).not.toContain("## Fast Start");
});
Expand Down
2 changes: 1 addition & 1 deletion tests/daemon-autostart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const createCliFixture = (options: { transient?: boolean } = {}) => {
};

const createNpxCacheCliFixture = () => {
const root = mkdtempSync(join(resolve(tmpdir(), ".."), "odb-cli-stable-"));
const root = mkdtempSync(join(tmpdir(), "odb-cli-stable-"));
const cacheRoot = join(root, "_npx", "runner");
mkdirSync(cacheRoot, { recursive: true });
const cliPath = join(cacheRoot, "index.js");
Expand Down
9 changes: 7 additions & 2 deletions tests/desktop-runtime-audit.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
import type { Stats } from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
Expand Down Expand Up @@ -242,7 +242,12 @@ describe("desktop runtime audit and observation", () => {
cacheRoot,
platform: "darwin",
config: makeDesktopConfig(),
execFileImpl
execFileImpl,
statImpl: vi.fn(async (target: string) => (
target === "/usr/sbin/screencapture"
? ({ size: 1 } as Stats)
: stat(target)
))
});

const result = await runtime.accessibilitySnapshot("capture-accessibility", "window-2");
Expand Down
9 changes: 7 additions & 2 deletions tests/desktop-runtime-permission.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
import type { Stats } from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
Expand Down Expand Up @@ -172,7 +172,12 @@ describe("desktop runtime permission and availability", () => {
cacheRoot,
platform: "darwin",
config: makeDesktopConfig(),
execFileImpl
execFileImpl,
statImpl: vi.fn(async (target: string) => (
target === "/usr/sbin/screencapture"
? ({ size: 1 } as Stats)
: stat(target)
))
});

const status = await runtime.status();
Expand Down
10 changes: 10 additions & 0 deletions tests/providers-product-video-workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,11 @@ describe("product-video substrate adoption", () => {
});

it("keeps usable product detail records even when fetch reports a non-ok aggregate", async () => {
vi.stubGlobal("fetch", vi.fn(async () => ({
ok: true,
url: "https://www.amazon.com/dp/B0PHASE5004",
text: async () => "<html><head><title>Amazon.com</title></head><body></body></html>"
})) as unknown as typeof fetch);
const fetch = vi.fn(async () => makeAggregate({
ok: false,
error: {
Expand Down Expand Up @@ -628,6 +633,11 @@ describe("product-video substrate adoption", () => {
});

it("reuses checkpointed resolution and fetch state without replaying completed adaptive steps", async () => {
vi.stubGlobal("fetch", vi.fn(async () => ({
ok: true,
url: "https://www.amazon.com/dp/B0PHASE5003",
text: async () => "<html><head><title>Amazon.com</title></head><body></body></html>"
})) as unknown as typeof fetch);
const search = vi.fn(async () => {
throw new Error("checkpointed resolution should not replay");
});
Expand Down
10 changes: 7 additions & 3 deletions tests/system-chrome-cookies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,13 @@ describe("loadSystemChromeCookies", () => {
throw new Error(`Unexpected command: ${command}`);
});

const { loadSystemChromeCookies } = await import("../src/browser/system-chrome-cookies");
const result = await loadSystemChromeCookies();
const { __test__ } = await import("../src/browser/system-chrome-cookies");
const result = await __test__.loadSystemChromeCookiesFromSqlite({
browserName: "chrome",
userDataDir: sourceRoot,
profileDirectory: "Default",
profilePath
}, "darwin");

expect(result.cookies).toEqual([{
name: "direct_cookie",
Expand Down Expand Up @@ -449,7 +454,6 @@ describe("loadSystemChromeCookies", () => {

expect(result.cookies).toEqual([]);
expect(result.source?.profileDirectory).toBe("Default");
expect(result.warnings.some((warning) => warning.includes("did not expose a readable cookie database"))).toBe(true);
expect(result.warnings.some((warning) => warning.includes("did not expose a readable cookie store"))).toBe(true);
expect(launchPersistentContext).not.toHaveBeenCalled();
});
Expand Down
Loading