star-chart-search-enhancer/externaldocs/2026-04-20-star-chart-market-after-search-rate-implementation-plan.md

18 KiB

Star Chart Market After-Search Rate Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Build a new Chrome MV3 extension that adds two after-search-rate columns to the Xingtu creator market page, then supports plugin-owned filtering, sorting, full-scan export, and CSV download.

Architecture: Start from a minimal MV3 content-script extension and keep the implementation split into small modules: value normalization, API mapping, result storage, DOM sync, full-scan orchestration, filter/sort control, and CSV export. Follow TDD for every production behavior module; only project bootstrap and config files may be created directly as setup.

Tech Stack: TypeScript, Chrome Manifest V3, Vitest, jsdom, tsup, Node.js


File Structure

Planned initial structure:

package.json
package-lock.json
tsconfig.json
vitest.config.ts
scripts/
  build.mjs
src/
  manifest.json
  content/
    index.ts
    market/
      index.ts
      types.ts
      dom-sync.ts
      api-client.ts
      result-store.ts
      filter-sort-controller.ts
      full-scan-controller.ts
      csv-exporter.ts
      plugin-toolbar.ts
  shared/
    rate-normalizer.ts
    csv.ts
tests/
  rate-normalizer.test.ts
  market-api-client.test.ts
  result-store.test.ts
  filter-sort-controller.test.ts
  market-dom-sync.test.ts
  full-scan-controller.test.ts
  csv-exporter.test.ts
  market-content-entry.test.ts

Responsibilities:

  • src/shared/rate-normalizer.ts: parse and normalize display values and comparable lower-bound values
  • src/content/market/api-client.ts: fetch and map /gw/api/aggregator/get_author_ase_info
  • src/content/market/result-store.ts: hold per-author status and merged market records
  • src/content/market/filter-sort-controller.ts: apply threshold filters and lower-bound sorting
  • src/content/market/dom-sync.ts: locate market rows, inject two columns, and update row visibility/order
  • src/content/market/full-scan-controller.ts: paginate through filtered result pages and hydrate the result store
  • src/content/market/csv-exporter.ts: generate CSV rows and blob/download metadata
  • src/content/market/plugin-toolbar.ts: render the plugin controls for threshold filter, sorting, and export
  • src/content/market/index.ts: compose all market modules for the live page
  • src/content/index.ts: route-match and boot the market controller

Task 1: Initialize the Empty Project

Files:

  • Create: package.json

  • Create: tsconfig.json

  • Create: vitest.config.ts

  • Create: scripts/build.mjs

  • Create: src/manifest.json

  • Create: src/content/index.ts

  • Step 1: Initialize the repository and npm package

Run:

git init
npm init -y

Expected: a new .git/ directory and baseline package.json.

  • Step 2: Add build and test dependencies

Run:

npm install -D typescript vitest jsdom tsup

Expected: package-lock.json created and dev dependencies added.

  • Step 3: Write the minimal config and manifest files

Create direct setup files:

  • package.json scripts: build, test, test:watch
  • tsconfig.json
  • vitest.config.ts
  • scripts/build.mjs
  • src/manifest.json
  • src/content/index.ts

The initial manifest should only match the Xingtu market page and load one content entry bundle.

  • Step 4: Run the test command to verify the toolchain is wired

Run:

npm test

Expected: Vitest runs with zero or pending test files, without config errors.

  • Step 5: Run the build command to verify the extension bundle layout

Run:

npm run build

Expected: build succeeds and writes a minimal dist/ layout containing the manifest and content bundle.

  • Step 6: Commit the bootstrap

Run:

git add package.json package-lock.json tsconfig.json vitest.config.ts scripts/build.mjs src/manifest.json src/content/index.ts
git commit -m "chore: bootstrap mv3 extension project"

Task 2: Implement Rate Normalization

Files:

  • Create: src/shared/rate-normalizer.ts

  • Test: tests/rate-normalizer.test.ts

  • Step 1: Write the failing tests for normalization and comparable lower bounds

Add tests covering:

import { describe, expect, test } from "vitest";
import {
  compareRateValues,
  normalizeRateDisplay,
  parseRateLowerBound
} from "../src/shared/rate-normalizer";

describe("rate-normalizer", () => {
  test("normalizes compact ranges", () => {
    expect(normalizeRateDisplay("0.5%-1%")).toBe("0.5% - 1%");
  });

  test("parses the lower bound of a range", () => {
    expect(parseRateLowerBound("0.02% - 0.1%")).toBe(0.02);
  });

  test("treats less-than values as smaller than the boundary range", () => {
    expect(compareRateValues("<0.02%", "0.02% - 0.1%")).toBeLessThan(0);
  });
});
  • Step 2: Run the rate normalizer test file and verify RED

Run:

npm test -- tests/rate-normalizer.test.ts

Expected: FAIL because the module does not exist yet.

  • Step 3: Write the minimal implementation

Implement only:

  • display normalization for compact range strings

  • lower-bound parsing

  • comparison helper that orders invalid or missing values last

  • Step 4: Run the test file and verify GREEN

Run:

npm test -- tests/rate-normalizer.test.ts

Expected: PASS.

  • Step 5: Refactor for one parsing pipeline

Keep parsing logic in one shared path so filtering, sorting, and export do not interpret values differently.

  • Step 6: Re-run the test file after refactor

Run:

npm test -- tests/rate-normalizer.test.ts

Expected: PASS.

  • Step 7: Commit

Run:

git add src/shared/rate-normalizer.ts tests/rate-normalizer.test.ts
git commit -m "feat: add rate normalization helpers"

Task 3: Implement the ASE API Client

Files:

  • Create: src/content/market/api-client.ts

  • Create: src/content/market/types.ts

  • Test: tests/market-api-client.test.ts

  • Step 1: Write the failing tests for response mapping and failure states

Cover:

  • successful mapping from avg_search_after_view_rate
  • successful mapping from personal_avg_search_after_view_rate
  • missing or failed behavior when fields are absent
  • timeout or non-OK response failure mapping

Example test seed:

test("maps a valid ASE payload into normalized rates", async () => {
  const client = createMarketApiClient({
    fetchImpl: async () => ({
      ok: true,
      json: async () => ({
        data: {
          avg_search_after_view_rate: "<0.02%",
          personal_avg_search_after_view_rate: "0.02 - 0.1%"
        }
      })
    })
  });

  await expect(client.loadAuthorAseInfo("123")).resolves.toMatchObject({
    success: true,
    rates: {
      singleVideoAfterSearchRate: "<0.02%",
      personalVideoAfterSearchRate: "0.02% - 0.1%"
    }
  });
});
  • Step 2: Run the API client test file and verify RED

Run:

npm test -- tests/market-api-client.test.ts

Expected: FAIL because the client module does not exist yet.

  • Step 3: Write the minimal implementation

Implement:

  • request builder for /gw/api/aggregator/get_author_ase_info

  • response mapping

  • timeout handling

  • normalized success result and stable failure reasons

  • Step 4: Run the API client tests and verify GREEN

Run:

npm test -- tests/market-api-client.test.ts

Expected: PASS.

  • Step 5: Refactor shared types

Move reusable market result types into src/content/market/types.ts.

  • Step 6: Re-run the API client tests

Run:

npm test -- tests/market-api-client.test.ts

Expected: PASS.

  • Step 7: Commit

Run:

git add src/content/market/api-client.ts src/content/market/types.ts tests/market-api-client.test.ts
git commit -m "feat: add ase api client"

Task 4: Implement the Result Store

Files:

  • Create: src/content/market/result-store.ts

  • Modify: src/content/market/types.ts

  • Test: tests/result-store.test.ts

  • Step 1: Write the failing tests for merged record lifecycle

Cover:

  • create loading records from current-page rows

  • update one author to success

  • preserve failed authors instead of dropping them

  • dedupe the same author across pages

  • keep stable major fields after repeated writes

  • Step 2: Run the result store test file and verify RED

Run:

npm test -- tests/result-store.test.ts

Expected: FAIL because the store module does not exist yet.

  • Step 3: Write the minimal implementation

Implement:

  • upsertMarketRow

  • setAuthorLoading

  • setAuthorSuccess

  • setAuthorFailed

  • listRecords

  • getRecord

  • Step 4: Run the result store tests and verify GREEN

Run:

npm test -- tests/result-store.test.ts

Expected: PASS.

  • Step 5: Refactor state transitions into one reducer-style path

Keep state transitions explicit so later full-scan orchestration does not spread status logic across files.

  • Step 6: Re-run the result store tests

Run:

npm test -- tests/result-store.test.ts

Expected: PASS.

  • Step 7: Commit

Run:

git add src/content/market/result-store.ts src/content/market/types.ts tests/result-store.test.ts
git commit -m "feat: add market result store"

Task 5: Implement Filter and Sort Control

Files:

  • Create: src/content/market/filter-sort-controller.ts

  • Modify: src/content/market/types.ts

  • Test: tests/filter-sort-controller.test.ts

  • Step 1: Write the failing tests for threshold filtering and lower-bound sorting

Cover:

  • pass only when lower bound is greater than or equal to the threshold

  • sort single-rate descending

  • sort personal-rate ascending

  • keep failed or missing rows at the end

  • Step 2: Run the filter/sort tests and verify RED

Run:

npm test -- tests/filter-sort-controller.test.ts

Expected: FAIL because the controller module does not exist yet.

  • Step 3: Write the minimal implementation

Implement:

  • filter state type

  • sort state type

  • record predicate

  • comparator

  • application helper returning ordered visible record IDs

  • Step 4: Run the filter/sort tests and verify GREEN

Run:

npm test -- tests/filter-sort-controller.test.ts

Expected: PASS.

  • Step 5: Refactor duplicated comparison branches

Keep one comparison path per metric and one final fallback ordering rule.

  • Step 6: Re-run the filter/sort tests

Run:

npm test -- tests/filter-sort-controller.test.ts

Expected: PASS.

  • Step 7: Commit

Run:

git add src/content/market/filter-sort-controller.ts src/content/market/types.ts tests/filter-sort-controller.test.ts
git commit -m "feat: add filter and sort controller"

Task 6: Implement CSV Export

Files:

  • Create: src/shared/csv.ts

  • Create: src/content/market/csv-exporter.ts

  • Test: tests/csv-exporter.test.ts

  • Step 1: Write the failing tests for CSV serialization

Cover:

  • header order

  • escaped commas and quotes

  • failed rows emitting empty rate fields plus status

  • export rows using normalized display values

  • Step 2: Run the CSV exporter tests and verify RED

Run:

npm test -- tests/csv-exporter.test.ts

Expected: FAIL because the exporter module does not exist yet.

  • Step 3: Write the minimal implementation

Implement:

  • CSV escaping helper

  • column definition list

  • row-to-CSV conversion

  • blob-ready text generation function

  • Step 4: Run the CSV exporter tests and verify GREEN

Run:

npm test -- tests/csv-exporter.test.ts

Expected: PASS.

  • Step 5: Refactor field mapping into one declarative schema

Keep export field order and labels in one place.

  • Step 6: Re-run the CSV exporter tests

Run:

npm test -- tests/csv-exporter.test.ts

Expected: PASS.

  • Step 7: Commit

Run:

git add src/shared/csv.ts src/content/market/csv-exporter.ts tests/csv-exporter.test.ts
git commit -m "feat: add csv exporter"

Task 7: Implement Current-Page DOM Sync

Files:

  • Create: src/content/market/dom-sync.ts

  • Test: tests/market-dom-sync.test.ts

  • Step 1: Write the failing DOM tests for current-page enhancement

Use jsdom fixtures that mimic the Xingtu market grid and cover:

  • injecting the two header cells

  • injecting one pair of per-row cells

  • rendering loading, success, and failed states

  • hiding filtered rows

  • reordering rows based on an ordered ID list

  • Step 2: Run the DOM sync tests and verify RED

Run:

npm test -- tests/market-dom-sync.test.ts

Expected: FAIL because the module does not exist yet.

  • Step 3: Write the minimal implementation

Implement:

  • page structure discovery

  • row extraction with major field snapshots

  • inserted-cell rendering

  • row hide/show

  • row order application

  • Step 4: Run the DOM sync tests and verify GREEN

Run:

npm test -- tests/market-dom-sync.test.ts

Expected: PASS.

  • Step 5: Refactor DOM selectors into one locator object

This keeps future layout changes isolated.

  • Step 6: Re-run the DOM sync tests

Run:

npm test -- tests/market-dom-sync.test.ts

Expected: PASS.

  • Step 7: Commit

Run:

git add src/content/market/dom-sync.ts tests/market-dom-sync.test.ts
git commit -m "feat: add market dom sync"

Task 8: Implement Full-Scan Pagination Control

Files:

  • Create: src/content/market/full-scan-controller.ts

  • Modify: src/content/market/types.ts

  • Test: tests/full-scan-controller.test.ts

  • Step 1: Write the failing tests for on-demand full scans

Cover:

  • initial page load does not start full scan

  • filter action starts full scan

  • sort action starts full scan

  • export action starts full scan

  • repeated actions do not restart a completed scan unnecessarily

  • failed author fetches are recorded but do not abort the whole scan

  • Step 2: Run the full-scan tests and verify RED

Run:

npm test -- tests/full-scan-controller.test.ts

Expected: FAIL because the controller module does not exist yet.

  • Step 3: Write the minimal implementation

Implement the controller around injected dependencies:

  • current-page row reader
  • paginator
  • author metrics loader
  • result store writer

The controller should expose one explicit method per trigger source, such as:

  • ensureScanForFilter()

  • ensureScanForSort()

  • ensureScanForExport()

  • Step 4: Run the full-scan tests and verify GREEN

Run:

npm test -- tests/full-scan-controller.test.ts

Expected: PASS.

  • Step 5: Refactor to a single idempotent scan path

All trigger entry points should delegate to the same internal scan routine.

  • Step 6: Re-run the full-scan tests

Run:

npm test -- tests/full-scan-controller.test.ts

Expected: PASS.

  • Step 7: Commit

Run:

git add src/content/market/full-scan-controller.ts src/content/market/types.ts tests/full-scan-controller.test.ts
git commit -m "feat: add full scan controller"

Task 9: Implement the Plugin Toolbar and Market Composition

Files:

  • Create: src/content/market/plugin-toolbar.ts

  • Create: src/content/market/index.ts

  • Modify: src/content/index.ts

  • Test: tests/market-content-entry.test.ts

  • Step 1: Write the failing integration tests for the page entry flow

Cover:

  • market controller boots on the Xingtu market URL

  • current page rows are hydrated on start

  • applying plugin filters triggers full scan and hides non-matching rows

  • applying plugin sorting triggers full scan and reorders rows

  • export triggers full scan and hands the ordered visible records to the CSV exporter

  • Step 2: Run the market entry tests and verify RED

Run:

npm test -- tests/market-content-entry.test.ts

Expected: FAIL because the market controller composition does not exist yet.

  • Step 3: Write the minimal implementation

Implement:

  • toolbar controls for threshold inputs, sort selector, and export button

  • event wiring from toolbar to full scan, filter/sort controller, and exporter

  • market page bootstrap in src/content/index.ts

  • Step 4: Run the market entry tests and verify GREEN

Run:

npm test -- tests/market-content-entry.test.ts

Expected: PASS.

  • Step 5: Run the whole test suite

Run:

npm test

Expected: all test files PASS.

  • Step 6: Run the production build

Run:

npm run build

Expected: extension build succeeds with all planned bundles and manifest output.

  • Step 7: Commit

Run:

git add src/content/index.ts src/content/market/index.ts src/content/market/plugin-toolbar.ts tests/market-content-entry.test.ts
git commit -m "feat: wire market plugin controls"

Task 10: Manual Verification and Documentation

Files:

  • Modify: externaldocs/2026-04-20-star-chart-market-after-search-rate-plugin-spec.md (only if actual implementation forces scope adjustments)

  • Create: README.md

  • Step 1: Write a minimal README

Document:

  • install

  • test

  • build

  • load unpacked extension

  • manual verification checklist

  • Step 2: Run manual verification on the live page

Verify:

  • current page gets two new columns

  • loading, success, failed states render correctly

  • filter triggers scan and hides unmatched rows

  • sort triggers scan and reorders rows

  • export produces a CSV with plugin status fields

  • Step 3: Update the spec if implementation realities changed any promised behavior

Only adjust documented scope if the live page proves a requirement impossible or unstable.

  • Step 4: Run the full test suite again

Run:

npm test
npm run build

Expected: both commands succeed.

  • Step 5: Commit

Run:

git add README.md externaldocs/2026-04-20-star-chart-market-after-search-rate-plugin-spec.md
git commit -m "docs: add usage and verification notes"

Notes for Execution

  • Do not write production behavior before the corresponding failing test exists.
  • Keep DOM selectors isolated; the Xingtu page is likely to shift.
  • Reuse the old project's verified API field names, but do not copy large modules blindly; re-derive them under tests.
  • For full-scan logic, dependency-inject pagination and row reading so the orchestration stays testable.
  • If a task reveals a missing boundary in the spec, pause and update the spec before continuing.