8.1 KiB
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:
payload = [{
"queryIndex": 0,
"queryItem": "A",
"stringId": "3562.A",
"ncbiTaxonId": 3562,
}]
StringAPI.validate_mapping_rows(payload, ["A"], "3562")
Exit code: 0
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:
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 plusPlantAIidentity.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
/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
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
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
/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
collected 31 items / 5 deselected / 26 selected
26 passed, 5 deselected in 5.65s
Syntax compilation
/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
/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
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 withnetwork_status=empty_networkand a header-onlyprotein1/protein2/scoreTSV. - Zero resolved STRING IDs return
status=errorwithppi_string_id_resolution_failedand do not callget_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
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 submitsresolution.selected_string_idsto 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
404and200 []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_addressandstring_stable_address; identifier endpoints use POST, mapping usesecho_query=1, scientific calls usecaller_identity=PlantAI, and the request timeout is 30 seconds. StringRateGateuses 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
preferredNameare rejected with a typed protocol error and covered by a regression test.