Gateway P2.5 — Task 3 (final): packaging, docs, smoke, OCR compat note
Branch: feat/gateway-p2.5-gpuserver-facade · Base HEAD: c4bfab0
What shipped
Task 3 is packaging + docs + smoke for the OCR gpu-server compat façade delivered
in Tasks 1–2. No behavior change to the façade itself; this makes it configurable,
documented, and smoke-able, and prepares the OCR engine flip.
1. Example config — deploy/gateway.example.yaml
Added a gpu-server upstream:
type: ocr-http,adapter: gpuserver-job,operation: ocr,class: on_premendpoints: ["http://127.0.0.1:8000"]— placeholder loopback per the host survey
(gpu-server is loopback-bound), with a comment to swap to the real URL at deploy.probe_path: /healthmodels:all 12 op-labels fromfacade.go'sFacadePaths(jobs-generic,
ktp-cleanup, override-scrutiny, perbaikan-advisory, perbaikan-ai-summary,
perbaikan-surat-pernyataan, pendirian-pp-surat-pernyataan, bukti-setor, domisili,
jual-beli, contact-info, berita-acara-rups), each commented with its POST path.slots: {total: 8, batch_max: 6}.- A block comment explaining that enabling this upstream activates the façade (owns
POST /jobs+ the 11 per-op paths +GET /jobs/{id}), that the OCR flip is a
GPU_SERVER_URLbase-URL swap, and that with no gpuserver-job upstream the gateway
is byte-identical to P2.
Verified config.Load accepts it: a throwaway test loaded the example config,
found the gpu-server upstream, and asserted adapter=gpuserver-job, operation=ocr,
12 models, slots {8,6}. Passed, then removed.
2. README — new "GPU-server compatibility façade" section
Placed before the deploy live-fire checklist. Covers: activation gating (mounts only
with a gpuserver-job upstream; dormant otherwise); the 12 mirrored submit paths +
op-labels table (all 202 {job_id}, body forwarded verbatim, Idempotency-Key
required); the gpu-server-shaped poll GET /jobs/{id} (queued/processing/completed/
failed field shapes + 404 {"code":"JOB_UNKNOWN"} + GET /health); that the OCR flip
is a single GPU_SERVER_URL base-URL swap with no engine code change; and a façade
smoke subsection (in-process Go test + deploy-time bash script). Also added two new
items to the live-fire checklist (gpu-server reachability via facade_smoke.sh; the
engine base-URL swap round-trip) and enriched the audit-stream item with the per-op
model label.
3. Smoke
Task 2 already ships TestFacadeAllPaths — a thorough 12-path table test through the
real mux against a stub gpu-server, with audit per-op attribution + idempotent
resubmit + 404 + drain + dormancy. To avoid pointless duplication I added a focused
pair (mirroring P2's TestJobAPISmoke + jobs_smoke.sh):
internal/server/facade_smoke_test.go—TestGPUServerFacadeSmoke(in-process).
Reuses the facade_test.go helpers (newFacadeStub / facadeServer / fastMgr /
pollUntilTerminal). Frames the stub URL as theGPU_SERVER_URLswap target and
asserts the round-trip specifically:
1.POST /jobs(generic path) → 202 {job_id} → poll → completed with the stub's
resultverbatim (path-distinctive markergpu-jobs).
2.POST /ktp/cleanup(per-op path) → same round-trip, markergpu-ktp-cleanup.
3. Idempotent resubmit of/domisili/extractwith the same Idempotency-Key → SAME
job_id AND the stub gpu-server saw exactly one submit for that path.scripts/facade_smoke.sh— deploy-time over-the-wire drill matching
scripts/jobs_smoke.shstyle:GET /health→ submit to a façade path (default
/ktp/cleanup, overridable viaPATH_UNDER_TEST) → poll to completed → idempotent
resubmit same job_id → unknown id 404.bash -nclean,chmod +x.
4. OCR compat note — docs/integration/prompt-ahu-ocr-tidyup.md
Added a P2.5 blockquote at the top: the gateway gpu-server compat façade now EXISTS;
it mirrors the contract (same 12 paths, 202 {job_id}, gpu-server-shaped
GET /jobs/{id}, 404 JOB_UNKNOWN); so the Phase-B flip is simply setting
GPU_SERVER_URL=<gateway> with no engine code change beyond the env flip already
prepared. Notes the façade activates only with a gpuserver-job upstream (dormant
otherwise), Idempotency-Key stays required, resubmit-on-404 works, and the other
Phase-B seams (Azure/Paddle/classifier) still use the generic Job API / adapters.
5. CONVENTIONS.md — untouched (correctly)
The façade mirrors the already-documented gpu-server contract; queue semantics,
JOB_UNKNOWN, and audit schema are all already specified. No genuine clarification was
needed, so per its own version-bump rule it was left alone.
Build / vet / test
go build ./...— clean.go vet ./...— clean.go test -race ./internal/jobs/ ./internal/server/— ok (jobs 4.35s, server 2.75s).go test ./...— all packages ok.
Deploy live-fire checklist (NOT run here — no real gpu-server reachable from dev box)
- [ ] gpu-server
endpoints[0](http://127.0.0.1:8000loopback per host survey —
gateway must share host netns or the URL must be adjusted) answersGET /health
from the gateway container. - [ ] A real document POSTed to a per-op path (e.g.
/ktp/cleanup) round-trips to
completedviascripts/facade_smoke.sh. - [ ] With
GPU_SERVER_URL=<gateway>set inahu-ocr-tidyup, one document flows
through a per-op path and the engine reads job_id / polls / gets result unchanged
(env-only flip, no engine code). - [ ] An OCR job produces an audit event on
ahu.ai.auditwithoperation=ocr+
the per-opmodellabel +doc_hash.
Concerns
- Loopback endpoint reachability: the gpu-server is loopback-bound (127.0.0.1:8000).
The gateway container can only reach it if it shares the host network namespace, or
the gpu-server is republished on a reachable interface. Flagged in the config comment
and the checklist. This is a deploy-time decision, not a code issue. - The in-process smoke necessarily overlaps
TestFacadeAllPathson mechanics; it is
kept intentionally narrow (base-URL-swap framing + result-passthrough + dedup-count)
rather than re-testing all 12 paths.