Wiki · Workflow companion

Bid amendment arrives

An amendment to an open bid lands via email, portal webhook, or manual upload. Diff against the prior amendment, re-cost the changed lines, regenerate affected spec sheets, bust the hub cache, and surface the delta for Mike's review before resubmission.

Mixed · Some steps live, others stubbed
What this is

Bid amendment arrives

Bid responses are amended frequently. B5875 (NYC DOE) hit Amendment 13 in the SY2026 cycle alone. Each amendment can change line specs, quantities, or terms — and the response has to reflect those changes within a tight clock.

This workflow runs the diff so Mike doesn't have to. The amendment doc parses, the lines diff against the prior version, changed lines get re-costed, the affected spec sheets regenerate, and the hub UI cache busts so the bid response page renders the new state.

Risk level 3 (medium). The cascade table list is bid_lines, bids, spec_versions, ns_pending_pushes.

When to use it

Trigger conditions

Heuristic

The amendment_sequential precondition warns if the new amendment number is not exactly prior + 1. This catches the "did we miss amendment 12?" case that bit us during the SY2025 cycle.

Step-by-step what happens

The 3 beats

  1. 01

    Update bid_lines for changed entries

    Changed lines UPDATE in place, new lines INSERT, removed lines set deleted_at=now. The bid_lines table is the source of truth for the bid response render.

    Writes bid_lines
    Time ~100ms per line batch
    Kind d1_write
    Status stub
  2. 02

    Regenerate affected spec sheets

    For any spec referenced by changed lines, a POST to /api/spec/regenerate rebuilds the PDF and stores it in R2.

    Writes R2 bucket specs/
    Time ~6s per spec
    Kind http_call
    Status stub
    Note conditional · changed lines reference a spec
  3. 03

    Invalidate hub UI cache

    Keys hub:nycdoe:bid_{bid_id} and hub:bids:summary are deleted. Next hub render pulls fresh state from D1.

    Writes HUB_CACHE KV
    Time ~40ms
    Kind kv_invalidate
    Status real
Outcomes

What's different after the workflow runs

Lines updated
In D1
≤ 5s
Spec sheets
Regenerated
per changed spec
Hub cache
Busted
≤ 60s
Delta summary
Exported
reviewable artifact
Failure modes

What can go wrong and how to recover

Amendment number gap

Precondition warns but doesn't block. Mike checks the bids@ inbox for the missing amendment. If genuinely skipped by the issuer, override the warn and continue.

Spec regen timeout

Browser Rendering exceeds 30s on a large spec. Old PDF stays in R2; manual POST /api/spec/regenerate?item_code=<code> retries.

Bid is closed/cancelled

Precondition blocks at bid.status NOT IN (open, pending_submission). Amendment to a closed bid is meaningless; the email-routing layer logs it for awareness.

Hub cache miss persists

KV propagation occasionally takes 60s+. If a customer-facing render still shows the old state after 2 minutes, manual POST /admin/hub/invalidate?key=<k> retries.

Related

Adjacent workflows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractworkflow_definitions WHERE workflow_type='bid_amendment_arrives'
Amendment doc parsersrc/document_converter.ts (PDF/DOCX → MD)
Diff enginesrc/chat_tools/impls.ts → bid_lines diff
Hub cache keyshub:nycdoe:bid_<bid_id>, hub:bids:summary
Reflexion tagbid_amendment, bid_id:<id>
Risk level3
Expected duration~30 min
Triggerevent · sources=email_intake, manual_upload, portal_webhook
// Sequential check (R550) if (amendment_number !== prior_amendment + 1) { warn('amendment_gap', { prior: prior_amendment, current: amendment_number }); } // Soft-delete removed lines, never DELETE await db.run("UPDATE bid_lines SET deleted_at=? WHERE id IN (?)", [now, removed_ids]);