cupsfactory. Docket · 31 Aug 2026 · live

Build docket: cupsfactory.om

Everything left to build, in the order it has to happen. 109 numbered jobs across eight phases. Thirty are ticked. The multi-storefront engine is deployed to production, this domain is live on a Cloudflare origin cert, and the brand system is built off the real logo file. Phase 1 is done: Cups for Trade now has its own ERP tenant, with cupsbyaa and cupsfactory registered as its two storefronts.

Jobs open
79of 109
Engine
SHIPPED23 commits, live on bhd-main
Domain
cupsfactory.omlive, HTTPS, this page
Books
Cups for TradeCR 1393567, not VAT registered
Rebuild base
custom-v2quantity-first flow
30 / 109 jobs ticked Phase 2 started · Phase 1 not begun
done and verified next up ! waiting on Ali open
#dd7d00  cups / dot / الأكواب
#2b2721  factory. / مصنع
Two inks only  read from Final Logo.svg

Read before starting anything

Phase 1 blocks everything else. You told me BHD and CupsByAA are separate legal entities and CupsFactory sits under CupsByAA financially. So CupsFactory's orders, money and production have to land in CupsByAA's books, and CupsByAA does not have an ERP tenant yet. Building the website first would leave it with nowhere to post.

One thing I cannot decide for you, flagged at job 24: whether CupsFactory takes card payments through CupsByAA's existing Paymob merchant or needs its own. That is a merchant-account and settlement question, not a code one.

0

Shipped, for the record

12 commits

The multi-storefront engine is built and reviewed, 22 commits after a final adversarial pass found three Criticals that were fixed. It is what lets cupsbyaa and cupsfactory share one set of books while staying separately trackable, which is exactly what “cupsfactory sits under cupsbyaa financially” requires. Not re-listed as tasks.

CommitWhat it does
da4f2d6…977735fStorefront registry, tenant-keyed cache, partial unique index so a retired brand's code is reusable
0c6f3d7…c6a3188Brand resolver on every public request, path-scoped after it was caught 400-ing the whole admin API
85e2e6e…22dcf2fStorefront label on all six chain documents, with real propagation tests
7b4e6daPer-suite test databases, fixing a 40% flake rate caused by a shared-DB wipe
c58808dOptional portal auth and price-list resolution
8747e24Per-brand invoiced-value endpoint
38b43f6…d6f03baBrand seed, per-tenant sets, refuses to guess a database or a set
505bfcfTrade pricing wired into product page and checkout, retail path proven unchanged (91 tests)
d2928f5Admin page for the per-brand report, tenant-neutral
1

CupsByAA gets its own ERP

Jobs 1–14

Done. A Paper & Pen tenant with its own database and its own general ledger, serving two storefronts. BHD's ERP is not touched. The monthly BHD↔CupsByAA intercompany settlement continues unchanged, because the two stay separate entities.

  1. 01

    Provision the cupsbyaa P&P tenant

    erp-tenancy Tenant provisioned on production. DB pnp_tenant_cupsbyaa. The Oman profile seeded a 5% VAT default onto a business with no VAT certificate; the script disabled it.

  2. 02

    Decide and register the ERP host

    erp.cupsbyaa.com is LIVE: DNS, certificate, nginx vhost, tenant map and a verified customdomains row. Isolation proven: X-Storefront cupsfactory returns 200 there, bhdoman returns 400 because that brand lives in BHD’s tenant.

  3. 03

    Seed the storefront registry for this tenant

    Script shipped and proven: the bhd set is seeded on staging AND production. The cupsbyaa set runs once the tenant exists.

  4. 04

    Chart of accounts for CupsByAA

    erp-finance Its own COA. Do not copy BHD's seed endpoint, which is gated to the bhd tenant for exactly this reason.

  5. 05

    !Opening balances and cutover date

    needs Ali Real figures from the accountant. I will not derive these.

  6. 06

    Company identity: CR, VATIN, address, bank

    Verbatim from CR 1393567: Cups for Trade, South Al Hayl Al Seeb, reg 1393567, tax 1742971, VAT number deliberately EMPTY.

  7. 07

    Tenant branding: logo, colours, PDF letterhead

    Tenant branding and email templates seeded with the brand orange.

  8. 08

    Module entitlements

    growth plan. Manufacturing stays with BHD: Cups orders already mirror there as ManufacturingOrders.

  9. 09

    Migrate the cup product catalogue

    erp-sales Sizes, variants, lids, sleeves, the BOM. Yield derived from the fitted dieline, never retyped.

  10. 10

    Migrate the pricing ladders

    Published tiers with lineTotal preserved exactly. A tier's total is authoritative; do not re-derive it from a unit rate.

  11. 11

    Create the CupsFactory wholesale price list

    A real PriceList of type wholesale, attached to the cupsfactory storefront. Until it exists, cupsfactory prices at retail by design.

  12. 12

    Migrate customers and open balances from the Laravel app

    Dedupe on phone and email. Exclude the abandoned-cart rows, which are not real orders.

  13. 13

    Migrate order history

    Reconcile the dual orders / woocommerce_orders split, 776 twin rows, before importing. One row per real order.

  14. 14

    Staff accounts and OTP login

    Provision the CupsByAA team on the new tenant. Roles mirrored from the Laravel admin.

2

Infrastructure for cupsfactory.om

Jobs 15–24

The Cloudflare zone is active with zero DNS records. Everything here is greenfield.

  1. 15

    DNS: A and www, proxied

    cloudflare-api-call Point at the group VPS. Proxied orange-cloud.

  2. 16

    Cloudflare Origin CA certificate

    cf-origin-ca-cert Cloudflare Origin CA, apex + wildcard, valid to 2041. LE retired.

  3. 17

    nginx vhost on the VPS

    PHP-FPM 8.3 runs as www behind a www-data nginx. http2 on; is unknown to this nginx build.

  4. 18

    Laravel app skeleton at the web root

    Correct ownership from the start. A Mac UID on the web root breaks checkout and every page still returns 200.

  5. 19

    Mail: DKIM, SPF, DMARC for cupsfactory.om

    bhd-mail MX, SPF, DMARC and a 2048-bit DKIM key are live. opendkim-testkey returns "key OK". DMARC rua points at a sink, never a person.

  6. 20

    Mailboxes: info@ and sales@

    info@ and sales@ created on the BHD mail stack, postfix resolves them, passwords stored gitignored.

  7. 21

    Deploy script with a fail-closed preflight

    deploy-project scripts/deploy.sh, fail-closed. It refuses openrsync (which rejects --chmod), sets 755/644 explicitly, then proves every file at ORIGIN before checking the edge, because a query string cannot reveal a stale edge. It caught the openrsync trap on first run.

  8. 22

    Staging host before production

    staging.cupsfactory.om is live on the same wildcard origin cert, so it needed no second certificate. noindex is enforced by an X-Robots-Tag header at the SERVER, not a meta tag, because a staging copy outranking production is a real way to lose a launch.

  9. 23

    ERP bridge credentials

    64-hex key set as a Setting on pnp_tenant_cupsbyaa, compared timing-safe, rotatable without a redeploy. Stored gitignored, never in git.

  10. 24

    Paymob merchant decision

    decided Same bank account, so CupsFactory runs on CupsByAA’s existing Paymob merchant. Settlement stays one reconciliation, not two.

3

Brand system

Jobs 25–36

Two inks and a bilingual lowercase wordmark is a tight brief, which is a gift. The whole identity has to come from type, spacing and paper, not from a colour palette. Arabic is set at parity with English in the logo, so it must be at parity on the site too.

  1. 25

    Extract logo masters from Final Logo.ai

    5 SVG variants and 9 PNGs from Final Logo.svg, with a usage spec. Full lockup dies below 90px, so a favicon needs its own mark (job 34).

  2. 26

    Identify the wordmark typeface

    5 SVGs and 9 PNGs shipped; no font name is embedded in the .ai, .pdf or .svg, so the face is characterised not identified. Single-storey geometric a rules out Montserrat, Nunito, DM Sans and Poppins.

  3. 27

    Host the type on fonts.bhd.om

    brand rule Figtree (variable 100-900) and Almarai both already hosted on fonts.bhd.om and now serving this page. No Google, no gstatic.

  4. 28

    Arabic face paired to the Latin

    Almarai, paired to Figtree and loaded from fonts.bhd.om. Set on :lang(ar) and [dir=rtl] in the token layer, so Arabic is chosen, not left to the browser.

  5. 29

    Build the token layer on design.bhd.om

    bhd-design themes/cupsfactory.css: brand inks, a measured 10-step orange ramp, semantic tokens, light and dark.

  6. 30

    Tailwind config bound to the tokens

    tailwind.tokens.js bound to the CSS variables, with the runtime-class purge trap safelisted.

  7. 31

    Wire FontAwesome Pro 7.2 from design.bhd.om/fa

    bhd-design Pinned to /fa/v7.2.0/, not /latest/, so a silent major bump cannot change glyphs under a live site. Verified 200.

  8. 32

    OMR rendering via BHDCurrency

    The official Central Bank Rial sign. Never the string “OMR”, never ‏ر.ع, never the generic ligature. Three decimals throughout.

  9. 33

    Accessible contrast pass on the two inks

    Measured. Orange on white is 3.00:1 and FAILS AA. Split the role: #dd7d00 for identity, #9f5a00 (5.33:1) for text and buttons.

  10. 34

    Favicon, app icons, maskable icon

    A mark drawn on a 64 grid, legible at 16px, in 8 sizes. Not a shrunken logo, because the lockup dies below 90px.

  11. 35

    Open Graph share card, 1200×630

    og-share-card Bilingual 1200x630 built from the real logo master, product drawn in the two inks so it needs no photography.

  12. 36

    Brand sheet for the team

    One A4 page, live at /brand-sheet.pdf. Carries the measured contrast numbers, so "do not put orange on white" is a fact on the page rather than an opinion.

4

The site: rebuilding v2

Jobs 37–68

The base is custom-v2.blade.php plus the OrderFormV2 Livewire component: pick a quantity, then compare what every cup costs at that quantity, then check out. The audit (job 37) established that v2 is a VIEW on a shared engine, so this phase rebuilds the view against that engine. Refactoring the engine is real work but it is shared with the live cupsbyaa.com surface, so it is deliberately NOT bundled in here.

  1. 37

    Audit v2 honestly before copying it

    Done, and it changes the plan: v2 is a VIEW, not an engine. OrderFormV2 is 46 lines subclassing a 2,291-line OrderForm, and 241 of the page’s 374 lines are inline CSS hardcoding CupsByAA pink ~30 times. So jobs 43-48 rebuild a view against the existing engine. Full audit in docs/v2-audit.md.

  2. 38

    Three creative directions, not one

    award-site-process Distinct concepts. Judged and scored before any build.

  3. 39

    Run the jury and pick a direction

    Independent scoring. Generic gets rejected, not softened.

  4. 40

    Page architecture and routes

    Home, order, sizes, capabilities, quality, contact, track, portal. Bilingual mirror at /ar/.

  5. 41

    RTL as a first-class layout

    Logical properties throughout. Test the Arabic mirror on every page, not just the home page.

  6. 42

    Hero: the factory thesis

    Lead with capacity and lead time, which is what a trade buyer needs. Not a stock coffee photo.

  7. 43

    Quantity ladder, rebuilt

    Wrapping chip grid, never a horizontal scroller: on a phone a scroller hides the big tiers behind a swipe nobody makes.

  8. 44

    Trade-scale tiers

    This is a factory. The ladder should run to pallet quantities, not stop where a retail shop stops.

  9. 45

    Price board, rebuilt

    Two cards per row on the narrowest phone. Comparing prices side by side IS the page; one column defeats it.

  10. 46

    Unavailable tiers stay readable

    A greyed card must still state the real minimum. That number is the answer the buyer needs.

  11. 47

    Best-value marker that is actually true

    Computed from the ladder, not hardcoded on one card.

  12. 48

    Live unit price and line total

    At a published tier the total is authoritative and the unit price is derived from it. Never re-multiply.

  13. 49

    To-scale size guide

    to-scale-size-guide True-proportion SVG, real cup silhouettes, ml per size. Responsive with no sideways scroll.

  14. 50

    Product photography set

    Transparent-background cutouts, consistent lighting, one per SKU. Composited on the site's own ground.

  15. 51

    Artwork upload with real validation

    Check true mm size, effective DPI, outlined text, bleed. Tell the customer what is wrong in their words.

  16. 52

    Live cup preview from uploaded artwork

    Wrap on a cone-accurate mockup, not a flat rectangle. This is the moment that sells a custom cup.

  17. 53

    Dieline download per size

    print-production Real fan blank on the measured die, with bleed and cut contour.

  18. 54

    Digital proof and approval step

    Nothing enters production without an approved proof. Reuse the cup-proof endpoints already in the ERP.

  19. 55

    Phone field with country selector

    intl-phone-input Chip stays on the LEFT in both LTR and RTL. E.164 normalised.

  20. 56

    Trade account application

    Credit terms, CR number, VATIN. Feeds the ERP credit workflow rather than an inbox.

  21. 57

    Logged-in trade pricing on the storefront

    The wholesale price list resolves for a signed-in buyer while browsing, not only at checkout.

  22. 58

    Reorder from a past order

    The single highest-value feature for a repeat trade buyer.

  23. 59

    Order tracking page

    Production stage in the customer's language, not the factory's internal stage names.

  24. 60

    Customer portal: quotes, invoices, statements

    Served from the ERP portal endpoints. PDFs on demand.

  25. 61

    Capability page: machines, capacity, lead times

    Real numbers. This is what separates a factory site from a shop site.

  26. 62

    Quality and food-safety page

    Materials, certifications, food-contact compliance. Buyers ask; answer before they have to.

  27. 63

    Sample request flow

    A trade buyer orders samples before a pallet. Make it one step, not an email.

  28. 64

    Motion pass

    One orchestrated moment beats scattered effects. Respect prefers-reduced-motion everywhere.

  29. 65

    Empty, loading and error states

    Every one designed. Errors say what went wrong and how to fix it, with no apology.

  30. 66

    WhatsApp contact and share links

    brand rule api.whatsapp.com/send?phone=, never wa.me.

  31. 67

    Short links for anything sent to a customer

    bhd-link-shortener Every customer-facing link minted to bhd.om/<code> first. No size threshold.

  32. 68

    Cookie and consent handling

    Decline non-essential by default. No dark patterns.

5

Commerce and money

Jobs 69–80

Every job here can lose a real order or misstate a real number, so each one gets a test that fails when it is wrong.

  1. 69

    Paymob integration

    bhd-paymob Intentions API, 3DS, OmanNet. Merchant per job 24.

  2. 70

    Apple Pay, including non-Safari desktop

    Apple's JS SDK plus the <apple-pay-button> component and a QR handoff.

  3. 71

    Inline Pixel card form

    No hosted redirect. The checkout stays on cupsfactory.om.

  4. 72

    Webhook with HMAC verification

    Idempotent. A replayed callback must never double-credit an invoice.

  5. 73

    Bank transfer and credit-terms checkout

    Trade buyers often will not pay by card. Both paths must reach the same order.

  6. 74

    Order pushes into the CupsByAA ERP

    Stamped storefront: cupsfactory, so it is separable in CupsByAA's own books.

  7. 75

    Two-way production status sync

    Forward-only stage mapping. A stale webhook must never drag a printing job back to design.

  8. 76

    Stale-order notification guard

    Moving an old job must not text a real customer “your order is ready” months later.

  9. 77

    VAT handling and the tax invoice

    CupsByAA's own VATIN on the document. Prices inclusive or exclusive, decided once and applied everywhere.

  10. 78

    Abandoned-cart definition

    An unpaid unstarted cart is not an order. Exclude it from counts, or every dashboard inflates.

  11. 79

    Order confirmation email and WhatsApp

    Deduped. A document header carrying a PNG returns a real message id and is never delivered.

  12. 80

    End-to-end money test on staging

    A real card, a real order, a real ledger entry, then reversed. Not a mocked callback.

6

Content, SEO and AI engines

Jobs 81–94
  1. 81

    Bilingual copy pass, written not translated

    Omani vocabulary in the Arabic. A translated site reads translated.

  2. 82

    Keyword and intent map

    Trade intent differs from retail: “paper cup supplier Oman”, not “buy 50 cups”.

  3. 83

    Landing pages per product and city

    Real content per page. Thin doorway pages get dropped from the index.

  4. 84

    Structured data

    Organization, Product, Offer, FAQ, Breadcrumb. Validated, and matching what the page actually says.

  5. 85

    hreflang for the EN and AR mirror

    Reciprocal, with a correct x-default.

  6. 86

    Sitemap and robots

    Per-crawler rules. Generated, never hand-maintained.

  7. 87

    Canonicals

    Every variant page resolves to one canonical. This is where the v2 page originally sat behind noindex.

  8. 88

    llms.txt, llms-full.txt, ai.txt

    bhd-aieo So answer engines describe the brand correctly rather than inventing it.

  9. 89

    Entity and sameAs graph

    Ties cupsfactory.om to the group estate without leaking internal structure.

  10. 90

    IndexNow wired to deploy

    Ping on publish, not on a schedule.

  11. 91

    Google Search Console verification

    seo Token read from its own verify URL. No OAuth over DNS.

  12. 92

    Bing verification, by API

    Same estate pattern already proven across 25 zones.

  13. 93

    Analytics and conversion events

    Quantity chosen, size chosen, artwork uploaded, checkout started, paid. The funnel, not pageviews.

  14. 94

    Truth pass on every public claim

    verify-cited-claims Capacity, lead time, certifications, client logos. Each resolved to a source or removed.

7

Quality gates

Jobs 95–104

Deterministic checks, not opinions. Each produces evidence that can be re-run.

  1. 95

    Responsive and overflow audit

    Every page, every breakpoint. The report names the widest offending element, not just “it scrolls”.

  2. 96

    Accessibility audit

    design-stack axe at both scopes, heading order, real keyboard skip link, tap targets, reduced motion.

  3. 97

    Contrast audit against the two inks

    Measured, not eyeballed. The orange is the risk.

  4. 98

    Lighthouse to 100 on mobile

    Gate heavy effects on viewport width BEFORE any capability probe, or mobile pays for a desktop flourish.

  5. 99

    Core Web Vitals on real hardware

    Watch CLS: a JS-driven nav with no cloak paints fully open until it boots.

  6. 100

    Link and asset audit

    Per-link 2xx. Catches basePath bugs and 404 assets that a build never surfaces.

  7. 101

    Playwright end-to-end suite

    add-e2e-tests Quantity, size, artwork, checkout, tracking. Both languages.

  8. 102

    Cross-browser and real-device pass

    Safari iOS especially: Apple Pay and RTL both bite there first.

  9. 103

    Security review

    Upload handling, rate limits, enumerable ids, secrets. Uploads are the exposed surface here.

  10. 104

    Independent design jury before launch

    Scored against the chosen direction. Generic is a fail, not a note.

8

Launch

Jobs 105–109
  1. 105

    Staging sign-off

    Full suite, real payment, real ERP order, then reversed.

  2. 106

    !Seed the registry on production

    manual step DATABASE=... npm run seed:storefronts -- cupsbyaa, in the same window as the deploy.

  3. 107

    Cut over DNS and watch

    Origin, then edge with a real cache-buster. A stale edge can serve the old bytes while origin is correct.

  4. 108

    Post-launch monitoring

    Uptime, checkout errors, webhook failures, mail deliverability. Alerts to a sink, not a person's inbox.

  5. 109

    Log the launch and hand over

    What shipped, what is deferred, what needs watching in week one.