feat(tendlc): band tendlc brand + vetting end to end (PR 3 of 5) - #33
feat(tendlc): band tendlc brand + vetting end to end (PR 3 of 5)#33kshahbw wants to merge 20 commits into
Conversation
…cape Capture and assert on r.URL.EscapedPath() instead of the decoded r.URL.Path to ensure the test detects when brandPath() stops calling url.PathEscape. The decoded path is identical whether the ID was escaped or not (both produce /brands/a/b c/history), so asserting on the decoded path would pass even if escaping was removed.
…overage for GOVERNMENT requirements
…and type on update
…ceipt-write failure
…he delete cascade claim
…bering, correct 12-key projection, label reconstructed receipt
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Independent adversarial pass (codex) — 4 Important + 1 Minor, all realRan a fresh reviewer over the branch after it had already cleared ten per-task reviews and a whole-branch review. It found five things, and I verified every one against the source before acting. Three were this PR's to fix; two are CLI-wide and are now documented rather than silently papered over. Fixed
A test that could not fail. Documented, not fixed — both are CLI-wide and belong in their own changeCtrl-C during
Both are pre-existing and shared with Heads-up on CI
|
What this adds
band tendlc brand(9 commands) andband tendlc vetting(3 commands), end to end — service writes, the per-brandTypevalidation matrix, lossless read-modify-write updates,--confirmfee gates, and--waitpolling.PR 3 of 5. Stacked on #32 (
feat/customer-profiles) — review that first; this diff is only the brand/vetting layer.brandcreaterefreshlistgetupdatedeletereverifyresend-2fahistoryvettinglistrequestimportThe old
tendlc campaigns/numbers/numbercommands are untouched. The command-tree cutover is PR 5, so both trees coexist for one release as a real deprecation window.The guard worth reviewing first
POST /brandssilently discards an invalidcustomerProfileIdand returns 202, creating an orphan brand with no profile association — a typo produces a billable, permanently-unverifiable brand and a success receipt. I hit this by accident while probing.brand createnow pre-flights the profile. Verified live: the exact input that produced the orphan brand now exits 3 and creates nothing (brand count 10 before, 10 after). The check degrades rather than blocks — Customer Profiles Access is a separate role from Campaign Management, so a 403 on the pre-flight warns and proceeds rather than locking out someone entitled to create brands.Two defects live testing caught that the stub suite could not
A registering brand is indistinguishable from a rejected one.
brandIdentityStatusreadsUNVERIFIEDfor the entire registration window;REGISTERINGis never returned on the read path despite being in the published enum. Two brands with byte-identical payloads: oneVERIFIEDat 46s, one stillUNVERIFIEDat 275s with no TCR response at all. Our classifier calledUNVERIFIEDterminal, sobrand create --waitwould have exited 4 — "identity could not be confirmed" — within seconds for every brand, including every one that goes on to verify. It now keeps polling and surfaces the last-seen status at timeout.I rejected the clever fix: you can separate the two cases by grepping brand history for a
BRAND_IDENTITY_STATUS_UPDATEentry, but that message is free text, undocumented, and unversioned. Coupling poll control flow to its wording would be worse than the latency.We were telling users something untrue about an irreversible operation. The API docs say deleting a brand deletes its customer profile. It does not — both test profiles survived with
softDeleted: false. The--confirmmessage repeated that claim; it now says the profile survives and must be deleted separately.Design decisions worth a second opinion
updatereturns an acceptance, not the resource, and takes ~5 minutes to apply. Measured. The receipt now says so. This independently confirmed the earlier decision to withhold--waitfromupdate— polling would have returned instantly against pre-update state and reported success.--bandwidth-idfilter onlist.bandwidthId[eq]is accepted and silently ignored, returning every brand. Handing back wrong data with no error is worse than not offering the flag.--class RCSis supported despite being absent from the published enum, because production accepts it.--countryflag — the API derivescountryfromcountryCodeA3. This corrects an earlier finding of our own that said both were required.SOLE_PROPRIETORgets common-field validation only. Its rules are account-gated and unobservable on any account we have; inventing them would reject requests the API accepts.brand createis billable but ungated — the fee lands ~6s after the 202, before verification resolves, and is charged even when the brand never verifies. Create needs ~13 explicit flags and can't happen by accident, so it stays ungated, but that exception is now documented rather than silent.Testing
Ten tasks, each with an independent review and fix loop, then a whole-branch review that returned six Important findings — all fixed here.
Live-verified against 9900000: every read command, all four
--confirmrefusals (each exit 6, zero write requests), validation aggregation, the pre-flight guard, a full create→verify→delete lifecycle, RMW losslessness (46 keys before and after, zero nulled), andrefreshaccepting either identifier. All test resources cleaned up; brand count back to its baseline.Stub-verified only:
vetting request(places a real billable order with an external provider) andbrand reverify($4 per call). Their refusal and validation paths were verified live; their happy paths were not.Follow-ups, not in scope
SilenceUsageis never set on the real root, so every error in the whole CLI dumps a usage block — pre-existing and CLI-wide, worth its own issue.softDeleted; the API's behavior with a soft-deleted profile is unmeasured, so blocking on it could refuse creates production would accept. Measure in PR 4.Live probing added 12 findings to the spec-vs-production log (now 31), including the two above. the internal API-spec tracking ticket has been updated.