Files
PlantAI-dev/docs/validation/2026-07-21-string-ppi-task2-identity.md
2026-08-03 18:42:47 +08:00

302 lines
11 KiB
Markdown

# STRING PPI production wiring — Task 2 identity validation
Date: 2026-07-21
Authoritative target: `/home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev`
Scope: job-local target identity input only; no resolver/preflight decision wiring.
## Boundaries
- Added a read-only job-local identity reader for `config.xlsx`.
- Added independent request fields `targetScientificName` and `ppiTargetTreeTaxonId`.
- Passed those fields to the backend and returned a separate `target_identity` object.
- Did not modify KaKs, Ortholog, PPI execution, legacy recommendation replacement, or STRING resolver preflight decisions.
## Discovery evidence
The required first CodeGraph query was attempted:
```bash
timeout 15s codegraph explore "PpiSpeciesResolutionRequest species-resolution API job config.xlsx Species_Code Species_Name Taxon_ID TaxID NCBI_TaxID targetScientificName ppiTargetTreeTaxonId"
```
Exit code: `124`; no output. Targeted `rg` and bounded source reads were used after recording the timeout.
## RED evidence
Initial identity suite:
```bash
ssh planai5090 'cd /home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev && /home/xzq/miniconda3/envs/test4py311/bin/python -m pytest -q tests/unit/test_ppi_target_identity.py'
```
Exit code: `1`. Result: `6 failed in 1.78s`.
- Five tests failed because `resolve_ppi_target_identity` did not exist.
- The request-contract test failed because `PpiSpeciesResolutionRequest` had no `targetScientificName` field.
Additional backend propagation RED:
```bash
ssh planai5090 'cd /home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev && /home/xzq/miniconda3/envs/test4py311/bin/python -m pytest -q tests/unit/test_ppi_target_identity.py::test_explicit_identity_reaches_backend_without_using_ppi_species'
```
Exit code: `1`. Result: `1 failed in 0.32s`; failure was `TypeError: resolve_ppi_species_resolution() got an unexpected keyword argument 'target_scientific_name'`.
## GREEN evidence
Focused Task 2 suite:
```bash
ssh planai5090 'cd /home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev && /home/xzq/miniconda3/envs/test4py311/bin/python -m pytest -q tests/unit/test_ppi_target_identity.py'
```
Exit code: `0`. Result: `7 passed in 1.80s`.
### Review-blocker correction: scientific-name conflict
The first Gate 2 review found that an explicit scientific name could be mixed
with a different job scientific name and the job TaxID. Two negative tests were
added before changing production code:
```bash
ssh planai5090 'cd /home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev && /home/xzq/miniconda3/envs/test4py311/bin/python -m pytest -q tests/unit/test_ppi_target_identity.py::test_explicit_scientific_name_conflict_never_mixes_with_job_taxon tests/unit/test_ppi_target_identity.py::test_species_resolution_api_surfaces_scientific_name_conflict'
```
RED exit code: `1`. Result: `2 failed in 1.31s`; both helper and API returned
`resolved` instead of `conflict`.
After adding only the normalized-name consistency check, the same command had
exit code `0`: `2 passed in 1.25s`. The full Task 2 identity suite then had exit
code `0`: `9 passed in 1.37s`.
The conflict response is `target_scientific_name_conflict` and deliberately
leaves both `scientific_name` and `tree_taxon_id` empty, preventing a mixed
name/TaxID identity from reaching later stages.
Syntax and related identity/API/snapshot regression:
```bash
ssh planai5090 'cd /home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev && /home/xzq/miniconda3/envs/test4py311/bin/python -m py_compile apps/web/pipeline_backend.py apps/web/vue3/main.py tests/unit/test_ppi_target_identity.py && /home/xzq/miniconda3/envs/test4py311/bin/python -m pytest -q tests/unit/test_ppi_target_identity.py tests/unit/test_ppi_frontend_display_contract.py tests/unit/test_string_species_snapshot.py tests/unit/test_string_species_registry.py'
```
Exit code: `0`. Result: `46 passed in 13.67s`.
Existing backend preflight regression probe:
```bash
ssh planai5090 'cd /home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev && /home/xzq/miniconda3/envs/test4py311/bin/python -m pytest -q tests/unit/test_pipeline_backend_execution_env.py -k "ppi_species_resolution or os_direct_ppi"'
```
Exit code: `1`. Result: `8 passed, 1 failed, 30 deselected`.
The sole failure is `test_os_direct_ppi_uses_complete_string_registry_without_ortholog`, which expects a top-level `selection_mode`. That field belongs to Task 3 resolver/preflight wiring and is intentionally absent after Task 2. It is not claimed as passing here and was not repaired out of scope.
## Identity contract verified
- `Species_Code=Sv` plus repeated `Setaria_viridis` / `Setaria viridis` rows yields one canonical `Setaria viridis` identity.
- `Taxon_ID`, `TaxID`, and `NCBI_TaxID` are accepted; within one row their priority is that order.
- Explicit tree TaxID has priority but a mismatch with the unique job TaxID returns `target_taxon_id_conflict`; no silent overwrite occurs.
- Bare `Sv` with no job or explicit identity returns `target_string_tree_unresolved`; no two-letter-code guess occurs.
- Existing curated `Os -> 39947` and `Ft -> 62330` compatibility identities remain available.
- `ppiSpecies` remains `auto` when explicit target identity fields are supplied; target tree identity is not placed in `ppiSpecies`.
## Changed files and hashes
```text
apps/web/pipeline_backend.py
1c94e33a061fdeba6cc3816968b4e722a03fd3a2886665f6e28d848e24003ccf
apps/web/vue3/main.py
f12cddaaeb0d3e39165884cf1603ef05a79a14ceb02da5e1629fe83fd4e4180f
tests/unit/test_ppi_target_identity.py
7282a78e0e8a384f1ea1ad2abc58464b7d204ddd27e65a8da6e46b11e5965bdc
```
## Gate 2
- Job scientific-name identity: PASS.
- Explicit scientific-name and target-tree-TaxID request channels: PASS.
- Supported TaxID columns and deterministic row priority: PASS.
- Explicit/job TaxID conflict is structured: PASS.
- Explicit/job normalized scientific-name conflict is structured at helper and API layers: PASS.
- Conflicting scientific name is never combined with the job TaxID: PASS.
- Bare unknown short code is not guessed: PASS.
- Os/Ft compatibility identity: PASS.
- Target identity remains separate from `ppiSpecies`: PASS.
- Task 2 focused and related non-resolver regressions: PASS.
- Task 3 `selection_mode` regression: NOT PASSED / out of Task 2 scope.
Task 2 Gate: **PASS for identity-input scope only**. This does not authorize or claim Task 3 resolver/preflight completion.
## Online Gate on port 9010
The target has no systemd unit or dedicated 9010 restart script. The observed
authoritative process-management pattern was:
- `start_celery_9000.sh` with externally supplied
`BBAV5_QUEUE_PREFIX=plantai_4090_9010` and
`BBAV5_REDIS_URL=redis://localhost:6379/11` for five workers;
- direct `nohup ... python -m uvicorn apps.web.vue3.main:app --port 9010`
for the Web process.
Before restart, the exact 9010 master PIDs were:
```text
Web 2391890
default 2391860
heavy 2391866
medium 2391873
light 2391880
rnaseq 2391891
launcher shell 2391849
```
All six service processes had cwd
`/home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev`, queue prefix
`plantai_4090_9010`, and Redis URL `redis://localhost:6379/11`.
Only those exact PIDs were stopped:
```bash
kill -TERM 2391890 2391860 2391866 2391873 2391880 2391891 2391849
```
Workers were restarted with:
```bash
env BBAV5_PROJECT_ROOT=/home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev \
PLANTAI_PROJECT_ROOT=/home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev \
BBAV5_CONDA_BASE=/home/xzq/miniconda3 \
BBAV5_DEFAULT_ENV=test4py311 \
BBAV5_QUEUE_PREFIX=plantai_4090_9010 \
BBAV5_REDIS_URL=redis://localhost:6379/11 \
BBAV5_CELERY_LOG_DIR=logs/runtime_9010/celery \
BBAV5_RNASEQ_CONCURRENCY=2 \
bash ./start_celery_9000.sh
```
Web was restarted with:
```bash
nohup env BBAV5_PROJECT_ROOT=/home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev \
PLANTAI_PROJECT_ROOT=/home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev \
BBAV5_QUEUE_PREFIX=plantai_4090_9010 \
BBAV5_REDIS_URL=redis://localhost:6379/11 \
/home/xzq/miniconda3/envs/test4py311/bin/python -m uvicorn \
apps.web.vue3.main:app --host 0.0.0.0 --port 9010 \
> logs/runtime_9010/web.log 2>&1 < /dev/null &
```
The combined remote shell initially attempted to write `web.pid` before its
backgrounded command group had created `logs/runtime_9010`, producing one
`No such file or directory` message. Process inspection showed the Web process
was healthy at PID `2455555`; the temporary wrapper shell PID `2455480` was
terminated and `logs/runtime_9010/web.pid` was corrected to `2455555`. No second
Web process was started.
After restart, the master PIDs were:
```text
Web 2455555
default 2455521
heavy 2455528
medium 2455537
light 2455544
rnaseq 2455556
```
Every PID changed. All six new processes have the authoritative target cwd,
queue prefix `plantai_4090_9010`, and Redis DB 11. The exact worker health check
was:
```bash
env BBAV5_PROJECT_ROOT=/home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev \
PLANTAI_PROJECT_ROOT=/home/xzq/5090/PlantAI-dev4090-7/PlantAI-dev \
BBAV5_QUEUE_PREFIX=plantai_4090_9010 \
BBAV5_REDIS_URL=redis://localhost:6379/11 \
/home/xzq/miniconda3/envs/test4py311/bin/celery -A celery_app inspect ping \
-d plantai_4090_9010_default@xzq-Rack-Server,plantai_4090_9010_heavy@xzq-Rack-Server,plantai_4090_9010_medium@xzq-Rack-Server,plantai_4090_9010_light@xzq-Rack-Server,plantai_4090_9010_rnaseq@xzq-Rack-Server \
--timeout=10
```
Exit code: `0`; result: `5 nodes online`, all five returned `pong`. HTTP
`GET http://127.0.0.1:9010/docs` returned `200`.
### Live API evidence
Both requests used the real Sv job `job_1784552755238_b8762f` and endpoint
`POST http://127.0.0.1:9010/api/ppi/species-resolution`.
Exact request commands:
```bash
curl -sS -X POST http://127.0.0.1:9010/api/ppi/species-resolution \
-H "Content-Type: application/json" \
--data-binary '{"job_id":"job_1784552755238_b8762f","prefix":"Sv.HKL","ppiSpecies":"auto","targetSpecies":["Sv"],"referenceSpecies":[],"geneFamily":"HKL","requestedStages":["ppi"],"targetScientificName":"Setaria viridis","ppiTargetTreeTaxonId":"4556"}'
curl -sS -X POST http://127.0.0.1:9010/api/ppi/species-resolution \
-H "Content-Type: application/json" \
--data-binary '{"job_id":"job_1784552755238_b8762f","prefix":"Sv.HKL","ppiSpecies":"auto","targetSpecies":["Sv"],"referenceSpecies":[],"geneFamily":"HKL","requestedStages":["ppi"],"targetScientificName":"Oryza sativa","ppiTargetTreeTaxonId":"39947"}'
```
Consistent explicit identity:
```json
{
"targetScientificName": "Setaria viridis",
"ppiTargetTreeTaxonId": "4556"
}
```
Response summary:
```json
{
"success": true,
"requested_ppi_species": "auto",
"selection_mode": null,
"target_identity": {
"status": "resolved",
"scientific_name": "Setaria viridis",
"tree_taxon_id": "4556",
"identity_source": "explicit_taxon_id",
"config_scientific_name": "Setaria viridis",
"issue_code": ""
}
}
```
Conflicting explicit identity:
```json
{
"targetScientificName": "Oryza sativa",
"ppiTargetTreeTaxonId": "39947"
}
```
Response summary:
```json
{
"success": true,
"requested_ppi_species": "auto",
"selection_mode": null,
"target_identity": {
"status": "conflict",
"issue_code": "target_scientific_name_conflict",
"scientific_name": "",
"tree_taxon_id": "",
"explicit_scientific_name": "Oryza sativa",
"config_scientific_name": "Setaria viridis"
}
}
```
`selection_mode=null` is expected because Task 3 resolver/preflight wiring is
not part of this Gate. Port 9000 and port 19000 processes were not targeted or
restarted.
Online Task 2 Gate: **PASS**.