Twelve slides, and only one of them a missing capability
- Symptom
A request, not a bug: make the generated report match the kickoff deck the bids team produces by hand. Twelve slides of it.
- Diagnosis
I graded the deck against what the system already did, slide by slide, and it was not twelve gaps.
Most of it was already covered by the existing report structure. Two of the remaining slides were already extracted. The buyer’s objectives and the weighted evaluation criteria were both being parsed out of the tender and stored as structured records, and no screen displayed either of them. That is why the deck read as a gap: the capability existed and was invisible. Exactly one slide was a genuine retrieval gap, covering contract vehicle, pricing basis and evaluation period.
And about two fifths of the deck was not in the tender at any price: relationship history, decision makers by name, win strategy, a SWOT, next steps with owners. The tender does not contain the sentence “a competitor already has a footprint here”, and it never will. That half is what makes the deck a kickoff rather than a summary.
There was also a trap in the “already extracted” rows. The structured criteria were being injected into the report prompt as framing under an explicit instruction that they must never carry a citation marker, because the marker parser drops markers it cannot resolve to a retrieved passage. So a new criterion asking for the evaluation weightings could not be grounded on the extraction that already held them, weighted and structured. The model would have to re-find them in raw passages, or return nothing.
- Decision
Three calls, and the cheapest one first.
Show them the feature before building anything. The report templates were already user-editable through an existing settings screen, so sections could be added and reordered without code. That may dissolve half of a request, and reshaping the compiled-in default is still worth doing afterwards because it is what every new bid gets before anyone edits anything. But the urgency of everything else depends on that conversation.
Reshape the existing report; do not add a third type. A new type needs generate, fetch and status handlers, a template mapping, a repository case, a card and a route, and it leaves the old type in place as a near-duplicate, meaning two model calls per bid and two section sets to keep in step. Reshaping is an edit to one data structure. Related: rename the user-facing label in the five places it appears, and never the persisted type string, which is stored on saved templates and on every generated report and filtered on. Changing it orphans both silently.
Leave the unsourceable half as empty boxes. Hand-authored slots, following the shape of the non-AI field the lane already had, with a pencil affordance instead of Regenerate, and the model call never touching them. Rather than re-derive the extracted criteria with a large model, surface the structured records that already existed: they are weighted and exact, and re-deriving them costs a criterion’s share of the passage budget to produce something worse.
I wrote down the risk the finished set carries, because it is not a code problem and would otherwise surface as disappointment. Done in full, the kickoff ends up spread over three screens: generated points, hand-authored assessment, and a timeline in the schedule that already owns dates and owners. There is no report export of any kind. Each placement is individually right, and duplicating dates onto a report is the worse bug. But nobody can present the result, so the deck may still get rebuilt by hand. If the deck itself is the deliverable rather than its content, that is a separate piece of work and it should be priced before the hand-authored half is built, not after.