Files
PlantAI-dev/.superpowers/sdd/task-3-verification.md
2026-08-03 18:42:47 +08:00

216 lines
8.1 KiB
Markdown
Executable File

# Task 3 Independent Verification — Typed STRING API and deterministic PPI ID resolution
## Verdict
**FAIL**
Most Task 3 behavior is implemented and passes fresh focused, backend, offline,
multiprocessing, syntax, and direct production-path checks. One blocking
protocol-validation defect remains: STRING mapping rows that omit (or provide a
non-string) `preferredName` are accepted as valid. The approved Task 3 requires
the mapping boundary to retain `queryItem/queryIndex/stringId/preferredName/
ncbiTaxonId`, reject malformed rows, and perform explicit JSON-shape validation.
## Blocking finding
### Mapping validation does not require `preferredName`
`skills/ppi/run.py:184-234` validates the payload container, `queryIndex`,
echoed `queryItem`, `stringId`, and `ncbiTaxonId`, but the malformed-row
condition at lines 226-232 never checks `preferredName`.
Fresh direct reproducer:
```python
payload = [{
"queryIndex": 0,
"queryItem": "A",
"stringId": "3562.A",
"ncbiTaxonId": 3562,
}]
StringAPI.validate_mapping_rows(payload, ["A"], "3562")
```
Exit code: `0`
```text
ACCEPTED_MALFORMED_MAPPING [{'queryIndex': 0, 'queryItem': 'A', 'stringId': '3562.A', 'ncbiTaxonId': 3562}]
```
This row should instead raise `StringApiError(kind="protocol")`. Without that
check, the typed STRING boundary silently converts absent/invalid preferred-name
evidence to an empty or stringified value later in resolution rather than
classifying the response as a protocol/shape failure. The focused malformed-row
parameterization at `tests/unit/test_ppi_id_resolution.py:507-534` covers a
missing `stringId` but has no missing/non-string `preferredName` case.
Required correction: include a non-empty string `preferredName` in the mapping
row shape validation and add focused missing/non-string regression cases.
## Baseline/current and scope evidence
Commands were run from `/data/project_roots/PlantAI-dev`. This workspace has no
functional Git repository, so the review-package baselines were used exactly as
directed:
```bash
diff -u /tmp/ppi-id-resolution-task3-baseline/run.py skills/ppi/run.py
diff -u /tmp/ppi-id-resolution-task3-baseline/pipeline_backend.py apps/web/pipeline_backend.py
diff -u /tmp/ppi-id-resolution-task3-baseline/test_ppi_string_policy.py tests/unit/test_ppi_string_policy.py
```
The expected exit status for changed pairs was `1`. The policy test pair was
identical (exit `0`). Complete-file inspection was performed for the new
`gfa_core/string_rate_gate.py` and `tests/unit/test_ppi_id_resolution.py` files.
The implementation changes are confined to the Task 3 allowlist:
- `skills/ppi/run.py`: typed API, relationship origins, deterministic candidate
resolution, selected-ID network submission, per-relationship report, and
empty-network handling.
- `gfa_core/string_rate_gate.py`: shared flock/timestamp pacing.
- `apps/web/pipeline_backend.py`: one import and the active optional STRING
image helper's injected client/gate plus `PlantAI` identity.
- `tests/unit/test_ppi_id_resolution.py`: new offline protocol, provenance,
execution, and multiprocessing tests.
- `.superpowers/sdd/task-3-report.md`: implementation record.
No Reset API, frontend labels, contract/diagnosis, broad Ortholog redesign, or
unrelated backend production change appears in the Task 3 baseline diff.
## Fresh required verification
### Focused Task 3 suite
```bash
/home/xia-lab/miniconda3/envs/test4py311/bin/python -m pytest \
tests/unit/test_ppi_id_resolution.py tests/unit/test_ppi_string_policy.py -q
```
Exit code: `0`
```text
collected 43 items
tests/unit/test_ppi_id_resolution.py ...............................
tests/unit/test_ppi_string_policy.py ............
43 passed in 0.62s
```
### Explicit no-live-network rerun
I reran the same 43 tests after replacing `socket.socket.connect` with a
function that raises `AssertionError("live network blocked by verifier")`.
Exit code: `0`
```text
43 passed in 0.57s
```
This establishes that the focused unit suite, including its forked-process
rate-gate checks, does not contact STRING or any other live network service.
### Relevant backend PPI/image selection
```bash
/home/xia-lab/miniconda3/envs/test4py311/bin/python -m pytest \
tests/unit/test_pipeline_backend_execution_env.py \
-k 'string_network_image or ppi' -q
```
Exit code: `0`
```text
collected 31 items / 5 deselected / 26 selected
26 passed, 5 deselected in 5.65s
```
### Syntax compilation
```bash
/home/xia-lab/miniconda3/envs/test4py311/bin/python -m py_compile \
gfa_core/string_rate_gate.py skills/ppi/run.py apps/web/pipeline_backend.py
```
Exit code: `0`; no output.
### Combined regression
```bash
/home/xia-lab/miniconda3/envs/test4py311/bin/python -m pytest \
tests/unit/test_id_resolution.py \
tests/unit/test_ppi_id_resolution.py \
tests/unit/test_ppi_string_policy.py \
tests/unit/test_pipeline_backend_execution_env.py -q
```
Exit code: `0`
```text
collected 86 items
86 passed in 6.59s
```
Pytest/import startup consistently emitted the pre-existing
`RequestsDependencyWarning` for the environment's installed urllib3 and
character-detection package versions. It did not affect any assertion.
## Direct production `PpiSkill.run()` probe
An independent temporary-file probe invoked the unmodified production
`PpiSkill.run()` with injected local fake STRING clients. It did not call the
network and verified all of the following in one run:
- 24 canonical-to-reference relationships remain 24 TSV data rows.
- The 24 origins collapse to 12 unique reference queries.
- Five reference queries resolve from raw versioned XP evidence and seven only
from local numeric GeneID fallback.
- `get_network()` receives exactly 12 deduplicated selected STRING IDs, not raw
XP IDs, local GeneIDs, or 24 duplicated relationship values.
- A valid `network -> []` returns stage success with
`network_status=empty_network` and a header-only
`protein1/protein2/score` TSV.
- Zero resolved STRING IDs return `status=error` with
`ppi_string_id_resolution_failed` and do not call `get_network()`.
- A network `StringApiError(kind="http", status_code=503)` propagates as that
typed external API failure and is not converted to empty-network success.
Exit code: `0`
```text
DIRECT_PROBE_PASS 24_relationships 12_queries 5_raw 7_geneid; empty_network_success; zero_resolution_short_circuit; api_failure_typed
```
## Other requirement evidence
- `PpiSkill.run()` constructs origins from exact versioned Ortholog references,
calls the integrated resolver, writes one summary row per retained
relationship, and submits `resolution.selected_string_ids` to the network
call.
- Candidate probes and final selected STRING IDs are deduplicated while origin
rows are not. Conflicting local candidates and conflicting Ortholog
references remain explicit; converging references remain resolved.
- Mapping `404` and `200 []` are zero-resolution responses. Other HTTP,
transport, JSON, and the currently validated protocol failures remain typed.
- `network -> []` and network HTTP failure follow distinct paths.
- Version discovery accepts both `stable_address` and
`string_stable_address`; identifier endpoints use POST, mapping uses
`echo_query=1`, scientific calls use `caller_identity=PlantAI`, and the
request timeout is 30 seconds.
- `StringRateGate` uses the common default/env-selected timestamp path,
`fcntl.flock`, an atomically replaced timestamp file, a one-second production
interval, and injectable clock/sleeper. Scientific API calls pass through it
in `_request_json`; every optional image/SVG attempt calls the same gate type.
Fresh real multiprocessing tests verified scientific/scientific and
scientific/image serialization.
## Scope and residual validation
- No production or test source file was edited during verification. This
verification document is the only created file.
- No live STRING call, backend service execution, Celery restart, or full real
job was performed. Those are explicitly deferred to Task 6; Task 3 uses
realistic local fakes and temporary data.
- Task 3 cannot pass until malformed mapping rows lacking a valid
`preferredName` are rejected with a typed protocol error and covered by a
regression test.