The Seven Shapes of Rework in Agentic Salesforce Work
Rework rarely announces itself. It shows up disguised as productivity — a revert here, a "fix:" follow-up there, a branch quietly closed and re-cut on a new base. Each looks like a small course correction. In aggregate, they are the single largest tax on a delivery stream, and in an agentic one — where an AI author ships fast and there is no default human reviewer standing between the model and the merge button — they compound faster than anyone expects.
We recently audited an entire merge-request history on a Salesforce engagement: 149 merge requests across roughly four months of near-solo, AI-authored work. The question wasn't what shipped. It was where did a later change have to fix, revert, or re-baseline what an earlier one missed — and why. About a quarter of all merge requests turned out to be rework. But the useful finding wasn't the percentage. It was that nearly every rework episode fit one of a small number of recurring shapes. Name the shape, and you can spot it forming on the next project before it costs you a release day.
This is that field taxonomy. Seven shapes, each with its tell (how you recognize it in flight), its root cause, and the guardrail that prevents it. It's a companion to our pillar on agentic development for Salesforce — the pillar makes the case for the operating model; this piece is the diagnostic you run inside it.
One pattern runs underneath all seven, so we'll name it up front: almost every shape reduces to acting on a stale or synthetic stand-in for the live production org instead of checking ground truth first. A local mirror, a ten-record fixture, a guessed pipeline, a spreadsheet pivot — each is a convenient substitute for reality, and each is where the rework was seeded. Keep that in mind as a decoder ring. The seven shapes are seven costumes worn by one root cause.
1. Stale-baseline revert & redo
The tell. A cluster of merge requests that all built cleanly, passed review, and then get reverted together — followed by a "v2" redo of the same work. In our audit this looked like a sequence of roughly !119–124, then a single revert MR !125, then a fresh !126–130 doing the same job again.
The root cause. The work was built and edited against a local sfdx-mirror snapshot that had silently diverged from the live org. On one high-fan-in file — a permission set — the local copy was 128 lines while production carried 773. Cascading the local version forward wouldn't have added a feature; it would have stripped 128 field-level permissions that existed in production and nowhere in the mirror. The mirror wasn't wrong when it was pulled. It was stale by the time it was trusted, and it drifts worst exactly where it hurts most: permission sets, profiles, large Apex — the shared, high-fan-in metadata many stories touch at once.
The guardrail. Build and edit shared metadata off the live target org, never the mirror — and make baseline freshness a mechanical check, not a habit. A CI job that diffs the shared files you're about to change against the live org and fails on unexplained divergence turns "I think the mirror is current" into a fact the pipeline verifies. This is the most expensive shape in the whole audit, and it is the purest expression of the throughline: an entire revert-and-redo cycle because a stand-in was trusted over the source of truth.
2. Wrong-base, close & recut
The tell. A branch's diff is enormous and full of files nobody meant to touch. Retargeting it only makes things worse, so the branch gets closed and the real change is cherry-picked onto a clean base as a new MR — !85→!86, !96→!97 in our data.
The root cause. The branch was cut from the wrong base. When the merge target and the branch's actual ancestor disagree, the diff doesn't show your change — it shows your change plus every difference between the two bases. In one case that meant thousands of unrelated files would have moved backward if the MR merged. No reviewer can evaluate a diff like that, and no AI author should open one.
The guardrail. Before opening any MR, confirm git diff --stat target...HEAD matches the scope you intended. If the file count is wildly larger than the change you made, you're on the wrong base — stop and recut now, cheaply, instead of after review. A branch-name-and-base lint in CI makes this unskippable.
3. Renumber churn
The tell. A merge request that is discarded and immediately re-created byte-for-byte identical. No code changed. The only difference is the identifier.
The root cause. The work was branched, committed, and opened under an internal ticket number, then had to be re-cut to carry the client's story number instead — !106 discarded, !107 opened with the same content under the client key. Pure ceremony, but not free: it costs a full open-review-close cycle and muddies the history for anyone tracing the change later.
The guardrail. Use the client's story number on every artifact from the very first commit — branch, MR, commit message, doc. This is the cheapest guardrail in the taxonomy to enforce and one of the easiest to forget: a branch-name lint that requires the client prefix catches it at creation, before a single line is written against the wrong identifier.
4. Iterative widening
The tell. The same bug gets "fixed" two or three times, and each fix is real — it genuinely closes a case the previous one missed. A data-cleanup filter went through three passes (v1 → !77 → !81), each surfacing a new real-world data shape the last one hadn't accounted for.
The root cause. The work was validated against a small synthetic fixture — in this case a handful of hand-built records — that reproduced the volume of production but not its shape. Real data carried relationship and collision classes (shared children, records pointing at the losing side of a merge) that the tidy fixture never contained. Every time the filter met production reality, it found a new edge, so the fix widened one pass at a time instead of landing complete.
The guardrail. Reproduce the data's shape, not just its size, before declaring a migration validated. Enumerate the relationship and collision classes that actually exist in production and seed a fixture row for each one. A fixture that mirrors production topology turns three reactive passes into one designed pass — and, again, it's the difference between checking ground truth and checking a convenient stand-in for it.
5. Reactive → preventive loops
The tell. A gate keeps rejecting the work one violation at a time, and each round-trip costs roughly a day. The sequence !64 → !75 → !82 is three passes at the same static-analysis gate, each fixing what the last round surfaced.
The root cause. No local visibility into the client's code-analysis gate (a PMD-style ruleset running in their pipeline, not one we could see). Without the ability to run the gate locally, every violation had to be discovered by submitting, waiting for the remote pipeline, reading the rejection, and resubmitting — a slow serial loop where a single local run would have surfaced them all at once.
The guardrail. When you lack visibility into a client's gate, reproduce it locally and run it preventively. Install the same ruleset, apply the known standard suppressions up front, and clear the findings before you ever submit. A reactive scan-fix loop pays a release day per round; a preventive local pass pays it once. The pattern generalizes: any time you're waiting on someone else's system to tell you what's wrong, ask whether you can run their check yourself first.
6. Suppression re-breaks
The tell. A fix you already made — and already learned — silently comes undone, and the same issue reappears in a later MR. Here a security-scanner suppression broke, got fixed, and then broke again across !111 → !113 → !114.
The root cause. Inline suppression comments (#nosemgrep, #noqa, #nosec) are position-sensitive — they only work on the exact line they annotate. An autoformatter re-wrapped a call across lines and relocated the suppression off its matched line, silently re-enabling the finding. Nothing looked broken; the comment was still there, just no longer attached to what it was suppressing. The nasty part is that this is a lesson the stream had already paid for once and then hit again, because the fix lived in someone's memory rather than in the tooling.
The guardrail. Fence inline suppressions so the formatter can't move them — wrap them in fmt: off / fmt: on, or keep the suppressed statement on a single line. Better, add a CI check that diffs suppression positions and fails when one drifts off its target. A lesson learned once should be encoded so it can't be un-learned by the next tool that touches the file.
7. Post-deploy remediation
The tell. Work is declared done — and then a separate, unplanned run has to go fix what slipped through after the fact. The most expensive kind of rework, because it surfaces in front of the client, in UAT or production, not in review.
The root cause. Something was validated against an incomplete view of reality. In one case (!87), a migration was reconciled against a spreadsheet pivot that had truncated its rows, so 218 opportunity records were simply missing from the "done" — invisible until someone counted against the source system. In another (!57), a metadata deploy didn't carry field-level security with it; the fields deployed, but the permissions didn't, and the gap surfaced downstream as Apex compile failures in UAT. Both are the same failure of ground truth: trusting a derived artifact (a pivot, a metadata push) to stand in for the authoritative state (a live record count, an actual permission grant).
The guardrail. Reconcile against the authoritative source before declaring done, and bake the easy-to-forget steps into the runbook. Validate processed counts against an independent query of the live org — never trust a pivot as the record source. Bundle an FLS/permission-set step and a post-deploy self-assign verification into every deployment, because metadata deploys don't grant field visibility on their own. If "done" isn't checked against ground truth, it isn't done — it's deferred.
The one guardrail underneath the seven
Lay the seven shapes side by side and the same sentence keeps surfacing. A stale mirror. A synthetic fixture. A gate you couldn't see. A suppression that drifted. A pivot that truncated. A deploy that didn't carry its permissions. In every case, work was performed against a stand-in for reality instead of reality itself — and the rework was the cost of the correction.
That's oddly good news, because it means you don't need seven different disciplines. You need one, expressed seven ways: check ground truth before you act, and make that check mechanical rather than optional. In a human-reviewed stream, a second person catches some of this by instinct. In an agentic Salesforce stream moving at machine speed, instinct isn't in the loop — so the check has to live in the system. A baseline-drift gate, a base-branch diff assertion, a client-story branch lint, a production-shaped fixture, a locally-run scanner, a suppression-position check, a reconcile-against-source step. None of them are clever. All of them make it impossible to trust a stand-in without noticing.
That's the real lesson of the audit: the wins in agentic delivery don't come from a smarter model. They come from wrapping a fast, capable, non-deterministic author in a thin spine of ground-truth checks it can't skip. Capability was never the constraint. Control is.
Naming the shape is half the fix. If you're running — or planning — agentic development on Salesforce and you'd like a second set of eyes on where your stream is quietly paying the rework tax, that's exactly the kind of audit we do. Talk to Facet about turning one engagement's hard-won lessons into guardrails your next project starts with, and read the agentic development for Salesforce pillar for the operating model these guardrails plug into.

