61pull requests merged (of 68 opened)
66commits, 27 Aug – 16 Sep
20,407lines added · 1,934 removed
95open Cases imported silently at go-live
17offline test harnesses
Summary
Winston is the in-house legal-operations application of a client’s
legal team, built and owned by their engineer on that team. Between 7
August and 16 September 2026 I joined it as the Salesforce and
integration contributor and built the Legal Front Door’s back half: the
layer that turns a Slack request into a Salesforce Case, keeps the two
systems agreeing about owner and status, self-serves NDAs, DPAs and ESAs
through Ironclad, and lets the app cut over from a sandbox to production
behind one switch. Sixty-one of my pull requests merged in three weeks.
The production cutover was proven on 15 September and the open Case
backlog was imported the same day without paging a single lawyer.
What Winston is
Winston is a React single-page app on Firebase Hosting with roughly
25 Firestore collections, Cloud Functions for every privileged write,
scheduled pollers, and an LLM-backed assistant behind a Cloud Run
proxy. Google SSO limits it to the company’s domain. It integrates with
Salesforce, Ironclad, DocuSign, Slack, Google Drive and Jira.
Thirteen modules sit in three tiers. The standard tier is Home, Deal
Desk, Deal Brain and Deal Lifecycle. The premium tier adds Matters,
Legal Brain, Legal Intake, Legal Front Door and Quote Integrity. The
operator tier is the author’s console. My work lives almost entirely in
the Legal Front Door and the Cloud Functions behind it.
The Legal Front Door is the intake product: a requester asks for
legal help in Slack, Winston classifies it into one of 13 request types,
files it, routes it to the right lawyer, and keeps the requester
informed through to close. Before my involvement it filed into a sandbox
only, nothing wrote Salesforce state back to Winston, and three of the
request types had no automated path.
My role and timeline
| Date |
Milestone |
| 7 Aug |
Read-only architecture review of the app at v2.33.0. Six ranked
findings delivered, including the “Needs attention” widget that silently
scoped to the viewer’s own name and 318 of 377 active deals with no
Legal Lead. |
| 20 Aug |
Repo write access and a staging Firebase project. Local environment
stood up; two setup traps documented for the next contributor. |
| 21 Aug |
First Salesforce-side change in both orgs: a read-only permission
set exposing account-level GenAI opt-in to the integration user. |
| 27 Aug |
First merged pull request: the Salesforce-to-Winston Case
mirror. |
| 9 Sep |
Backend test pass on the intake pipeline; audit register
delivered. |
| 10–14 Sep |
The self-serve document lanes, reassignment, Chatter, and the
Salesforce field contract. Forty-five pull requests merged in five
days. |
| 15 Sep |
Production cutover switch merged and exercised. Ninety-five open
production Cases imported as Winston tickets, silently. |
| 16 Sep |
Pre-go-live review: stop runbook, orphan-notification sweep, access
matrix and routing matrix reconciled with main. |
Throughout, the application’s author owned the product, the
architecture, review and deployment. I opened pull requests; they merged
and shipped them.
What I built
1. Salesforce as the system
of record
- Case mirror (#23). A scheduled job reads owner,
status and first-response time from Salesforce every 30 minutes and
writes them onto the matching ticket, keyed on a 15-character Case id,
so 15- and 18-character ids never miss each other. Winston’s consumer
side had been live for a week with nothing feeding it.
- Reassignment, Salesforce first (#47, #48). A lawyer
can reassign a Case-backed ticket from the queue. The Case owner is
written to Salesforce first and read back; only on success do the
Firestore write and the Slack side effects fire, and the owner is
stamped so the next mirror pass sees no diff. Every Case update sends
Sforce-Auto-Assign: false, because the routing rule was
silently reverting reassignments.
- Close, reopen and status in both directions (#54, #57, #60,
#69). Tickets and Cases move together through Out for
signature, Closed and Not required. Reopen reaches Salesforce and a
reopened ticket can close again.
- The field contract (#42, #43, #53, #55, #58). Every
request type lands the paper choice on
Form_of_Agreement__c, amendments collect the eight fields
Salesforce requires, MSA reviews enforce counterparty paper, and
required fields are required in the form rather than failing at the Case
insert.
- Chatter on the ticket (#72, #91, #94). Case,
Account and Opportunity Chatter read live into the ticket, with tabs, up
to the close.
- Create Order Form (#45). A new request type with an
opportunity and quote picker that writes
Case.Quote__c.
2. Self-serve documents
through Ironclad
- NDA (#30, #32, #54, #57, #74, #75). The our-paper
NDA launches an Ironclad workflow from the request. I fixed a live
defect where the unsigned signature packet could be attached and
announced as “fully signed”, because a regex scored both the packet and
the executed copy in the same tier and enumeration order decided.
Execution is now confirmed from Ironclad’s own sign status, the ticket
and Case stay Out for signature until every party has signed, the
executed copy is attached before the close, and the Slack copy is linked
by permalink. A Word-template lane and account-less NDA filing were
added for the cases that need them.
- DPA and ESA (#70, #76–#83, #85, #88, #97). Both
generate from the Ironclad template and hand the draft to the requester,
who sends it manually. A two-minute poller retrieves the draft, uploads
it to the requester’s thread, attaches it to the Case and sets the Case
to Drafted. Page two of the form collects the customer legal name,
signer name and title, address and notice email, and the ESA type so the
Case routes. A Government ESA is never generated on the commercial
paper.
- Beta Terms (#31). The ticket reads the real
DocuSign send outcome back rather than a stamp that was being set even
when the send failed.
3. Safety interlocks
- Environment interlock on outbound signatures (#34,
#40). Nothing reaches Ironclad or DocuSign unless the function
is in production or an explicit live-send flag is set. Staging can
rehearse without leaving a real workflow behind to void.
- Production cutover switch (#96).
LFD_TARGET_ORG selects sandbox, production or off.
Production login checks both IsSandbox = false and the
exact org id or refuses. Credentials are separate triples with no
fallback. Every ticket records the org it was filed in, and every poller
and triage write checks it, so a sandbox ticket is never touched with
production credentials. Off is the emergency stop: pollers skip, and a
Slack filing gets a clear “paused” message instead of a retry loop. The
deal calculator, which shares the module, was left untouched.
- Claim before filing (#33). Intake work is claimed
before the Case is created so a warm instance can’t file twice.
- Exact-org and authorisation checks (#40, #118). Raw
inbound events are readable by triagers only.
4. Go-live operations
- Backlog import (#111, #116). Ninety-five open
production Cases owned by the legal roster became Winston tickets with
their real created dates, owners pre-resolved through the roster and
every notification emitter held. I enumerated each thing that would
fire, the bell, the assignee DM, the health alarms, the daily and Monday
digests and the poller alarm, and what holds each one. The author’s
review found that the global mute switches also silenced a real request
filed during the window, so the second pull request moved the gate onto
the document: the create of an imported ticket is silent on its own, and
future imports need no switches at all.
- Lawyer notifications as a console switch (#99) and
roster access grant with a runbook (#100).
- Analytics and Team tabs (#113) describe tickets
from the last 15 days and exclude imported history by its marker, so the
backlog doesn’t read as three weeks of missed SLA.
- Stop runbook (#117). There was no incident
procedure. The runbook orders the levers by failure class, fastest
first, explains why pointing the target back at the sandbox is
credential isolation but not a rollback, tables every scheduler job and
what it does if left running, and gives the reconcile-before-reopen
SQL.
- Documentation (#120). The demo runbook and Ironclad
prerequisites marked superseded; access matrix and routing matrix
aligned with main.
How I worked
- Measure, then have it attacked. Every claim in a
pull request description carries the query or log it came from. Before
the backlog import I ran the real 95-Case manifest through staging end
to end, then stated what staging did not prove. Two of the larger
changes went through independent code review rounds before the author
saw them, and the findings are listed in the pull request.
- Offline harnesses. Seventeen test harnesses now run
the intake, triage, backfill, notification and health paths against fake
Firestore and intercepted Ironclad, Slack and Salesforce. The document
lane shipped with 35 cases; the import gate with deliberate mutations,
each caught.
- Runbooks for the person who is not me. Every
operational change shipped with the deploy steps, the canary line to
look for in the logs, and what to flip back.
- Salesforce work alongside the code. The integration
user’s Case permissions, the
Form_of_Agreement__c value
missing from the production record type, the routing entries for the new
request types, and the layout defaults that decide whether assignment
rules run were all found and fixed on the Salesforce side so the code
had somewhere to land.
By the numbers
Measured on origin/main and the GitHub pull request list
on 16 September 2026.
|
Mine |
The author |
| Pull requests opened / merged |
68 / 61 |
46 / 40 |
| Commits, excluding merges |
66 |
392 |
| Lines added / removed |
20,407 / 1,934 |
159,320 / 20,192 |
| Active window |
27 Aug – 16 Sep 2026 |
since inception |
Total pull requests on the repo: 119. Files I touched most:
lfd-intake.ts (33 commits), sf-demo.ts (17),
lfd-triage.ts (16), ironclad-launch.ts (15),
index.ts (15).
Findings worth keeping
- A “signed” regex that matched the outgoing signature packet meant
enumeration order decided which document was attached to a Case.
- A Case status stamp that was set on send, not on outcome, reported a
failed DocuSign send as sent.
- A Salesforce PATCH without
Sforce-Auto-Assign: false
re-runs assignment rules and reverts the owner you just wrote.
- A record-type change does not re-run Case assignment, and a layout
deploy can quietly untick the assignment-rule default.
- Global mute switches silence real traffic too. Gate on the
document.
- Pointing an integration back at the sandbox isolates credentials but
strands every production ticket and recalls nothing downstream. It is
not a rollback.
Numbers measured on the repository's main branch and its pull request list, 16 September 2026.