Skip to content

feat(health): bind probe process from listen env - #132

Closed
cursor[bot] wants to merge 23 commits into
mainfrom
cursor/bc-7685d1c2-599c-4a23-b5ee-495fc24b1b3f-068b
Closed

feat(health): bind probe process from listen env#132
cursor[bot] wants to merge 23 commits into
mainfrom
cursor/bc-7685d1c2-599c-4a23-b5ee-495fc24b1b3f-068b

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Why

PR #122 can serve GET /live and GET /ready after accept, but a buyer still cannot start a process from environment input. Without HEALTH_LISTEN_ADDR or platform PORT, a load balancer has nothing to probe.

TDD

RED required missing/padded/unknown listen, store, and backlog values to fail closed, GET /live to stay HTTP 200 when DATABASE_URL is down, and GET /ready to stay HTTP 503 without driver text. GREEN adds run_health_process plus those contracts. RFC 9110 is recorded in doctoring for origin-server authority.

Scope

Out of scope

Test plan

  • cargo test --lib health_process --test health_process_contract --test documentation_architecture_contract --test traceability_active_pr_contract
  • cargo clippy --all-targets -- -D warnings
  • cargo test --test postgres_health_process_contract (needs TEST_DATABASE_URL)

Do not merge until exact-head checks and independent last-push approval are satisfied. Never self-approve.

Operator next action

Prefer this head over #122 once checks are green. Set HEALTH_LISTEN_ADDR or PORT, then call run_health_process. Point liveness at GET /live and readiness at GET /ready. Set DATABASE_URL only when readiness should observe PostgreSQL. Set HEALTH_BACKLOG_HEALTH=within_bounds only after backlog is measured. Close or retarget #122, #117, #111, #91, and #102 after this lands.

Open in Web View Automation 

seonghobae and others added 19 commits August 16, 2026 19:51
RED: the domain health snapshot is not yet exposed as GET /live and
GET /ready with fail-closed readiness and a safe as-built OpenAPI 3.2
contract.
Map RuntimeHealthSnapshot to GET /live and GET /ready with fail-closed
readiness, RFC 9457 problem details for unsupported requests, and an
as-built OpenAPI 3.2.0 document that lists only those operations.
Exercise HealthHttpResponse::content_type and GET /ready?capability= from
the library test module so Linux line coverage can reach the remaining
unexecuted instantiations.
* feat(health): bind a TCP listener for operator probes

GET /live and GET /ready already exist as a request translator. Bind a
blocking listener that serves one HTTP/1.1 request per accepted
connection without adding public product routes, TLS, or SLO values.

* docs(traceability): name Active PR #92 on the bound health listener

* feat(health): answer probes from a PostgreSQL operational snapshot

* test(health): require a composed PostgreSQL operational snapshot

RED: runtime and relation probes exist, but callers still assemble
liveness, backlog, integrity, and the postgres capability by hand.

* feat(health): compose PostgreSQL probes into one operational snapshot

Map runtime and relation probes plus caller-supplied backlog into a
RuntimeHealthSnapshot. Probe failure becomes unknown/unready evidence
and never returns a driver error.

* feat(health): answer probes from a PostgreSQL operational snapshot

Rebuild #95 on the current #92 listener plus the #93 snapshot composer.
handle_postgres_health_http_request and accept_one_postgres_health_http
observe the caller-owned store and reuse the existing probe translator.

* docs(health): name #95 snapshot wiring on the current listener stack

Restore the architecture mappings the rewrite dropped: TRACEABILITY
module and capability rows, OPERABILITY fail-closed snapshot readiness,
ADR-0014 as-built status, and the composed-snapshot changelog line.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Keep Active PR #91 as the remaining health HTTP stack after #76
landed on main. Drop the stale #76 active-work entry.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
The inherited #72 recovery fixture inserted a processing consumption
row without claim_deadline_at. Migration 0019 requires that column for
processing rows, and the deadline trigger is UPDATE-only, so exact-head
CI failed closed. Seed a valid persisted claim and assert the deadline
survives COPY restore.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
#92, #93, and #95 were absorbed into this branch. Keep TRACEABILITY and
ADR-0014 as-built status pointing at the remaining health HTTP PR so
closed stacked numbers are not treated as independent Active work.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
…port

RED: GET /live must not observe PostgreSQL, bare GET /ready must fail
closed on a read-only store, incomplete or oversized requests must
return 400 without echoing input, and the as-built OpenAPI /live
operation must document HTTP 503.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Observe PostgreSQL only for GET /ready, after accept. Bare /ready
requires postgres_operational_store. The listener applies a 2-second
I/O timeout, rejects incomplete or oversized requests without echo,
and adds Cache-Control: no-store plus Allow: GET on 405.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Operators can now run a blocking accept loop so a load balancer can keep
asking GET /live and GET /ready. Interrupted accepts retry; any other
accept, read, or write error stops the loop. TLS and keep-alive stay out
of this slice.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Keep #91 as the predecessor translator head. Operators should land the
serve-loop successor so a load balancer can keep probing.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
A load-balancer reset after accept no longer stops GET /live or GET /ready.
Accept still retries Interrupted/ConnectionAborted/ConnectionReset and stops on WouldBlock.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Prove the accept-retry or-arm and the PostgreSQL serve loop still
answer GET /live after a dropped probe. Cite RFC 9293 for TCP reset
processing.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Unsupported probe methods and paths now emit
urn:psychometrics-commons:problem:* instead of about:blank so operators
can distinguish bad-request, not-found, and method-not-allowed without
echoing raw input.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Add a fail-closed process entrypoint so operators can start GET /live and
GET /ready from HEALTH_LISTEN_ADDR or platform PORT. DATABASE_URL is
observed only after accept and only for readiness. A down store keeps
liveness up and never echoes driver text.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@cursor
cursor Bot requested a review from seonghobae August 16, 2026 15:46
Record the process-entrypoint successor so operators and reviewers treat
#132 as the current health-probe tip and keep #122 parked.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact head e744049. Draft. Checks are still queued. No blocking production defect in the unique process-bind slice.

Operator next action

Keep this head preferred over #122, #117, #111, #91, and #102. Do not merge those in parallel. Do not open another overlapping health-transport PR.

After exact-head required checks pass, this still needs independent last-push approval. Do not self-approve. Do not merge while Draft or while mergeable_state is blocked.

Set HEALTH_LISTEN_ADDR or PORT, then call run_health_process. Point liveness at GET /live and readiness at GET /ready. Set DATABASE_URL only when readiness should observe PostgreSQL. Set HEALTH_BACKLOG_HEALTH=within_bounds only after backlog is measured. A /ready 200 is not product-schema proof: this process does not declare product relations.

What this head proves

  • Missing, blank, padded, or unknown listen/store/backlog values fail closed before bind.
  • HEALTH_LISTEN_ADDR wins over PORT. PORT alone binds 0.0.0.0:$PORT.
  • GET /live answers process liveness without store I/O and stays HTTP 200 when DATABASE_URL is down.
  • GET /ready is HTTP 503 without driver text when the store is unreachable or backlog is unknown.
  • Bare /ready injects postgres_operational_store, so a load balancer that omits capability= cannot advertise ready for read-only or unsupported majors.
  • /ready?capability=scoring fail-closes because this process does not observe scoring.
  • Debug redacts the store URL. No main/bin. RFC 9110 is recorded. Traceability names Active PR #132, not protected-main.

Residual, not blocking

  • Empty required-relation set: /ready 200 is store/write/major/backlog evidence, not migration proof.
  • Process-level live-store coverage is narrow; adapter contracts cover the missing backlog/bare-ready arms.
  • Accept loop is single-threaded. Semantic live/ready isolation is real; a hung /ready connect can delay later /live on the same thread. Do not treat that as a new #132 blocker.

HTTP session start still waits on #121 / #87 / #98. Next unique buyer slice is not more health transport.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment thread src/health_process.rs
Some(mut client) => handle_postgres_health_http_request(
request,
&mut client,
&[],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Process /ready always passes an empty required-relation set. On a reachable writable store plus HEALTH_BACKLOG_HEALTH=within_bounds, the adapter can return HTTP 200 with data_integrity_health: verified even when no product schema exists.

That matches the current adapter contract. Do not invent a relation env in this slice. Operators must not treat process /ready 200 as proof that product tables can accept state-changing work.

let ready = exchange(
addr,
&format!(
"GET {HEALTH_READY_PATH}?capability={POSTGRES_OPERATIONAL_STORE_CAPABILITY_REF} HTTP/1.1\r\nHost: localhost\r\n\r\n"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only live-store process test, and it only proves GET /ready?capability=postgres_operational_store with within_bounds.

Bare GET /ready and reachable-store + missing/unknown/stalled backlog are covered in the HTTP/snapshot contracts, not through serve_health_process. Not a current defect. A later process change that dropped ready_required_capabilities would make bare /ready fail-open on read-only or unsupported majors.

seonghobae and others added 3 commits August 17, 2026 05:36
Runtime CI sets TEST_DATABASE_URL as host=localhost user=... dbname=...,
which rust-postgres already accepts. The probe process rejected that
form as InvalidDatabaseUrl and failed the reachable-store contract.
Production line coverage missed the accept-loop return and the last
listen map_err after bind. Connection errors never stopped the loop,
so that return was dead. Force WouldBlock on accept instead.
…9c-4a23-b5ee-495fc24b1b3f-068b

# Conflicts:
#	docs/TRACEABILITY.md
#	tests/postgres_recovery_invariants.rs

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

Copy link
Copy Markdown
Contributor

Closing as superseded by #247. #247 explicitly replays the full operator-health stack through this PR onto current protected main aac99d0b523b8f765846fc6edad834e5cf903ca8, preserves the HEALTH_LISTEN_ADDR/PORT, /live, /ready, PostgreSQL-readiness, fail-closed backlog-input, timeout, and RFC 9457 behavior, and is the stated merge candidate. Keeping this older 60-commit-behind landing vehicle open would duplicate the same health transport lane.

@seonghobae seonghobae closed this Aug 17, 2026
cursor Bot pushed a commit that referenced this pull request Aug 17, 2026
Record the process-entrypoint successor so operators and reviewers treat

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 17, 2026
Record the #91/#132 operator health HTTP stack as a successor replayed
onto aac99d0, including modules and migrations that landed after the
stale 085ef4b traceability baseline.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 18, 2026
Record the process-entrypoint successor so operators and reviewers treat

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 18, 2026
Record the #91/#132 operator health HTTP stack as a successor replayed
onto aac99d0, including modules and migrations that landed after the
stale 085ef4b traceability baseline.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 18, 2026
Record the process-entrypoint successor so operators and reviewers treat

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 18, 2026
Record the #91/#132 operator health HTTP stack as a successor replayed
onto aac99d0, including modules and migrations that landed after the
stale 085ef4b traceability baseline.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants