From 466c1d3d2c69c20c6410290296dfed0fcfde8680 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Mon, 17 Aug 2026 17:40:24 +0530 Subject: [PATCH 1/8] docs(readiness): align v0.5.0 release evidence Signed-off-by: Tapish Khandelwal --- docs/PUBLICATION_READINESS.md | 107 ++++++++++++------ tests/docs/publication-readiness.test.ts | 134 +++++++++++++++++++++++ 2 files changed, 205 insertions(+), 36 deletions(-) create mode 100644 tests/docs/publication-readiness.test.ts diff --git a/docs/PUBLICATION_READINESS.md b/docs/PUBLICATION_READINESS.md index f5182fb..f791f85 100644 --- a/docs/PUBLICATION_READINESS.md +++ b/docs/PUBLICATION_READINESS.md @@ -26,12 +26,16 @@ durable full-year, or stable-release claims. 2026-07-06 records item ID `nfnbhekccajjfgkppolomflaeledoccb`, item name `ComplyEaze Pack: GSTR-1/GSTR-3B Downloader`, version `0.3.2`, and visibility `Public`. -- Repository source is the pre-1.0 `v0.5.0` beta release. The current - Store-published package evidence is for `v0.3.2` only. Workflow run - `29507382500` built and verified the exact `v0.4.0` package and uploaded it - with Store state `SUCCEEDED`, but publish returned HTTP 400 because dashboard - requirements were incomplete. The pending ZIP SHA-256 is +- The past `v0.4.0` release workflow run `29507382500` built and verified the + exact package and uploaded it with Store state `SUCCEEDED`, but publish + returned HTTP 400 because dashboard requirements were incomplete. It was not + published. The historical pending ZIP SHA-256 is `6ee4be24cafbe15db69275cac4da6b212f3de49b0f747eb9909eed7d293347c6`. +- Repository source and the GitHub release are the pre-1.0 `v0.5.0` beta, + published as a pre-release. The Chrome Web Store package for `v0.5.0` is + submitted and in review as a draft; it is not approved, published, or live. + `v0.3.2` remains the last confirmed Store publication and the only basis for + current Store-published public claims. - The `v0.5.0` beta does not expand Store-facing, durable full-year, or universal live-period/format claims. Synthetic regression coverage of target binding, artifact selection, and recovery is not a substitute for the @@ -135,17 +139,19 @@ durable full-year, or stable-release claims. - [ ] Legal review of live GST Portal terms against exact extension behaviour. - [ ] Privacy Policy, Terms, support URL, source URL, and limited-use statement are live and accessible without login. -- [ ] Chrome privacy declarations match the exact final build. For `v0.4.0`, - disclose personally identifiable information, financial and payment - information, and website content because Chrome counts local processing. - Keep authentication information, web history, and user activity - unchecked unless runtime behavior changes. +- [ ] Chrome privacy declarations match the exact final build. Disclose + personally identifiable information, financial and payment information, + and website content because Chrome counts local processing. Keep + authentication information, web history, and user activity unchecked + unless runtime behavior changes. - [x] Source-controlled Store screenshots and promotional images use only - synthetic/redacted data. The seven `v0.4.0` PNG exports were generated - from refreshed source SVGs, visually checked, and recorded in + synthetic/redacted data. The seven PNG exports prepared for the past + `v0.4.0` dashboard update were generated from refreshed source SVGs, + visually checked, and recorded in `docs/chrome-web-store/assets/exports/asset-hashes.json`. -- [ ] Upload the generated `v0.4.0` Store screenshot/promotional PNGs to the - Chrome Web Store dashboard, then record dashboard image review state. +- [ ] Upload the generated Store screenshot/promotional PNGs to the Chrome Web + Store dashboard, then record dashboard image review state for the exact + submitted release. - [ ] Exact ZIP tested in a clean Chrome profile. - [ ] Exact ZIP tested against the live GSTR-3B and GSTR-1 flows by an authorised user. @@ -156,19 +162,44 @@ durable full-year, or stable-release claims. cookies, credentials, OTP, or CAPTCHA data. - [ ] Authorised live full fiscal year run reconciles every eligible target as downloaded, positively not filed, blocked, or failed in the local ledger. -- [ ] Authorised live full fiscal year recovery matrix records every currently - offered return/artifact selection as exercised or as explicitly expected - fail-closed/not applicable when it cannot reach an acquisition checkpoint. - For each acquisition-capable selection, cover service-worker restart, - browser restart, interrupted download, cancellation/discard and its - cleanup outcome, and a retained checkpoint whose browser record is no - longer available. A resumed path must not repeat a - completed target; an unproven path must remain non-complete until retry - or cancellation. Manual observation may be recorded only as an explicit - non-completing action that still requires retry before ZIP staging. Record - category/state summaries only—never raw portal URLs, - filenames, download IDs, page or DOM text, local paths, or taxpayer/session - data. +- [ ] The authorised live full fiscal year recovery matrix below is complete: + every observation is dated, no cell remains `not-yet-run`, and every + `fail-closed-as-expected` or `not-applicable` entry has a reason. + +The selection rows are derived from the supported Cartesian product of +`FILED_RETURNS_RETURN_TYPES` and `FILED_RETURNS_ARTIFACT_TYPES`; a test keeps +this instrument aligned with those canonical constants. For each +acquisition-capable selection, record service-worker restart, browser restart, +interrupted download, cancellation/discard and its cleanup outcome, and a +retained checkpoint whose browser record is no longer available. A resumed +path must not repeat a completed target. An unproven path remains non-complete +until retry or cancellation. Manual observation is only an explicit +non-completing action and still requires retry before ZIP staging. + +Use only `pass`, `fail`, `fail-closed-as-expected`, `not-applicable`, or +`not-yet-run`, followed by `date: YYYY-MM-DD`; use `date: not-recorded` only +with `not-yet-run`. A reason is required for `fail-closed-as-expected` and +`not-applicable`, using the final column when the selection cannot reach an +acquisition checkpoint. Record category and state summaries only. Never record +raw portal URLs, filenames, download IDs, page or DOM text, local paths, or +taxpayer/session data in a cell. + + + +| Return type | Artifact type | Service-worker restart | Browser restart | Interrupted download | Cancellation/discard and cleanup | Retained checkpoint; browser record unavailable | Expected fail-closed / not applicable | +| ----------- | ------------- | ------------------------------- | ------------------------------- | ------------------------------- | -------------------------------- | ----------------------------------------------- | ----------------------------------------------------- | +| GSTR-3B | PDF | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | +| GSTR-3B | JSON | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | +| GSTR-1 | PDF | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | +| GSTR-1 | EXCEL | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | +| GSTR-1 | PDF_AND_EXCEL | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | +| GSTR-2B | PDF | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | +| GSTR-2B | JSON | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | +| GSTR-2B | EXCEL | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | +| GSTR-2B | PDF_AND_EXCEL | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded | not-yet-run; date: not-recorded; reason: not-recorded | + + + - [ ] Action-bound capture is tested in clean Chrome and Brave profiles plus the real profile where the native Save dialog appeared, with "Ask where to save each file" on and off, existing filename collisions, and @@ -255,16 +286,19 @@ durable full-year, or stable-release claims. `nfnbhekccajjfgkppolomflaeledoccb`, item name `ComplyEaze Pack: GSTR-1/GSTR-3B Downloader`, item type `extension`, version `0.3.2`, and visibility `Public`. -- [x] Upload the exact `v0.4.0` package. Workflow run `29507382500` reported - upload state `SUCCEEDED`; publication remains blocked on dashboard - closeout and is not claimed complete. +- [x] Record the historical `v0.4.0` package upload. Workflow run `29507382500` + reported upload state `SUCCEEDED`; that release was not published. +- [x] Record the `v0.5.0` Chrome Web Store submission. The package is submitted + and in review as a draft; approval, publication, and live availability + are not claimed. - [x] Add a read-only Chrome Web Store status monitor for submitted packages. Scheduled runs use the dedicated `chrome-web-store-status` environment so publication/rejection monitoring is not blocked by the protected publishing approval gate. - [ ] Record a read-only Chrome Web Store Status run with - `expected_version=0.4.0` and `require_published=false` after dashboard - submission, then `require_published=true` after publication. Use + `expected_version=0.5.0` and `require_published=false` while the submitted + draft is in review, then `require_published=true` only after confirmed + publication. Use [`docs/chrome-web-store/dashboard-closeout.md`](chrome-web-store/dashboard-closeout.md) for the dashboard and read-only status-monitor closeout checklist. @@ -278,10 +312,11 @@ version here. ## Not Yet Stable Or Broad-Claim Ready Until -The V0 listing exists for Store-published `v0.3.2`, `v0.4.0` is uploaded but not -yet published, and the source-build alpha -full-year workflow has maintainer evidence for automatic local downloads after -user initiation. Pack must not claim stable Chrome Web Store maturity, +The last confirmed Store publication is `v0.3.2`; the past `v0.4.0` package was +uploaded but not published, and the `v0.5.0` package is submitted and in review +as a draft. The source-build alpha full-year workflow has maintainer evidence +for automatic local downloads after user initiation. Pack must not claim stable +Chrome Web Store maturity, store-facing full-year availability, durable restart-safe full-year support, legal approval, live manifest/index/exception output, or broad GST coverage until legal review, live public policy URL confirmation, exact-ZIP manual QA, diff --git a/tests/docs/publication-readiness.test.ts b/tests/docs/publication-readiness.test.ts new file mode 100644 index 0000000..15b3042 --- /dev/null +++ b/tests/docs/publication-readiness.test.ts @@ -0,0 +1,134 @@ +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { LIVE_RUN_SENSITIVE_PATTERNS } from "../../scripts/lib/live-run-evidence-redaction"; +import { + FILED_RETURNS_ARTIFACT_TYPES, + supportsFiledReturnsArtifactType, +} from "../../src/connectors/gst/filed-returns-artifacts"; +import { FILED_RETURNS_RETURN_TYPES } from "../../src/connectors/gst/filed-returns-return-types"; + +const rootDir = process.cwd(); +const matrixStart = ""; +const matrixEnd = ""; +const observationPattern = + /^(pass|fail|fail-closed-as-expected|not-applicable|not-yet-run); date: (\d{4}-\d{2}-\d{2}|not-recorded)(?:; reason: (.+))?$/; +const matrixSensitivePatterns = [ + ...LIVE_RUN_SENSITIVE_PATTERNS, + { id: "raw-url", pattern: /https?:\/\/\S+/i }, + { id: "matrix-filename", pattern: /\b[\w.-]+\.(?:html?|json)\b/i }, + { id: "portal-html-tag", pattern: /<\/?[a-z][^>]*>/i }, + { + id: "download-id", + pattern: /\bdownload(?:\s+|[-_])?id(?:\s*[:=]\s*|\s+)\d+\b/i, + }, + { + id: "page-or-dom-text", + pattern: /\b(?:page|dom)\s+text(?:\s*[:=]\s*|\s+)\S+/i, + }, + { + id: "taxpayer-name", + pattern: /\btaxpayer(?:\s+name)?(?:\s*[:=]\s*|\s+)\S+/i, + }, + { id: "session-data", pattern: /\b(?:session|token)(?:\s*[:=]\s*|\s+)\S+/i }, +]; +const recoveryMatrixCheckboxPattern = + /^- \[( |x)\] The authorised live full fiscal year recovery matrix below is complete:/m; + +describe("publication readiness recovery matrix", () => { + it("tracks every canonical offered return and artifact selection once", async () => { + const matrix = await readRecoveryMatrix(); + const documentedSelections = matrixRows(matrix).map(([returnType, artifactType]) => + [returnType, artifactType].join(" | "), + ); + const offeredSelections = FILED_RETURNS_RETURN_TYPES.flatMap((returnType) => + FILED_RETURNS_ARTIFACT_TYPES.filter((artifactType) => + supportsFiledReturnsArtifactType(returnType, artifactType), + ).map((artifactType) => [returnType, artifactType].join(" | ")), + ); + + expect(documentedSelections).toEqual(offeredSelections); + expect(new Set(documentedSelections).size).toBe(documentedSelections.length); + }); + + it("keeps every observation fillable, dated, and reasoned when required", async () => { + const matrix = await readRecoveryMatrix(); + + for (const [, , ...observations] of matrixRows(matrix)) { + expect(observations).toHaveLength(6); + + for (const [index, observation] of observations.entries()) { + for (const { id, pattern } of matrixSensitivePatterns) { + expect(pattern.test(observation), `matrix cell contains sensitive marker: ${id}`).toBe( + false, + ); + } + + const parsed = observation.match(observationPattern); + expect(parsed, "matrix cell has an invalid observation format").not.toBeNull(); + if (!parsed) continue; + + const [, state, date, reason] = parsed; + expect(date === "not-recorded").toBe(state === "not-yet-run"); + if (index === observations.length - 1) { + expect(["fail-closed-as-expected", "not-applicable", "not-yet-run"]).toContain(state); + } + if ( + index === observations.length - 1 || + state === "fail-closed-as-expected" || + state === "not-applicable" + ) { + expect(reason, "matrix cell is missing its required reason").toBeTruthy(); + expect( + state === "not-yet-run" || reason !== "not-recorded", + "completed matrix cell has an unfilled reason", + ).toBe(true); + } + } + } + }); + + it("cannot mark the recovery gate complete while any observation is unfilled", async () => { + const readiness = await readPublicationReadiness(); + const checkbox = readiness.match(recoveryMatrixCheckboxPattern); + + expect(checkbox).not.toBeNull(); + if (checkbox?.[1] !== "x") return; + + for (const [, , ...observations] of matrixRows(recoveryMatrix(readiness))) { + for (const observation of observations) { + expect(observation).not.toContain("not-yet-run"); + expect(observation).not.toContain("not-recorded"); + } + } + }); +}); + +async function readRecoveryMatrix(): Promise { + return recoveryMatrix(await readPublicationReadiness()); +} + +async function readPublicationReadiness(): Promise { + return readFile(path.join(rootDir, "docs", "PUBLICATION_READINESS.md"), "utf8"); +} + +function recoveryMatrix(readiness: string): string { + const start = readiness.indexOf(matrixStart); + const end = readiness.indexOf(matrixEnd); + + expect(start).toBeGreaterThanOrEqual(0); + expect(end).toBeGreaterThan(start); + return readiness.slice(start + matrixStart.length, end); +} + +function matrixRows(matrix: string): string[][] { + return matrix + .split("\n") + .filter((line) => /^\| GSTR-(?:1|2B|3B)\s+\|/.test(line)) + .map((line) => + line + .split("|") + .slice(1, -1) + .map((cell) => cell.trim()), + ); +} From a45cc0880b89189ec926f1c0b53e3089d99f116b Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Mon, 17 Aug 2026 17:40:34 +0530 Subject: [PATCH 2/8] fix(ci): skip unconfigured store status checks Signed-off-by: Tapish Khandelwal --- .github/workflows/chrome-web-store-status.yml | 27 +++++++++++++++++++ tests/extension/ci-workflow.test.ts | 7 +++++ 2 files changed, 34 insertions(+) diff --git a/.github/workflows/chrome-web-store-status.yml b/.github/workflows/chrome-web-store-status.yml index 6169e7e..9715a7f 100644 --- a/.github/workflows/chrome-web-store-status.yml +++ b/.github/workflows/chrome-web-store-status.yml @@ -22,8 +22,35 @@ permissions: contents: read jobs: + configuration: + name: Detect Chrome Web Store status configuration + runs-on: ubuntu-latest + timeout-minutes: 1 + environment: chrome-web-store-status + outputs: + configured: ${{ steps.credentials.outputs.configured }} + + steps: + - name: Detect status credentials + id: credentials + shell: bash + env: + CWS_SERVICE_ACCOUNT_JSON: ${{ secrets.CWS_SERVICE_ACCOUNT_JSON }} + run: | + if [[ -z "$CWS_SERVICE_ACCOUNT_JSON" ]]; then + echo "configured=false" >> "$GITHUB_OUTPUT" + echo "::notice::Chrome Web Store status check skipped because credentials are not configured. See #59." + echo "### Chrome Web Store status check skipped" >> "$GITHUB_STEP_SUMMARY" + echo "Credentials are not configured, so the read-only status check did not run. See #59." >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + + echo "configured=true" >> "$GITHUB_OUTPUT" + status: name: Check Chrome Web Store status + needs: configuration + if: needs.configuration.outputs.configured == 'true' runs-on: ubuntu-latest timeout-minutes: 5 environment: chrome-web-store-status diff --git a/tests/extension/ci-workflow.test.ts b/tests/extension/ci-workflow.test.ts index 38d7dff..73cac1b 100644 --- a/tests/extension/ci-workflow.test.ts +++ b/tests/extension/ci-workflow.test.ts @@ -142,6 +142,13 @@ describe("Pack CI workflow", () => { expect(statusWorkflow).toContain("schedule:"); expect(statusWorkflow).toContain("workflow_dispatch:"); expect(statusWorkflow).toContain("environment: chrome-web-store-status"); + expect(statusWorkflow).toContain("id: credentials"); + expect(statusWorkflow).toContain('if [[ -z "$CWS_SERVICE_ACCOUNT_JSON" ]]'); + expect(statusWorkflow).toContain('echo "configured=false" >> "$GITHUB_OUTPUT"'); + expect(statusWorkflow).toContain('echo "configured=true" >> "$GITHUB_OUTPUT"'); + expect(statusWorkflow).toContain("Chrome Web Store status check skipped"); + expect(statusWorkflow).toContain("the read-only status check did not run"); + expect(statusWorkflow).toContain("if: needs.configuration.outputs.configured == 'true'"); expect(statusWorkflow).toContain("node scripts/check-chrome-web-store-status.mjs"); expect(statusWorkflow).toContain("CWS_REQUIRE_PUBLISHED"); expect(statusWorkflow).toContain("CWS_SERVICE_ACCOUNT_JSON"); From 58c30da8140c8d50f48bf6c2275e6bd62be0c133 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Mon, 17 Aug 2026 20:15:18 +0530 Subject: [PATCH 3/8] fix(ci): enforce Store status authentication Signed-off-by: Tapish Khandelwal --- .github/workflows/chrome-web-store-status.yml | 53 +++--- tests/extension/ci-workflow.test.ts | 152 ++++++++++++++++-- 2 files changed, 167 insertions(+), 38 deletions(-) diff --git a/.github/workflows/chrome-web-store-status.yml b/.github/workflows/chrome-web-store-status.yml index 9715a7f..698d533 100644 --- a/.github/workflows/chrome-web-store-status.yml +++ b/.github/workflows/chrome-web-store-status.yml @@ -22,35 +22,8 @@ permissions: contents: read jobs: - configuration: - name: Detect Chrome Web Store status configuration - runs-on: ubuntu-latest - timeout-minutes: 1 - environment: chrome-web-store-status - outputs: - configured: ${{ steps.credentials.outputs.configured }} - - steps: - - name: Detect status credentials - id: credentials - shell: bash - env: - CWS_SERVICE_ACCOUNT_JSON: ${{ secrets.CWS_SERVICE_ACCOUNT_JSON }} - run: | - if [[ -z "$CWS_SERVICE_ACCOUNT_JSON" ]]; then - echo "configured=false" >> "$GITHUB_OUTPUT" - echo "::notice::Chrome Web Store status check skipped because credentials are not configured. See #59." - echo "### Chrome Web Store status check skipped" >> "$GITHUB_STEP_SUMMARY" - echo "Credentials are not configured, so the read-only status check did not run. See #59." >> "$GITHUB_STEP_SUMMARY" - exit 0 - fi - - echo "configured=true" >> "$GITHUB_OUTPUT" - status: name: Check Chrome Web Store status - needs: configuration - if: needs.configuration.outputs.configured == 'true' runs-on: ubuntu-latest timeout-minutes: 5 environment: chrome-web-store-status @@ -65,10 +38,34 @@ jobs: node-version: 22.13.0 - name: Check Chrome Web Store status - run: node scripts/check-chrome-web-store-status.mjs + shell: bash + run: | + set +e + status_output="$(node scripts/check-chrome-web-store-status.mjs 2>&1)" + status_code=$? + set -e + + if [[ $status_code -eq 0 ]]; then + printf '%s\n' "$status_output" + exit 0 + fi + + if [[ "$GITHUB_EVENT_NAME" == "schedule" && "$CWS_REQUIRE_PUBLISHED" == "false" ]] && + [[ "$status_output" == "Missing Chrome Web Store credentials."* ]]; then + echo "::notice::Chrome Web Store status check skipped because credentials are not configured. See #59." + echo "### Chrome Web Store status check skipped" >> "$GITHUB_STEP_SUMMARY" + echo "Credentials are not configured, so the non-strict scheduled status check did not run. See #59." >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + + printf '%s\n' "$status_output" >&2 + exit "$status_code" env: CWS_EXTENSION_ID: nfnbhekccajjfgkppolomflaeledoccb CWS_EXPECTED_VERSION: ${{ inputs.expected_version }} CWS_REQUIRE_PUBLISHED: ${{ inputs.require_published || 'false' }} CWS_PUBLISHER_ID: ${{ vars.CWS_PUBLISHER_ID }} CWS_SERVICE_ACCOUNT_JSON: ${{ secrets.CWS_SERVICE_ACCOUNT_JSON }} + CWS_CLIENT_ID: ${{ secrets.CWS_CLIENT_ID }} + CWS_CLIENT_SECRET: ${{ secrets.CWS_CLIENT_SECRET }} + CWS_REFRESH_TOKEN: ${{ secrets.CWS_REFRESH_TOKEN }} diff --git a/tests/extension/ci-workflow.test.ts b/tests/extension/ci-workflow.test.ts index 73cac1b..c262c8b 100644 --- a/tests/extension/ci-workflow.test.ts +++ b/tests/extension/ci-workflow.test.ts @@ -1,4 +1,6 @@ -import { readdir, readFile } from "node:fs/promises"; +import { spawnSync } from "node:child_process"; +import { chmod, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; @@ -138,24 +140,154 @@ describe("Pack CI workflow", () => { path.join(rootDir, ".github", "workflows", "chrome-web-store-status.yml"), "utf8", ); + const publisher = await readFile( + path.join(rootDir, "scripts", "publish-chrome-web-store.mjs"), + "utf8", + ); + const missingCredentialContract = publisher.match( + /"(Missing Chrome Web Store credentials\.[^"]+)"/, + )?.[1]; + const contractCredentialNames = [ + ...new Set(missingCredentialContract?.match(/CWS_[A-Z_]+/g) ?? []), + ]; expect(statusWorkflow).toContain("schedule:"); expect(statusWorkflow).toContain("workflow_dispatch:"); expect(statusWorkflow).toContain("environment: chrome-web-store-status"); - expect(statusWorkflow).toContain("id: credentials"); - expect(statusWorkflow).toContain('if [[ -z "$CWS_SERVICE_ACCOUNT_JSON" ]]'); - expect(statusWorkflow).toContain('echo "configured=false" >> "$GITHUB_OUTPUT"'); - expect(statusWorkflow).toContain('echo "configured=true" >> "$GITHUB_OUTPUT"'); + expect(statusWorkflow).toContain( + 'if [[ "$GITHUB_EVENT_NAME" == "schedule" && "$CWS_REQUIRE_PUBLISHED" == "false" ]]', + ); + expect(statusWorkflow).toContain( + '[[ "$status_output" == "Missing Chrome Web Store credentials."* ]]', + ); + expect(statusWorkflow).toContain('exit "$status_code"'); expect(statusWorkflow).toContain("Chrome Web Store status check skipped"); - expect(statusWorkflow).toContain("the read-only status check did not run"); - expect(statusWorkflow).toContain("if: needs.configuration.outputs.configured == 'true'"); + expect(statusWorkflow).toContain("the non-strict scheduled status check did not run"); expect(statusWorkflow).toContain("node scripts/check-chrome-web-store-status.mjs"); expect(statusWorkflow).toContain("CWS_REQUIRE_PUBLISHED"); - expect(statusWorkflow).toContain("CWS_SERVICE_ACCOUNT_JSON"); - expect(statusWorkflow).not.toContain("CWS_REFRESH_TOKEN"); - expect(statusWorkflow).not.toContain("CWS_CLIENT_SECRET"); + expect(contractCredentialNames.length).toBeGreaterThan(0); + for (const name of contractCredentialNames) { + expect(statusWorkflow).toContain(`${name}: \${{ secrets.${name} }}`); + } expect(statusWorkflow).not.toContain("scripts/publish-chrome-web-store.mjs"); expect(statusWorkflow).not.toContain(":publish"); expect(statusWorkflow).not.toContain(":upload"); }); + + it("skips only an unconfigured non-strict schedule and admits either credential form", async () => { + const statusWorkflow = await readFile( + path.join(rootDir, ".github", "workflows", "chrome-web-store-status.yml"), + "utf8", + ); + const publisher = await readFile( + path.join(rootDir, "scripts", "publish-chrome-web-store.mjs"), + "utf8", + ); + const missingCredentials = publisher.match( + /"(Missing Chrome Web Store credentials\.[^"]+)"/, + )?.[1]; + expect(missingCredentials).toBeTruthy(); + + const fixtureDir = await mkdtemp(path.join(tmpdir(), "pack-cws-status-workflow-")); + try { + const fakeNode = path.join(fixtureDir, "node"); + await writeFile( + fakeNode, + `#!/bin/sh +if [ "$FAKE_CREDENTIAL_FORM" = "service-account" ] && [ -z "$CWS_SERVICE_ACCOUNT_JSON" ]; then exit 9; fi +if [ "$FAKE_CREDENTIAL_FORM" = "oauth" ] && { [ -z "$CWS_CLIENT_ID" ] || [ -z "$CWS_CLIENT_SECRET" ] || [ -z "$CWS_REFRESH_TOKEN" ]; }; then exit 9; fi +printf '%s\\n' "$FAKE_NODE_OUTPUT" +exit "$FAKE_NODE_EXIT" +`, + ); + await chmod(fakeNode, 0o755); + + const runScript = extractLastWorkflowRunScript(statusWorkflow); + const scheduledSummary = path.join(fixtureDir, "scheduled-summary.md"); + const scheduled = runWorkflowShell(runScript, fixtureDir, { + CWS_REQUIRE_PUBLISHED: "false", + FAKE_NODE_EXIT: "1", + FAKE_NODE_OUTPUT: missingCredentials ?? "", + GITHUB_EVENT_NAME: "schedule", + GITHUB_STEP_SUMMARY: scheduledSummary, + }); + expect(scheduled.status).toBe(0); + expect(await readFile(scheduledSummary, "utf8")).toContain( + "the non-strict scheduled status check did not run", + ); + + const strictDispatch = runWorkflowShell(runScript, fixtureDir, { + CWS_REQUIRE_PUBLISHED: "true", + FAKE_NODE_EXIT: "1", + FAKE_NODE_OUTPUT: missingCredentials ?? "", + GITHUB_EVENT_NAME: "workflow_dispatch", + GITHUB_STEP_SUMMARY: path.join(fixtureDir, "strict-summary.md"), + }); + expect(strictDispatch.status).toBe(1); + expect(strictDispatch.stderr).toContain("Missing Chrome Web Store credentials."); + + const serviceAccount = runWorkflowShell(runScript, fixtureDir, { + CWS_REQUIRE_PUBLISHED: "false", + CWS_SERVICE_ACCOUNT_JSON: "configured", + FAKE_CREDENTIAL_FORM: "service-account", + FAKE_NODE_EXIT: "0", + FAKE_NODE_OUTPUT: "status-ok", + GITHUB_EVENT_NAME: "schedule", + GITHUB_STEP_SUMMARY: path.join(fixtureDir, "service-account-summary.md"), + }); + expect(serviceAccount.status).toBe(0); + + const oauth = runWorkflowShell(runScript, fixtureDir, { + CWS_CLIENT_ID: "configured", + CWS_CLIENT_SECRET: "configured", + CWS_REFRESH_TOKEN: "configured", + CWS_REQUIRE_PUBLISHED: "false", + FAKE_CREDENTIAL_FORM: "oauth", + FAKE_NODE_EXIT: "0", + FAKE_NODE_OUTPUT: "status-ok", + GITHUB_EVENT_NAME: "schedule", + GITHUB_STEP_SUMMARY: path.join(fixtureDir, "oauth-summary.md"), + }); + expect(oauth.status).toBe(0); + } finally { + await rm(fixtureDir, { force: true, recursive: true }); + } + }); }); + +function extractLastWorkflowRunScript(workflow: string): string { + const marker = " run: |\n"; + const start = workflow.lastIndexOf(marker); + expect(start).toBeGreaterThanOrEqual(0); + const lines = workflow.slice(start + marker.length).split("\n"); + const script: string[] = []; + + for (const line of lines) { + if (line.startsWith(" ")) { + script.push(line.slice(10)); + continue; + } + if (line === "") { + script.push(""); + continue; + } + break; + } + + return script.join("\n"); +} + +function runWorkflowShell( + script: string, + fixtureDir: string, + env: Record, +): ReturnType { + return spawnSync("bash", ["-c", script], { + encoding: "utf8", + env: { + ...process.env, + ...env, + PATH: `${fixtureDir}:${process.env.PATH ?? ""}`, + }, + }); +} From 810a6123d997098a5c59f0b264b6312aeb304023 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Mon, 17 Aug 2026 20:15:34 +0530 Subject: [PATCH 4/8] docs(readiness): close recovery evidence vocabulary Signed-off-by: Tapish Khandelwal --- docs/PUBLICATION_READINESS.md | 11 +- docs/RELEASE.md | 11 +- docs/chrome-web-store/dashboard-closeout.md | 42 ++-- docs/chrome-web-store/listing.md | 32 +-- tests/docs/publication-readiness.test.ts | 204 +++++++++++++------- 5 files changed, 198 insertions(+), 102 deletions(-) diff --git a/docs/PUBLICATION_READINESS.md b/docs/PUBLICATION_READINESS.md index f791f85..6089a51 100644 --- a/docs/PUBLICATION_READINESS.md +++ b/docs/PUBLICATION_READINESS.md @@ -180,9 +180,14 @@ Use only `pass`, `fail`, `fail-closed-as-expected`, `not-applicable`, or `not-yet-run`, followed by `date: YYYY-MM-DD`; use `date: not-recorded` only with `not-yet-run`. A reason is required for `fail-closed-as-expected` and `not-applicable`, using the final column when the selection cannot reach an -acquisition checkpoint. Record category and state summaries only. Never record -raw portal URLs, filenames, download IDs, page or DOM text, local paths, or -taxpayer/session data in a cell. +acquisition checkpoint. The value after `reason:` is not free text; use exactly +one of `selection-not-acquisition-capable`, +`recovery-scenario-not-applicable`, or `expected-fail-closed-boundary`. Use +`reason: not-recorded` only with `not-yet-run`. If these categories are +insufficient, add a category through review before recording the observation. +No other cell text is permitted, so raw portal URLs, filenames, download IDs, +page or DOM text, local paths, and taxpayer/session data are unrepresentable in +the matrix. diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 1e4a381..52cbd68 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -154,10 +154,13 @@ dispatch it with `require_published=true` when final publication, not just submission, is the release gate. Configure the status workflow with a dedicated `chrome-web-store-status` -environment that has no required reviewer protection. Give it a read-only -service-account `CWS_SERVICE_ACCOUNT_JSON` secret plus `CWS_PUBLISHER_ID`; do -not copy the publish workflow's OAuth client secret or refresh token into this -environment. Keep the publishing workflow on the protected `chrome-web-store` +environment that has no required reviewer protection. Give it +`CWS_PUBLISHER_ID` plus either `CWS_SERVICE_ACCOUNT_JSON` or the complete OAuth +set `CWS_CLIENT_ID`, `CWS_CLIENT_SECRET`, and `CWS_REFRESH_TOKEN`. Use a +dedicated OAuth set provisioned with the read-only grant; do not copy broader +publishing credentials into this environment. The status checker performs only +the `fetchStatus` call, and its service-account flow requests the read-only API +scope. Keep the publishing workflow on the protected `chrome-web-store` environment. For local dry-runs against a generated release package: diff --git a/docs/chrome-web-store/dashboard-closeout.md b/docs/chrome-web-store/dashboard-closeout.md index 0c680ed..2c08e29 100644 --- a/docs/chrome-web-store/dashboard-closeout.md +++ b/docs/chrome-web-store/dashboard-closeout.md @@ -5,12 +5,14 @@ not replace the protected package submit workflow. It records the manual Chrome Web Store Developer Dashboard steps that cannot be proved from source control alone. -The public item remains on `v0.3.2`. The exact `v0.4.0` package upload -succeeded in workflow run `29507382500`, but publication is blocked by -incomplete dashboard requirements. The 2026-07-16 dashboard snapshots show a -blank required `offscreen` justification, stale GSTR-3B-only text, stale -promotional assets, and data-usage selections that do not disclose local -handling. +The public item remains on `v0.3.2`. The `v0.5.0` package is submitted and in +review as a draft; it is not approved, published, or live. The exact historical +`v0.4.0` package upload succeeded in workflow run `29507382500`, but that +release was not published. The 2026-07-16 dashboard snapshots are historical +`v0.4.0` evidence: they showed a blank required `offscreen` justification, +stale GSTR-3B-only text, stale promotional assets, and data-usage selections +that did not disclose local handling. They do not prove the current draft's +dashboard fields. ## Source Inputs @@ -35,9 +37,9 @@ Official Chrome references to re-check before editing the dashboard: 1. Open the Chrome Web Store Developer Dashboard for the existing Pack item: `nfnbhekccajjfgkppolomflaeledoccb`. -2. Confirm the dashboard package under review is version `0.4.0`, source commit - `eb21404d274917876fcba20abce09216ce6348f4`, and release asset SHA-256 - `6ee4be24cafbe15db69275cac4da6b212f3de49b0f747eb9909eed7d293347c6`. +2. Confirm the dashboard package under review is version `0.5.0`, source commit + `985e9aa91d30e2955c996cd483496ebe92cfeef6`, and release ZIP SHA-256 + `1ecea75998ce69ae79caf8e6d27134516320a527d298ef164543cf87f6c07e62`. 3. Copy every Store listing and Privacy practices field from [`listing.md`](listing.md). Do not keep the previous GSTR-3B-only description or permission justifications. @@ -70,18 +72,24 @@ Official Chrome references to re-check before editing the dashboard: ## Status Monitor Closeout The read-only status workflow is separate from the protected upload/publish -workflow. Configure the GitHub environment named `chrome-web-store-status` with: +workflow. Configure the GitHub environment named `chrome-web-store-status` +with `CWS_PUBLISHER_ID` and either accepted credential form: -- variable `CWS_PUBLISHER_ID`; -- read-only service-account secret `CWS_SERVICE_ACCOUNT_JSON`. +- service-account secret `CWS_SERVICE_ACCOUNT_JSON`; or +- dedicated read-only OAuth secrets `CWS_CLIENT_ID`, `CWS_CLIENT_SECRET`, and + `CWS_REFRESH_TOKEN`. -Do not copy the publish workflow's OAuth client secret or refresh token into the -status environment. +The checker owns credential-form validation and performs only the read-only +`fetchStatus` call. Its service-account flow requests the read-only API scope; +an OAuth refresh token must have been provisioned with the read-only grant. Do +not copy broader publishing credentials into this environment. A non-strict +scheduled run without either form records an explicit skip. A manual dispatch, +including `require_published=true`, fails when it cannot authenticate. After the environment is configured, dispatch `Chrome Web Store Status` with: ```text -expected_version=0.4.0 +expected_version=0.5.0 require_published=false ``` @@ -90,7 +98,7 @@ requiring final publication. After Chrome publishes the item, dispatch the same workflow with: ```text -expected_version=0.4.0 +expected_version=0.5.0 require_published=true ``` @@ -100,7 +108,7 @@ successful package upload or a saved dashboard draft as publication evidence. ## Evidence To Record -Before considering the `v0.4.0` Store closeout done, record: +Before considering the `v0.5.0` Store closeout done, record: - dashboard listing text review state; - dashboard screenshot and promotional image review state; diff --git a/docs/chrome-web-store/listing.md b/docs/chrome-web-store/listing.md index 43ed6b2..4c181ed 100644 --- a/docs/chrome-web-store/listing.md +++ b/docs/chrome-web-store/listing.md @@ -10,18 +10,26 @@ Use [`dashboard-closeout.md`](dashboard-closeout.md) for the action sequence. ## Current Store State - Published package: `v0.3.2`. -- Pending package: `v0.4.0`, source commit - `eb21404d274917876fcba20abce09216ce6348f4`. -- Release asset: `complyeazepack-0.4.0-chrome.zip`. -- Release asset SHA-256: - `6ee4be24cafbe15db69275cac4da6b212f3de49b0f747eb9909eed7d293347c6`. -- GitHub release: . -- Workflow run `29507382500` built, tested, verified, zipped, published the - prerelease assets, and uploaded the exact ZIP to Chrome Web Store. The Store - upload state was `SUCCEEDED`; publish returned HTTP 400 because dashboard - requirements were incomplete. -- The dashboard screenshots supplied on 2026-07-16 show stale GSTR-3B-only - copy/assets and a blank required `offscreen` permission justification. +- Submitted package: `v0.5.0`, source commit + `985e9aa91d30e2955c996cd483496ebe92cfeef6`. +- GitHub pre-release: . +- Release ZIP SHA-256: + `1ecea75998ce69ae79caf8e6d27134516320a527d298ef164543cf87f6c07e62`. +- Chrome Web Store state: submitted and in review as a draft. It is not + approved, published, or live; `v0.3.2` remains the last confirmed Store + publication and the basis for Store-published public claims. +- Historical `v0.4.0` workflow run `29507382500` built, tested, verified, and + uploaded that release's exact ZIP with Store upload state `SUCCEEDED`, but + publish returned HTTP 400 because dashboard requirements were incomplete. + It was not published. +- The dashboard screenshots supplied on 2026-07-16 are historical `v0.4.0` + evidence. They showed stale GSTR-3B-only copy/assets and a blank required + `offscreen` permission justification; they do not prove the current draft's + dashboard fields. + +This section is the canonical source-controlled record for the current Store +version and review state. Release-readiness and dashboard-closeout documents +must not claim a newer state than this record. The Store-supported alpha claims remain GSTR-1 and GSTR-3B single-period downloads. Private GSTR-2B and full-fiscal-year workflows are source-build diff --git a/tests/docs/publication-readiness.test.ts b/tests/docs/publication-readiness.test.ts index 15b3042..3edd918 100644 --- a/tests/docs/publication-readiness.test.ts +++ b/tests/docs/publication-readiness.test.ts @@ -1,7 +1,6 @@ import { readFile } from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { LIVE_RUN_SENSITIVE_PATTERNS } from "../../scripts/lib/live-run-evidence-redaction"; import { FILED_RETURNS_ARTIFACT_TYPES, supportsFiledReturnsArtifactType, @@ -11,44 +10,66 @@ import { FILED_RETURNS_RETURN_TYPES } from "../../src/connectors/gst/filed-retur const rootDir = process.cwd(); const matrixStart = ""; const matrixEnd = ""; -const observationPattern = - /^(pass|fail|fail-closed-as-expected|not-applicable|not-yet-run); date: (\d{4}-\d{2}-\d{2}|not-recorded)(?:; reason: (.+))?$/; -const matrixSensitivePatterns = [ - ...LIVE_RUN_SENSITIVE_PATTERNS, - { id: "raw-url", pattern: /https?:\/\/\S+/i }, - { id: "matrix-filename", pattern: /\b[\w.-]+\.(?:html?|json)\b/i }, - { id: "portal-html-tag", pattern: /<\/?[a-z][^>]*>/i }, - { - id: "download-id", - pattern: /\bdownload(?:\s+|[-_])?id(?:\s*[:=]\s*|\s+)\d+\b/i, - }, - { - id: "page-or-dom-text", - pattern: /\b(?:page|dom)\s+text(?:\s*[:=]\s*|\s+)\S+/i, - }, - { - id: "taxpayer-name", - pattern: /\btaxpayer(?:\s+name)?(?:\s*[:=]\s*|\s+)\S+/i, - }, - { id: "session-data", pattern: /\b(?:session|token)(?:\s*[:=]\s*|\s+)\S+/i }, +const matrixColumns = [ + "Return type", + "Artifact type", + "Service-worker restart", + "Browser restart", + "Interrupted download", + "Cancellation/discard and cleanup", + "Retained checkpoint; browser record unavailable", + "Expected fail-closed / not applicable", ]; +const observationPattern = + /^(pass|fail|fail-closed-as-expected|not-applicable|not-yet-run); date: (\d{4}-\d{2}-\d{2}|not-recorded)(?:; reason: ([a-z]+(?:-[a-z]+)*))?$/; +const reasonCategories = new Set([ + "not-recorded", + "selection-not-acquisition-capable", + "recovery-scenario-not-applicable", + "expected-fail-closed-boundary", +]); const recoveryMatrixCheckboxPattern = /^- \[( |x)\] The authorised live full fiscal year recovery matrix below is complete:/m; describe("publication readiness recovery matrix", () => { + it("keeps status-closeout consumers aligned to the canonical listing version", async () => { + const [readiness, listing, dashboardCloseout] = await Promise.all([ + readPublicationReadiness(), + readFile(path.join(rootDir, "docs", "chrome-web-store", "listing.md"), "utf8"), + readFile(path.join(rootDir, "docs", "chrome-web-store", "dashboard-closeout.md"), "utf8"), + ]); + const submittedVersion = listing.match(/^- Submitted package: `(v\d+\.\d+\.\d+)`/m)?.[1]; + + expect(submittedVersion).toBeTruthy(); + const expectedVersion = submittedVersion?.slice(1); + expect(readiness).toContain(`expected_version=${expectedVersion}`); + expect( + [...dashboardCloseout.matchAll(/^expected_version=(\d+\.\d+\.\d+)$/gm)].map( + (match) => match[1], + ), + ).toEqual([expectedVersion, expectedVersion]); + }); + it("tracks every canonical offered return and artifact selection once", async () => { + assertCanonicalSelections(matrixRows(await readRecoveryMatrix())); + }); + + it("rejects every unexpected data row instead of filtering it out", async () => { const matrix = await readRecoveryMatrix(); - const documentedSelections = matrixRows(matrix).map(([returnType, artifactType]) => - [returnType, artifactType].join(" | "), - ); - const offeredSelections = FILED_RETURNS_RETURN_TYPES.flatMap((returnType) => - FILED_RETURNS_ARTIFACT_TYPES.filter((artifactType) => - supportsFiledReturnsArtifactType(returnType, artifactType), - ).map((artifactType) => [returnType, artifactType].join(" | ")), - ); - - expect(documentedSelections).toEqual(offeredSelections); - expect(new Set(documentedSelections).size).toBe(documentedSelections.length); + const unfilled = "not-yet-run; date: not-recorded"; + const unexpectedRow = [ + "Notes", + "unexpected", + unfilled, + unfilled, + unfilled, + unfilled, + unfilled, + `${unfilled}; reason: not-recorded`, + ]; + const matrixWithUnexpectedRow = `${matrix.trimEnd()}\n| ${unexpectedRow.join(" | ")} |\n`; + + expect(() => assertCanonicalSelections(matrixRows(matrixWithUnexpectedRow))).toThrow(); }); it("keeps every observation fillable, dated, and reasoned when required", async () => { @@ -58,36 +79,30 @@ describe("publication readiness recovery matrix", () => { expect(observations).toHaveLength(6); for (const [index, observation] of observations.entries()) { - for (const { id, pattern } of matrixSensitivePatterns) { - expect(pattern.test(observation), `matrix cell contains sensitive marker: ${id}`).toBe( - false, - ); - } - - const parsed = observation.match(observationPattern); - expect(parsed, "matrix cell has an invalid observation format").not.toBeNull(); - if (!parsed) continue; - - const [, state, date, reason] = parsed; - expect(date === "not-recorded").toBe(state === "not-yet-run"); - if (index === observations.length - 1) { - expect(["fail-closed-as-expected", "not-applicable", "not-yet-run"]).toContain(state); - } - if ( - index === observations.length - 1 || - state === "fail-closed-as-expected" || - state === "not-applicable" - ) { - expect(reason, "matrix cell is missing its required reason").toBeTruthy(); - expect( - state === "not-yet-run" || reason !== "not-recorded", - "completed matrix cell has an unfilled reason", - ).toBe(true); - } + validateObservation(observation, index === observations.length - 1); } } }); + it("accepts only closed reason categories", () => { + expect(() => + validateObservation( + "not-applicable; date: 2026-08-17; reason: recovery-scenario-not-applicable", + false, + ), + ).not.toThrow(); + expect(() => + validateObservation("not-applicable; date: 2026-08-17; reason: free-text", false), + ).toThrow(); + }); + + it.each(["2026-99-99", "2026-02-29", "2026-04-31"])( + "rejects the non-calendar date %s", + (date) => { + expect(() => validateObservation(`pass; date: ${date}`, false)).toThrow(); + }, + ); + it("cannot mark the recovery gate complete while any observation is unfilled", async () => { const readiness = await readPublicationReadiness(); const checkbox = readiness.match(recoveryMatrixCheckboxPattern); @@ -122,13 +137,70 @@ function recoveryMatrix(readiness: string): string { } function matrixRows(matrix: string): string[][] { - return matrix + const lines = matrix .split("\n") - .filter((line) => /^\| GSTR-(?:1|2B|3B)\s+\|/.test(line)) - .map((line) => - line - .split("|") - .slice(1, -1) - .map((cell) => cell.trim()), - ); + .map((line) => line.trim()) + .filter(Boolean); + expect(lines.every((line) => line.startsWith("|") && line.endsWith("|"))).toBe(true); + expect(lines.length).toBeGreaterThanOrEqual(3); + + const [header, separator, ...dataRows] = lines.map(parseMatrixRow); + expect(header).toEqual(matrixColumns); + expect(separator).toHaveLength(matrixColumns.length); + expect(separator?.every((cell) => /^:?-{3,}:?$/.test(cell))).toBe(true); + + for (const row of dataRows) expect(row).toHaveLength(matrixColumns.length); + return dataRows; +} + +function parseMatrixRow(line: string): string[] { + return line + .split("|") + .slice(1, -1) + .map((cell) => cell.trim()); +} + +function assertCanonicalSelections(rows: string[][]): void { + const documentedSelections = rows.map(([returnType, artifactType]) => + [returnType, artifactType].join(" | "), + ); + const offeredSelections = FILED_RETURNS_RETURN_TYPES.flatMap((returnType) => + FILED_RETURNS_ARTIFACT_TYPES.filter((artifactType) => + supportsFiledReturnsArtifactType(returnType, artifactType), + ).map((artifactType) => [returnType, artifactType].join(" | ")), + ); + + expect(documentedSelections).toEqual(offeredSelections); + expect(new Set(documentedSelections).size).toBe(documentedSelections.length); +} + +function validateObservation(observation: string, expectationColumn: boolean): void { + const parsed = observation.match(observationPattern); + expect(parsed, "matrix cell has an invalid observation format").not.toBeNull(); + if (!parsed) return; + + const [, state, date, reason] = parsed; + if (state === "not-yet-run") { + expect(date).toBe("not-recorded"); + if (reason !== undefined) expect(reason).toBe("not-recorded"); + } else { + expect(isCanonicalCalendarDate(date ?? "")).toBe(true); + expect(reason).not.toBe("not-recorded"); + } + + if (reason !== undefined) expect(reasonCategories.has(reason)).toBe(true); + if (state === "pass" || state === "fail") expect(reason).toBeUndefined(); + if (state === "fail-closed-as-expected" || state === "not-applicable") { + expect(reason).toBeTruthy(); + } + if (expectationColumn) { + expect(["fail-closed-as-expected", "not-applicable", "not-yet-run"]).toContain(state); + expect(reason).toBeTruthy(); + } +} + +function isCanonicalCalendarDate(value: string): boolean { + if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false; + const parsed = new Date(`${value}T00:00:00.000Z`); + return !Number.isNaN(parsed.valueOf()) && parsed.toISOString().slice(0, 10) === value; } From f091740151c6c1d71a0ca6653ef0c946be16c3a4 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Mon, 17 Aug 2026 21:43:06 +0530 Subject: [PATCH 5/8] fix(store): bind exports to source assets Signed-off-by: Tapish Khandelwal --- docs/PUBLICATION_READINESS.md | 3 + docs/chrome-web-store/assets/README.md | 15 ++-- .../assets/exports/asset-hashes.json | 9 ++- .../screenshot-local-downloads-1280x800.png | Bin 121545 -> 122028 bytes .../assets/marquee-promo-1400x560.svg | 2 +- .../assets/small-promo-440x280.svg | 2 +- docs/chrome-web-store/listing.md | 14 ++-- scripts/export-chrome-web-store-assets.d.mts | 6 ++ scripts/export-chrome-web-store-assets.mjs | 12 ++-- .../export-chrome-web-store-assets.test.ts | 65 +++++++++++++++++- 10 files changed, 106 insertions(+), 22 deletions(-) diff --git a/docs/PUBLICATION_READINESS.md b/docs/PUBLICATION_READINESS.md index 6089a51..14772cd 100644 --- a/docs/PUBLICATION_READINESS.md +++ b/docs/PUBLICATION_READINESS.md @@ -149,6 +149,9 @@ durable full-year, or stable-release claims. `v0.4.0` dashboard update were generated from refreshed source SVGs, visually checked, and recorded in `docs/chrome-web-store/assets/exports/asset-hashes.json`. +- [ ] The regenerated Store exports are visually reviewed by a maintainer and + bound to the exact `v0.5.0` submission. Source/export hash agreement alone + does not satisfy this review. - [ ] Upload the generated Store screenshot/promotional PNGs to the Chrome Web Store dashboard, then record dashboard image review state for the exact submitted release. diff --git a/docs/chrome-web-store/assets/README.md b/docs/chrome-web-store/assets/README.md index 40cc68a..f32312d 100644 --- a/docs/chrome-web-store/assets/README.md +++ b/docs/chrome-web-store/assets/README.md @@ -1,10 +1,10 @@ # Chrome Web Store Asset Sources And Exports These source assets and generated exports are the safe synthetic asset set for -the `v0.4.0` Chrome Web Store dashboard update. They use the current Pack popup -visual language as a reference while intentionally excluding real GST Portal -screenshots and taxpayer data. Store publication is recorded in -[`../listing.md`](../listing.md); these files remain source-controlled inputs, +the current Chrome Web Store submission recorded in +[`../listing.md`](../listing.md). They use the current Pack popup visual +language as a reference while intentionally excluding real GST Portal +screenshots and taxpayer data. These files remain source-controlled inputs, not dashboard-held proof. The asset dimensions follow Chrome's official "Supplying Images" guidance: @@ -50,9 +50,10 @@ dashboard review state in [`../listing.md`](../listing.md). ## Generated Exports `exports/asset-hashes.json` is the authoritative generated inventory. It records -each PNG's source, exact dimensions, and SHA-256. Re-export and review that -manifest whenever any source SVG changes; do not duplicate hashes manually in -this README. +each PNG's source, the source SVG's SHA-256, the exact dimensions, and the PNG's +SHA-256. The asset test fails when a source digest no longer matches, so an SVG +edit must be followed by `pnpm store:assets`. Do not edit the generated manifest +or duplicate its hashes manually in this README. For each Store release, keep the exported assets, package upload, privacy declarations, reviewer instructions, review result, and publication state tied diff --git a/docs/chrome-web-store/assets/exports/asset-hashes.json b/docs/chrome-web-store/assets/exports/asset-hashes.json index 82d388c..627bc77 100644 --- a/docs/chrome-web-store/assets/exports/asset-hashes.json +++ b/docs/chrome-web-store/assets/exports/asset-hashes.json @@ -6,6 +6,7 @@ "height": 280, "sha256": "8ab52389de440d2c3957922e9dcc0c766a06b767a5933c25ff11d09ed952e54d", "source": "small-promo-440x280.svg", + "sourceSha256": "5f3995d98f73a0e6258e2768917de4a6e65f9b8f6f7f0ef660ed085066bc190d", "width": 440 }, { @@ -13,13 +14,15 @@ "height": 560, "sha256": "6097bf88a0e482234b34b16dff17bdd53e1762a27ea9b6d3f51dc1852aba5c0a", "source": "marquee-promo-1400x560.svg", + "sourceSha256": "30b4603db304b54aac6e23fff212ef0b86043afbd7e944d962fcf79c0e263863", "width": 1400 }, { "file": "screenshot-local-downloads-1280x800.png", "height": 800, - "sha256": "4f3c4dbdb163022ba1d78ceb0ad9ff841a8885babf8d68a9ee82cb60be2d8acb", + "sha256": "aa9de58498f39d44044881daf4e189893411f833b910f342b36493b4af2f8ba0", "source": "screenshot-local-downloads-1280x800.svg", + "sourceSha256": "c9428696fcec974161b36d80515d098fb063b9cb963640951e64c01a041e6ec4", "width": 1280 }, { @@ -27,6 +30,7 @@ "height": 800, "sha256": "f764fa8cbe44e006ff65dcbdefaa3398b3a6538348fd491c26a670ebebd09bc5", "source": "screenshot-gstr3b-summary-pdf-1280x800.svg", + "sourceSha256": "b86a46f379c84392c506c5f1124b3d88f4ea006f0aa4eecf6df5b99e8addd18e", "width": 1280 }, { @@ -34,6 +38,7 @@ "height": 800, "sha256": "8ae1ff6df21a59fa4d88b85f82d8a785f2d04b0aaaa57ed091368c675856ae8c", "source": "screenshot-local-review-state-1280x800.svg", + "sourceSha256": "bad727ff699781a3fa39e4e09ccc1f369b5acf7de5af0da2378021a0eaee8f5a", "width": 1280 }, { @@ -41,6 +46,7 @@ "height": 800, "sha256": "cb6ed48ac4c38913115b8c24b4869c508926251ca9fbc9ea31f9b20f0c8d7134", "source": "screenshot-options-clear-data-1280x800.svg", + "sourceSha256": "9c13b9fa072e8fd1cc0e695f44225491711c5f5e97da452891433f83d2df09b0", "width": 1280 }, { @@ -48,6 +54,7 @@ "height": 800, "sha256": "a6d66974398fd9053b9199c6e5521740bd3e2b12e2c831b778e19097bef1ef43", "source": "screenshot-reviewer-demo-1280x800.svg", + "sourceSha256": "a300d88dc294c0684b7cc5d6a89572316a9535096a724a2064359e8e0387f840", "width": 1280 } ] diff --git a/docs/chrome-web-store/assets/exports/screenshot-local-downloads-1280x800.png b/docs/chrome-web-store/assets/exports/screenshot-local-downloads-1280x800.png index 8b041135ad5477d5bdd0287408ba86a1904b5af5..654814ff9b51fd449b1823d610bc4e66bfdad639 100644 GIT binary patch delta 74186 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�)>{PX^_f0`HaXfL=L!XUO&+WLnS#9`+7GmU*g> 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

ovee{A#jl>*%=<#y~GpFcKU*#ao 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 delta 73696 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 QVw&lJ$Zs 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%0AURn&E))+rA?cN`T}QFk4>C z6$O%fJo;O*4osxEPYHqKW|xg$u!E287lVu>)wtagT&>@G*Ia8>R&W<%OHXP^=vERu zPGc^6UIN1?7pavqO2tjUagi3PDGTvsea%9`#pH1#fusfgVU59IdCu3>qGrzcW&8fUuJ)bGofXT~qg zr<`q8W~Mu3N#kgN8kddV^@B+ttU6Gv8Gr}~|Dc#&(p2IUiRVi4QLpX@oazX;s^1erTu(a?kGWa6o%hqTix<$eiB#yj0fYW z>5hp}TaE!8Me})oVygR^@ar5cxh0zrnRnWb-D3)!KMfE2XKzOqOv*~f20}?{&hss7 zO3t-_D-J^YrN^rwDqX&#j(a3_4m;p`^kdk9b-hpXjmcy{FMNqYPZhfPL?VT4)GNE4 zyh-dPchC^-z}-^7viKmY^vdI4p|k13o1q26K17TG$XV-*^}@+?em6-?`=!hT_ve!4 z+&DIBwdbtb!xsJ71qTA=U981T!-!%&m(#|(IFAss3v-f^2j}&3!CTk`d__Q{cc%v* z*A*IMW-w2s*=t%NZCHqgE>XOhj-0yGm;F9627#p#aatEyx~MSjsF>bO3wkm23jzx> zLABIM@blCX9wv}1v`Cyx04K6(wuF5I^qMEMiS8%^Hg~UgOs6G~p5h*E(DIWb|-|sHW0aV>WoPMkT|I%z6;;>4^RjSM0e> zN$KB>L%7a-bmk5Cl}K=>W_ZK( zW<)%>_FRg^c7Vy`2S8u`7?yZ)arx-tTe;mn+-!f(H(ZKG$j2+|lkBoAD>v-==wgz3bK?!)T*2}bs;7zM^+!;oz$E^o9t7W5VzVcutJ>HHmU`K{PP?PKy9ew4ur?@Q!l4SI6aBS z;Q=7P5+HiCWCbX48@=e=bsK6BC85*hnoLT$e@MgGze^w&l)PZRy>gG4cT#x zy6M?B4G_WrID3Ul84vGxf5v?;nH6lbo0QM=$SS`?gD*^cQHx<^2aE8}!<>)rRA2-x zBdw=+HiLu`v0+HPGjzf{dKYkVy`nah;P8Z&-=JBfDa#M<)WB6WZ}m=TxSd>=E!N-F zr#2oJJ9}Q-P93X(ljs1~gam(jN{Nh$q;@ZF@LyM%d^A>deTz`l$c&bs+i>aOFt z#R}AIFi*PZaY0BPoDOfhgj3$rEVkeS{K854L`#BM|a90wyZ~OeHR0A2fbLHqo7hB;O?nxdV>Vnti-sOHqKK#*!@3L zy>}p-UC=kYNR)(#5`rLzh@R-3NDu@O(aRDhcn>ll?^PS^*K~Z@J=mU*n$ILv4TPOQti5&HG^Qr1JSv z?vZq!r{8}^|3&!(N=gU=RF=wmm^eWk=ozh!kK@QtLeOXW4t4&Zx!XGAbU&AOFdNdAMYA(Nr~> z;r_%}GPoM*y&KFVce1Pvh1gizJ`xgnO}>T{Q_u~nA|}49d7KKDOf{@0dqtrBRkmW( z&ek$Y&G?ge!p_OwLoOHpypDVaovF!=%Y)?}@-t7Wl8GRajBt4^@K*E1q$iN{{L>Mw z{>xFJCk13_R8)D8i=OJ9tJ1gjfRI#gzQv5s*?{%;K+T=m30Z#ST-$*x5%Y>D)*s-g zhRKOW62T8j8t@tSzQ9Mn3;xtR-4rzPz=j(^BH?#gR>TZI#eA^UmXC4-Qgs9M5Wu`6CDP~#=k3VR_ovk2_K~o(P+NeP}ht% z+?)wKr0DRDeY%9^l&GlOvMeMrGnlGBGn@-Ko2?m>m&-ldr?XWHu^!`-{WJ;ESE?qT zqm@Az1?0j&zOx-}{3vEzP{8|oo)EjzznmQ2)imQzp5Dt2w6V@jxGe*4cva~we!fLw zTS$|r{e3bUOjz{QOaXM)&|1GlrHhVLD~?(y&6>>dB*Vos&ON znS^EYTE?4M4XffDXWQ8kf<;q2y5q@xfj-HIE#2nNFS{D5ZvobLNNUuxDcxTG8Jx|# zb>+P)7q=eEfaSEKFKoV%d<%W?D#Pz(4azuI%7swnp_bRGA32pb99i+2Us}MeCwP$2($pd+eAxky{CVm zQ@rmzAO$-+UU_azvumJem0Ps2&p;mcD!Uq-({p@q{&mo5&+CC63CiDZK$|S0GZCEC z4?U!SE%l%Z?_D|Aobh`62mV+sqAW&0#{ID%m(n9}Of7k^yrU8htwQrIW#UsWECHW?^%nI>Fr zUCyX!*uE^GCi6%<`(vuS-eTe%#S6Ko9FMyWNZf6nN=IG9aYCg~f2I)4jkRoZkt06a zv@0dYuR6mLn^uaMOuMMcEu@OuSPF~CyQ$UVF^!9uiOTe*zon3(&=h?-hp!$jys9%| z@lc-PUwSW;@~V>5ntO0Fq@#>#pD?TSzD{@=Lh|i9JbySuNP?Qy?0clv4(FUzBztus z;KOcGBr8`V#9nP&t>VJmn$wi5G`n3x3{($sDEvB_2J@P~?CXSrMa%$_jg;SM^=FIX&I$0LhP6e$^qoPGT+_YT z5>n|eXEACkk$SJ%35JI5N&dxyp-&Wn1TYKO(~wG?_x{+55m)E(;=;Z`xo58ax4#v4 zRFV;K(!D-lQTrp+N)It{Y0sGlSK6Lz{m-8!E(DcYDti@IGxB1;z&+spx4%TTkmq-V z9Gh|z^&)LLy6*wz@_>kfeE+?O3K8r}E29zewsM?H%x#%T_DoJ$6GO7vSIl0dKx;*9 z52ji%`)N)3>(I$(?pM~)YVJ4(1Vhq&bKIn2PWfu--JKchIG(8v zE^~#ND*pHpiDGe*sU?$rTT?zwfaUzL70XkVSwtULzxng;U`4tgFX$WOd^oG{7lZ@^ zMbn;5a$mX)re4qNZ>n|V=w6)84P7I@$(6!7#k{7^iTFvy%<@;l|H3cF7_yArFdi6!4*OgiQl+D?d>*)!wfs4Hd-Lt8ZcdK&k=P3*ZAi1p;kJtD= zrJmPA?n!U&DKPr%?Td>FyF|=?&X!b_^k-e0C%8JBSD7lefH@AX+~3)ygL)$~(beBN z)eMj4uzTJ07YN0>hc#*fuA_Qleflrc`}tn#lQNcewO%Vldqvt1?{jr=23HBRw2$My zH|$E3E*;elPSV#)y8_U0j*Ly|EC;VTBj#4d$MO-?k%x^EXS=2VYOKyG*}1x!+XE6I z=Ipf>Emv#!xuuHn94;4AGh^47E!9Eu<*?=RTJ^4h5MnRr>Yy=>ad0E}a+Utm-t|N& z=_4U79Xj=iiSkb;uK)~O!O>{_2kxL>kf@Zyf-qf@B%&Ij1Js_TgVB7~+x(|9h+JT? z7dB#X^;s8MiEX6vX>9tc_4$8`1xs<%qQ8H#Co_EB7w&xff+usq;EX~W6UX^{aZcR5 zE7gryc3?ZvrgS~VuzKm$NXDU2kkLW235C#eXiQ{+1ZG1yz6phos2&lTv-kePmcQGy z1Gzo}oJD;OyVI|qU)RqNq+Ua=R9=xLt$G-1c^$N9IHMNQc2tF-xy?ftM;cQG^)82h z<9}qIUiNC&<9m6+9hPggc`=sx6sle;3!d??>xGQv_A`k~W&TrmI?UrLUAVu5#^V(5}z+mY0*DV+4NR)^HH` ze5G6?e@$#Qz3J%4T}^UOyL;MdOnA<7aW7Jo$BkKi;{23_^sv4v*+jpk^;Bxd)?Lgi zRPxIfD<^gT#*bLPS3!23<1Z>z&r|xh2Z9)Wdbcl5mFoPy;wF`{or+Oiw-MJts*3{X z9#;yGOlAP~2;`t>_NOd5iE@M>_r9}XrLBlTnB7Q1X?}HTRdHN{hm%$tW@;S7N?fQ2 z)wUSv8+A2iA)%3JCm7Akw(<6TYBa9nv#heE!WgY)d-3F|II3B&)o(cniy)p(d=5G6 z=B(6coX89kSG^}gf024OHIfceD+##%s!#B`?x2=DKrh*h~HD&p3TB<3SJm((DaUBY3rsB(RfC;NwY_ zD0v=HU8E~Lf3ku|_gN`PMgXmSwTl%V%Wc<}>j?zu*Yr|%@@LkN;D>x3{TJrS^~9s? zT_3++9l`8ZLQNwzhQfLwZD9kV1yUP>fHIE6nIBq&K+C*X838-jMo63zilfY?c)_PI z@U^Vv$5{qXM^RCd>z+~I9)sYhXsDNciz%Ge%C)+-y3_~`AKA}YT|ADrPvI;|%3VPZ zNj2{_yQK)_9!pqi5O`FQ~9vTAL zlHy%gQxK?`$RSMEUQ1Ex1aH3A@u;-u6#4bO|2LAAow5PUM2fN1TyNaE!dVBrSi;;2 zMQK#u{!`{R@OMU=LpLl?}cAa;sFElt%m%CfytrD*3UUBv~JyH~DN3>lEo;IJ9qh?)7b-Wh(Qm8=l9am{)Dx z`#vscWA$8lVh@MR7}OLy{2;Ro8hqUMPWA6}X*Dh|%=ijwqVvI4AXOJJlu@QH@A3TV|mv&Zo#OLZjo6O^{3jH=t%uCmaKxsw^{lppRXPiHf#Av zJv&zCVsLKsA>XGk0q6zhaIm&r_@~>vJ&zU5i=&@l%z6v>GNl95w-o4Tv<$O=Hm}!# zucZKvLaO$w4X;%6K+2FQ(CjoWPZyN8Wl$TjqB`HcaF$Yf*zC|@Aw3}Ly7rY+7o?v{ zFTS4xGaRr!4H=^EqP3dwMC!OT8FP&OhA+2g@h{uI@us0KgcKeLx*ZqLN1}<96@d89W|rNoSo9!r6eQhvrJV2ZlAmr^CwUE0Zu>L~c6>G)dl5o~ zuD?^LyVT~wn5WG)QqR!6?(jW`z4S#eWmZ1svhz2K!WFW~(M@D2P~UE0CyW={R|DI^ zLK#<-C*lgZvtLh9C#^T#y5 z%_QUxW|Vn1&iAAT(RZ|uc9WqiJI@ub*e>^yOei|95yG&)oIwC^C<$}Z`q0bjxEQsa z(3K+u_g1@yY&nE^N_niwrGp|0}`W%O}OJw z-rAa0%7j@#*Kx0tc3n`JiUzz_$*Axx8Gm$@-eSNLGxBw9nNL7Kp!st1m9W=Vm0&29 zqJ@2_16PE(_g|ZhzUR~gc%;`?krR(OLO;j9M;o6G71uNHg64Z3 z*$_(W^6MUCeAqsDy!@MRp_dh8_EwP)>{_<9@9FS-@(pZCiWlxvg#76gxAaO&H-0#Wb?3;O5-0L!H$_TCAJlf&<;bcr(Pr2CwdTqbq{Dz$^C5_e&Wp^>8~DTK zF5^dVceV83&sxGEV^CwQ$Wp7xO4pf_OK#_yaV5G{pWDh3x5=`<+!H0h z(IoZ_8Wm$;@ZQ;rx;1tSa+G)21%N=8^c&ErCL#Ar=GnYWAtg>t9rM6`iQ8gkKba&O zlPm{f86JhNwg}Q889YCDs5YPGX#m$gEB$JZR&$m!#-s-&(aX}9czI?H}l!0j;TtsJ1zG>QsftUVAh)7WXG7hrEZG4Y6(QZ7ma{ z?E6>QQ%p4e+)bA)+F7#q_S_iqQoWLYbeN3IGOw=|YP0W6@r@=Wmn3pl)Mc$Y=++%M z1cfiQy0?^1TBh2rfLuin0e_(G{3?|9@5udoHbv?K3q~t6aCda(Kz+oi*R)1s_B~qQ z>uoEcmA%_GMu_T=agpI*g#xuNgB!)A9*qrNo}x2CBvso?pYB9?Y1L!3D>b0=y-oj?K91}e46{x?=RNEq@K-A02-Fouzg42@YKcC z(c1;8&&J!G5f<4iy87rnqyn@_kTcD#$f^dCk-R^Kd#r0iQB^7X=~mfe#CXPsJ?m&| z5jo4*Sl!05!@rQ)GPjj6I*Bji4Y|tH{q7x#?aRFPP=nqFSgbL$ehUtPyszuktotCL z_iBY{@j~_OSNBLsfSIu$*+FMFgD$Sjg${eh)wxbf&$f4lZ|r%hNha>>8sng@b}gP^ zV!2U2hG@YdM?&&GA zKhSx#;R9S^p@GL=Eq(Esh%|}Beh_a$Q26!LDnSChqex_%2$6?MAdT}oB`?W<7+du_ z8$F506Vw%dr{MFCcq?z$`f{fd+gUwYT-A+ZgKXa8TP=EaHhlRO_T4LVVzVdtjp^^c zPK_3$t0CBx3+BaCx^tm|t%CLnRxoLv*UhH+Zvk;M*fsS4E#23R)F+j0^;^@vLh`m$ ztHGAzc@5n6sw7YOFp*Nz0tJDuj&0QzJsF`xuds*T<;kka4y}IKLbKgH8k1fh2AMW| z37>u7qNqoOdKIEN@*rQeTD^XUvf zPlk|UPMLXrro_x_c{_cMqK`oLze+G@vRx3@29{sP-&#h9G1D4L4{S~%VM55IS6yo9 zzZ<;{=J~mx24vF>5sv2wH9%(l!$x}xfV|Xiao|ztVQ4;%B=AAvvK7${Nf}Bx21BIi zsfyI=f1v7YaC-;eqFNx1!f^?;vLO) zI;~8v<2!$CteQyU`uLFAw5=oi^i#Wf!BEt|=}hDVqVKw8K$necnbvgqPxN_xG8BBg zo#LA2_TfTT)6R4CMd|#ZJv5wC`pinIVZ47XN$uXofvv*XEF1K^uTf3HfXfl> zN@FCioG6J&GE0P(ee+LiTUwEWa5RF|v09qIbFEs;1equ?crx4U>X>@q+*XTf(+uCPheDHfeTb(?~D(JwUe>}&?X?`(V;O{E?rIUZ2f0X@thUs)nJspQho zoXxhxP}h5ml=xE;9MHD!lag>M6McB>^l>gxQE^lv;q&@-V)i-BJclQ_cXlWu(j0hG z&@;ut=R;oMz2oQuhWr0pD|p3ozE387&Yq}j5_vg+kXKE5T_g0Vr@TF1s(Dd+P!l=Q z|7(03hh}0%;;P`s}4^MI{0MeHUIHi zZP%?hQ|1djJr~hi8pSVPYkQZ}vxNm-nPH252|V!S=@pUH4kh0Sk@PJrx0|@GvcH9-_IFxIqu{`Zp6ka(=?hR5;aV7)5j#vD+wpknkj9Mx^R$EA+aXxumfq9N?lFZA`^< z^oRpAKQ_FFuD0dpAVc`qNnT7UaA)BP-pJ;m^NR6WgW6=o84=3UTVTWdUI@eG7`SH! zf#FW?kOE)yZuV%Luhe`}58uf0K7ZN&lER2IQ*amN#B!@pg8i3|Me4x}7RxE;g~e)f z*&r77_e)5{-}lIO-||<8h~IXNNp-F9*$09dlP({P(+k7h2x*GIQol>?NqQx>M;^0= z7Iuc$z)gn6hW^g3^e+FVZt1U)I4`4*lr$L*{-I!SPSK(z`e*>gm)`a#QGd@p>5vOb zAAve{QJ^0X<#KoADOx<_|Mck4KfT+X!g@eq?a`NeR>Ha*4Vn8zlSk4cpzZg zubb-e-elf16&FRY=8jl_n{57!h-y07Jni)hF8GTF#RBe*su}a!C`{BcOW+ZIejVJn z|6L)6#~f!)K1Rrx$Q2@L(9^(E)s?ZsP);Newdh>hR4~w9aTJ_|DqRsXDC!F!5 zi^lEeW+{1@6UPNTOM@;-xJphRDLZjfB^J5Bor z8-9xj9e2)@CL!fH@1)N9CsrD0?l5M>p7qn4DxrIU2&ofIoy;-1BtSE6nO-~SCR`Gl`_7qf!6}(}=(3Z6~<;aBz*Op_DuZK*+koBZ9 zRZ9qWV6J(QPdcPC1YXOE(&H}qU=sJB0-K_pR1dPxet^72;@>J%@JHQW>C9tJgUpRh zuujla|-bY2=p_U0UvUsvN!=A~fu1{K7IeJh7ETCR*As>fFiJhj&2UFULe) z7;|9R;4*6AF-c%apUO>7A^a@GJJz>wKk+(_@r_R23>ow#{{&0RU)5)=I8v)J9z+pm%GvAN&zP9lbBzeUj5_j?3h+&=mGJeeJP7%kAiydPV& zYDhhyrsQ!0VkdB)*tqwnzNgTz#)wjwZ2^+4UrdO%dS$64Z%a?$W_^ENdmr?!Qo!C< z7=(8}B2AAvkUam4F`*3qp|2QA;q5n*M^VBm*36tNX56^NoY;6Y^MNiHhb~4a00hdI z9R3!(u6PSX$kJg)#Hy;+z_!gCYex>sU0v4Ab#>hz9OTWAtDZN<_4a^^w;Z?OE{oj=6^)L`>_>s!7qzF zU}}q)m0X_M_7EYBKA!Cr^1e`*`nPmzk{2>nwsPSt*p|0Kxpu;ACUQJN(0w&p zL$a;>KU+N%JtOdQH5caRAm55$YnsGXNWTl^n^7W8qAgzowJj5vB$P##)*pudKQYMqnYX#B_Q7&)>83}%E^3* zzl?i8O;CA7=Y?C&LbLCeKI~ET1<1|?|7~X$7!;Bb0DWV0hE^d}gjG3DO(L%Ztd)@N zLjSa3$q*0Nt$HqtwDrC^k5^Jb%yIte{#WMUiuD_<+SQZqe%BvAE^=q&AxNfUxbDYt zUgu9Y+>^U9>N!)~nWK#_LKs2sNMBcj9zY;>W@(sA$&77$`3)OStsZjFexsEivTn9` zuqOTn!L-dPsetVW!YY_P)vFz$DnbUZ&BK@2?_ZBT%S2~(y5&lzYiBjOb3%8f-QU~7 z?UTWcTR5N-Urw`r!B*9~*;X!GQx%%9v7>jEJF6-y>oHAneS|YKTebKPUti1-kBGcq zTGj!9um|K(O?}JTMxMOCJWqV3Il@h1vSP3=9**F-jN*Z|abA`7HX1qVgH-rynTm{5aYn^n;i26P9 zDEEhwcFrT1F5wg@sDL0#&$qDVO)ME7CC<}jnkA>b|KBL-tx0}kqV@~WIEgy9 zjHZ50G4_D|c@MBmV$=Pbe_A(<@n*&hf%jV>u`6tuF@FiLJ_$QAPCv&s>X%^aI>?`I zHDk^{bq$4cGg9gGhqBPPNu^!eTcUuF$5>}I=T_jJPc z$1@f)URZu;94S$RO;b4L6fwUDDVh?#-Ck*STVB za!#fa47i|i94rscwb*Jg70y#KKFqtk|M+||isf^aItln>tLO;f7QYk&$%qmnRo;9p z)^1R0RMSg)C~Y2KE9a}IjW6ot+MUTI{g3U~*BpjFvMxuuSV1!M_h&v5mfYbcXKsy? z{W;LgxB7UVDX0R2HD)a5U zbr1Xg>3`y6PKCbl@VOz_?yijB$dQ>1z7jUk7bH|7^Uwa@K}@;O;GL-eEmf&opaQ&J zD|cf1-j5un{O>9n@kIpbZq~)_AR<22iUO2zub?`fz{Zy|z&^>-TIl9~GFCYY-0Wjd zv82DV;f_rEMe_`p(Z#b3oQ0cwtFrwkyk|t}Jv>TbCH0l}CI6ALoF)IEM6P#|ICi%f z=V^YX+RW3NxqPFCIcMaEW|M}^c!fFsBNAGHln20UmaPNE_0={y(#LsMhfQk}@q# z_%;%;HGmZ`c&v|UNtXNDT;ZRZhgwC^Y2CfIus`I&ojbzZhzP_QjdkRR{QJ@~hs(oW zVJ-Qej-~4@-hzC6NFp73(1wI$6TruZ>bm@$$yt_4e}`_}c=jclsyDy?GakMzSM_IB z9+h}p0pv9Ma~eoncpR&=}XUpTlgcA)$1KT^#`5n0b1;8~-{ z8qTR#*tNo&212b(Qgj^Jj#;rG2tOT{{?V6bL;o!SU=6h4nO}E*3Kc`!MDI`z3Q0t|4WJGVj!tTrPNSb~gO+bI@hN z;Qsuq^G=o?IT*cko?4Sd{_zf01?A3X;R3czcOL$eim?;Z=ze@ubKMm7{p&aO^DPT@ z?>!ZSv0;>*{s>=fzEH09c+00EW2M?d7pid*vEmgeY|d(ayZ63a;Y+i%C& z=t-8~=+?Z`_m>GZDan5<>>U{Jz&wCh#cwiDj4&w;K9b0CL!B@4i-!AFuF3|<5YpLtmfUi{pFrdr5ryuOY0@hWQA?|LVt$LwX4l_3_?fO-IZ{ zO={6!{r$~clz6TTb$5Sw)c&dNsa|`#_48O3dTTO(;`ZOipP~5)*jn`0eS@J8%^l*t zq;gx{E8cOw*VPuj&;0T65JsI4f8I|)fY9kk9~}4mS(+jmB#>V(IU5_Q?+}mMCLMjM&f!)AesSw-bsOE%(f%eXnRbL(hnf8gANM0CAQ`cZ%uGHFRVI|hJp9fj>Vi^PIxZdoT=kIaH3Qi93?{&pxe8fC)E$%*B6!>OxV{DEl_p# zmt=hmxsRjDXfYGkpfXgtVdc!3M|7i78K8NSCA(MKhGgjq0CZ+-SI|>6RvvopoXyjD z_ua0uw;4)MMPvswH``E$2OkS1_=pqr;=INX%g{-QA)jgyJ5oG_s=fBF_PdOdC@V@J z@DC+(>x0&Zdc$!{1co$U-gGT4%!Gd*z7bQfU*ny<^|?rnkq6}vlbH{XC4bO{kE~^9 z44jw6v?K#D#fDSm#)rL7yQVWT-~Qp5iDu)&5J4O9{*z`y>sU=4k4bBi)!CprDtU;a zfV$|5xCdBA{uITrac98qk z;X;<~THbp1UN^GD)7k1hMjnF*3oJ`SleNx+z_leRNs!(pRuftEaJ2^1(3q#Kp+~O9 zxm@+6WwL!rY^G9K$$qBd=4Qisq-%8ZU{b$K0Jpx+``Rnm-3J(Du}?#hDmZjK9Fy`Ku4 zB~-bt?uuh|11HRL=I5%(juF#_^v>`2ml_}2THyYWDko17=x zg(ztJk(VitPSfA)Ku+%8Hr&Kd$KW>WONj2Q=no6p)E=)~Di=c8wE6fAs zdkNc&=9&F><7%{PT~viyza_!3LQnvd{vhxRTYj8>?u{yR4P z$*bh7{dm$4nE)LTP`Rg1V>ziu1aHcQuoe&ADu*O49QC>Qrg*37j;U5@IqETJn%%xl z{a~us{C2suA$!rwaMIizpP_q2|>!AAks@U-D4%J=~!V z5ZsvYIEp)|09(}iUzSid#;2!v<$x2&jJs-b3#&?ml!A?cyX!tXCkoYWdnGmjpZa+x zV@nL*l0LC0*2&*Wi?YX-Q-Y(-T&Y-Ey=w8O}q1m?Qs26@J*3QWIm`o!1~cCo%kNV*Y|fa=?P;hN{% zJQDHbE)76(WqBA9Nmcqtw8DIIDgL*Al0H63l(!a0W^Hp|V@|wbc2<0<))lG;&(kfO z3_I?n8?MW43}Ahnip?J4e^(4Vht7CB^sM(``(m@3tE(@b+c;6uoFegR_8A9MB|9vt zhwyWCjfx5hH(pYbC{HnsDfi?byc_A8J(>*Q0$AMVQg_x0?4kL>ClR*-v2_KJk|`IB z-e8`s!CJepseL(i219LI3ts!Ar)sod5r1AsKSKclcZu zSWh@#cu>POH5a#oa@(S1bNPiYDJUG0tijw6l0GibmLTuSrh>-(wLXRuaNk6IF@rt~ z0I|(8f#~kK>izAVzJU-;bVMMz6MC zQbrrqZBMb+hi%SU&HpUxvhOhIPb=a%imkPcRyVI{ye+uXu#+J9iG}v>uNqMnyJQP1`2jXM92$R6}fu zDrMuDTeZILby5oL^6iO9m`|6L0QKs0We#g+_1ulZhdy0{bC;}rYLg0VBwI(B+Q8~@ zijz%d>0BlGr{n2rbK$->Bde#qUhcEz8kb)s=wa`~kLR5mF)yr)&(NH(#p#H)yDBV{ zm-(|t{&wQI@^Rv`4YNmmn825+wZzLg>ad!lS`u?G_5wyJZ}Ar~nrCZ7v)k@+sO;V1 z*S`Gx`5h9XaE1_H67kz~nmCQB5MUf+Yk<-%7hIk-J2NX0IASmTws%325Qpt!7ffeU zj^8^LlB?atqYJI?_lCemcqvcI%{)AvvmV6pMOyldrl^*93IDR$QKFgmGS=>sC&@7r zcV(0_Kn>ErbfHc3_Yh8W&2>l+buovIpAGUJ*Ovv72G8L-lPy%_w< zHmaGhOw%qaioBH+Q1tn57jv?uK++0Mzv760!Z_*uUR*QnDsEp2HFI7HxA}79fY+cD zrcHu7=dM$307u7J394tLudgGxgC%&uMBo3a5&HTBv%hJFJw~glj}guxP?q+AmD%0y zSxq%^HGf@Nm`^z8qKbe^WmGaxssvbZUZ?8JDTRuw7a?uc5lQ&D_ooH5)GA0ClZ#U4 zKA8H&C8-9D0Y4xktOlC9V_+Y!$7Bl(*|f>#p`JplH4=pwjx zA+e)0+v#ivf7#|pJ$;t(bl!8IMzI;){~_Ll=;a7Eu4_S3WEp}Bx!{T{M;K^0D^x#` zJ=bGUp@_4Zsd%~0+-lI#CRGC^&ch0X$Vaxi6`bHR4#LzO6={$=hC^^*hXEYvi`!aT zN7oB-jeMt^yFbh5O@-zG_sB&D&&yBH0X{V(kcOE!+FU@s@7p?GYpKJv>5rr>2wr^O zW+bi4F?P4gI~BY@ysnKdZ+ekW4_+n<}t#fv17Q28{b`z7@lDC%!QU)U#R=t zEw?(p)g?fb3v-aLTzdHTHz~l1sRqlQFP8?uo+^Fp&@;VkxPH-B(e=df1Nwu!myRL3 zZ7E_RPNa;jd4K&E^p4Bl{(lL6E=2xoyz1!WrC*Nhmy@}Yi%^74})1K0=bj8gG`;5zc0Wc5f zwx@ty@$Pz-ReH82pOXmw56}fU`Q0f;i+a}-RD_-WjK{vEN_{pxEC!ZrR69B+HYo}A zT&>oBgrCXXl`yr5#;G3tMpkhK)N;PB~=d1sL0Mz0};`&$L;BC3#E{;gy;M!8T;I`EI7bHS%u)91YI z{xEEb6@Ilqjl+;f{Bg^ViQs%0;QbwG?Z^0^t)ge`yOdvz>Zh`dl{!^Z!pLE{2Mk(u zix_4uNz`S|A5ZVDYHA{MA0nJ|MXAVK+WN6Kj9$U{vSuOX&}c{BT(WGt+_c^&GSw&m zy_fk6kTQF~xmu^%n<1Trk(*LOUpO;^$aE2=NH=a$=2nO4cH(t_RDR)x4&D(g88C*+ z0-SS)HH)lOm)gN{_;FjzQ?4Y+gP7g-_+xb5FKIGIzdGo3*`IZoQx+*i)jwE`Bn?`l z*zSt^&IuE`>@e!-HOwGF?>}Z+D9T|ojO_2?@=xv5sx)9&KAWp?!0o&;f;DANAirQj z40|s6Z!RkVrX*lHllyM+ zQVgzRPkZEaA>mVtxe{!}Q!1dCKKZbcmyYjmw&uB0kBscV%re;jvs?`eKjf1Oy|59-?XW2j)4fiVX#GKhSQu zce#ufSvdh0`yU6Xuc=J+@$^+VpGvZ8H!?J_JW}MmjM)3*JTRy8{kZqo=(f$i`MQ0N zg4JChS~tnZ!Y0R6DfgE`W`^N!2u<*j3-7*;Hx*92JPp~>60NaqIk%&7!L1vr^lghO zNc4$pAtgNTb(E%R#x`TMFRh3Adb%QxIoMx~5=6+MzOJ#m@l1c5L)5-6>~u?stCihy z7qc2aY5vxA9NIehcEwBUC{~KiqExy?LJ=6uEr5vH_tlJH$5G?*U zlhaNL(A;=z=l}88@ya~oc50c&om@)rM7+TzUdaD85NsWT6|WQqVU8Ztqe3y34XF!; zzurJqPBCv^;s81q1zzMA{D+N8y@-S>r&6;M)#G#aw$gdsBd8OwPq;`A=}f$It%{T9 z&Fd&L9ehwf8baZ!nXSd#I#_!Y!_XQA>T7T=oStd>zp;MGd7Ky<+iWre2B&@>xBM)e z5w18Y7kk%9vj#t24c**!k(#s+WZZbJB=A!fvpO@?`&;Q1(^}8PgHiCMC6W`7u>Ar1 z#kVQ!wZ;2kU9@>GJazM^DUUr(o3zM}6v9w05B44QvJ2J@J3V`Rnhd+$>(|(;8Dv>n z+e9+xNcL;VmY&G5x71CN((v5`eb)aQ^szn%dt;bai3MF;qjE}PW#{oF4WVT1A!xmN zK0v87KRw;x!vw1yjp9|cxdQWxR{>gH(_SQnG4emXEdi<9m%1oJJ}i{^bD`Y$Q4{u* zc3fQcr+%svXEQ}U$*L*5qh|4~DJt+C!qR=Ep4&fVz@#B|p%~6bSZy9`r^xX>T~4tv zgs{^r2>SZgbooSy0N30pH4V7nutT==uy^<{vz@feB*7$`!+5GFoYoiHXR}lDxj|kjfbBlP zQ{neHr+>x;+g)hcvU2I{#5n-v--JJD87J@N5mgT~_w#W!TTI7Qs^iJc^pu*NLCX;p zxK{I`)T@b_OjWIh>`w3oeREpoox$peT+YAUb|jb`PjpK-x5|p<(|$VFKT;7Oef_y- zSf^q;Z9W%J=xdlz1I-pPUg<5RpW8iqomXxc57wT%^f)~!B{`M;GrDSJcKt+aRqmE2H5pWbGOE z=i=cb^eOiY4>#_khD~84+0qBI=I8hi&G@Y`1VXztRLrw0*8C@18J_OwbLpb*|~pElj4> z!E5`D`+PeQ{1qSeJ#udc+mJYY>7|%xJ0DFjwZT5gVYAATxaW-NX~m^1V;jhZWGO_Nz0uSw+EK?md?G1k) z_gGFjx^ztgTUEtGVxDo+ZP3A$mHUk0))_qwdSMrA6L``XIh6BF%7OR~#@O`odMX}n z?E$S4X?0#rTLYG5$*_INEE+KhF+d5fC0vVi{Z0v!+zug6JQbFlFoO85dpft6yI(MAAN+Ovl{O-oJaMs*Zidb*D4t(ly1o zH1RC&m(SkOGi)YhFr}fYf{MpY+eD6zLWK>7&f@cq?u7gqXMchF_RBEuECnsjsaI8a zKq>CJJpl6aOGwvm0xq(}z5d zMw|gDW_2m8A+AGrnjfq8rFS`I3jB_3`CcUvAuLyzT5w?V=a{7Nn8L-`DN*Q~*vO|J zdrk94-rN7y*{!Xup6&SHAh};_=KZ8Y&1Fd9{Dq~0J9+e{TOxV)Z}(9r$&|2#q3Df5U+A8rkVFCyf9Y++7lPKM?c8e5|g7z zdqmE6a0B8inYkaXX-+)NiZdhzvWU5LDXrvvH7#P7?I0}MnyomIQU$qk`QHNC`O8eG ztHS9(SO+EbKMu+s`Bd>@SjT3w#jg&E%Gn&2IH@~_yph5>*mf=3`tM4nKQqyHemr5c z51Sg)2!3LI7cNY%kJ#I^q&8Y5x+TGUMK*l+O{u4_?!ArgN+=x=H;sDOpy8>XtlLic z(C2v(gU@;Bakk!4srG+5MDwh^o;_t@a|9prMEi-yrOOAULr1PRBAmo;Lp{x?C13t47F$>nkj z$@XJxW87qoP-YmrLZv9zAj(pao$j?|Z0YJwRMy@hz0cq0Jm2R$=Q+e|NH)J^Y`^O1AMP<|CiN zLbGKgHC8bIww^K{981W5i=Lio4Ucv`A$48i%H_vzSrfqD_@OQw`BEbD7OCZtcK`e2 zUDY`FABq9*uKR2Jz>b~^z8saZZq=REUxlRy;24nl$Ju~7$E5<+zchh?C#Q5oT+js7 zn0Vn?@#jL;u3a=ZXK`%F`s-)Li+eQ_Vk#os7nB z0yoib7sCcWRbNx*K-8A;e|}NixqlP#+P?{X94ekYp@k{9SE`R&P7;iM_>kw#E3cs? zslnn~caoYL<=n|Zo7?Ym6O^T6>kLoB4*T)N5c5s_CXG7XI?1nX) z8Ih|k+y(Lc)tajU$}-Lw$PwI<@$Ul$VyH9r6|={+BS->#XZ9<^pkW@|Uv*hSqPc_y z%OjL~DD0CmHFihQn{1)f#rN+=vqLgpg&A<&O}>wgpr*qh-aQG)x3c;hh`+`0zzd2v z<%vP49j0ua3^iR_mRe)HIy zKAWpH!-QQ{mLZShuE#?Dva(Uev%213%-!dudNf!rOe_MvEHvnl~lzFqt!X1*eW@D!aT2V z*Oxqd_mZ^xV;e*m=_h2GoDi*&S3F4Nv z>;CW6*HODYwxUH%GX_W^d}oA9BHVL=Rj?Unt$oqQNtlN1GunERcP-|zv~!U&EsbSiYI7d5_UA56*_w%pt#8N4$t-&+Yf zHBu*_PqT^?88V%^HTX~@*ysU|-_o1Q_MH>C*LrW}ob)Kb^N6kR#wz!leKQNae{Ka6 zDJ<}V_YJ@*vlmK)A-peJQd5Z1dt!#H3ij4|DR`#t8~~h=Yg}`3H)vOL+1!-J9A(`u zBELp)d2M__&5xNI6!)>1tBmMOuj2LG=SPG&ttG<$&Pn=xKE5{<mahb_ zImkZKhw*-XnGEnH1R`sr%kEkG4?h*EZX%wGFPG(6JPTLRxI~$pLU68G?h)L_o-FI1 z_FT)PyI3U)b|tWut&Z1ORw-@3&-(*1QJS?Gmu>3r(sG!2ZjqxLQBmH03;V;_1LluT zq4-f!W5pMdZ~HQEHwL3NrX>56t78Arp0Y5CFo-;HeP^Doa8aOy)QPmuglHfk?Ay25 z9I2x0mfsnoH@)b&5i^wX@&$zt@@|XQl#0#DeiJ?jI}4-F@VGrQM&h3^T(Y~?-sKkm zlVf*3u69pEzL#rBeL?s7=VK#)`g;wcxm`1Gf6b=UuK9Jf6~Gr_lGyz3`G_&_wGk^m zm42+7*s2?}_IueI!*kh>?{)@$?jL&)FVKoE)q$JYFXLAoixB;kHTZ?5y{#X^A~P^prvQ9J?#06aa{+xm#DniVUDh4^3RYUFbw1}RS4d8|ZAqB^Bg!Y4 z`l4tlrxrVpQ!{@Kw=Il*@z@hPX5-~~zSVs}9HJQng34mdjGdHz-w3{==%5uN$Ogw;0*q^>(aP~0*(DgeM77Fzk+W31gRh3U z5Ma|U|^HYb|6q_@UGZlBvq2FE8T z$D7`?;&NwgMf(Fmm=-9@eMpeXfUbBkvEIy@X{j$jn(l@~ z!3C1w@x4#m)io3loRAh_O6Qvx<7H35dMvV?&lDdoSpVB}9?E6egWvv0;mA6m#mES) z=YT5Ri0{rLOt|V_ec;*20@NOR?}8N#pIaQ8ohCm~MM3bm*QNI>^JVJQ*^mQm{9i-; znLYSL>pQ2+Dd3cYIRHQ)nG2nr?xhQ=7Y6f4J0_>9w)jr_M9xk-CH^ro#bG0e9pCjo z62H;bvLwd0I??-QOAYZahlv|ULz){?QAP)&1I6{?T0MAKqp}{7Oj`CLJJZ{vO{RAx z9P3+Y57~7C;Gsq9&cwEAutIRHt`kR!?0K$iogd9{LM}j`o%uZ$i~K;0f&^enCT6M} zZzuE;kHm7k7f`%0QYXI0C#7Gl;dFzLn+}4Tza^$mYU&D}X%cIqfWIo#*WV(Xl~xXP z2+feSJMzx^P*WKSD?v8<80go1%3~=@=zE###>bZ_tU$g0IoQ3UY0i<7#>bvVi3j|^ zuxCQLp+WT$0_FCalF|-@~Ssj zl1et+iPfAo#TZE*1PzNW1I%R7XmZI!qFoP&NKNL{ky-)3+dd2~Km{4jGLkuGkk(=l{_(9zI3DDVr6r zH&J@|Y>%O0IK=a5+I%h2y0Wi7X10r*$+WMR|AlYB=ks~C7I4mLcrHIwQ96j zYn~tFH8;0^=YDKeZ>8(Sp(j1EXktC*ry)|6X#FQhxx(}pwsV$c9Oh1cf!UVBjg_bP z)5?~;>`jklLZXSR@QhVySvutb&8rGCHRk+3q8~Qb*LX&Nu2gv`&54fTcDdA>xV~&! zZ`LVS2Pmn0wWf;aD}`b-Q2p&CMmkAo0vW=Sr=BXvw!cNOWSCu0T=%T;YY)HTb>v!! zH(EZ@^p;R|>s6Y1+y~R=i~#%)Qx&RU4%SOdPU4sw^?o}z$}2a|^RsuQcG;D#8M_8y zDvwzVN`MuK3>&}7X{`STG##7@lrYpLIv`;%Ob)*y)dL;LaOi;Ja^%^M?lL{c%>6((|qbcj*5bf9iWu2B*v$(MqBc2nAVM6B>ul! zxe|gRUJ;*`*Xm@`@)SMmUEK@w$3Vq(2h(r2!(632>{dm8B7g$2eIL2;bj71g+Tm3Z zeK@*E8Iiqd#%zfwo=-$f`|J5Sgc?B|DGx4|w736Eh^)Q;lW?hb2VmL`(+_SwyKLQ6 zGfgJf^;*PP546GT@8M>>$(il2io_oH3z%m!gU-vMtRG0VIpHp*;hAg2qM*zd1i>l zcq~xPvih4v3=M*`A#D7P@kh^n8xU^M_yvYG#?5t2A}s*;v1V1o;Dt#c#_)0)Bm^y< zPagS86FdHO>uNf}bx%l5_t3mbfv&1ooJW(&=W;`s(OIj`&@F%N&>r!8SubN4G6CfR zjdDmH4K_n)%87$Oto#cCWnjp_ti90X9z6wh3*^#soi?bBhKNH8^(|#dbt&}oJTLVy zn3gI?+MOZK2J^P;cO9e+Ih9kv=~y^}rV%4ief#(voezm=I83G#_Z%6yw2>FM|G@ujX;h9< z3cwEi$&VFs&sG>@&jlWjDFi zqw#`cW|zBQTb7VX`%3Ge(#qx_&PeJoojYiII>b?fm; zZ#q>b3*c@1SGW>+SVZ)nVubUxvhgzoS)&C74nbICRySVROBj%z5%kb1Bj@h*`3W0t ze2SCQ_rsY2$t1@<^3i_J?%MS&+vw=8mRHutF3WF7v#rXlA4x#7R9YPXvAzqE3ZaX% zQDHk*s2PX;+=aHRSuj}NOMrg-$h^Or%4!MTKR~gl`NP&|TR7z9eqW5ao!J8uJn?^C C&59NP diff --git a/docs/chrome-web-store/assets/marquee-promo-1400x560.svg b/docs/chrome-web-store/assets/marquee-promo-1400x560.svg index 99a5081..38d1887 100644 --- a/docs/chrome-web-store/assets/marquee-promo-1400x560.svg +++ b/docs/chrome-web-store/assets/marquee-promo-1400x560.svg @@ -1,6 +1,6 @@ ComplyEaze Pack marquee promotional image source - Synthetic Chrome Web Store marquee source for Pack v0.4.0. It shows no real GST Portal or taxpayer data. + Synthetic Chrome Web Store marquee source for Pack. It shows no real GST Portal or taxpayer data. diff --git a/docs/chrome-web-store/assets/small-promo-440x280.svg b/docs/chrome-web-store/assets/small-promo-440x280.svg index 736751a..9be1a19 100644 --- a/docs/chrome-web-store/assets/small-promo-440x280.svg +++ b/docs/chrome-web-store/assets/small-promo-440x280.svg @@ -1,6 +1,6 @@ ComplyEaze Pack small promotional image source - Synthetic Chrome Web Store promotional image for Pack v0.4.0. No real GST Portal data is shown. + Synthetic Chrome Web Store promotional image for Pack. No real GST Portal data is shown. diff --git a/docs/chrome-web-store/listing.md b/docs/chrome-web-store/listing.md index 4c181ed..2a20640 100644 --- a/docs/chrome-web-store/listing.md +++ b/docs/chrome-web-store/listing.md @@ -161,9 +161,12 @@ requires disclosure: Use the seven generated PNGs under [`assets/exports/`](assets/exports/). They are generated from the source SVGs, contain synthetic UI only, and exclude GST Portal screenshots, taxpayer data, -real GST filenames, local paths, and downloaded content. The committed hashes -pin the reviewed dashboard bytes; regeneration on a host with different installed -fonts can produce different pixels, so upload the committed hash-matched exports. +real GST filenames, local paths, and downloaded content. The committed manifest +binds every generated PNG to the exact source SVG bytes. Regeneration on a host +with different installed fonts can produce different pixels, so upload only the +committed hash-matched exports after a maintainer has visually reviewed them for +the exact submission. The regenerated `v0.5.0` exports have not yet received +that visual review. | Dashboard slot | Export | | ------------------ | ----------------------------------------------------------- | @@ -176,8 +179,9 @@ fonts can produce different pixels, so upload the committed hash-matched exports | Screenshot 4 | `assets/exports/screenshot-options-clear-data-1280x800.png` | | Screenshot 5 | `assets/exports/screenshot-reviewer-demo-1280x800.png` | -The asset filenames, dimensions, source files, and current SHA-256 values are -recorded in `assets/exports/asset-hashes.json`. +The asset filenames, dimensions, source files, source SVG SHA-256 values, and +generated PNG SHA-256 values are recorded in +`assets/exports/asset-hashes.json`. ## Dashboard Evidence Boundary diff --git a/scripts/export-chrome-web-store-assets.d.mts b/scripts/export-chrome-web-store-assets.d.mts index ab17f83..20c897c 100644 --- a/scripts/export-chrome-web-store-assets.d.mts +++ b/scripts/export-chrome-web-store-assets.d.mts @@ -4,4 +4,10 @@ export interface ChromeWebStoreAssetDimensions { width: number; } +export interface ChromeWebStoreAsset extends ChromeWebStoreAssetDimensions { + source: string; +} + +export const CHROME_WEB_STORE_ASSETS: readonly ChromeWebStoreAsset[]; + export function assertOpaqueRgbPng(buffer: Buffer, asset: ChromeWebStoreAssetDimensions): void; diff --git a/scripts/export-chrome-web-store-assets.mjs b/scripts/export-chrome-web-store-assets.mjs index a0b237e..9d8d695 100644 --- a/scripts/export-chrome-web-store-assets.mjs +++ b/scripts/export-chrome-web-store-assets.mjs @@ -7,7 +7,7 @@ import { chromium } from "@playwright/test"; const SOURCE_DIR = path.join("docs", "chrome-web-store", "assets"); const EXPORT_DIR = path.join(SOURCE_DIR, "exports"); -const ASSETS = [ +export const CHROME_WEB_STORE_ASSETS = [ { file: "small-promo-440x280.png", height: 280, @@ -68,10 +68,10 @@ export async function exportChromeWebStoreAssets({ const exportedAssets = []; try { - for (const asset of ASSETS) { + for (const asset of CHROME_WEB_STORE_ASSETS) { const sourcePath = path.join(sourceDir, asset.source); const outputPath = path.join(exportDir, asset.file); - const svg = await readSvgSource(sourcePath, asset); + const { sourceSha256, svg } = await readSvgSource(sourcePath, asset); const page = await browser.newPage({ deviceScaleFactor: 1, @@ -91,6 +91,7 @@ export async function exportChromeWebStoreAssets({ height: asset.height, sha256: sha256(buffer), source: asset.source, + sourceSha256, width: asset.width, }); } finally { @@ -119,7 +120,8 @@ export async function exportChromeWebStoreAssets({ } async function readSvgSource(sourcePath, asset) { - const svg = await readFile(sourcePath, "utf8"); + const source = await readFile(sourcePath); + const svg = source.toString("utf8"); const width = readSvgNumericAttribute(svg, "width", sourcePath); const height = readSvgNumericAttribute(svg, "height", sourcePath); if (width !== asset.width || height !== asset.height) { @@ -127,7 +129,7 @@ async function readSvgSource(sourcePath, asset) { `${path.basename(sourcePath)} must be ${asset.width}x${asset.height}; got ${width}x${height}.`, ); } - return svg; + return { sourceSha256: sha256(source), svg }; } function readSvgNumericAttribute(svg, attribute, sourcePath) { diff --git a/tests/scripts/export-chrome-web-store-assets.test.ts b/tests/scripts/export-chrome-web-store-assets.test.ts index 3fa3655..17ddd2e 100644 --- a/tests/scripts/export-chrome-web-store-assets.test.ts +++ b/tests/scripts/export-chrome-web-store-assets.test.ts @@ -1,10 +1,14 @@ import { execFile } from "node:child_process"; +import { createHash } from "node:crypto"; import { mkdtemp, readFile, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { inflateSync } from "node:zlib"; import { afterEach, describe, expect, it } from "vitest"; -import { assertOpaqueRgbPng } from "../../scripts/export-chrome-web-store-assets.mjs"; +import { + assertOpaqueRgbPng, + CHROME_WEB_STORE_ASSETS, +} from "../../scripts/export-chrome-web-store-assets.mjs"; const rootDir = process.cwd(); const createdDirs: string[] = []; @@ -26,7 +30,14 @@ describe("Chrome Web Store asset exporter", () => { const manifest = JSON.parse( await readFile(path.join(exportDir, "asset-hashes.json"), "utf8"), ) as { - assets: Array<{ file: string; sha256: string; width: number; height: number }>; + assets: Array<{ + file: string; + height: number; + sha256: string; + source: string; + sourceSha256: string; + width: number; + }>; }; expect(manifest.assets).toEqual([ expect.objectContaining({ @@ -81,6 +92,56 @@ describe("Chrome Web Store asset exporter", () => { }); expect(readPngColorMode(buffer)).toEqual({ bitDepth: 8, colorType: 2 }); expect(readNonWhitePixelRatio(buffer)).toBeGreaterThan(0.1); + const source = await readFile( + path.join(rootDir, "docs/chrome-web-store/assets", asset.source), + ); + expect(asset.sourceSha256).toBe(createHash("sha256").update(source).digest("hex")); + } + }); + + it("binds every checked-in export to its current SVG source", async () => { + const manifest = JSON.parse( + await readFile( + path.join(rootDir, "docs/chrome-web-store/assets/exports/asset-hashes.json"), + "utf8", + ), + ) as { + assets: Array<{ + file: string; + height: number; + sha256: string; + source: string; + sourceSha256: string; + width: number; + }>; + }; + + expect( + manifest.assets.map(({ file, height, source, width }) => ({ + file, + height, + source, + width, + })), + ).toEqual(CHROME_WEB_STORE_ASSETS); + + for (const asset of manifest.assets) { + const source = await readFile( + path.join(rootDir, "docs/chrome-web-store/assets", asset.source), + ); + expect( + asset.sourceSha256, + `${asset.source} changed without regenerating its checked-in export`, + ).toBe(createHash("sha256").update(source).digest("hex")); + + const exportedPng = await readFile( + path.join(rootDir, "docs/chrome-web-store/assets/exports", asset.file), + ); + expect( + createHash("sha256").update(exportedPng).digest("hex"), + `${asset.file} does not match its generated manifest digest`, + ).toBe(asset.sha256); + assertOpaqueRgbPng(exportedPng, asset); } }); From fa6ab92ddb83bcca5188d35196a41a43b979b9ac Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Mon, 17 Aug 2026 22:09:28 +0530 Subject: [PATCH 6/8] fix(readiness): validate whole recovery cells Signed-off-by: Tapish Khandelwal --- docs/PUBLICATION_READINESS.md | 38 ++-- tests/docs/publication-readiness.test.ts | 258 ++++++++++++++++++----- 2 files changed, 234 insertions(+), 62 deletions(-) diff --git a/docs/PUBLICATION_READINESS.md b/docs/PUBLICATION_READINESS.md index 14772cd..85055a1 100644 --- a/docs/PUBLICATION_READINESS.md +++ b/docs/PUBLICATION_READINESS.md @@ -166,8 +166,8 @@ durable full-year, or stable-release claims. - [ ] Authorised live full fiscal year run reconciles every eligible target as downloaded, positively not filed, blocked, or failed in the local ledger. - [ ] The authorised live full fiscal year recovery matrix below is complete: - every observation is dated, no cell remains `not-yet-run`, and every - `fail-closed-as-expected` or `not-applicable` entry has a reason. + every observation matches a completion-eligible row in the cell legend, + and every recorded date is valid and no later than the current UTC date. The selection rows are derived from the supported Cartesian product of `FILED_RETURNS_RETURN_TYPES` and `FILED_RETURNS_ARTIFACT_TYPES`; a test keeps @@ -179,18 +179,28 @@ path must not repeat a completed target. An unproven path remains non-complete until retry or cancellation. Manual observation is only an explicit non-completing action and still requires retry before ZIP staging. -Use only `pass`, `fail`, `fail-closed-as-expected`, `not-applicable`, or -`not-yet-run`, followed by `date: YYYY-MM-DD`; use `date: not-recorded` only -with `not-yet-run`. A reason is required for `fail-closed-as-expected` and -`not-applicable`, using the final column when the selection cannot reach an -acquisition checkpoint. The value after `reason:` is not free text; use exactly -one of `selection-not-acquisition-capable`, -`recovery-scenario-not-applicable`, or `expected-fail-closed-boundary`. Use -`reason: not-recorded` only with `not-yet-run`. If these categories are -insufficient, add a category through review before recording the observation. -No other cell text is permitted, so raw portal URLs, filenames, download IDs, -page or DOM text, local paths, and taxpayer/session data are unrepresentable in -the matrix. +Every cell must match one complete row in this legend. The test renders the +legend from the same rule table used for validation, so state, reason, date, +column, and completion semantics cannot drift into an independent vocabulary. + + + +| State | Date constraint | Reason | Allowed column | Completion-eligible | +| ------------------------- | ------------------------------------------- | ----------------------------------- | ------------------------ | ------------------- | +| `pass` | valid `YYYY-MM-DD`, today or earlier in UTC | none | scenario columns | yes | +| `fail` | valid `YYYY-MM-DD`, today or earlier in UTC | none | scenario columns | no | +| `fail-closed-as-expected` | valid `YYYY-MM-DD`, today or earlier in UTC | `expected-fail-closed-boundary` | any observation column | yes | +| `not-applicable` | valid `YYYY-MM-DD`, today or earlier in UTC | `recovery-scenario-not-applicable` | scenario columns | yes | +| `not-applicable` | valid `YYYY-MM-DD`, today or earlier in UTC | `selection-not-acquisition-capable` | final expectation column | yes | +| `not-yet-run` | `not-recorded` | none | scenario columns | no | +| `not-yet-run` | `not-recorded` | `not-recorded` | final expectation column | no | + + + +If these combinations are insufficient, add a rule through review before +recording the observation. No other cell text is permitted, so raw portal URLs, +filenames, download IDs, page or DOM text, local paths, and taxpayer/session +data are unrepresentable in the matrix. diff --git a/tests/docs/publication-readiness.test.ts b/tests/docs/publication-readiness.test.ts index 3edd918..9dbeaef 100644 --- a/tests/docs/publication-readiness.test.ts +++ b/tests/docs/publication-readiness.test.ts @@ -10,6 +10,8 @@ import { FILED_RETURNS_RETURN_TYPES } from "../../src/connectors/gst/filed-retur const rootDir = process.cwd(); const matrixStart = ""; const matrixEnd = ""; +const legendStart = ""; +const legendEnd = ""; const matrixColumns = [ "Return type", "Artifact type", @@ -21,13 +23,67 @@ const matrixColumns = [ "Expected fail-closed / not applicable", ]; const observationPattern = - /^(pass|fail|fail-closed-as-expected|not-applicable|not-yet-run); date: (\d{4}-\d{2}-\d{2}|not-recorded)(?:; reason: ([a-z]+(?:-[a-z]+)*))?$/; -const reasonCategories = new Set([ - "not-recorded", - "selection-not-acquisition-capable", - "recovery-scenario-not-applicable", - "expected-fail-closed-boundary", -]); + /^([a-z]+(?:-[a-z]+)*); date: ([^;\s]+)(?:; reason: ([a-z]+(?:-[a-z]+)*))?$/; +type DateConstraint = "not-recorded" | "recorded-not-future"; +type ColumnConstraint = "any" | "expectation-only" | "scenario-only"; +interface ObservationCellRule { + columnConstraint: ColumnConstraint; + completionEligible: boolean; + dateConstraint: DateConstraint; + reasons: readonly (string | undefined)[]; + state: string; +} +const observationCellRules: readonly ObservationCellRule[] = [ + { + columnConstraint: "scenario-only", + completionEligible: true, + dateConstraint: "recorded-not-future", + reasons: [undefined], + state: "pass", + }, + { + columnConstraint: "scenario-only", + completionEligible: false, + dateConstraint: "recorded-not-future", + reasons: [undefined], + state: "fail", + }, + { + columnConstraint: "any", + completionEligible: true, + dateConstraint: "recorded-not-future", + reasons: ["expected-fail-closed-boundary"], + state: "fail-closed-as-expected", + }, + { + columnConstraint: "scenario-only", + completionEligible: true, + dateConstraint: "recorded-not-future", + reasons: ["recovery-scenario-not-applicable"], + state: "not-applicable", + }, + { + columnConstraint: "expectation-only", + completionEligible: true, + dateConstraint: "recorded-not-future", + reasons: ["selection-not-acquisition-capable"], + state: "not-applicable", + }, + { + columnConstraint: "scenario-only", + completionEligible: false, + dateConstraint: "not-recorded", + reasons: [undefined], + state: "not-yet-run", + }, + { + columnConstraint: "expectation-only", + completionEligible: false, + dateConstraint: "not-recorded", + reasons: ["not-recorded"], + state: "not-yet-run", + }, +]; const recoveryMatrixCheckboxPattern = /^- \[( |x)\] The authorised live full fiscal year recovery matrix below is complete:/m; @@ -54,6 +110,13 @@ describe("publication readiness recovery matrix", () => { assertCanonicalSelections(matrixRows(await readRecoveryMatrix())); }); + it("renders the matrix legend from the canonical whole-cell rules", async () => { + const readiness = await readPublicationReadiness(); + expect(markedSection(readiness, legendStart, legendEnd).trim()).toBe( + renderObservationCellLegend(), + ); + }); + it("rejects every unexpected data row instead of filtering it out", async () => { const matrix = await readRecoveryMatrix(); const unfilled = "not-yet-run; date: not-recorded"; @@ -84,16 +147,35 @@ describe("publication readiness recovery matrix", () => { } }); - it("accepts only closed reason categories", () => { - expect(() => - validateObservation( - "not-applicable; date: 2026-08-17; reason: recovery-scenario-not-applicable", - false, - ), - ).not.toThrow(); - expect(() => - validateObservation("not-applicable; date: 2026-08-17; reason: free-text", false), - ).toThrow(); + it.each([ + "not-applicable; date: 2026-08-17; reason: expected-fail-closed-boundary", + "fail-closed-as-expected; date: 2026-08-17; reason: recovery-scenario-not-applicable", + "fail-closed-as-expected; date: 2026-08-17; reason: selection-not-acquisition-capable", + ])("rejects a reason assigned to the wrong state: %s", (observation) => { + expect(() => validateObservation(observation, false)).toThrow(); + }); + + it("accepts today and past dates but rejects future evidence", () => { + expect(() => validateObservation(`pass; date: ${utcDateOffset(-1)}`, false)).not.toThrow(); + expect(() => validateObservation(`pass; date: ${utcDateOffset(0)}`, false)).not.toThrow(); + expect(() => validateObservation(`pass; date: ${utcDateOffset(1)}`, false)).toThrow(); + }); + + it("allows date not-recorded only for the not-yet-run placeholder", () => { + expect(() => validateObservation("not-yet-run; date: not-recorded", false)).not.toThrow(); + expect(() => validateObservation("pass; date: not-recorded", false)).toThrow(); + expect(() => validateObservation(`not-yet-run; date: ${utcDateOffset(0)}`, false)).toThrow(); + }); + + it("rejects a combination absent from the whole-cell table", () => { + expect(() => validateObservation(`manual-review; date: ${utcDateOffset(0)}`, false)).toThrow(); + }); + + it.each([ + ["not-applicable; date: 2026-08-17; reason: recovery-scenario-not-applicable", true], + ["not-applicable; date: 2026-08-17; reason: selection-not-acquisition-capable", false], + ])("rejects a reason in the wrong column: %s", (observation, expectationColumn) => { + expect(() => validateObservation(observation, expectationColumn)).toThrow(); }); it.each(["2026-99-99", "2026-02-29", "2026-04-31"])( @@ -105,20 +187,55 @@ describe("publication readiness recovery matrix", () => { it("cannot mark the recovery gate complete while any observation is unfilled", async () => { const readiness = await readPublicationReadiness(); - const checkbox = readiness.match(recoveryMatrixCheckboxPattern); + assertRecoveryGate(readiness); + }); - expect(checkbox).not.toBeNull(); - if (checkbox?.[1] !== "x") return; + it("cannot mark the recovery gate complete when any filled observation failed", async () => { + const today = utcDateOffset(0); + const completed = fillRecoveryMatrix(await readPublicationReadiness()).replace( + `pass; date: ${today}`, + `fail; date: ${today}`, + ); - for (const [, , ...observations] of matrixRows(recoveryMatrix(readiness))) { - for (const observation of observations) { - expect(observation).not.toContain("not-yet-run"); - expect(observation).not.toContain("not-recorded"); - } - } + expect(() => assertRecoveryGate(completed)).toThrow(); + }); + + it("accepts a checked matrix only when every cell is completion-eligible", async () => { + const completed = fillRecoveryMatrix(await readPublicationReadiness()); + expect(() => assertRecoveryGate(completed)).not.toThrow(); }); }); +function assertRecoveryGate(readiness: string): void { + const checkbox = readiness.match(recoveryMatrixCheckboxPattern); + + expect(checkbox).not.toBeNull(); + if (checkbox?.[1] !== "x") return; + + for (const [, , ...observations] of matrixRows(recoveryMatrix(readiness))) { + for (const [index, observation] of observations.entries()) { + const rule = validateObservation(observation, index === observations.length - 1); + expect(rule.completionEligible, "matrix completion requires an eligible cell state").toBe( + true, + ); + } + } +} + +function fillRecoveryMatrix(readiness: string): string { + const today = utcDateOffset(0); + return readiness + .replace( + recoveryMatrixCheckboxPattern, + "- [x] The authorised live full fiscal year recovery matrix below is complete:", + ) + .replaceAll( + "not-yet-run; date: not-recorded; reason: not-recorded", + `not-applicable; date: ${today}; reason: selection-not-acquisition-capable`, + ) + .replaceAll("not-yet-run; date: not-recorded", `pass; date: ${today}`); +} + async function readRecoveryMatrix(): Promise { return recoveryMatrix(await readPublicationReadiness()); } @@ -128,12 +245,16 @@ async function readPublicationReadiness(): Promise { } function recoveryMatrix(readiness: string): string { - const start = readiness.indexOf(matrixStart); - const end = readiness.indexOf(matrixEnd); + return markedSection(readiness, matrixStart, matrixEnd); +} + +function markedSection(document: string, startMarker: string, endMarker: string): string { + const start = document.indexOf(startMarker); + const end = document.indexOf(endMarker); expect(start).toBeGreaterThanOrEqual(0); expect(end).toBeGreaterThan(start); - return readiness.slice(start + matrixStart.length, end); + return document.slice(start + startMarker.length, end); } function matrixRows(matrix: string): string[][] { @@ -174,29 +295,21 @@ function assertCanonicalSelections(rows: string[][]): void { expect(new Set(documentedSelections).size).toBe(documentedSelections.length); } -function validateObservation(observation: string, expectationColumn: boolean): void { +function validateObservation(observation: string, expectationColumn: boolean): ObservationCellRule { const parsed = observation.match(observationPattern); expect(parsed, "matrix cell has an invalid observation format").not.toBeNull(); - if (!parsed) return; + if (!parsed) throw new Error("matrix cell has an invalid observation format"); const [, state, date, reason] = parsed; - if (state === "not-yet-run") { - expect(date).toBe("not-recorded"); - if (reason !== undefined) expect(reason).toBe("not-recorded"); - } else { - expect(isCanonicalCalendarDate(date ?? "")).toBe(true); - expect(reason).not.toBe("not-recorded"); - } - - if (reason !== undefined) expect(reasonCategories.has(reason)).toBe(true); - if (state === "pass" || state === "fail") expect(reason).toBeUndefined(); - if (state === "fail-closed-as-expected" || state === "not-applicable") { - expect(reason).toBeTruthy(); - } - if (expectationColumn) { - expect(["fail-closed-as-expected", "not-applicable", "not-yet-run"]).toContain(state); - expect(reason).toBeTruthy(); - } + const rule = observationCellRules.find( + (candidate) => + candidate.state === state && + candidate.reasons.includes(reason) && + dateMatchesConstraint(date ?? "", candidate.dateConstraint) && + columnMatchesConstraint(expectationColumn, candidate.columnConstraint), + ); + expect(rule, "matrix cell combination is not allowed").toBeDefined(); + return rule as ObservationCellRule; } function isCanonicalCalendarDate(value: string): boolean { @@ -204,3 +317,52 @@ function isCanonicalCalendarDate(value: string): boolean { const parsed = new Date(`${value}T00:00:00.000Z`); return !Number.isNaN(parsed.valueOf()) && parsed.toISOString().slice(0, 10) === value; } + +function dateMatchesConstraint(value: string, constraint: DateConstraint): boolean { + if (constraint === "not-recorded") return value === "not-recorded"; + return isCanonicalCalendarDate(value) && value <= utcDateOffset(0); +} + +function columnMatchesConstraint( + expectationColumn: boolean, + constraint: ColumnConstraint, +): boolean { + if (constraint === "any") return true; + return expectationColumn === (constraint === "expectation-only"); +} + +function utcDateOffset(days: number): string { + const date = new Date(); + date.setUTCDate(date.getUTCDate() + days); + return date.toISOString().slice(0, 10); +} + +function renderObservationCellLegend(): string { + const header = ["State", "Date constraint", "Reason", "Allowed column", "Completion-eligible"]; + const rows = observationCellRules.map((rule) => { + const date = + rule.dateConstraint === "not-recorded" + ? "`not-recorded`" + : "valid `YYYY-MM-DD`, today or earlier in UTC"; + const reasons = rule.reasons + .map((reason) => (reason === undefined ? "none" : `\`${reason}\``)) + .join(" or "); + const column = + rule.columnConstraint === "any" + ? "any observation column" + : rule.columnConstraint === "expectation-only" + ? "final expectation column" + : "scenario columns"; + return [`\`${rule.state}\``, date, reasons, column, rule.completionEligible ? "yes" : "no"]; + }); + const widths = header.map((cell, index) => + Math.max(cell.length, ...rows.map((row) => row[index]?.length ?? 0)), + ); + const renderRow = (row: readonly string[]) => + `| ${row.map((cell, index) => cell.padEnd(widths[index] ?? 0)).join(" | ")} |`; + return [ + renderRow(header), + renderRow(widths.map((width) => "-".repeat(width))), + ...rows.map(renderRow), + ].join("\n"); +} From 2790995f4cf72eeaf7449d9430ca22671ab7f947 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Mon, 17 Aug 2026 22:33:57 +0530 Subject: [PATCH 7/8] test(docs): derive recovery row capability --- docs/PUBLICATION_READINESS.md | 38 ++-- tests/docs/publication-readiness.test.ts | 260 +++++++++++++++++++---- 2 files changed, 246 insertions(+), 52 deletions(-) diff --git a/docs/PUBLICATION_READINESS.md b/docs/PUBLICATION_READINESS.md index 85055a1..b16360f 100644 --- a/docs/PUBLICATION_READINESS.md +++ b/docs/PUBLICATION_READINESS.md @@ -171,13 +171,17 @@ durable full-year, or stable-release claims. The selection rows are derived from the supported Cartesian product of `FILED_RETURNS_RETURN_TYPES` and `FILED_RETURNS_ARTIFACT_TYPES`; a test keeps -this instrument aligned with those canonical constants. For each -acquisition-capable selection, record service-worker restart, browser restart, -interrupted download, cancellation/discard and its cleanup outcome, and a -retained checkpoint whose browser record is no longer available. A resumed -path must not repeat a completed target. An unproven path remains non-complete -until retry or cancellation. Manual observation is only an explicit -non-completing action and still requires retry before ZIP staging. +this instrument aligned with those canonical constants. The same test derives +each row's acquisition capability from +`supportsFullFiscalYearFiledReturnsRun` and +`supportsFiledReturnsArtifactType`; the document cannot declare that fact. For +each row, the final expectation cell's capability claim must agree with the +derived value. For each acquisition-capable selection, record service-worker +restart, browser restart, interrupted download, cancellation/discard and its +cleanup outcome, and a retained checkpoint whose browser record is no longer available. A +resumed path must not repeat a completed target. An unproven path remains +non-complete until retry or cancellation. Manual observation is only an +explicit non-completing action and still requires retry before ZIP staging. Every cell must match one complete row in this legend. The test renders the legend from the same rule table used for validation, so state, reason, date, @@ -185,15 +189,17 @@ column, and completion semantics cannot drift into an independent vocabulary. -| State | Date constraint | Reason | Allowed column | Completion-eligible | -| ------------------------- | ------------------------------------------- | ----------------------------------- | ------------------------ | ------------------- | -| `pass` | valid `YYYY-MM-DD`, today or earlier in UTC | none | scenario columns | yes | -| `fail` | valid `YYYY-MM-DD`, today or earlier in UTC | none | scenario columns | no | -| `fail-closed-as-expected` | valid `YYYY-MM-DD`, today or earlier in UTC | `expected-fail-closed-boundary` | any observation column | yes | -| `not-applicable` | valid `YYYY-MM-DD`, today or earlier in UTC | `recovery-scenario-not-applicable` | scenario columns | yes | -| `not-applicable` | valid `YYYY-MM-DD`, today or earlier in UTC | `selection-not-acquisition-capable` | final expectation column | yes | -| `not-yet-run` | `not-recorded` | none | scenario columns | no | -| `not-yet-run` | `not-recorded` | `not-recorded` | final expectation column | no | +| State | Date constraint | Reason | Allowed column | Derived row capability | Recorded capability claim | Completion-eligible | +| ------------------------- | ------------------------------------------- | ----------------------------------- | ------------------------ | ------------------------- | ------------------------- | ------------------- | +| `pass` | valid `YYYY-MM-DD`, today or earlier in UTC | none | scenario columns | any derived capability | none | yes | +| `fail` | valid `YYYY-MM-DD`, today or earlier in UTC | none | scenario columns | any derived capability | none | no | +| `fail-closed-as-expected` | valid `YYYY-MM-DD`, today or earlier in UTC | `expected-fail-closed-boundary` | scenario columns | any derived capability | none | yes | +| `fail-closed-as-expected` | valid `YYYY-MM-DD`, today or earlier in UTC | `expected-fail-closed-boundary` | final expectation column | `acquisition-capable` | `acquisition-capable` | yes | +| `not-applicable` | valid `YYYY-MM-DD`, today or earlier in UTC | `recovery-scenario-not-applicable` | scenario columns | `acquisition-capable` | none | no | +| `not-applicable` | valid `YYYY-MM-DD`, today or earlier in UTC | `recovery-scenario-not-applicable` | scenario columns | `not-acquisition-capable` | none | yes | +| `not-applicable` | valid `YYYY-MM-DD`, today or earlier in UTC | `selection-not-acquisition-capable` | final expectation column | `not-acquisition-capable` | `not-acquisition-capable` | yes | +| `not-yet-run` | `not-recorded` | none | scenario columns | any derived capability | none | no | +| `not-yet-run` | `not-recorded` | `not-recorded` | final expectation column | any derived capability | none | no | diff --git a/tests/docs/publication-readiness.test.ts b/tests/docs/publication-readiness.test.ts index 9dbeaef..6fbf290 100644 --- a/tests/docs/publication-readiness.test.ts +++ b/tests/docs/publication-readiness.test.ts @@ -3,9 +3,14 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; import { FILED_RETURNS_ARTIFACT_TYPES, + isFiledReturnsArtifactType, supportsFiledReturnsArtifactType, } from "../../src/connectors/gst/filed-returns-artifacts"; -import { FILED_RETURNS_RETURN_TYPES } from "../../src/connectors/gst/filed-returns-return-types"; +import { + FILED_RETURNS_RETURN_TYPES, + isFiledReturnsReturnType, + supportsFullFiscalYearFiledReturnsRun, +} from "../../src/connectors/gst/filed-returns-return-types"; const rootDir = process.cwd(); const matrixStart = ""; @@ -26,11 +31,15 @@ const observationPattern = /^([a-z]+(?:-[a-z]+)*); date: ([^;\s]+)(?:; reason: ([a-z]+(?:-[a-z]+)*))?$/; type DateConstraint = "not-recorded" | "recorded-not-future"; type ColumnConstraint = "any" | "expectation-only" | "scenario-only"; +type RowCapability = "acquisition-capable" | "not-acquisition-capable"; +type RowCapabilityConstraint = "any" | RowCapability; interface ObservationCellRule { columnConstraint: ColumnConstraint; completionEligible: boolean; dateConstraint: DateConstraint; + recordedRowCapability?: RowCapability; reasons: readonly (string | undefined)[]; + rowCapabilityConstraint: RowCapabilityConstraint; state: string; } const observationCellRules: readonly ObservationCellRule[] = [ @@ -39,6 +48,7 @@ const observationCellRules: readonly ObservationCellRule[] = [ completionEligible: true, dateConstraint: "recorded-not-future", reasons: [undefined], + rowCapabilityConstraint: "any", state: "pass", }, { @@ -46,27 +56,49 @@ const observationCellRules: readonly ObservationCellRule[] = [ completionEligible: false, dateConstraint: "recorded-not-future", reasons: [undefined], + rowCapabilityConstraint: "any", state: "fail", }, { - columnConstraint: "any", + columnConstraint: "scenario-only", + completionEligible: true, + dateConstraint: "recorded-not-future", + reasons: ["expected-fail-closed-boundary"], + rowCapabilityConstraint: "any", + state: "fail-closed-as-expected", + }, + { + columnConstraint: "expectation-only", completionEligible: true, dateConstraint: "recorded-not-future", + recordedRowCapability: "acquisition-capable", reasons: ["expected-fail-closed-boundary"], + rowCapabilityConstraint: "acquisition-capable", state: "fail-closed-as-expected", }, + { + columnConstraint: "scenario-only", + completionEligible: false, + dateConstraint: "recorded-not-future", + reasons: ["recovery-scenario-not-applicable"], + rowCapabilityConstraint: "acquisition-capable", + state: "not-applicable", + }, { columnConstraint: "scenario-only", completionEligible: true, dateConstraint: "recorded-not-future", reasons: ["recovery-scenario-not-applicable"], + rowCapabilityConstraint: "not-acquisition-capable", state: "not-applicable", }, { columnConstraint: "expectation-only", completionEligible: true, dateConstraint: "recorded-not-future", + recordedRowCapability: "not-acquisition-capable", reasons: ["selection-not-acquisition-capable"], + rowCapabilityConstraint: "not-acquisition-capable", state: "not-applicable", }, { @@ -74,6 +106,7 @@ const observationCellRules: readonly ObservationCellRule[] = [ completionEligible: false, dateConstraint: "not-recorded", reasons: [undefined], + rowCapabilityConstraint: "any", state: "not-yet-run", }, { @@ -81,6 +114,7 @@ const observationCellRules: readonly ObservationCellRule[] = [ completionEligible: false, dateConstraint: "not-recorded", reasons: ["not-recorded"], + rowCapabilityConstraint: "any", state: "not-yet-run", }, ]; @@ -98,7 +132,10 @@ describe("publication readiness recovery matrix", () => { expect(submittedVersion).toBeTruthy(); const expectedVersion = submittedVersion?.slice(1); - expect(readiness).toContain(`expected_version=${expectedVersion}`); + expect( + readiness.includes(`expected_version=${expectedVersion}`), + "publication readiness must use the canonical submitted version", + ).toBe(true); expect( [...dashboardCloseout.matchAll(/^expected_version=(\d+\.\d+\.\d+)$/gm)].map( (match) => match[1], @@ -138,11 +175,12 @@ describe("publication readiness recovery matrix", () => { it("keeps every observation fillable, dated, and reasoned when required", async () => { const matrix = await readRecoveryMatrix(); - for (const [, , ...observations] of matrixRows(matrix)) { - expect(observations).toHaveLength(6); + for (const [returnType = "", artifactType = "", ...observations] of matrixRows(matrix)) { + const rowCapability = deriveRowCapability(returnType, artifactType); + expect(observations.length, "matrix row must have six observation cells").toBe(6); for (const [index, observation] of observations.entries()) { - validateObservation(observation, index === observations.length - 1); + validateObservation(observation, index === observations.length - 1, rowCapability); } } }); @@ -152,36 +190,63 @@ describe("publication readiness recovery matrix", () => { "fail-closed-as-expected; date: 2026-08-17; reason: recovery-scenario-not-applicable", "fail-closed-as-expected; date: 2026-08-17; reason: selection-not-acquisition-capable", ])("rejects a reason assigned to the wrong state: %s", (observation) => { - expect(() => validateObservation(observation, false)).toThrow(); + expect(() => validateObservation(observation, false, "acquisition-capable")).toThrow(); }); it("accepts today and past dates but rejects future evidence", () => { - expect(() => validateObservation(`pass; date: ${utcDateOffset(-1)}`, false)).not.toThrow(); - expect(() => validateObservation(`pass; date: ${utcDateOffset(0)}`, false)).not.toThrow(); - expect(() => validateObservation(`pass; date: ${utcDateOffset(1)}`, false)).toThrow(); + expect(() => + validateObservation(`pass; date: ${utcDateOffset(-1)}`, false, "acquisition-capable"), + ).not.toThrow(); + expect(() => + validateObservation(`pass; date: ${utcDateOffset(0)}`, false, "acquisition-capable"), + ).not.toThrow(); + expect(() => + validateObservation(`pass; date: ${utcDateOffset(1)}`, false, "acquisition-capable"), + ).toThrow(); }); it("allows date not-recorded only for the not-yet-run placeholder", () => { - expect(() => validateObservation("not-yet-run; date: not-recorded", false)).not.toThrow(); - expect(() => validateObservation("pass; date: not-recorded", false)).toThrow(); - expect(() => validateObservation(`not-yet-run; date: ${utcDateOffset(0)}`, false)).toThrow(); + expect(() => + validateObservation("not-yet-run; date: not-recorded", false, "acquisition-capable"), + ).not.toThrow(); + expect(() => + validateObservation("pass; date: not-recorded", false, "acquisition-capable"), + ).toThrow(); + expect(() => + validateObservation(`not-yet-run; date: ${utcDateOffset(0)}`, false, "acquisition-capable"), + ).toThrow(); }); it("rejects a combination absent from the whole-cell table", () => { - expect(() => validateObservation(`manual-review; date: ${utcDateOffset(0)}`, false)).toThrow(); + expect(() => + validateObservation(`manual-review; date: ${utcDateOffset(0)}`, false, "acquisition-capable"), + ).toThrow(); }); it.each([ - ["not-applicable; date: 2026-08-17; reason: recovery-scenario-not-applicable", true], - ["not-applicable; date: 2026-08-17; reason: selection-not-acquisition-capable", false], - ])("rejects a reason in the wrong column: %s", (observation, expectationColumn) => { - expect(() => validateObservation(observation, expectationColumn)).toThrow(); - }); + [ + "not-applicable; date: 2026-08-17; reason: recovery-scenario-not-applicable", + true, + "acquisition-capable" as const, + ], + [ + "not-applicable; date: 2026-08-17; reason: selection-not-acquisition-capable", + false, + "not-acquisition-capable" as const, + ], + ])( + "rejects a reason in the wrong column: %s", + (observation, expectationColumn, rowCapability) => { + expect(() => validateObservation(observation, expectationColumn, rowCapability)).toThrow(); + }, + ); it.each(["2026-99-99", "2026-02-29", "2026-04-31"])( "rejects the non-calendar date %s", (date) => { - expect(() => validateObservation(`pass; date: ${date}`, false)).toThrow(); + expect(() => + validateObservation(`pass; date: ${date}`, false, "acquisition-capable"), + ).toThrow(); }, ); @@ -200,6 +265,51 @@ describe("publication readiness recovery matrix", () => { expect(() => assertRecoveryGate(completed)).toThrow(); }); + it("cannot complete an acquisition-capable row with every scenario not applicable", async () => { + const today = utcDateOffset(0); + let completed = fillRecoveryMatrix(await readPublicationReadiness()); + + for (let scenario = 0; scenario < 5; scenario += 1) { + completed = completed.replace( + `pass; date: ${today}`, + `not-applicable; date: ${today}; reason: recovery-scenario-not-applicable`, + ); + } + + expect(() => assertRecoveryGate(completed)).toThrow(); + }); + + it("allows a canonically non-capable selection to complete through its expected path", () => { + const today = utcDateOffset(0); + const scenario = `fail-closed-as-expected; date: ${today}; reason: expected-fail-closed-boundary`; + const expectation = `not-applicable; date: ${today}; reason: selection-not-acquisition-capable`; + + expect(() => + assertRecoveryRowComplete([ + "GSTR-1", + "JSON", + ...Array(5).fill(scenario), + expectation, + ]), + ).not.toThrow(); + }); + + it("rejects a recorded capability claim that contradicts the derived value", () => { + const today = utcDateOffset(0); + const expectation = `not-applicable; date: ${today}; reason: selection-not-acquisition-capable`; + + expect(() => + assertRecoveryRowComplete([ + "GSTR-3B", + "PDF", + ...Array(5).fill(`pass; date: ${today}`), + expectation, + ]), + ).toThrow( + "matrix row capability mismatch: derived acquisition-capable; recorded not-acquisition-capable", + ); + }); + it("accepts a checked matrix only when every cell is completion-eligible", async () => { const completed = fillRecoveryMatrix(await readPublicationReadiness()); expect(() => assertRecoveryGate(completed)).not.toThrow(); @@ -212,13 +322,17 @@ function assertRecoveryGate(readiness: string): void { expect(checkbox).not.toBeNull(); if (checkbox?.[1] !== "x") return; - for (const [, , ...observations] of matrixRows(recoveryMatrix(readiness))) { - for (const [index, observation] of observations.entries()) { - const rule = validateObservation(observation, index === observations.length - 1); - expect(rule.completionEligible, "matrix completion requires an eligible cell state").toBe( - true, - ); - } + for (const row of matrixRows(recoveryMatrix(readiness))) assertRecoveryRowComplete(row); +} + +function assertRecoveryRowComplete(row: string[]): void { + const [returnType = "", artifactType = "", ...observations] = row; + const rowCapability = deriveRowCapability(returnType, artifactType); + expect(observations.length, "matrix row must have six observation cells").toBe(6); + + for (const [index, observation] of observations.entries()) { + const rule = validateObservation(observation, index === observations.length - 1, rowCapability); + expect(rule.completionEligible, "matrix completion requires an eligible cell state").toBe(true); } } @@ -231,7 +345,7 @@ function fillRecoveryMatrix(readiness: string): string { ) .replaceAll( "not-yet-run; date: not-recorded; reason: not-recorded", - `not-applicable; date: ${today}; reason: selection-not-acquisition-capable`, + `fail-closed-as-expected; date: ${today}; reason: expected-fail-closed-boundary`, ) .replaceAll("not-yet-run; date: not-recorded", `pass; date: ${today}`); } @@ -266,11 +380,21 @@ function matrixRows(matrix: string): string[][] { expect(lines.length).toBeGreaterThanOrEqual(3); const [header, separator, ...dataRows] = lines.map(parseMatrixRow); - expect(header).toEqual(matrixColumns); - expect(separator).toHaveLength(matrixColumns.length); + expect( + header?.every((cell, index) => cell === matrixColumns[index]) && + header.length === matrixColumns.length, + "matrix header must match the canonical columns", + ).toBe(true); + expect(separator?.length, "matrix separator must match the canonical column count").toBe( + matrixColumns.length, + ); expect(separator?.every((cell) => /^:?-{3,}:?$/.test(cell))).toBe(true); - for (const row of dataRows) expect(row).toHaveLength(matrixColumns.length); + for (const row of dataRows) { + expect(row.length, "matrix data row must match the canonical column count").toBe( + matrixColumns.length, + ); + } return dataRows; } @@ -291,23 +415,57 @@ function assertCanonicalSelections(rows: string[][]): void { ).map((artifactType) => [returnType, artifactType].join(" | ")), ); - expect(documentedSelections).toEqual(offeredSelections); + expect( + documentedSelections.length === offeredSelections.length && + documentedSelections.every((selection, index) => selection === offeredSelections[index]), + "matrix selections must match canonical offered selections in order", + ).toBe(true); expect(new Set(documentedSelections).size).toBe(documentedSelections.length); } -function validateObservation(observation: string, expectationColumn: boolean): ObservationCellRule { +function deriveRowCapability(returnType: string, artifactType: string): RowCapability { + if (!isFiledReturnsReturnType(returnType) || !isFiledReturnsArtifactType(artifactType)) { + throw new Error("matrix row does not use canonical return and artifact types"); + } + + return supportsFullFiscalYearFiledReturnsRun(returnType) && + supportsFiledReturnsArtifactType(returnType, artifactType) + ? "acquisition-capable" + : "not-acquisition-capable"; +} + +function validateObservation( + observation: string, + expectationColumn: boolean, + rowCapability: RowCapability, +): ObservationCellRule { const parsed = observation.match(observationPattern); expect(parsed, "matrix cell has an invalid observation format").not.toBeNull(); if (!parsed) throw new Error("matrix cell has an invalid observation format"); const [, state, date, reason] = parsed; - const rule = observationCellRules.find( + const matchingCellRules = observationCellRules.filter( (candidate) => candidate.state === state && candidate.reasons.includes(reason) && dateMatchesConstraint(date ?? "", candidate.dateConstraint) && columnMatchesConstraint(expectationColumn, candidate.columnConstraint), ); + const rule = matchingCellRules.find((candidate) => + rowCapabilityMatchesConstraint(rowCapability, candidate.rowCapabilityConstraint), + ); + if (!rule) { + const contradictoryClaim = matchingCellRules.find( + (candidate) => + candidate.recordedRowCapability !== undefined && + candidate.recordedRowCapability !== rowCapability, + )?.recordedRowCapability; + if (contradictoryClaim) { + throw new Error( + `matrix row capability mismatch: derived ${rowCapability}; recorded ${contradictoryClaim}`, + ); + } + } expect(rule, "matrix cell combination is not allowed").toBeDefined(); return rule as ObservationCellRule; } @@ -331,6 +489,13 @@ function columnMatchesConstraint( return expectationColumn === (constraint === "expectation-only"); } +function rowCapabilityMatchesConstraint( + rowCapability: RowCapability, + constraint: RowCapabilityConstraint, +): boolean { + return constraint === "any" || constraint === rowCapability; +} + function utcDateOffset(days: number): string { const date = new Date(); date.setUTCDate(date.getUTCDate() + days); @@ -338,7 +503,15 @@ function utcDateOffset(days: number): string { } function renderObservationCellLegend(): string { - const header = ["State", "Date constraint", "Reason", "Allowed column", "Completion-eligible"]; + const header = [ + "State", + "Date constraint", + "Reason", + "Allowed column", + "Derived row capability", + "Recorded capability claim", + "Completion-eligible", + ]; const rows = observationCellRules.map((rule) => { const date = rule.dateConstraint === "not-recorded" @@ -353,7 +526,22 @@ function renderObservationCellLegend(): string { : rule.columnConstraint === "expectation-only" ? "final expectation column" : "scenario columns"; - return [`\`${rule.state}\``, date, reasons, column, rule.completionEligible ? "yes" : "no"]; + const rowCapability = + rule.rowCapabilityConstraint === "any" + ? "any derived capability" + : `\`${rule.rowCapabilityConstraint}\``; + const recordedCapability = rule.recordedRowCapability + ? `\`${rule.recordedRowCapability}\`` + : "none"; + return [ + `\`${rule.state}\``, + date, + reasons, + column, + rowCapability, + recordedCapability, + rule.completionEligible ? "yes" : "no", + ]; }); const widths = header.map((cell, index) => Math.max(cell.length, ...rows.map((row) => row[index]?.length ?? 0)), From a89d806224ced0aee9d5f6cd3f36139baf289fb7 Mon Sep 17 00:00:00 2001 From: Tapish Khandelwal Date: Tue, 18 Aug 2026 01:05:46 +0530 Subject: [PATCH 8/8] docs(readiness): require evidence for checked claims Signed-off-by: Tapish Khandelwal --- docs/PUBLICATION_READINESS.md | 52 ++++++++++++++++-------- tests/docs/publication-readiness.test.ts | 37 +++++++++++++++++ 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/docs/PUBLICATION_READINESS.md b/docs/PUBLICATION_READINESS.md index b16360f..e2e7268 100644 --- a/docs/PUBLICATION_READINESS.md +++ b/docs/PUBLICATION_READINESS.md @@ -108,22 +108,35 @@ durable full-year, or stable-release claims. ## Chrome Web Store Checklist +Every checked item below records at least one verifiable source path, workflow +run identifier, or dated observation. Unevidenced claims stay unchecked. + ### Done In Source -- [x] Manifest V3. -- [x] Exact GST host permission allow-list only. +- [x] Manifest V3 is asserted by `scripts/verify-extension-browser.mjs`. +- [x] Exact GST host permission allow-list only, defined in + `src/extension/manifest-policy.ts`. - [x] No ``, cookies, history, webRequest, debugger/CDP, - nativeMessaging, tabs, identity, or externally_connectable in any Pack build. -- [x] Restrictive extension CSP. -- [x] No remote executable code allowed by package verifier. -- [x] No extension analytics SDK. -- [x] Synthetic reviewer demo exists. + nativeMessaging, tabs, identity, or externally_connectable in any Pack + build; `scripts/verify-extension-package.mjs` enforces the package boundary. +- [x] Restrictive extension CSP, defined in + `src/extension/manifest-policy.ts`. +- [x] No remote executable code allowed by + `scripts/verify-extension-package.mjs`. +- [x] No extension analytics SDK; packaged markers are rejected by + `scripts/verify-extension-package.mjs`. +- [x] Synthetic reviewer demo exists in `src/background/synthetic-demo.ts`. - [x] Reviewer instructions exist in `docs/CHROME_REVIEWER_TEST.md`. -- [x] Manifest icons are present in source and verified in the built package. -- [x] Manifest homepage URL points to `https://pack.complyeaze.com/gst`. -- [x] Protected Chrome Web Store workflow exists for future release updates. +- [x] Manifest icons are defined in `src/extension/manifest-policy.ts` and + verified in the built package by `scripts/verify-extension-package.mjs`. +- [x] Manifest homepage URL is defined in + `src/extension/manifest-policy.ts` and points to + `https://pack.complyeaze.com/gst`. +- [x] Protected Chrome Web Store workflow exists for future release updates at + `.github/workflows/chrome-web-store.yml`. - [x] Protected Chrome Web Store status monitor exists for post-submit - review/publication checks without upload or publish side effects. + review/publication checks without upload or publish side effects at + `.github/workflows/chrome-web-store-status.yml`. ### Must Complete Before Future Store Updates Or Broader Store Claims @@ -297,7 +310,8 @@ data are unrepresentable in the matrix. - [ ] Publisher account MFA, recovery, and team access are verified. - [ ] Product, engineering, security, privacy/legal, open-source, and release manager sign-offs are recorded. -- [x] Initial Chrome Web Store V0 listing published. +- [x] Initial Chrome Web Store V0 listing published. The dated 2026-07-06 + `v0.3.2` publication evidence recorded below is the basis for this item. - [x] Protected Chrome Web Store release update submitted through workflow dispatch. The `v0.2.1` package was uploaded through GitHub Actions run `28542410006` with Chrome Web Store upload state `SUCCEEDED`, publish @@ -312,13 +326,19 @@ data are unrepresentable in the matrix. version `0.3.2`, and visibility `Public`. - [x] Record the historical `v0.4.0` package upload. Workflow run `29507382500` reported upload state `SUCCEEDED`; that release was not published. -- [x] Record the `v0.5.0` Chrome Web Store submission. The package is submitted - and in review as a draft; approval, publication, and live availability - are not claimed. +- [ ] Record the `v0.5.0` Chrome Web Store submission. The release sequence + currently describes the package as submitted and in review as a draft; + approval, publication, and live availability are not claimed. Required + evidence remains unfilled: `observation-date: not-recorded`; + `observed-state: not-recorded`; `observation-location: not-recorded`; + `submission-method: not-recorded`; `workflow-run-id: not-recorded` (or + `manual; workflow-run-id: none`); and + `observer-or-approver: not-recorded`. - [x] Add a read-only Chrome Web Store status monitor for submitted packages. Scheduled runs use the dedicated `chrome-web-store-status` environment so publication/rejection monitoring is not blocked by the protected publishing - approval gate. + approval gate; the source is + `.github/workflows/chrome-web-store-status.yml`. - [ ] Record a read-only Chrome Web Store Status run with `expected_version=0.5.0` and `require_published=false` while the submitted draft is in review, then `require_published=true` only after confirmed diff --git a/tests/docs/publication-readiness.test.ts b/tests/docs/publication-readiness.test.ts index 6fbf290..183a5c5 100644 --- a/tests/docs/publication-readiness.test.ts +++ b/tests/docs/publication-readiness.test.ts @@ -17,6 +17,8 @@ const matrixStart = ""; const matrixEnd = ""; const legendStart = ""; const legendEnd = ""; +const storeChecklistStart = "## Chrome Web Store Checklist"; +const storeChecklistEnd = "## Suggested Store Copy"; const matrixColumns = [ "Return type", "Artifact type", @@ -120,6 +122,8 @@ const observationCellRules: readonly ObservationCellRule[] = [ ]; const recoveryMatrixCheckboxPattern = /^- \[( |x)\] The authorised live full fiscal year recovery matrix below is complete:/m; +const storeChecklistEvidenceTokenPattern = + /`(?:\.github\/|docs\/|scripts\/|src\/|tests\/|wxt\.config\.ts)[^`]*`|\b20\d{2}-\d{2}-\d{2}\b|\b(?:GitHub Actions run|[Ww]orkflow run|Run) `\d{8,}`/; describe("publication readiness recovery matrix", () => { it("keeps status-closeout consumers aligned to the canonical listing version", async () => { @@ -147,6 +151,23 @@ describe("publication readiness recovery matrix", () => { assertCanonicalSelections(matrixRows(await readRecoveryMatrix())); }); + it("requires every checked Store item to carry a recorded evidence token", async () => { + const checkedItems = checklistItems( + markedSection(await readPublicationReadiness(), storeChecklistStart, storeChecklistEnd), + ).filter((item) => item.checked); + + expect( + checkedItems.length, + "Store checklist must contain checked evidence items", + ).toBeGreaterThan(0); + for (const [index, item] of checkedItems.entries()) { + expect( + storeChecklistEvidenceTokenPattern.test(item.text), + `checked Store item ${index + 1} must carry a source, run, or dated evidence token`, + ).toBe(true); + } + }); + it("renders the matrix legend from the canonical whole-cell rules", async () => { const readiness = await readPublicationReadiness(); expect(markedSection(readiness, legendStart, legendEnd).trim()).toBe( @@ -405,6 +426,22 @@ function parseMatrixRow(line: string): string[] { .map((cell) => cell.trim()); } +function checklistItems(section: string): Array<{ checked: boolean; text: string }> { + const items: Array<{ checked: boolean; text: string }> = []; + + for (const line of section.split("\n")) { + const item = line.match(/^- \[([ x])\] (.+)$/); + if (item) { + items.push({ checked: item[1] === "x", text: item[2] ?? "" }); + } else if (items.length > 0 && /^ {6}\S/.test(line)) { + const current = items[items.length - 1]; + if (current) current.text += ` ${line.trim()}`; + } + } + + return items; +} + function assertCanonicalSelections(rows: string[][]): void { const documentedSelections = rows.map(([returnType, artifactType]) => [returnType, artifactType].join(" | "),