diff --git a/docs/BLOG-POST.md b/docs/BLOG-POST.md index 317c988..ead7413 100644 --- a/docs/BLOG-POST.md +++ b/docs/BLOG-POST.md @@ -70,19 +70,29 @@ advisory-only: it explains, it never decides. ## The architecture, and why each piece is load-bearing -``` -StockSync Counter (React, IndexedDB offline queue) - │ POST /transactions ▲ WSS live push - ▼ │ -API Gateway (REST + WebSocket) - │ - ▼ -Lambda: write-intake → SQS FIFO (MessageGroupId = item_id) → Lambda: conflict-resolver - │ idempotency check │ vector-clock / PN-Counter merge - │ │ TransactWriteItems (record + audit + dedup) - ▼ │ Bedrock explanation (non-blocking) - Amazon DynamoDB ▼ -``` +A write makes one trip down this pipeline, and the live push back to +every open counter is a separate hop off the same event: + +1. **StockSync Counter** (React, IndexedDB offline queue) sends + `POST /transactions` — queued locally first, so a flaky connection + never blocks a sale. +2. **API Gateway** (REST) receives it and hands off to Lambda. +3. **Lambda `write-intake`** does the idempotency check, then enqueues. +4. **SQS FIFO**, grouped by `MessageGroupId = item_id` — not by which + counter sent it — so every write touching one item is processed in + strict order, no matter how many counters are concurrently active. +5. **Lambda `conflict-resolver`** does the actual merge: vector-clock + comparison, PN-Counter arithmetic for stock, field-level merge for + everything else. It commits the record, its audit-log entry, and its + idempotency claim together via `TransactWriteItems`, and — only on a + genuine same-field conflict — asks **Amazon Bedrock** for a + plain-language explanation, non-blocking. +6. **Amazon DynamoDB** is the single source of truth the whole pipeline + converges on. +7. A DynamoDB Streams event off that same write fans out through + **API Gateway's WebSocket API** as a live push (`WSS`) back to every + other open counter — so a second screen sees the new stock count + arrive without ever polling for it. The detail that matters most and is easiest to get wrong: the **SQS FIFO queue's `MessageGroupId` is the inventory item's ID, not the sending diff --git a/docs/SUBMISSION-FORM.md b/docs/SUBMISSION-FORM.md index 3a41030..ec6d5ff 100644 --- a/docs/SUBMISSION-FORM.md +++ b/docs/SUBMISSION-FORM.md @@ -110,33 +110,96 @@ the PN-Counter/vector-clock design decisions, the load-bearing hardening pass caught. Nothing left to do but publish it and paste the link._ +### AWS Builder Center "Create an article" form — ready-to-paste fields + +**Title** (short, descriptive, unique): + +``` +Two billing counters, one bad connection, and the bug that erases a sale +``` + +**Description** (1–2 sentences, doesn't repeat the title): + +``` +How a hackathon inventory-sync engine uses vector clocks, a PN-Counter +CRDT, and SQS FIFO ordering on AWS to guarantee two offline point-of-sale +counters can never silently overwrite each other's sales — plus a real +concurrency bug a dedicated hardening pass caught before launch. +``` + +**Tags** (pick the closest 5 available in the dropdown — it's a +controlled vocabulary, so exact matches may not exist): + +``` +AWS, Serverless, DynamoDB, TypeScript, Distributed Systems +``` + +**Body:** copy the full contents of [`docs/BLOG-POST.md`](BLOG-POST.md) +(everything after the title line) into the body editor — it's already +written in Markdown, and the editor's toolbar shows a Markdown cheatsheet +that confirms the same syntax (headers, bold, code fences, links) works +as-is. No reformatting needed. + +**Cover image (optional, 1200×675px, no text in the image):** this field +needs an actual image file, which I can't generate directly. Use the +prompt below in an image generator (e.g. Midjourney, DALL·E, Stable +Diffusion) and upload the result: + +``` +A clean, modern tech-editorial illustration for a software engineering +blog post about distributed systems and offline data synchronization. +Two abstract point-of-sale terminal icons on the left and right, each +glowing softly, connected by flowing data-stream lines that merge into a +single glowing node in the center, symbolizing conflict-free merge of +concurrent data. Isometric flat-design style, dark navy background +(#0F1629) with vibrant orange (#FF9900) and indigo (#4F46E5) accent +lighting on the connection lines and nodes. Minimalist, high-contrast, +no text, no logos, no people, no wordmarks — purely abstract +geometric/network shapes. 1200x675 landscape aspect ratio, suitable as a +blog cover image. +``` + --- ## Team leader's contributions * > Individual roles and key deliverables completed by team member. -_TODO — edit this to describe your actual role. Draft below assumes a -solo build; adjust if that's not accurate:_ +_TODO — review and adjust to match what each person actually did before +submitting; these are drafted splits, not a verified record. Contributions +below are intentionally sized as an even three-way split — same scope of +ownership, same depth, no single person carrying disproportionately more._ -**Maha Kisore** — sole builder. Designed and implemented the full stack -end to end: the CRDT/vector-clock conflict-resolution core -(`packages/core`), all Lambda handlers and the SQS FIFO/DynamoDB Streams -pipeline, the CDK infrastructure, the React client (offline queue, -real-time WebSocket sync, conflict review UI, barcode scanning), Cognito -multi-tenant auth, the analytics dashboard, SES notifications, and the -full UI/UX design pass (dark mode, animations, premium icon set). Also -ran a dedicated edge-case hardening pass against a 25-scenario catalog, -which caught and fixed two real concurrency/correctness bugs before -launch. +**Maha Kisore (team leader)** — Owned the correctness core: designed and +implemented the CRDT/vector-clock conflict-resolution engine +(`packages/core` — PN-Counter for stock, vector clocks, field-level +merge) and the write pipeline (`write-intake` → SQS FIFO grouped by +`item_id` → `conflict-resolver`, with atomic `TransactWriteItems` +commits). Coordinated the overall architecture and team task split. ## Second team member's contributions -_Leave blank if solo, or fill in if you had a teammate._ +**Ramkumar** — Owned the client and the auth layer: built the StockSync +Counter React client (offline queue, real-time WebSocket sync, the +conflict-review UI, barcode/QR quick-entry, the products/checkout flow) +and implemented Cognito-based multi-tenant authentication end to end — +self-signup, owner/manager/counter-staff roles, the JWT authorizer on +every API route, and the staff invite flow. ## Third team member's contributions -_Leave blank if solo, or fill in if you had a teammate._ +**Yashwanth** — Owned infrastructure and the platform layer: the CDK +stack defining every AWS resource, the owner-facing analytics dashboard, +the SES low-stock/conflict notification pipeline off DynamoDB Streams, +CloudWatch observability, and the Amazon Bedrock same-field-conflict +explainer integration. + +All three worked together on the dedicated edge-case hardening pass +against a 25-scenario catalog (which caught and fixed two real +concurrency/correctness bugs before launch), the property-based test +suite, and the UI/UX design pass (dark mode, animations, the icon-driven +visual system) — shared, cross-cutting work none of the three owns +alone. --- diff --git a/docs/VIDEO-SCRIPT.md b/docs/VIDEO-SCRIPT.md new file mode 100644 index 0000000..6efe0d0 --- /dev/null +++ b/docs/VIDEO-SCRIPT.md @@ -0,0 +1,191 @@ +# StockSync — 3-Minute Demo Video Script + +The video is done: **`demo-assets/stocksync-full-tour.mp4`** (also +`.webm`), at the repo root, gitignored. It's a real, automated 2:59 +recording — dark mode throughout, an animated visible cursor for every +click and hover, and genuine data at every step (real stock arithmetic, +a real price conflict, a real completed checkout). Nothing here is +freeze-framed or stretched — the full 3 minutes is real screen time, in +the order below. You just need to add your voice. + +**Timestamp precision:** four points are exact, read directly off the +file: **0:00**, **0:42** (end of the intro), **2:04** (end of the +two-counter demo), and **3:00** (end). Everything in between is +sequenced correctly (I wrote the automation, so the order is certain) +but the seconds are reasonable estimates, not frame-measured — page +loads and network calls don't take identical time on every run. Watch +the video once with this script open and nudge your narration to match +what you actually see; don't read it as a stopwatch drill. + +--- + +## Segment 1 — Hero, dark mode, sign-in, counter picker (0:00–0:42) + +**0:00–0:08 — Hook + dark mode** +Hero page loads in light mode, headline animates in, then the cursor +moves to the theme toggle (top right) and clicks it — the whole page +switches to dark. + +> "This is StockSync — an offline-first inventory sync engine for shops +> running more than one billing counter. And yes, it supports dark +> mode." + +**0:08–0:20 — Design tour** +Cursor scrolls down through the "How it works" steps and the feature +grid (offline-first, conflict-free merge, real-time sync, AI-assisted +review, real multi-tenant auth, barcode quick-entry). + +> "Two counters can go offline, sell the same item, restock it, change +> its price — in any order, for any duration — and when they reconnect, +> the stock always converges to one correct number. Every one of these +> pieces is real: real auth, real AI review, real barcode scanning." + +**0:20–0:38 — Sign in + counter picker** +Cursor clicks the email field, types it out; clicks the password field, +types it out; clicks "Sign in." The counter picker appears; the cursor +hovers "Counter B" briefly, then clicks "Counter A." + +> "Signing in is real Cognito authentication — this account owns a real +> shop. Once you're in, you pick which physical counter this browser +> session is." + +**0:38–0:42 — Counter A glance** +The item grid loads — Bread, Milk, Parle-G, Rice — cursor glances over +a couple of cards and the barcode-scan button. + +> "Four items, seeded with real stock and prices. Let's break it." + +--- + +## Segment 2 — The two-counter conflict demo, side by side (0:42–2:04) + +**0:42–1:00 — Both counters sign in, side by side** +Two windows, side by side: the left signs in and picks Counter A, the +right signs in and picks Counter B — same shop account, two sessions, +both switch to dark mode independently. + +> "Here's the real test: two windows, same shop, different counters. +> Counter A on the left, Counter B on the right." + +**1:00–1:05 — Both go offline** +Both connectivity toggles get clicked — both badges flip from green +"Online" to red "Offline." + +> "Both go offline — right now, live. Neither can see the other." + +**1:05–1:15 — Selling while offline** +Counter A's "Sell 1" gets clicked five times, pauses, then two more. +Counter B's "Sell 1" gets clicked three times. Stock numbers drop +independently on each side. + +> "Counter A sells 5, then 2 more. Counter B sells 3. Both counting down +> from the same starting stock, with zero visibility into each other." + +**1:15–1:20 — Reconnect** +Both toggles get clicked back to "Online"; both pages refresh. + +> "Reconnect both — right now." + +**1:20–1:26 — Arithmetic proof** +The stock number settles at **40** on both screens simultaneously. + +> "Fifty, minus five, minus two, minus three — forty. On both screens. +> Every sale counted, nothing silently overwritten." + +**1:26–1:32 — Audit trail** +Cursor clicks "parle-g" under the audit trail section on Counter A — +the sale history expands, showing each transaction attributed to the +right counter. + +> "And the audit log proves it — every sale, correctly attributed, in +> the order it actually happened." + +**1:32–1:38 — Setting up the hard case** +Both counters toggle offline again. + +> "Now the case that can't be auto-merged." + +**1:38–1:45 — Concurrent price edits** +Counter A's price field gets clicked and set to **₹15**; Counter B's +price field gets clicked and set to **₹18**. + +> "Both counters set a different price for the same item, while both are +> offline." + +**1:45–1:50 — Reconnect** +Both toggles click back online. + +> "Reconnect." + +**1:50–2:04 — The conflict banner** +An amber "Needs your review" banner appears on Counter A, showing both +candidate values — ₹15 from counter_a, ₹18 from counter_b — with a +"Generating explanation…" line where Amazon Bedrock's plain-language +summary would appear. + +> "StockSync never guesses on a genuine conflict. It flags it for a +> human, shows both real values, and calls Amazon Bedrock to explain the +> disagreement in plain language — advisory only, it never decides for +> you." + +_If Bedrock's explanation has actually rendered by the time you record +this (it depends on account access clearing), read the generated text +instead of "calls Bedrock to explain" — say what's actually on screen._ + +--- + +## Segment 3 — Products, Checkout, Dashboard, Staff (2:04–3:00) + +**2:04–2:12 — Back in, single window** +A fresh sign-in as Counter A, straight to the item grid, dark mode. + +> "That's the core engine. Everything past here is the product built on +> top of it." + +**2:12–2:20 — Products** +Cursor clicks "Products" in the nav; the catalog table appears — name, +SKU, category, supplier, price. + +> "A real product catalog — categories, suppliers, pricing — all backed +> by the same DynamoDB tables." + +**2:20–2:32 — Checkout** +Cursor clicks "Checkout"; two product cards get clicked to add them to +the cart; "Complete checkout" gets clicked; a real order confirmation +and toast notification appear. + +> "Checkout builds a real cart and submits it — and it goes through the +> exact same write pipeline as a manual sale. Same guarantees, same +> conflict resolution, zero special-casing." + +**2:32–2:42 — Dashboard** +Cursor clicks "Dashboard"; the trust-score, revenue, and low-stock cards +are visible — the trust score reflects the real conflict from Segment 2. + +> "The owner's dashboard — daily revenue, and a trust score that +> reframes the conflict rate as a business signal, not just an internal +> correctness number. Right now it's showing the exact conflict we just +> caused." + +**2:42–2:49 — Staff** +Cursor clicks "Staff"; an email gets typed into the invite field; the +role dropdown gets set to "Manager." + +> "And owners can invite real staff accounts — managers, counter staff — +> each with their own scoped permissions, enforced server-side, not just +> hidden in the UI." + +**2:49–3:00 — Close** +Held on the Staff page as the final frame. + +> "That's StockSync — offline-first, mathematically correct, and a +> complete product around it. Live on AWS today." + +--- + +## If you want a shorter cut + +The single most important 45 seconds, if you ever need a trimmed version: +**1:00–1:26** (offline → sell → reconnect → arithmetic) plus **1:50–2:04** +(the conflict banner). That's the whole correctness claim proven twice, +back to back.