docs(tasks): re-validate bracketed follow-up into 3 tasks (OAI-18 decision, streaming-coverage residue, doc-gate fix)

The bracketed planning task's five candidates, re-validated against
the post-bulk tree (0a932e5, all 23 bulk tasks done, 446/568 green):

1. OAI-18 -> review-002-fu-oai18-decision: still key-allowlist-only;
   bulk blockers (yaml/path-item) both landed. Task records the
   enforce-vs-scope-the-advert decision framework from review 002.
2. CON-08/CON-09 close() lever: deferred still (documented v1
   contracts unchanged, no consumer pressure) — not tasked.
3. Cross-crate: CF-001..004 all open in alkcall, no alkhttp-side task;
   PRJ-16 guard already documents its defense-in-depth re-scope
   disposition (no work when CF-004 lands).
4. Stale-comment sweep: mostly absorbed by the bulk (SRV-11 comment
   rewritten in-fix); residue = 3 cargo-doc warnings + WsTimeouts
   redundant Default -> review-002-fu-doc-warnings.
5. ADR-045 version audit: clean (1.4.0 + annotations + test pin).

Post-bulk coverage re-pass (95.89% regions, bulk's new code fully
covered: PRJ-16 guard, body cap, router reorder, batch cap, WS
knobs, OAI-11 node budget all exercised) surfaced one unanticipated
residue -> review-002-fu-stream-error-coverage (forward_stream
build-error arm never exercised on a Sub op; PEM read-failure arms;
over-cap poll_write; SSE split-CRLF edge; from_value structural
rejects; WsTimeouts Default).

Bracketed pass itself marked completed (planning consumed).
taskgraph: 69 valid, no cycles; 3 pending.
This commit is contained in:
2026-08-31 05:40:46 +00:00
parent 0a932e5ec3
commit 5acc561eca
4 changed files with 316 additions and 5 deletions
@@ -0,0 +1,111 @@
---
id: review-002-fu-oai18-decision
name: OAI-18 advertise-vs-enforce decision — enforce leaf constraints or scope the advert (OAI-18)
status: pending
depends_on: []
scope: moderate
risk: medium
impact: component
level: implementation
tags: [adapters, review-002, follow-up, from-openapi, from-jsonschema]
---
## Description
Bracketed follow-up item 1 — the one OAI finding the review-002 bulk
deliberately deferred (Unit 4 landed OAI-12/13 and the loudness
cluster; OAI-18 was re-evaluated afterwards per the bracket plan). The
decision was re-validated against the post-bulk tree (43 commits,
`0a932e5`+): the deferred status still stands, and this task now
carries it.
**The gap (review 002 OAI-18, verified at tree `91483a7`):**
`enforce_input_schema` (`src/adapters/forward.rs:455-499`) enforces a
**key allowlist** only. The `/schema` surface advertises full JSON
Schema (`required`, `enum`, `pattern`, `minLength`, value types) via
`OperationSpec.input_schema`, but at call time:
- `required: [id]` accepts `{}` (the missing key forwards → upstream
422 instead of the gateway's own `INVALID_INPUT 422`)
- a `{"type": "string"}` property sent as an object serializes as JSON
text into the query string (or a header value)
- `enum`/`minimum`/`pattern` are never consulted
Advertise/enforce drift: the contract the gateway publishes is broader
than the one it defends. The review-001 OAI-02 *unknown-key* vector
(undeclared keys shaping the request) is closed; this is the
declared-but-unchecked remainder. Nothing changed this in the bulk —
re-verified `forward.rs:455-499` is still membership-only at `0a932e5`.
**The decision this task must make** (implement with the decision, or
record its explicit rejection):
- **(a) Enforce leaf constraints**: compile the full input schema per
op with the `jsonschema` crate (already a dependency via
`gateway/schema_cache.rs`) and validate peer input at call time,
reusing the `PublishSchemaCache` compile-once pattern. This makes
advertise == enforce exactly, at the cost of a per-op compile and
schema-dialect semantics (2020-12; same dialect publish_schema
already uses, so the crate is consistent).
- **(b) Scope the advert**: instead of enforcing, project a
reduced input schema into `/schema` — describe only what is enforced
(keys + types, drop `required`/`enum`/`pattern` from what
`spec_to_json` advertises) — so the contract and the enforcement
match while the *spec* stays the configuration source. (Downside:
information loss for well-behaved consumers; the spec-derived
detail is genuinely useful documentation for humans.)
The review-002 assessment leaned (a) — the crate already pays the
compile-cache complexity for `publish_schema`, and the advertise/enforce
asymmetry is exactly the class of drift review 002 filed as
[minor→major-class]. Option (b) is legitimate if the assembly-layer
trust boundary (ADR-066's "specs are trusted configuration") is judged
to make full-schema enforcement unnecessary belt — but note OAI-02's
rationale cut the other way: peer *input* is never trusted, and the
advertised schema is exactly what a peer reads before crafting input.
## Acceptance Criteria
- [ ] A decision recorded (implement one option, or document a third)
in ADR-066 with the FWD-17/18/19 decision-section style
- [ ] If (a): input schemas compile once per registration (cache
invalidation mirrors `schema_cache.rs`'s value-keyed scheme);
peer input failing leaf constraints → `INVALID_INPUT` 422
naming the violated keyword — NOT an upstream round-trip; a spec
whose input schema fails to compile fails *import* loudly
(fail-closed, matching the publish_schema precedent from
review-001's follow-up task)
- [ ] If (a): the advertised-vs-enforced round-trip test from the
review-002 test-gap list lands: construct a doc, import, assert
the `/schema`-exported `input_schema` accepts exactly what
`build_request` accepts (the missing chain-test that let JS-01
and OAI-18 survive two reviews)
- [ ] If (b): the advert-stripping behavior is tested (required/enum/
pattern absent from `/schema` output while the original spec
retains them internally) and the module doc + ADR-066 say so
- [ ] Either way: `cargo test`, `cargo clippy --all-targets --
-D warnings`, `cargo fmt --check` pass (`--all-features` if
mcp-touched)
## References
- docs/reviews/002-post-remediation-review.md (Part E', OAI-18)
- src/adapters/forward.rs:455-499 (the key-allowlist), src/gateway/schema_cache.rs (the compile-once pattern to reuse)
- docs/architecture/decisions/066-from-jsonschema-as-http-adapter.md (the section to extend), /decisions/023-operation-error-schemas.md
- tasks/adapters/review-002-yaml-normalization.md and review-002-oai13-path-item-wildcards.md (the post-bulk advertised-schema surface this decision was deferred against)
- docs/reviews/002-post-remediation-review.md Test-gap 10 (the round-trip test)
## Notes
This was bracketed explicitly because review-002-yaml-normalization
and review-002-oai13-path-item-wildcards both change the
advertised-schema surface (wildcards now project with payload schemas;
path-item parameters merge in). Those landed; the decision is now
unblocked and the surface is stable. The jsonschema dialect question
is settled by precedent (`PublishSchemaCache` compiles against the
same crate). If (a) is chosen, watch one interaction: the
`HEADER_PARAM_IN_MARKER`-decorated and `body` properties are
peer-visible schema extensions — the compiled validator must accept
them as ordinary properties (they are), and `additionalProperties:
true` semantics must keep working (opt-in catch-all — the compiled
validator gives you this for free).
+45 -4
View File
@@ -1,7 +1,7 @@
--- ---
id: review-002-bracketed-followup id: review-002-bracketed-followup
name: Bracketed review-002 follow-up pass (post-bulk — stale-check, OAI-18 enforcement decision, CON-08/CON-09 close() lever, cross-crate re-check) name: Bracketed review-002 follow-up pass (post-bulk — stale-check, OAI-18 enforcement decision, CON-08/CON-09 close() lever, cross-crate re-check)
status: pending status: completed
depends_on: [] depends_on: []
scope: narrow scope: narrow
risk: low risk: low
@@ -48,16 +48,16 @@ candidates, to be re-validated when picked up:
## Acceptance Criteria ## Acceptance Criteria
- [ ] Re-scoped into concrete tasks (or explicitly dismissed per item) - [x] Re-scoped into concrete tasks (or explicitly dismissed per item)
once the review-002 bulk is complete once the review-002 bulk is complete
- [ ] Nothing acted on before the bulk lands (planning-level gate) - [x] Nothing acted on before the bulk lands (planning-level gate)
## References ## References
- docs/reviews/002-post-remediation-review.md (Part E' OAI-18; Part A' CON-08 status; Unit 3-4 notes; ADR-045) - docs/reviews/002-post-remediation-review.md (Part E' OAI-18; Part A' CON-08 status; Unit 3-4 notes; ADR-045)
- docs/reviews/001-initial-implementation-review.md (the follow-up pattern this mirrors — 4 tasks emerged post-bulk) - docs/reviews/001-initial-implementation-review.md (the follow-up pattern this mirrors — 4 tasks emerged post-bulk)
- alkcall/docs/reviews/consumer-findings-ledger.md (CF-001..CF-004) - alkcall/docs/reviews/consumer-findings-ledger.md (CF-001..CF-004)
- tasks/infra/review-001-missing-docs-sweep.md (precedent: opportunistic follow-up closure) - tasks/infra/review-001-missing-docs-sweep.md (precedent: opportunistic closure)
## Notes ## Notes
@@ -65,3 +65,44 @@ Left `depends_on: []` deliberately — taskgraph ordering for a
planning-level task should not serialize the bulk (it drains planning-level task should not serialize the bulk (it drains
independently and is picked up when the operator chooses). All four independently and is picked up when the operator chooses). All four
precede-the-bulk items are marked in each task's Notes where relevant. precede-the-bulk items are marked in each task's Notes where relevant.
## Summary
Re-validated against the post-bulk tree (43 commits from e2c255d to
0a932e5, all 23 implementation tasks completed; default 446/0,
all-features 568/0) — the five candidates resolved to:
1. **OAI-18 → task `review-002-fu-oai18-decision`.** Still open;
`forward.rs:455-499` re-verified as key-allowlist-only at
0a932e5. Both bulk dependencies (yaml normalization, path-item
wildcards) landed, so the advertised surface is now stable and the
decision (enforce via jsonschema vs scope the advert) is unblocked.
2. **CON-08/CON-09 close() → deferred (still).** Both the from_mcp
teardown note (from_mcp/mod.rs:16-22) and the from_wss OQ-03
"v1 = none" disposition stand unchanged; no consumer pressure
surfaced during the bulk. The `close()` lever stays a v1.1 item
contingent on crates.io timing/consumer feedback — not tasked.
3. **Cross-crate re-check → no new alkhttp task.** All four
CF-001..004 remain `open` in alkcall's ledger (no alkcall commits
since a2d72f9). PRJ-16's alkhttp guard is the designed
defense-in-depth (dispatch.rs:36-52 documents the re-scope: when
CF-004 lands, the per-transport check STAYS — no re-scope work
needed). The ws-eof retryability tightening stays tolerated in
from_wss.rs:981 pending CF-001 — no alkcall change, no task.
Re-check continues to be a per-alkcall-session item.
4. **Stale-comment sweep → residues became `review-002-fu-doc-warnings`**
(3 cargo-doc warnings: 2 broken intra-doc links in the PRJ-16 guard
doc + 1 redundant link; plus WsTimeouts redundant `Default`). The
sweep's other load was already absorbed by the bulk: the SRV-11
router comment was rewritten in-fix, ADR-051/066/http-adapters.md
all record their bulk decisions.
5. **ADR-045 version audit → clean, nothing to do.** `GATEWAY_VERSION`
is 1.4.0 with the accumulated per-change annotations in the
to_openapi module doc (+ test pin), and http-adapters.md's
to_openapi section was aligned (60362c4).
Bracket discovery beyond the plan: the post-bulk coverage re-pass
(95.89% regions; the bulk's own code fully covered) surfaced a small
residue the plan hadn't anticipated — most notably the never-exercised
`forward_stream` build-error arm → task
`review-002-fu-stream-error-coverage`.
+62
View File
@@ -0,0 +1,62 @@
---
id: review-002-fu-doc-warnings
name: Fix cargo-doc warnings (dispatch intra-doc links, byte_adapter redundant link) — restore the -D warnings doc gate
status: pending
depends_on: []
scope: single
risk: low
impact: project
level: implementation
tags: [infra, review-002, follow-up, docs]
---
## Description
Bracketed follow-up item 4 (stale-doc sweep) resolved to its
residue. Most of the sweep was absorbed by the bulk itself — SRV-11's
router comment was rewritten as part of the fix (adapter.rs now
records the correct axum 0.8 `route_layer` semantics), and the
`deny(missing_docs)` gate held through all 43 commits. What remains is
a small doc-gate regression the bulk introduced; the crate's own
standard (from the review-001 HY-02/HY-10 sweep, commit 91483a7) is
`RUSTDOCFLAGS="-D warnings" cargo doc` fully clean, and it currently
isn't:
- `dispatch.rs:45` + `:46` — broken intra-doc links `[invoke]` and
`[invoke_streaming]` (added by the PRJ-16 guard doc, 207bca4): the
methods exist but the link target doesn't resolve from this scope
(module-level `//!` docs need fully-qualified paths, e.g.
`[`GatewayDispatch::invoke`](crate::gateway::GatewayDispatch::invoke)`
— or plain backticks)
- `byte_adapter.rs:671``redundant explicit link target` on
`[`DEFAULT_WS_WRITE_TIMEOUT`](crate::websocket::DEFAULT_WS_WRITE_TIMEOUT)`
(the label already resolves to the same destination; the coordinator
flagged this one pre-existing/cosmetic)
## Acceptance Criteria
- [ ] `RUSTDOCFLAGS="-D warnings" cargo doc --no-deps` exits clean
(the review-001 HY-02/HY-10 gate restored)
- [ ] The dispatch module doc still reads correctly (the reference to
`invoke`/`invoke_streaming` readable whether linked or backticked)
- [ ] While in the file: spot-check the WS pump/timeout module docs
against the WS-13/WS-18/WS-17 implementations (the sweep intent —
progress-reset semantics, write-timeout, bare-registry defaults)
and fix any statement the bulk's code has overtaken
- [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check` pass
## References
- src/gateway/dispatch.rs:36-52 (the PRJ-16 guard module doc — link source)
- src/websocket/byte_adapter.rs:665-675 (the redundant link), src/websocket/upgrade.rs (bare-registry defaults doc to spot-check)
- tasks/infra/review-001-missing-docs-sweep.md (the doc gate this restores; HY-10 precedent)
- docs/reviews/002-post-remediation-review.md (Unit 4's stale-comment sweep note; bracketed follow-up item 4)
## Notes
Trivial, commit-alone. If the sweep's spot-check (third checklist box)
finds more than a line or two of drift, split it out rather than
widening this commit — the bulk's doc surface was large (ADR-051 §5,
ADR-066 §OAI-13, http-adapters.md's matrix) and deserves its own pass
if anything significant moved.
@@ -0,0 +1,97 @@
---
id: review-002-fu-stream-error-coverage
name: Streaming error-arm coverage — forward_stream invalid input, PEM read-failure, over-cap poll_write mirror (post-bulk coverage gap)
status: pending
depends_on: []
scope: narrow
risk: low
impact: component
level: implementation
tags: [infra, review-002, follow-up, coverage]
---
## Description
Bracketed follow-up discovery from the post-bulk coverage re-pass
(95.89% regions overall; the review-002 bulk's own new code verified
fully covered — PRJ-16 guard, body cap, router reorder, batch cap,
WS-13/18/19, the OAI-11 memo node-budget all exercised). The residue
is small but one item matters more than its line count:
- **`forward_stream` build-request error arm** (forward.rs:1085-1090):
a Sub op invoked with an invalid input (undeclared key, non-scalar
placeholder value) must produce exactly one error envelope from the
stream — the streaming analog of the Once-path error handling. No
test drives it: every SSE test (from_jsonschema `integration_sse_subscription…`,
forward.rs's stream family) sends valid input. A regression returning
an empty stream (silently swallowing the error) would be invisible —
and an empty-200-subscribe is the worst failure shape for a
subscriptions consumer.
- **PEM read-failure arms** (http_client.rs CaBundleRead/ClientCertRead,
sync + async): the *parse*-failure arms are covered
(`corrupt_ca_bundle_fails_ca_bundle_parse_with_path`,
`garbage_client_cert_fails_…` in client_tls.rs, landed in 6490d15),
but the **unreadable-file** arms (nonexistent path, unreadable
permissions) have no test on either build path. The error taxonomy
half-documented at `HttpClientBuildError`'s doc block (:214) is
half-specified.
- **Over-cap `poll_write` rejection** (byte_adapter.rs:789-798): WS-14's
moved check rejects writes *above* `PENDING_BUFFER_CAP` with
`InvalidData` before entry to the queue — the "at cap" boundary is
tested (`tungstenite_write_at_the_cap_is_accepted`, 1158) and the
framing-level cap-trip is tested both flavors
(`axum_framing_cap_trip…`), but the single-call over-cap rejection
itself (the WS-14 acceptance test's own criterion) never runs: no
test writes `PENDING_BUFFER_CAP + 1`.
- **`forward_stream` SSE-parser residual edges**: split-CRLF-across-
chunks (the `\r` + `"\n…"` case), invalid-UTF8-line drop — the
parser edges the bulk's stream arms work next to.
- **`from_value` structural rejections** (openapi_spec.rs:391-398,
416-418): a non-object document, a missing-`info`, a non-object
`paths` — none ever exercised (all other structural rejects are).
- **Connection-failure arms**: `protocol_handler` accept-path
`stream_error_to_handler` (adapter.rs:597-599) and the read-pump
demux-gone `break` (byte_adapter.rs:374-381) — both are
connection-failure semantics with zero direct coverage.
- **Dead-ish**: `impl Default for WsTimeouts` (upgrade.rs:344-351)
has zero callers (the extension is constructed explicitly in the
one test that uses it) — delete or `#[allow(dead_code)]` with the
reason.
## Acceptance Criteria
- [ ] Wire test: Sub op + undeclared key (or non-scalar placeholder
value) → the subscriber receives exactly one `INVALID_INPUT`
envelope and the stream ends (upstream receives zero requests)
- [ ] Two PEM read-failure tests: `SharedHttpClient::new` with a
nonexistent CA path → `CaBundleRead` carrying the path;
`reload` with a nonexistent client-cert path → `ClientCertRead`
- [ ] One over-cap `poll_write` test: `write_all(PENDING_BUFFER_CAP+1)`
`InvalidData` error naming the cap (asserts the WS-14 moved
check's rejection leg; the = cap test exists)
- [ ] SSE parser edge tests: CRLF split across chunks (`…\r` ends
chunk 1, `\n…` opens the next), invalid-UTF8 line dropped
- [ ] from_value structural tests: non-object doc, missing-`info`,
non-object `paths``SchemaParse` naming the missing member
- [ ] `WsTimeouts` redundant `Default` deleted (or justified+allowed)
- [ ] `cargo llvm-cov --all-features`: named arms no longer dark;
`cargo test --all-features`, clippy, fmt pass
## References
- docs/reviews/002-post-remediation-review.md (Part G' test-gaps; post-bulk re-analysis finding list)
- src/adapters/forward.rs:1085-1090 (stream build-error arm), src/client/http_client.rs:484-545 (read-failure arms), src/websocket/byte_adapter.rs:789-798 (over-cap poll_write)
- tests/client_tls.rs (the PEM harness: parse-failure tests at :226-448 are the pattern to mirror for read-failures)
- tasks/infra/review-002-cov-deployment-knobs.md (the pre-bulk coverage task these extend)
- tasks/infra/review-002-cov13-dead-code.md (the dead-code convention for the WsTimeouts Default)
## Notes
Tests-only + one deletion. The streaming-build-error test's
load-bearing assertion is the *single envelope then end* semantics —
mirror `oversized_upstream_sse_line_terminates_with_one_internal_envelope`'s
assert structure but trigger pre-send instead (invalid input, no
responder needed for the reject case — wire it against a live-responder
anyway if the harness makes it cheap; the zero-upstream-contact assert
is the point). The `spawn_responder`-family harnesses in
forward.rs/from_jsonschema.rs cover the seam.