Image rights / copyright detection system: SQLite store, HTTP app, search integrations (Naver, Google Custom Search, Google Cloud Vision web detection), image analysis (fingerprints, face/person detection, evidence enrichment, risk scoring), an admin/review layer, governance and retention policies, batch jobs, and a browser-based operator GUI. This baseline incorporates a full code-review remediation pass (46 fixes; 358 tests passing). Highlights: CRITICAL - Prevent evidence cascade-delete during the schema-constraint migration by disabling FK enforcement around the table rebuild. Security - Sandbox served media (neutralize stored XSS from uploaded/collected SVGs) via CSP + nosniff on the untrusted media routes. - Strip embedded EXIF/GPS from external image derivatives before they are sent to third-party APIs. - Return a clean 404 (not an uncaught StopIteration) for PATCH on an unknown provider. Correctness - LLM-summary failures no longer add +30 to the risk score. - Decode only explicit JS escapes so Korean image URLs are not mangled. - Consume search quota only after a successful request. - Naver/Google adapters map responses inside the failure boundary, so a malformed response degrades to evidence instead of crashing enrichment. - Domain-aware provider attribution; face-box IoU de-duplication; count searches (not result items); per-box crop isolation; clamp evidence confidence and Google CSE num; real submittedEpoch; and more. Robustness - Offline LLM connect fast-fails (short connect timeout) so seed/reload requests are not stalled; full read timeout preserved for generation. - Malformed numeric env vars fall back to defaults instead of crashing startup. Performance - Per-submission evidence reads (no full-table scan per rescore), audit-log LIMIT, lazy active-store lookup, hoisted timestamps. Tests - ~24 regression tests added pinning the above fixes. Runtime data (data/, outputs/, *.sqlite3, *.log), secrets (.env), and node_modules are gitignored.
113 lines
4.9 KiB
Markdown
113 lines
4.9 KiB
Markdown
# Multi Candidate Knowledge Promotion 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:** Allow an operator to select several collected image-search candidates and promote them into one knowledge-base entry with multiple visual samples.
|
|
|
|
**Architecture:** Keep the current Naver candidate collection flow and add a batch-promotion path beside the existing single-candidate path. The SQLite store owns the merge behavior, the HTTP layer exposes one batch endpoint, and the GUI adds selection controls plus one shared promotion form.
|
|
|
|
**Tech Stack:** Python standard-library HTTP server, SQLite JSON payload store, pytest, static HTML/CSS/JavaScript operator GUI.
|
|
|
|
---
|
|
|
|
### Task 1: Store Contract
|
|
|
|
**Files:**
|
|
- Modify: `tests/rights_filter/server/test_sqlite_store.py`
|
|
- Modify: `src/rights_filter/server/sqlite_store.py`
|
|
|
|
- [ ] **Step 1: Write the failing test**
|
|
|
|
Add a test that collects two candidates and calls `promote_collection_candidates({"candidate_ids": [...]})`. Assert that one knowledge entry is created, both candidate fingerprints are preserved, and both candidates point to the same `promotedKnowledgeId`.
|
|
|
|
- [ ] **Step 2: Run the test to verify RED**
|
|
|
|
Run: `python -m pytest tests/rights_filter/server/test_sqlite_store.py::test_sqlite_store_promotes_multiple_candidates_into_one_knowledge_entry -q`
|
|
|
|
Expected: fail because `CopyrighterStore.promote_collection_candidates` does not exist.
|
|
|
|
- [ ] **Step 3: Implement the store merge**
|
|
|
|
Add `promote_collection_candidates` and let the existing `promote_collection_candidate` delegate to it with a single ID. The merged knowledge payload must include `sourceCandidates`, `sampleFingerprints`, `imageAsset`, `imageAssets`, `imageFacts`, and operator metadata.
|
|
|
|
- [ ] **Step 4: Run the store tests**
|
|
|
|
Run: `python -m pytest tests/rights_filter/server/test_sqlite_store.py::test_sqlite_store_collects_keyword_candidates_and_promotes_one_to_knowledge tests/rights_filter/server/test_sqlite_store.py::test_sqlite_store_promotes_multiple_candidates_into_one_knowledge_entry -q`
|
|
|
|
Expected: pass.
|
|
|
|
### Task 2: HTTP Endpoint
|
|
|
|
**Files:**
|
|
- Modify: `tests/rights_filter/server/test_http_app.py`
|
|
- Modify: `src/rights_filter/server/http_app.py`
|
|
|
|
- [ ] **Step 1: Write the failing test**
|
|
|
|
Add a test that posts to `POST /api/collections/candidates/promote-batch` with two candidate IDs and asserts that the response contains one merged knowledge entry.
|
|
|
|
- [ ] **Step 2: Run the test to verify RED**
|
|
|
|
Run: `python -m pytest tests/rights_filter/server/test_http_app.py::test_http_server_promotes_multiple_collection_candidates_into_one_knowledge_entry -q`
|
|
|
|
Expected: fail with HTTP 404 or missing route.
|
|
|
|
- [ ] **Step 3: Implement the route**
|
|
|
|
Route `/api/collections/candidates/promote-batch` to `store.promote_collection_candidates(body)` and keep `/api/collections/candidates/{id}/promote` intact.
|
|
|
|
- [ ] **Step 4: Run the HTTP tests**
|
|
|
|
Run: `python -m pytest tests/rights_filter/server/test_http_app.py::test_http_server_collects_keyword_candidates_and_promotes_candidate tests/rights_filter/server/test_http_app.py::test_http_server_promotes_multiple_collection_candidates_into_one_knowledge_entry -q`
|
|
|
|
Expected: pass.
|
|
|
|
### Task 3: Operator GUI
|
|
|
|
**Files:**
|
|
- Modify: `tests/operator_gui/test_static_workbench.py`
|
|
- Modify: `web/operator-gui/index.html`
|
|
- Modify: `web/operator-gui/app.js`
|
|
- Modify: `web/operator-gui/styles.css`
|
|
|
|
- [ ] **Step 1: Write the static GUI test**
|
|
|
|
Assert that the GUI exposes candidate checkboxes, a shared collection promotion form, and a call to `/api/collections/candidates/promote-batch`.
|
|
|
|
- [ ] **Step 2: Run the static GUI test to verify RED**
|
|
|
|
Run: `python -m pytest tests/operator_gui/test_static_workbench.py::test_operator_gui_exposes_keyword_candidate_collection_workflow -q`
|
|
|
|
Expected: fail because the batch form and handler are not present.
|
|
|
|
- [ ] **Step 3: Implement the UI**
|
|
|
|
Add checkboxes to candidate cards, a compact batch promotion form under the candidate list, and a `promoteSelectedCollectionCandidates` handler that posts selected IDs plus name/type/aliases/keywords/memo.
|
|
|
|
- [ ] **Step 4: Run GUI checks**
|
|
|
|
Run: `python -m pytest tests/operator_gui/test_static_workbench.py -q`
|
|
Run: `node --check web/operator-gui/app.js`
|
|
|
|
Expected: pass.
|
|
|
|
### Task 4: End-To-End Verification
|
|
|
|
**Files:**
|
|
- No additional source files.
|
|
|
|
- [ ] **Step 1: Run full automated verification**
|
|
|
|
Run: `python -m pytest`
|
|
|
|
Expected: all tests pass.
|
|
|
|
- [ ] **Step 2: Restart local server on port 9500**
|
|
|
|
Run: `python run_copyrighter_server.py --host 127.0.0.1 --port 9500`
|
|
|
|
Expected: `/health` returns `{"status":"ok","port":9500}`.
|
|
|
|
- [ ] **Step 3: Visual smoke check**
|
|
|
|
Open `http://127.0.0.1:9500`, switch to the Knowledge Base view, and confirm candidate cards show stable checkboxes and a single batch-promotion control.
|