Skip to content

docs(policy): clarify HTTP budget recording time - #449

Open
kevin-orellana wants to merge 3 commits into
mainfrom
feature/http-budget-recording-docs
Open

kevin-orellana wants to merge 3 commits into
mainfrom
feature/http-budget-recording-docs

Conversation

@kevin-orellana

@kevin-orellana kevin-orellana commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

Human readable section

Problem: The HTTP byte-budget example can read as a cap on bytes as they leave Box. Box records request bytes when the exchange ends, so requests still waiting for a response can exceed the threshold.

Solution: Clarify when request bytes enter history, how the time window works, and why pending requests and the next request's size can take the total over the threshold.

Trade-off: The example remains a budget over recorded outcomes. This documentation change does not strengthen enforcement or change policy expressions.

Existing HTTP budget timing. Pending exchanges contribute no bytes until Box records their outcomes. This documentation clarification leaves runtime behavior unchanged.

Policy checks recorded bytes. A denied request is not sent. For an allowed request, Box sends the request and waits for the response. While the response remains pending, its request bytes are absent from budget history, and other requests can pass using earlier recorded outcomes. When the response completes, fails, or times out, Box records the request outcome and its known byte count. Later decisions see those bytes within the policy window.

Editable diagram.

Description

Clarify the byte-budget fixtures, policy reference, and HTTP rate-limit guide. The reference also distinguishes definite delivery failures, which produce error events, from completed, partial, or uncertain deliveries, which produce response events. The recorded byte count is the number of request bytes known to have reached the operating system.

Runtime code, policy expressions, event names, thresholds, and stored history remain unchanged.

Related Issues

No public issue. This clarifies the existing counting-rule contract.

Type of Change

Documentation update

Testing

Linux ARM64, based on 70eb3afc, with --locked --all-features:

  • 42 focused tests passed: 6 byte-budget, 3 gateway/streaming, 6 README examples, and 27 site-manifest tests.

  • cargo test --workspace --locked --all-features -- --show-output ran but stopped at two unchanged mcp_alias_fallback tests with Text file busy while spawning a copied test executable. An independent control at the same unchanged base reproduced that fixture failure. The full workspace suite is not green locally; this PR does not change the launcher code or tests.

  • The same nine launcher-fixture tests passed when run serially using the existing binary. This does not replace the failed full-workspace result.

  • git diff --check passed. Policy predicates are unchanged; the guide example's description now uses recorded-outcome wording. All 27 site-manifest tests passed again after that wording update.

  • The diagram passed layout checks and visual inspection. Export used rlespinasse/drawio-desktop-headless:v1.75.0, scale 2, border 20. A control export of the existing statfs figure was byte-identical.

  • I ran the relevant suites (just check, or cargo test --workspace --all-features)

  • If I touched Rust, I ran cargo fmt --all and cargo clippy --workspace --all-targets --all-features -- -D warnings

    • Not applicable: no Rust source changed.

Checklist

  • I have read the CONTRIBUTING document
  • I have reviewed and understand every line of code in this PR, including any generated by AI tools, and I can explain why it works
  • My change is focused and reasonably small; I have split unrelated work into separate PRs
  • I have added any necessary tests that prove my fix is effective or my feature works
    • Existing tests cover the documented behavior; this change adds no runtime behavior.
  • I have updated the documentation accordingly
  • My changes generate no new warnings

Prepared with AI assistance.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

## Problem

The HTTP byte-budget example can read as a cap on bytes as they leave Box. Request bytes enter history only when their exchange ends, so pending exchanges and a newly admitted request can exceed the threshold.

## Solution

Clarify recording time and overshoot in the sample comments and policy references. Distinguish definite failed delivery from partial or uncertain delivery, and define the recorded byte count. Add an editable timing figure for the review. Runtime code, policy expressions, public interfaces, thresholds, and stored history remain unchanged.

## Tests

All 42 focused tests passed with --locked --all-features: temporal_egress_budget (6), temporal_egress_mitm_e2e (3), readme_examples (6), and site_manifest (27). They cover budget decisions, completion-time history, reference examples, and publication metadata. Policy expressions and Markdown code blocks are unchanged; git diff --check passed.

The full workspace test command stopped at two unchanged mcp_alias_fallback fixture spawns with Text file busy. An independent control at the same base70eb3af reproduced that failure. Full workspace validation is not green locally; no launcher repair is included.

The timing figure passed layout checks and visual inspection. The pinned draw.io renderer reproduced the existing statfs control image byte-identically.
@github-actions github-actions Bot added Area - policy-engine documentation Improvements or additions to documentation strands-running labels Nov 13, 2026
@kevin-orellana
kevin-orellana enabled auto-merge (squash) November 13, 2026 19:00
Comment thread docs/user/policy.md
the ten-minute window. The gateway records the outcome when the exchange ends, including failure
or timeout. A refused request or a request that definitely failed to reach upstream does not
count. Pending exchanges do not count yet, so several requests can pass before earlier exchanges
end. Those requests can make the total exceed the limit.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion (non-blocking): This bullet is accurate, but the lead paragraph of the same section (line 186) still describes the rule the way the PR says it shouldn't be read.

Example: Line 186 says "At most sixty completed requests to one host in ten minutes. The sixty-first is refused…" Take 61 concurrent requests to api.github.com with nothing recorded yet. All 61 pass, because formerly within 10m sees zero ::response events, and each one records ::response when its exchange ends. That matches what this bullet now says. Also, "completed" in that lead (and in the @description) doesn't match the rule. The count has no output.status or delivery filter. So an exchange that timed out after a partial write (Partial/Indeterminate → ::response, see delivery_of in crates/policy/src/adapters/egress.rs) counts too. This bullet says that ("including failure or timeout"). The write_budget section above can say "completed" because its rule filters output.result: "completed". This rule has no such filter.

Suggestion: Reword the lead to describe recorded outcomes. For example: "A request to one host is refused once sixty exchanges with it have ended in the last ten minutes." Then the first thing a reader sees doesn't contradict the bullet. If you'd rather keep this PR to the bullets, that's fine too. Then please note it as a follow-up.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Addressed in 148b2fa. The lead and @description now describe the threshold as "at least sixty response outcomes recorded", which matches the count over http:request::response with no status filter, and they say pending exchanges don't count. Nothing else in the repo copies the old description string, and the policy predicates are unchanged. This can be resolved.

@github-actions

Copy link
Copy Markdown

Assessment: Approve (with one non-blocking suggestion inline)

I checked the new wording against the gateway and policy code at 049d5dc, and it matches the runtime behaviour. The one open point is that a lead sentence in docs/user/policy.md still states the strict-cap reading this PR is correcting.

Assumptions and rules checked that held
  • Recorded when the exchange ends: l7.rs passes the request permit to UpstreamBody::track_request. ResponseReader records it when the body finishes, fails, or hits the deadline, or when the reader is dropped. If the exchange gets no response, the outcome is recorded immediately. This is pinned by a_disconnected_workload_cannot_complete_an_open_upstream_response.
  • ::response vs ::error: outcome_kind sends only Delivery::Failed to ::error. Partial, Indeterminate, Completed and Replied all go to ::response, as the README says.
  • Definite failure doesn't count: a TLS handshake failure, or a deadline before any write, goes through Writes::outcome(0, kind) to Failed, which records ::error.
  • output.status only on reply: reply_status_of returns Some only for Replied.
  • Window uses recording time; denied requests add no bytes: these match docs/design/policy.md and decisions.md#L937-L940.
  • Fixtures unchanged in behaviour: only comments changed. cargo test -p strands-box-policy --test temporal_egress_budget --all-features passed 6/6 locally.
  • Unreferenced diagram: this follows the existing practice for PR-description figures (for example server-grammar-start-check.png).
  • Frozen surfaces: no action, attribute, event or manifest changes.

Optional follow-up, not required by the root AGENTS.md doc-review rule: three places still say "at reply time" for the HTTP outcome, which the README no longer says. They are crates/policy/AGENTS.md:179, the Outcome::Http doc in crates/policy/src/outcome.rs:95, and the test doc at tests/temporal_egress_budget.rs:186.

The rewrite is clear and correctly separates recorded outcomes from enforcement strength.

## Problem

The rate-limit guide still describes a strict limit on completed requests in
its opening paragraph and example description, although pending exchanges do
not count and some failed exchanges produce response outcomes.

## Solution

Describe the threshold in terms of recorded response outcomes and state that
pending exchanges do not count. Keep the policy predicates unchanged.

## Tests

All 27 site-manifest tests pass. The example code is unchanged except for its
description string. git diff --check passes.
@github-actions

Copy link
Copy Markdown

Assessment: Approve

Re-reviewed at 148b2fa. The only change since 049d5dc is the rate-limit lead and @description in docs/user/policy.md, which fixes the one inline suggestion. I have no new findings.

Checked at this head
  • New lead matches the rule: "refuses … when Box has recorded at least sixty response outcomes" matches count(... ::response{ input.host: "api.github.com" }) >= 60. It no longer says "completed" or "the sixty-first is refused", so it agrees with the bullet below it.
  • Example still valid: only the description string changed. No other file copies the old string, and the policy has no length limit on descriptions.
  • Earlier checks: they still hold, because the code, fixtures and README are unchanged since 049d5dc.

The optional "at reply time" follow-up from the earlier summary is still open, and it doesn't block the merge.

## Problem

The review-only diagram appeared as repository files in the pull request.

## Solution

Keep the image and editable source in GitHub review attachments and remove
the repository copies. The four documentation changes remain unchanged.

## Tests

Verified the attachment renders in the PR, the four prose blobs are unchanged,
and git diff --check passes. No runtime or documentation text changed.
@github-actions

Copy link
Copy Markdown

Assessment: Approve

Re-reviewed at 831bb86. The only change since 148b2fa deletes docs/design/images/http-budget-recording.{drawio,png}. The PR description now loads the figure and its editable source from user-attachments URLs. I have no new findings.

Checked at this head
  • No dangling references: nothing in the tree mentions http-budget-recording any more. No doc, README or site manifest pointed at the deleted files.
  • No written rule about diagram placement: the root AGENTS.md doesn't say where PR figures must live, so removing them from the tree breaks no rule.
  • Remaining diff: the PR now changes 4 files (crates/policy/README.md, the two byte-budget fixtures, and docs/user/policy.md), and none of them changed since 148b2fa. The earlier checks of the gateway recording point, ::response vs ::error, and the rate-limit wording still hold.

The optional "at reply time" follow-up (crates/policy/AGENTS.md:179, outcome.rs:95, temporal_egress_budget.rs:186) is still open, and it doesn't block the merge.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area - policy-engine documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants