deliverables builder
Eighty-four broadcast deliverable codes took a producer eight unbroken hours. Eighty-four is really twenty-one decisions — so that's all anyone types now, and the acceptance test is her real sheet, cell for cell.
team one internal tool, deployed on the agency's internal aws/eks platform. producers, executives, partners, and real code strings withheld; 'a december sales event' is the only campaign detail.
A project-manager role was eliminated and its work — assigning the industry advertising codes and the agency's generic-master codes to every broadcast asset with union talent, then building the deliverables sheet that post partners work from — landed on broadcast producers without its documentation or its tooling. The only living document was one colleague's file, still addressed to the eliminated role. One December sales-event package: 84 codes, about eight hours. The account brief for the whole package was two lines; everything else was in the producer's head.
The shared code counter made it an eight-hour sitting, not just eight hours of work: codes are minted from a sequential counter, deleting one never returns the number, another user can take a number mid-package, and “once you start, if you want it to be sequential, you have to keep going.” The producer was the first person to try it; the ask, verbatim, was to make it “a dream-come-true task.”
Five days of discovery with one producer as patient zero — a state-of-play document, a 28-question scorecard (eleven answered from code and data before asking anyone), an acceptance-criteria doc, an input/output reference, 5,693 log records analyzed. What it found: the 84 codes reduce to 21 rows of real decisions — three spots, version matrices of 8, 4, and 9, times two tag variants, times a titled/generic twin — and only three distinct matrices; half the package was a byte-identical duplicate retyped by hand. About 1,940 cells to express those 21 decisions, a 90:1 ratio of transcription to thought. Fourteen percent of titles in the existing log had already drifted (lost their price suffix — all in the second, fatigued block).
And a correction at the walkthrough: I had the order backwards. The sheet precedes minting — the producer builds it to bid the work, shares it externally while it's still changing, and fills codes in later. So the tool can be a genuine standalone artifact with its own deadline, and no integration with the minting tool is needed — or even possible: its counter is one transaction per call, so contiguity is unachievable from outside; block reservation can only live inside that tool, and was filed as a change request to its owner instead of faked. Two “cosmetic” requirements turned out to be functional: the titled→generic interleave is a QC mechanism (the post partner posts masters in that order so a spot is watched with and without supers back to back), and title ≠ file name because the storage system forbids a character the slate title must keep.
Templates as data, not code. Historical sheets show the naming convention drifting year to year — a qualifier moved segments, a price treatment was renamed, one qualifier was written three different ways across three authoritative sources. Anything hard-coded would have been wrong last year and wrong again in two. So lift sets and vocabulary live in a producer-editable templates table; the one thing that has never changed — the base four tag variants, identical across a sales event, a launch, and an update, by four producers over three years — is the seed.
One generator, three consumers. Titles and file names are always generated, never stored; the same generator package is imported by the API, the UI, and the export so the three cannot drift. The slate-title grammar is frozen by an external automated parser at the post partner, and the generator's header says so: do not tidy the spacing, casing, or separators. Codes are stored separately from rows, keyed by a stable per-version id, because titles churn and codes are irreversible; duplicating a package deliberately does not copy codes. A pre-flight inspect flags duplicate codes, titled/generic mismatches, and a non-contiguous run — “another user may have taken these while you worked” — which in production correctly flagged the producer's burned number.
The acceptance test is the real sheet. Rebuild the December package from 21 authored rows and assert all 84 output rows match the shipped CSV cell for cell — in unit test, through a local database, and in production. If it fails, the grammar has drifted; don't deploy. The input UX was validated by the user rather than designed in a vacuum: add a spot, pick its type, the standard lift set appears pre-checked, uncheck what you don't need — which is what she described wanting before she saw it.
Built and shipped in two days; then the producer used it, and the first thirty-six hours taught more than discovery had. Her very first request — reorder and insert versions — would, with the original positional keys, have silently slid every irreversible code below it onto the wrong row; the fix was a stable per-version key, and a migration that rewrote 95 real assignments across three packages in place rather than losing them. Adding versions to a partly-coded package and pasting the new block silently reassigned from the top and burned good codes — so the default became fill-empty-rows-only, with replace-from-top opt-in and a live preview of what a paste will do. The minting tool's grid can't select a single column (a paid feature), so paste accepts a bare column, a full export, or several dragged columns; it lists newest-first, backwards from a sheet that builds downward, so a descending block is detected and a flip offered. And a generic-master code pattern collided with the billboard-pool pattern and would have landed in the wrong column — caught by a test, fixed with a lookahead.
The database password is resolved from the secrets store at runtime and the pool self-heals on auth errors — lifted verbatim from a sibling app, because the weekly rotation had already bitten six tools and this one didn't need to be the seventh (see rotation-proof).
Live and password-gated, 38 of 38 tests green, the real 84-row package reproduced cell for cell from 21 rows. By the end of the first week the producer had three packages and 95 code assignments in production and had sent two rounds of concrete feedback; her reaction at the walkthrough was “that would be such a huge help,” and her own workflow summary was recorded as exactly right. The agreed target was automate about 75%, she adjusts about 25%.
Honest scope: the tool removes roughly 59% of the manual entries — about 1,140 of 1,940 — collapsing the sheet assembly to ~111 typed values and one column paste. It does not remove the minting tool's 19-field form times 42 codes; that is the block-reservation change request, filed with that tool's owner. “Eight hours to minutes” was the emotional bar, not a measured after-time, and rollout to the other producers is planned (patient zero will teach them), not yet verified. The broader observation, stated as one: every system in this chain does spreadsheet-mediated handoffs with a human as the integration layer, and each re-entry is where the drift enters.
The deliverable is the sheet, not the codes — and nothing derived is persisted. One generator, imported by API, UI, and export, so they can't drift; templates as data, because the convention demonstrably drifts; and a real shipped package as the acceptance test, because if it fails the grammar has drifted and you don't deploy.
the insight