think
16px
820px

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_prem
  • endpoints: ["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: /health
  • models: all 12 op-labels from facade.go's FacadePaths (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_URL base-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.goTestGPUServerFacadeSmoke (in-process).
    Reuses the facade_test.go helpers (newFacadeStub / facadeServer / fastMgr /
    pollUntilTerminal). Frames the stub URL as the GPU_SERVER_URL swap target and
    asserts the round-trip specifically:
    1. POST /jobs (generic path) → 202 {job_id} → poll → completed with the stub's
    result verbatim (path-distinctive marker gpu-jobs).
    2. POST /ktp/cleanup (per-op path) → same round-trip, marker gpu-ktp-cleanup.
    3. Idempotent resubmit of /domisili/extract with 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.sh style: GET /health → submit to a façade path (default
    /ktp/cleanup, overridable via PATH_UNDER_TEST) → poll to completed → idempotent
    resubmit same job_id → unknown id 404. bash -n clean, 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:8000 loopback per host survey —
    gateway must share host netns or the URL must be adjusted) answers GET /health
    from the gateway container.
  • [ ] A real document POSTed to a per-op path (e.g. /ktp/cleanup) round-trips to
    completed via scripts/facade_smoke.sh.
  • [ ] With GPU_SERVER_URL=<gateway> set in ahu-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.audit with operation=ocr +
    the per-op model label + 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 TestFacadeAllPaths on mechanics; it is
    kept intentionally narrow (base-URL-swap framing + result-passthrough + dedup-count)
    rather than re-testing all 12 paths.