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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 0.1.1 — 2026-08-10

Presentation consistency patch for the public-ready repository.

- Replaced the command center, side panel, boot state, and Queue Lab CSS logo approximations with the canonical transparent QueueScope app icon.
- Added browser assertions that verify both application surfaces load the packaged 128×128 icon asset.
- Regenerated and manually reviewed every tracked extension screenshot, including fresh 420×840 side-panel captures with the full header and footer visible.

## 0.1.0 — 2026-08-10

First public-ready developer-mode alpha.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This is the retailer-neutral public edition. It contains no site-specific adapte

![QueueScope command center](assets/screenshots/command-center.png)

## What works in v0.1.0
## What works in v0.1.1

- One-time, daily, weekday, weekend, and custom-weekday schedules.
- Passive watches with user-controlled cadence and no attempt ceiling.
Expand Down Expand Up @@ -54,7 +54,7 @@ Queue Lab proves product, queue, challenge, availability, position/ETA, and post

### From the release archive

1. Download `QueueScope-0.1.0-unpacked.zip` from the release assets.
1. Download `QueueScope-0.1.1-unpacked.zip` from the release assets.
2. Extract it to a permanent local folder.
3. Open your Chromium browser's extensions page (for Chrome, `chrome://extensions`).
4. Enable **Developer mode**.
Expand Down Expand Up @@ -117,7 +117,7 @@ See [Privacy and safety](docs/PRIVACY_AND_SAFETY.md), [Architecture](docs/ARCHIT

## Validation

The v0.1.0 release gate covers:
The v0.1.1 release gate covers:

- ESLint and TypeScript.
- 26 deterministic unit tests.
Expand All @@ -131,7 +131,7 @@ The detailed evidence is in [Test report](docs/TEST_REPORT.md), [Security review

## Project status

v0.1.0 is a functional developer-mode alpha. The private repository is ready for its owner to review and manually change to public. The current scope intentionally favors transparent, user-authored visible-page rules over bundled site integrations.
v0.1.1 is a functional developer-mode alpha. The private repository is ready for its owner to review and manually change to public. The current scope intentionally favors transparent, user-authored visible-page rules over bundled site integrations.

- [Plan and completed scope](PLAN.md)
- [Changelog](CHANGELOG.md)
Expand Down
Binary file modified assets/screenshots/command-center.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/screenshots/live-run.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/screenshots/queue-lab-waiting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/screenshots/queue-lab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/screenshots/side-panel-watches.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/screenshots/side-panel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/screenshots/watch-builder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions docs/RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# v0.1.0 release checklist
# v0.1.1 release checklist

## Source and trust boundary

Expand All @@ -25,7 +25,8 @@
- [x] Production artifact verifier passes.
- [x] Disposable-profile unpacked-extension E2E passes.
- [x] Side panel reviewed at 420×840.
- [x] Screenshots regenerated from v0.1.0 and manually reviewed.
- [x] Canonical transparent app icon rendered consistently across all extension surfaces.
- [x] Screenshots regenerated from v0.1.1 and manually reviewed.
- [x] Dependency audit reports zero vulnerabilities.
- [x] Security, CSP, permissions, secret, and neutrality reviews complete.

Expand Down
2 changes: 1 addition & 1 deletion docs/SECURITY_REVIEW.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# v0.1.0 security review
# v0.1.1 security review

Date: 2026-08-10

Expand Down
7 changes: 4 additions & 3 deletions docs/TEST_REPORT.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# v0.1.0 test report
# v0.1.1 test report

Date: 2026-08-10
Environment: Windows, Node.js 24.11.1, disposable Chromium profile, 1440×1000 dashboard, 420×840 side panel.
Expand All @@ -19,6 +19,7 @@ Environment: Windows, Node.js 24.11.1, disposable Chromium profile, 1440×1000 d

- Loaded the production `dist` folder in a new temporary Chromium profile.
- Opened the dashboard with tab title `QueueScope`.
- Confirmed the dashboard and standalone Queue Lab render the packaged `/icons/icon-128.png` asset at its canonical 128×128 intrinsic dimensions.
- Confirmed the Queue Lab orbit marker remained within 3 CSS pixels of the orbit radius.
- Opened the synthetic Queue Lab and transitioned from product to queue.
- Persisted classification `queue`, position `428`, and provider ETA `00:08:30`.
Expand All @@ -29,11 +30,11 @@ Environment: Windows, Node.js 24.11.1, disposable Chromium profile, 1440×1000 d
- Confirmed side-panel footer was pinned to the viewport bottom.
- Seeded a future watch in the side panel, toggled auto-start, copied it as a paused watch, and deleted the copy.
- Opened the watch builder and confirmed passive mode displays no attempt ceiling.
- Captured browser-generated release screenshots without console/page errors.
- Captured browser-generated release screenshots without console/page errors, with side-panel header and footer geometry asserted before capture.

## Manual visual review

Reviewed command center, expanded live run, Queue Lab overview, Queue Lab waiting state, watch builder, and 420×840 side panel at original resolution. Verified hierarchy, no clipped status indicators, collapsed-card density, pinned panel footer, and responsive orbit alignment. The screenshots tracked in `assets/screenshots` are the reviewed release captures.
Reviewed command center, expanded live run, Queue Lab overview, Queue Lab waiting state, watch builder, and both 420×840 side-panel states at original resolution. Verified canonical icon consistency, hierarchy, no clipped status indicators, collapsed-card density, complete panel headers, pinned panel footers, and responsive orbit alignment. The screenshots tracked in `assets/screenshots` are the reviewed release captures.

## Boundaries

Expand Down
2 changes: 1 addition & 1 deletion harness/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</head>
<body>
<main>
<header><div class="lab-brand"><div class="scope-mark"><i/></div><div><strong>QueueScope</strong><span>QUEUE LAB</span></div></div><div class="local-pill">SYNTHETIC · LOCAL</div></header>
<header><div class="lab-brand"><span class="scope-mark" aria-hidden="true"><img src="/icons/icon-128.png" alt="" /></span><div><strong>QueueScope</strong><span>QUEUE LAB</span></div></div><div class="local-pill">SYNTHETIC · LOCAL</div></header>
<section class="lab-console">
<div class="radar"><span class="sweep"></span><div class="ring ring-one"></div><div class="ring ring-two"></div><div class="lab-node" id="lab-node"></div><div class="center"><strong id="stage-code">01</strong><span>STATE</span></div></div>
<div class="stage-copy"><span class="kicker">CONTROLLED REHEARSAL</span><h1 id="stage-title">Product page observed</h1><p id="stage-body">The page is loaded and no configured attention signal is present.</p><div class="telemetry"><div><span>CLASSIFICATION</span><strong id="classification">product</strong></div><div><span>POSITION</span><strong id="position">—</strong></div><div><span>PROVIDER ETA</span><strong id="eta">—</strong></div></div></div>
Expand Down
2 changes: 1 addition & 1 deletion harness/lab.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "queuescope",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"description": "Retailer-neutral local-first browser operations console for scheduled page observation and virtual-queue awareness.",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "QueueScope",
"version": "0.1.0",
"version": "0.1.1",
"description": "A retailer-neutral local-first console for scheduled page observation and virtual-queue awareness.",
"minimum_chrome_version": "120",
"permissions": [
Expand Down
1 change: 1 addition & 0 deletions release/QueueScope-0.1.1-unpacked.sha256.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
64a57b7201c139d45b7971a98bda8fdadce75283d1da014e860ef699135125c2 QueueScope-0.1.1-unpacked.zip
38 changes: 35 additions & 3 deletions scripts/e2e-extension.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ try {
await dashboard.goto(`chrome-extension://${id}/dashboard.html`);
await dashboard.getByRole("heading", { name: "Observe the moment. Preserve the tab." }).waitFor();
if ((await dashboard.title()) !== "QueueScope") throw new Error("Dashboard tab title is not QueueScope.");
const dashboardBrand = await dashboard.locator(".brand-mark img").first().evaluate((image) => ({
complete: image.complete,
naturalWidth: image.naturalWidth,
naturalHeight: image.naturalHeight,
file: new URL(image.src).pathname
}));
if (!dashboardBrand.complete || dashboardBrand.naturalWidth !== 128 || dashboardBrand.naturalHeight !== 128 || dashboardBrand.file !== "/icons/icon-128.png") {
throw new Error(`Dashboard did not render the canonical app icon: ${JSON.stringify(dashboardBrand)}`);
}
await dashboard.screenshot({ path: join(results, "command-center.png"), fullPage: true });

await dashboard.getByRole("button", { name: "Queue Lab", exact: true }).click();
Expand All @@ -61,6 +70,15 @@ try {
}
if (!lab) throw new Error(`Queue Lab tab was not exposed to Playwright. Pages: ${context.pages().map((page) => page.url()).join(", ")}`);
await lab.getByRole("heading", { name: "Product page observed" }).waitFor();
const labBrand = await lab.locator(".scope-mark img").evaluate((image) => ({
complete: image.complete,
naturalWidth: image.naturalWidth,
naturalHeight: image.naturalHeight,
file: new URL(image.src).pathname
}));
if (!labBrand.complete || labBrand.naturalWidth !== 128 || labBrand.naturalHeight !== 128 || labBrand.file !== "/icons/icon-128.png") {
throw new Error(`Queue Lab did not render the canonical app icon: ${JSON.stringify(labBrand)}`);
}
await lab.getByRole("button", { name: /Queue Lock navigation/ }).click();
await lab.getByRole("heading", { name: "Waiting room detected" }).waitFor();
await lab.screenshot({ path: join(results, "queue-lab-waiting.png"), fullPage: true });
Expand Down Expand Up @@ -92,7 +110,7 @@ try {
const footer = await panel.locator(".panel-footer").boundingBox();
const viewport = panel.viewportSize();
if (!footer || !viewport || Math.abs(footer.y + footer.height - viewport.height) > 2) throw new Error("Side-panel footer is not pinned to the viewport.");
await panel.screenshot({ path: join(results, "side-panel.png"), fullPage: true });
await panel.screenshot({ path: join(results, "side-panel.png") });

const panelWatchName = "Synthetic recurring watch";
const savedWatch = await dashboard.evaluate(async (name) => {
Expand All @@ -118,8 +136,22 @@ try {
await panelCopy.getByTitle("Delete watch").click();
await panelCopy.waitFor({ state: "detached" });
await panel.waitForTimeout(4_200);
await panel.evaluate(() => window.scrollTo({ top: 0, left: 0, behavior: "instant" }));
await panel.screenshot({ path: join(results, "side-panel-watches.png"), fullPage: true });
await panel.goto(`chrome-extension://${id}/sidepanel.html`);
await panel.getByRole("button", { name: "Watches", exact: true }).evaluate((button) => button.click());
await panel.locator(".watch-card").filter({ hasText: panelWatchName }).waitFor();
await panel.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))));
const watchViewport = await panel.evaluate(() => ({
scrollY: window.scrollY,
documentTop: document.documentElement.scrollTop,
bodyTop: document.body.scrollTop,
visualTop: window.visualViewport?.pageTop ?? 0
}));
if (Object.values(watchViewport).some((value) => Math.abs(value) > 1)) throw new Error(`Side-panel watch viewport was not reset: ${JSON.stringify(watchViewport)}`);
const watchHeader = await panel.locator(".panel-header").boundingBox();
if (!watchHeader || Math.abs(watchHeader.y) > 2) throw new Error(`Side-panel watch capture retained a ${watchHeader?.y ?? "missing"}px header offset.`);
const watchFooter = await panel.locator(".panel-footer").boundingBox();
if (!watchFooter || !viewport || Math.abs(watchFooter.y + watchFooter.height - viewport.height) > 2) throw new Error("Side-panel watch footer is not pinned to the viewport.");
await panel.screenshot({ path: join(results, "side-panel-watches.png") });

await dashboard.getByRole("button", { name: "Watches", exact: true }).click();
await dashboard.getByRole("button", { name: "New watch" }).click();
Expand Down
6 changes: 3 additions & 3 deletions scripts/package-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ $ErrorActionPreference = "Stop"
$root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$dist = (Resolve-Path (Join-Path $root "dist")).Path
$release = Join-Path $root "release"
$archive = Join-Path $release "QueueScope-0.1.0-unpacked.zip"
$checksum = Join-Path $release "QueueScope-0.1.0-unpacked.sha256.txt"
$archive = Join-Path $release "QueueScope-0.1.1-unpacked.zip"
$checksum = Join-Path $release "QueueScope-0.1.1-unpacked.sha256.txt"

if ($dist -eq $root -or -not $dist.StartsWith($root, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to package an unsafe distribution path."
Expand All @@ -12,6 +12,6 @@ New-Item -ItemType Directory -Force -Path $release | Out-Null
if (Test-Path -LiteralPath $archive) { Remove-Item -LiteralPath $archive -Force }
Compress-Archive -Path (Join-Path $dist "*") -DestinationPath $archive -CompressionLevel Optimal
$hash = (Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash.ToLowerInvariant()
Set-Content -LiteralPath $checksum -Value "$hash QueueScope-0.1.0-unpacked.zip" -Encoding utf8
Set-Content -LiteralPath $checksum -Value "$hash QueueScope-0.1.1-unpacked.zip" -Encoding utf8
Write-Output "Packaged $archive"
Write-Output "SHA256 $hash"
2 changes: 1 addition & 1 deletion scripts/verify-build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ for (const file of required) await access(resolve(root, file));

const manifest = JSON.parse(await readFile(resolve(root, "manifest.json"), "utf8"));
if (manifest.manifest_version !== 3) throw new Error("Release manifest is not MV3.");
if (manifest.version !== "0.1.0") throw new Error("Release manifest version does not match 0.1.0.");
if (manifest.version !== "0.1.1") throw new Error("Release manifest version does not match 0.1.1.");
if (!Array.isArray(manifest.optional_host_permissions) || manifest.optional_host_permissions.length !== 2) throw new Error("Origin access is not optional and bounded as expected.");
if (manifest.content_scripts) throw new Error("Public build must not inject globally declared content scripts.");

Expand Down
Loading
Loading