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 valuessrc/content/market/api-client.ts: fetch and map/gw/api/aggregator/get_author_ase_infosrc/content/market/result-store.ts: hold per-author status and merged market recordssrc/content/market/filter-sort-controller.ts: apply threshold filters and lower-bound sortingsrc/content/market/dom-sync.ts: locate market rows, inject two columns, and update row visibility/ordersrc/content/market/full-scan-controller.ts: paginate through filtered result pages and hydrate the result storesrc/content/market/csv-exporter.ts: generate CSV rows and blob/download metadatasrc/content/market/plugin-toolbar.ts: render the plugin controls for threshold filter, sorting, and exportsrc/content/market/index.ts: compose all market modules for the live pagesrc/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.jsonscripts:build,test,test:watchtsconfig.jsonvitest.config.tsscripts/build.mjssrc/manifest.jsonsrc/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 missingorfailedbehavior 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.