Repository navigation
Conversation
Six methods over Tango's new `/api/sled/` namespace — `list_sled_opportunities`/`get_sled_opportunity`, `list_sled_opportunity_revisions`, `get_sled_coverage`, `list_sled_forecasts`/`get_sled_forecast` — plus both `PageStream` iterators, four `bon`-derived options builders and five `SHAPE_SLED_*` constants. Every one of the API's 27 solicitation filters and 13 forecast filters is a named field. Parity with the Python, Node and Go SLED PRs. Four behaviors are documented on the options builders because each one misleads a caller who assumes federal semantics. Leaving both `status` and `active` unset returns open solicitations only. That default is the API's, and `list_sled_opportunities` deliberately does not synthesize `status=open` — doing so would make `active = Some(false)` unreachable, since `active=false` is the complement of open. A test pins that. `status` is API-derived liveness, recomputed every fifteen minutes. The source portal's own word is served as `source_status`, frozen at last capture, and is not a liveness filter. Category scheme tagging is partial, so `naics` matches only the small tagged share and `category_code` is the escape hatch. `meta.attachment_count` can be lower than the length of `attachments`, because an auto-generated portal cover sheet is listed and flagged but excluded from the count. `active`, `has_documents` and `source_declared` are `Option<bool>` rather than `bool`, so `false` reaches the server as a filter value instead of collapsing into the default. `SHAPE_SLED_REVISIONS_MINIMAL` omits `changes` on purpose: the per-field before/after needs a Small plan, so naming it in a suggested shape would 403 a Free caller. 191 unit tests pass (16 of them new), clippy clean under `-D warnings`, `cargo fmt --check` clean. Blocked until the SLED endpoints are live on the production API. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`attachments(extracted_text)` serves a SLED document body on a Small plan or above from API 4.25.1. This SDK returns `Record`, so the leaf needs no type change — what it needed was saying so, in the three places a caller looks. The leaf must be named: no `SHAPE_SLED_*` constant includes it and `attachments(*)` does not carry it, because the API resolves the body only for a caller who asked. A constant naming it would make every detail fetch pay for a document nobody wanted to read, so `suggested_shapes_do_not_name_the_paid_document_body` pins both against it. The key is absent rather than null whenever the text is not being served — below Small it is withheld and named in `meta.upgrade_hints`. A contested document never returns text at any plan, because its stored bytes disagree with what the record advertised. Searching document text and reading it stay separate: `search` matches inside attachment text on every plan and returns no fragment of it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SDK support for Tango's new
/api/sled/namespace — state, local and education (SLED) procurement. Blocked: the endpoints are not live on the public API yet. Hold until the API ships them.Siblings: makegov/tango-python#62, makegov/tango-node#18, makegov/tango-go#6.
What's here
crates/tango/src/resources/sled.rs, plus five constants inshapes.rs:list_sled_opportunities/iterate_sled_opportunitiesGET /api/sled/opportunities/get_sled_opportunityGET /api/sled/opportunities/{opportunity_id}/list_sled_opportunity_revisionsGET /api/sled/opportunities/{opportunity_id}/revisions/get_sled_coverageGET /api/sled/opportunities/coverage/list_sled_forecasts/iterate_sled_forecastsGET /api/sled/forecasts/get_sled_forecastGET /api/sled/forecasts/{forecast_id}/Four
bon-derived options builders name every one of the API's 27 solicitation filters and 13 forecast filters.Four things a reviewer should check
The SDK must not synthesize
status=open. The API defaults a list request with nostatusand noactiveto open-only. Making that explicit client-side breaksactive = Some(false), which is the complement of open and therefore has to reach the server as the only liveness param.no_liveness_filter_is_sent_when_none_requestedandactive_false_is_sent_not_droppedpin both halves.Option<bool>, notbool, for the three boolean filters.active,has_documentsandsource_declaredare optional sofalsereaches the server instead of collapsing into theDefault— the same reasonpush_opt_boolexists ininternal.rs. Getting this wrong would silently make "closed solicitations" and "records with no documents" unqueryable, with no error to notice.SHAPE_SLED_REVISIONS_MINIMALomitschanges. The per-field before/after requires a Small plan; naming it in a suggested shape would 403 every Free caller on a field they never asked to gate.changed_fieldsis in the shape and available at every plan. A test asserts the split rather than trusting the constant to stay right.change_seen_afterhas no_beforetwin, deliberately. The API exposes only the lower bound on that field. The doc comment says so, rather than leaving a reader to assume the pair was forgotten.Scope note
This SDK has no
dibbs,exclusionsorsbirresources, so — as with tango-go — there was no vendored contract to refresh and no conformance or shape-coverage gate to satisfy. SLED lands as a plain resource module following theprotests.rsshape, returningRecord/Page<Record>like every other list resource here.Testing
cargo test --workspace— 191 unit tests pass, 16 of them new (plus the existing 16 + 19 + 6 + 4 across the other suites).cargo clippy --workspace --all-features -- -D warnings— clean.cargo fmt --all -- --check— clean.Docs
New State & Local — SLED section in
docs/API_REFERENCE.md, and an addition todocs/WEBHOOKS.md's alert semantics: an exclusion or DIBBS solicitation reaching its date fires nothing, because open/closed is derived at query time — butalerts.sled_opportunity.matchdoes fire when a SLED solicitation closes, since its liveness is a stored field the API recomputes every fifteen minutes.🤖 Generated with Claude Code
Update: the paid document-body leaf
Pushed a second commit adding
attachments(extracted_text)— a SLED document body, served on the Small plan and above, from Tango API 4.25.1.Three rules are documented wherever a caller will look, because each one is otherwise a support ticket:
attachments(*)does not carry it — the API resolves the body only for a caller who asked, so a default naming it would make every detail fetch pay for a document nobody wanted to read. A test pins the shapes against it.meta.upgrade_hints), on a contested document, or where the text could not be resolved.And the distinction worth stating plainly, because it reads backwards in both directions: searching document text is ungated (every plan, ids only, no fragment of the document) while reading a body is paid (Small and above, one record at a time). Buying the body does not change search.
Sequencing: the SLED surface itself is live on the production API.
extracted_textneeds 4.25.1, which is not deployed yet — so this PR is mergeable, but that one field will 400 until the API catches up.GET /api/versionreports what a caller is talking to.