Work

Systems I directed, and systems I built.

30 projects. 22 run as demos on synthetic data; how each demo is labelled.

30 projects

  1. PeopleOS

    2026 · ~300-employee target · phased rollout

    I built.One auditable system of record for HR, attendance and statutory payroll.

    CREDITS

    Engineering
    Denandro Yusuf — architecture, domain and rule design, implementation, statutory rule interpretation, release decision, directing Claude Code against written specs, review of every later change
    Further development
    Prada Dipa
    Go deeper

    An original HR, attendance, statutory-payroll and workforce platform for a ~300-person Indonesian garment manufacturer, replacing a 291-column master workbook, a legacy Apps Script attendance app, paper leave forms and a macro-driven payroll spreadsheet with one auditable system of record. The ~300 is the company's headcount and the system's design target, not a count of people using it: the August 2026 cutover began a phased rollout that is still in progress.

    Status: Cloud production since the August 2026 cutover, phased rollout in progress. Release v10.1.0 reached production on 3 Oct 2026, after its four database migrations were rehearsed on a restored copy of production; the current release, v10.4.0, followed on 9 Oct 2026. I promoted both.

    What it does

    At the August 2026 cutover (v9.30): 17 domain modules (identity, org, people, time, leave, payroll, approvals, performance, recruitment, reports, audit, theme, company, team, tasks, notifications, platform) over a 102-model Postgres schema. Three interface types (Admin / Manager / Employee) with Director-only role administration, effective-dated RoleAssignment history and field-level masking of identity, bank, BPJS and compensation data. Attendance is an append-only event ledger with audited void+replace edits; overtime is computed automatically from actual attendance with no request/approval workflow (confirmed as the company's real rule). Payroll runs through separately versioned rule engines in exact decimal arithmetic, snapshots every rate and threshold onto every result, and produces publish-gated HTML+PDF payslips plus a checksummed bulk-transfer file in the bank's own macro-enabled XLSM template. Also: 13 leave types, THR with automatic payroll inclusion, master-data import/export with preview → transactional apply → rollback, versioned contract PDFs, per-user liquid-glass themes, 30 reports, EN/ID localisation. The only model calls are in recruitment, where screening ranks, scores and recommends and a person decides; nothing probabilistic sits between an attendance event and a payslip. Since September 2026 every merged change ships as one immutable image through staging and smoke tests. Changes to payroll, leave, shared calculations, the database layer, access control or the recruitment AI wait at staging until I promote that exact image, and an integrity compare after every production release rolls it back if anything protected moved. A push that carries a migration releases nothing, so schema changes go in separately, ahead of the code.

    • Next.js 16 (App Router, Server Actions)
    • React 19
    • TypeScript
    • PostgreSQL
    • Prisma 6
    • Tailwind CSS 4
    • decimal.js
    • argon2id (@node-rs/argon2)
  2. Order Export for Production Planning

    2026 · Internal production

    I built.Planners export Shopify orders themselves, each line tagged to its shipping warehouse.

    CREDITS

    Engineering
    Denandro Yusuf — design, directing Claude Code from specs, the warehouse-attribution rule and the conservative stock verdict, requirements with the planning team, deployment and the handover to planners
    Further development
    Prada Dipa, Luthfi Aditya
    Go deeper

    An internal web app that lets a production-planning team export Shopify orders themselves — one row per line item, each tagged to the warehouse it will actually ship from, with a conservative can-we-fulfil-from-stock verdict.

    Status: Internal production

    What it does

    Removes the developer from a routine data request. A planner picks a date range in local (Bali) time behind company Google sign-in, previews the orders, and downloads Excel or CSV — or pushes to Google Sheets. Stateless, no database. The genuinely hard parts are the warehouse decision (Shopify has three plausible 'location' values that do not mean the same thing) and matching order lines against three stock spreadsheets kept in a different naming system.

    • FastAPI (Python 3.12)
    • Shopify Admin GraphQL API
    • Pydantic
    • Jinja templates + vanilla JS
    • XlsxWriter
    • gspread / Google Sheets API
    • Docker
    • Google Cloud Run + IAP
  3. Image Order Sync

    2026 · Live · v2 since 1 Oct

    I built.One photo reorder carries to every sibling page, linked sale page and size.

    CREDITS

    Engineering
    Denandro Yusuf — design, directing Claude Code, the rule that conflicting edits pause rather than guess, the live rollout and its acceptance
    Go deeper

    A Shopify app that keeps a colour's photos in one order on every product page that sells it. Reorder them on any one page, and every sibling page, every linked sale page and every size's stored image follows. When pages disagree, it waits for a person instead of guessing, and read-only screens inside the Shopify admin show what is waiting, each style's photo order and the history.

    Status: Live on the store since 23 Sep 2026. It checks every active and unlisted product page every ten minutes, and again shortly after each save. Its second version has been live since 1 Oct 2026, and the build running at the last read of the server, on 9 Oct 2026, has been live since 5 Oct 2026, with read-only screens inside the Shopify admin. Copying photos that are added to or removed from a page has been switched on since 4 Oct 2026 (WITA), but by 9 Oct it had not copied a real change. No record shows who uses the screens.

    What it does

    The store sells one style across several product pages, so the same colour's photos sit on more than one page. Shopify keeps photo order per page, so a drag on one page left the sibling pages, the sale pages selling that colour and every size's stored image out of step. The app checks every active and unlisted product page every ten minutes, and within about half a minute of a save through a signed product-update webhook. It works out which pages share a colour from their structure: the style family, the colours and sizes each page offers, and the exact photo files. It then writes the new order into each page's own slots for that colour with the fewest moves, so no other colour moves, points every size at the new first photo, re-reads to verify and records what it did. Its second version, live since 1 Oct 2026, records every reorder on every page, moves a lone page's sizes to its new first photo, and keeps a sale page that the inventory sync links to a full-price colour in the same photo order as that colour, in both directions; the page that holds the stock leads the first time. When a photo is added to or removed from the page a colour's photos come from, it can attach or detach the same existing file on the colour's other pages, never uploading, renaming or deleting a file. Read-only screens inside the Shopify admin, built from Shopify's own components, list what waits for a person, each style's photo order and the history. The hard part is changing a live system while people edit it. Every new behaviour sits behind its own switch, off by default, and with every switch off a parity test pins the engine to the previous version on three real catalogue snapshots. Conflicting edits wait for a person rather than guess, a lost reorder response is never re-sent, writes per check are capped, and a copied photo change has an undo that refuses to run if anything has changed since.

    • Node.js (ES modules, zero npm dependencies)
    • Shopify Admin GraphQL API (bulk operations, metaobjects, file references)
    • Shopify webhooks (HMAC-verified)
    • Shopify App Bridge
    • Shopify Polaris web components
    • Shopify Liquid
    • Shopify CLI
    • node:test
  4. Inventory Sync

    2026 · Live on opted-in pages

    I built.A sale on one product page comes off every opted-in sibling page within seconds.

    CREDITS

    Engineering
    Denandro Yusuf — design, directing Claude Code, write gates that default to a dry run, the opt-in rollout after a pilot on a hidden test family
    Go deeper

    A Shopify app for a store that sells the same piece on several product pages: when a size sells on a page a person has opted in, it takes the same units off the other opted-in pages within seconds. On its own it writes only to opted-in pages, and anything it cannot account for becomes an alert rather than a guess.

    Status: Live on the store since 30 Sep 2026 for every product page ticked "Sync siblings", an opt-in box on each product, after a pilot on a hidden test family the same day. A sale on one ticked page is taken off the other ticked pages that sell the same SKU at the same location, and a sale colour shown on a full-price page is linked to its own sale product. The company chose the rules on 29 Sep: every safety hold only alerts, the stock sheet only proposes, and if two buyers take the last piece on two pages within seconds, the app lists it for a person rather than preventing it. Added since then, each behind its own switch and all on at a read of the server on 9 Oct 2026: full-price colours on sale pages and quick set-up of new SKUs (both 1 Oct), sale colours shared between sale pages, and copying of counts typed by hand. Two edits a person starts, Add colour and Remove sold out, have been on since 7 Oct and had not been used on a real product by that read.

    What it does

    The store sells the same physical piece on several product pages, and Shopify counts each page's variant separately, so a piece sold on one page still shows in stock on the others. Shopify cannot share one inventory item between variants, so the app keeps the pages in step itself, on the products a person opts in with a "Sync siblings" box. When a size sells on one ticked page, it takes the same units off every other ticked page selling the same SKU at the same location. Each write is a compare-and-set lower with an idempotency key recorded before it is sent, so a crash or a lost reply never sends a write twice. A sale colour offered on a full-price page is linked through the theme to its own sale product, so the cart and the order carry the sale product. The hard part is reading intent from inventory events on a live catalogue. The app tells a sale apart from a count typed, a write-off, a shipment, a cancellation or a return by how Shopify's reserved, available and on-hand figures moved. Webhooks are only hints: stocks with open orders are re-read every minute, because a change in reservations alone sends no webhook, and the whole catalogue is re-read every fifteen minutes. The app's own writes read back as no change, and whatever it cannot classify becomes an alert, as do its holds and any disagreement with the stock sheet. The first design kept each finite piece on one page so the last one could only sell once. The company chose the simpler mirror instead, accepting that two buyers can take a last piece on two pages within seconds; the app lists that for a person, and the first design stays in the code as the rollback. Moving the store's multi-page styles onto sibling pages was a one-off migration that backed up every page, checked each write against its plan and ticked the box last.

    • Node.js 22 (zero runtime dependencies)
    • node:sqlite
    • Shopify Admin GraphQL API
    • Shopify webhooks (HMAC-verified)
    • Shopify App Bridge + Polaris web components
    • Shopify Liquid + vanilla JS (storefront routing)
    • Google Sheets API (read-only)
    • node:test
  5. Price Sync

    2026 · Live since 4 Oct 2026

    I built.One price per piece on every page that sells it, previewed before saving.

    CREDITS

    Engineering
    Denandro Yusuf — design, directing Claude Code, one guarded write path with preview and undo, the live rollout
    Go deeper

    A Shopify admin app for a store that sells one style across several product pages. It sets one full price per style and one sale price per sale colour, shows exactly which pages and colours a save will change, and writes them to every page in one save, with history and undo. The theme only offers a colour from another page while both pages ask the same price, so a price changed on one page could leave a colour showing sold out on another.

    Status: Live on the store since 4 Oct 2026 (WITA). It was installed in the store's Shopify admin that day and opened for saving that evening to every staff account that Shopify lets edit products, after a live test on two throwaway pages that were then deleted. Changing many styles at once has been on since 5 Oct, and a colour's own full price since 8 Oct, confirmed by a second live test minutes later. As of 9 Oct 2026 the app records no save by the team yet.

    What it does

    The store sells one style across several product pages, and most colours are offered on more than one of them, so one SKU sits on several pages at once. The theme only sells a colour from another page while both pages ask the same price. Shopify's own editor changes one page at a time, so a price changed on one page left a colour showing sold out on another, although the piece was in stock. Price Sync is a small embedded admin app that prices a style as a whole. A style has one full price, which is also every sale colour's crossed-out price, and each sale colour has one sale price, written to every page that shows it. Since 8 Oct 2026 a single colour can also have its own full price, which counts only when a person set it in the app, never because Shopify happens to show it. A person types the prices and gets a check that names every colour and page that will change, and Save stays off until they tick any price someone changed in Shopify after they opened the screen. The app then writes the style page by page and reads every page back. History keeps every save with a one-step Undo, and a bulk mode applies one rule, such as raising every full price by an amount or a percentage, across up to fifty styles at a time through the same single-style save. The hard part is writing prices on a live catalogue that people edit at the same moment. A save is refused if it would leave any SKU with two prices or two crossed-out prices across its pages. It locks the style, re-reads it, refuses any change since the check and journals every old and new value before the first write, then re-reads each page just before writing it. A write whose answer was lost is never re-sent: the app waits out a landing window and settles it by reading. An Undo puts back one price per SKU or refuses with the reason. Products whose prices legitimately vary by option, such as a gift card or a page whose sizes cost different amounts, are shown read-only and never flattened.

    • Node.js 22 (ES modules, zero npm dependencies)
    • node:sqlite
    • Shopify Admin GraphQL API (productVariantsBulkUpdate)
    • Shopify App Bridge session tokens + token exchange
    • Shopify Polaris web components
    • Shopify CLI (app configuration)
    • node:test
    • systemd + nginx + Let's Encrypt on a Google Compute Engine VM
  6. Variant-Aware Product Pages

    2026 · Title live · blocks in use

    I built.The selected variant decides a product page's sale title, blocks and stock badge.

    CREDITS

    Engineering
    Denandro Yusuf — design, directing Claude Code, the kill switch and the byte-identity checks, deployment to the live theme
    Go deeper

    A storefront theme change that lets the selected variant decide what a product page shows: a sale title only for a discounted variant, and which blocks, description and stock badge appear, with no reload and no template swap. A product whose variants are all regular renders exactly the HTML it did before.

    Status: Two parts, dated separately (UTC). The sale-price title has been live for all shoppers since 25 Sep 2026, on every product page and in quick buy of the store's live theme. The per-variant presentation blocks went onto the live theme on 26 Sep 2026, behind a kill switch, and have been in use on real product pages since 2 Oct 2026, when sale and full-price colours were first sold on the same pages. A page renders as before unless one of its variants is another kind than its template, has its own description, or is sold from another page whose description differs. The October changes built on it, a look per colour on the sale and nearly-gone templates and the sale descriptions, are listed under Storefront Theme Changes.

    What it does

    The store sold one style as separate products (a regular page, a sale page and a nearly-gone page, each on its own template) because a product's description and template are shared by all its variants. This lets one product mix them and has the selected variant drive the page; real product pages have used it since 2 Oct 2026, when sale and full-price colours were first sold on the same pages. The first part, shipped on its own, is the title: it carries the sale marker only for a variant whose compare-at price is above its price, the same test the price block already uses, and it never doubles the marker or touches the admin title or the SEO fields. The second part adds a 'Show for' setting to five block types, a section-level switch, a per-variant description and a per-colour stock badge, all rendered on the server for the selected variant so first paint and deep links are right, with a small script following later switches on the theme's existing variant-change event. The hard part was making it invisible where it is not needed: a product whose variants are all regular renders byte-identical HTML, checked on real products, and switching it off returns every page to the old HTML. A companion command-line tool, dry-run by default, moves a sale page's description onto the mixed product's variants, and refuses rather than guesses whenever the rich text or the variant pairing does not fit. The limits are written down: orders, checkout and analytics still show the admin title.

    • Shopify Liquid
    • Online Store 2.0 section and block settings
    • Vanilla JavaScript (no framework)
    • Shopify variant metafields
    • Shopify Admin GraphQL API (through the Shopify CLI)
    • Node.js 22 + node:test
    • Headless Chrome (DevTools protocol) acceptance checks
  7. Storefront Theme Changes

    2026 · Live · 1–7 Oct 2026

    I built.Pages show only the sizes a colour comes in, and that colour's own wording.

    CREDITS

    Engineering
    Denandro Yusuf — diagnosis, directing Claude Code, file-by-file pushes read back from the live theme
    Go deeper

    Seven changes to the store's live Shopify theme in October 2026, each dated: a look per colour on the sale and nearly-gone templates; a sale colour on a full-price page showing its sale page's description; sizes a colour does not come in hidden, and one fixed size order; collection cards that no longer cross out a sale colour another sale page holds; a quick-buy guard for personalised products; and backup site names in the homepage structured data.

    Status: Live on the store's theme, each change dated (UTC): sizes a colour does not come in hidden, and a look per colour on the sale and nearly-gone templates, on 1 Oct 2026; backup site names in the homepage structured data and one fixed size order on 2 Oct; sale colours that another sale page holds no longer crossed out on collection and search cards on 3 Oct; the quick-buy guard for personalised products and the sale descriptions on 7 Oct. Every one was on the live theme at its last recorded pull, 9 Oct 2026. Whether search results now show the backup site name is not recorded.

    What it does

    The store's live Shopify theme is edited by other people as well, so each of these changes was built against a copy of the live files it touches, kept as a backup, and read back from the live theme after the push. Two of them build on the variant-aware product pages. A 'Template kind' setting gives the sale and nearly-gone templates a look per colour, so a full-price colour on a sale page shows free exchanges and its own description (1 Oct 2026, UTC). A sale colour offered on a full-price page shows the final-sale description of the sale page it is sold from, found through the same route the cart uses (7 Oct). It was checked on a full copy of the live theme first, where no product page changed beyond the intended description, and in the browser on the live store afterwards, on desktop and on a phone. The size picker hides sizes the selected colour does not come in, crossing a colour out only when none of its sizes can be bought (1 Oct), and the knit sizes always show in one fixed order (2 Oct). Collection and search cards no longer cross out a sale colour that another sale page holds, extending the inventory sync's swatch rule (3 Oct). In quick buy, a personalised product shows a 'Personalize' link instead of Add to cart, so it cannot be bought without its personalisation (7 Oct). And the homepage's structured data lists backup site names (2 Oct), with no errors or warnings in a schema validator. The hard part is changing files that other people push to at the same time. Five of the changes went through a guarded push: refused unless the live file still matched the copy the change was built from, or, for the sale descriptions, unless a clean three-way merge reproduced exactly the change's own lines, then pulled back and compared byte for byte. At the last recorded pull, after other editors had added their own lines to the same files, every change was still in place.

    • Shopify Liquid
    • Online Store 2.0 section and block settings, JSON templates
    • Vanilla JavaScript (no framework)
    • Shopify variant metafields
    • schema.org structured data (JSON-LD)
    • Shopify CLI (theme pull and push)
    • Python 3 standard library (guarded push, three-way merge, rollback)
    • Node.js 22 + node:test
  8. Shoppable Product Video App

    2026 · Test theme · not launched

    I built.Shoppable videos that play the shopper's chosen colour and add to cart.

    CREDITS

    Engineering
    Denandro Yusuf — design, directing Claude Code, the media pipeline and the storefront block, acceptance on a test theme
    Go deeper

    A custom Shopify app for shoppable product videos, built to replace a third-party video widget. A floating video on the product page, in a storewide frame (4:5 by default), opens a popup where shoppers pick the colour and size and add to cart, and collection pages can lead with short loops of each product's own colour. Behind it, a conversion pipeline keeps every shopper-facing file as the app's own file in Shopify Files, and every Shopify write must pass an allow-list that refuses anything outside the app's own data.

    Status: In review and not launched as of 9 Oct 2026. Its storefront parts appear only on an unpublished copy of the live theme kept for review: the latest recorded check of the live theme, on 9 Oct 2026, found neither the product-page video nor the collection-card videos there, and the existing third-party video widget still in place, so no shopper is served by it. The latest recorded acceptance run passed 22 of 22 live checks on that test theme, on app version 21, the same day. Versions 22 and 23 followed that afternoon, after an internal design review. The code is on a local branch and has not been merged or pushed.

    What it does

    The store's product pages relied on a third-party video widget, and the brief was a replacement the company owns: play the colour the shopper has selected, let them buy from the video, and never disturb the theme's gallery, image order or cart. Staff upload a video in an embedded admin and assign it to colourways, with one assignment covering every size. A worker probes each upload and converts it without cropping into a verified MP4, a poster, a silent preview loop, and a collection-card loop and still, and stores every shopper-facing file as the app's own file in Shopify Files, so shoppers stream from Shopify's CDN. On the product page a floating preview sits in a storewide frame, 4:5 by default or 9:16, 1:1 or 16:9, cropped only in the browser so a change re-converts nothing. It opens a popup with the looping video beside the product's options and Add to cart, which goes through the theme's own cart. On collection pages an app embed lets a card lead with its own colour's loop; at most two play at once, and each has its own pause button for accessibility. The hard part is correctness against a real catalogue: a colour may live on a sibling page, a sale page never picks up a full-price video, a colour without its own video falls back by a stated rule rather than showing nothing, a size change never swaps the video, and customisable products hand off to the product page's own fields. Every Shopify write passes a parsed allow-list that admits the app's own metafields, its own files (never attached to a product, and deletable only if the app recorded creating them) and, when a person connects it, its own analytics pixel; everything else is refused before it leaves the server. Apart from that optional pixel, the storefront never calls the app at runtime, so if the app is down the product page and Add to cart are unaffected.

    • TypeScript
    • React Router 7
    • React 18
    • Shopify App Bridge + Polaris web components
    • Shopify Admin GraphQL API
    • Shopify theme app extension (Liquid + vanilla JS)
    • Shopify Web Pixels
    • Shopify Files (staged uploads)
  9. Seasonal Catalog Media Pipeline

    2026 · Internal tooling

    I built.Eight dry-run-first tools move a season's images into the wholesale catalogue.

    CREDITS

    Engineering
    Denandro Yusuf — design, implementation, the naming convention and the dry-run-by-default rule, the season run and its runbooks
    Go deeper

    Eight single-purpose command-line tools that take a season's swatch and colourway images from Drive to a live WordPress wholesale catalogue — every one dry-run by default, every one safe to re-run after an interruption.

    Status: Internal tooling, used for the S27 drop

    What it does

    A season drop means ~166 products, several colourways each, a swatch tile and a photo per colourway, all named by whoever exported them from Drive — and a shared host that rejects large uploads and cannot replace a media file in place. Rather than one fragile pipeline, this is a chain of small tools with sharply separated blast radii: rename, upload-what's-missing, link products to their images, three read-only audits, a careful refresher, and a probe that finds out what the server will actually accept.

    • Python 3 (standard library only, plus Pillow for downscaling)
    • WordPress REST API + ACF
    • WordPress application passwords
    • CSV export of a Google Master Sheet
  10. Wholesale Order Platform

    2026 · Production

    I built.Replaced an Excel order form with validated, signed orders on live Salesforce data.

    CREDITS

    Engineering
    Denandro Yusuf — architecture, implementation, the standing rules — no full card number stored, logged or rendered; every Salesforce call server-side; order minimums enforced on the server, QA and the rollout to the reps and the planning team
    Go deeper

    A wholesale ordering platform that replaced an Excel order form and the email thread around it. Buyers and sales reps order against live Salesforce product and account data; the order is validated, persisted, rendered to a PDF, signed, and routed to the planning team for accept or decline — with a nearby-stockist conflict check running in the background on every new account.

    Status: Production — public order form plus password-gated admin and sales-rep dashboards, released through a dev stack before production

    What it does

    Takes wholesale ordering off a spreadsheet emailed back and forth and puts it on one form with a review queue behind it. A buyer or a rep picks a season, looks the account up in Salesforce, adds products at the season's prices, and signs; the planning team sees every order in one table with its PDF, its value, its ship window and its decision. Three surfaces, deliberately separated: the public order form, the internal admin page, and a read-only rep dashboard that shows a rep only their own book and a card-masked PDF. Two of the more interesting decisions are defensive rather than featureful — the 'is this a new account' verdict is taken from Salesforce rather than from what the buyer ticked, and accepting an order is refused unless an account actually exists to file it under.

    • FastAPI + Pydantic v2
    • SQLAlchemy 2.0 + psycopg 3
    • Alembic
    • PostgreSQL
    • React + Vite (no router dependency)
    • simple-salesforce
    • Google Sheets API (ship windows)
    • Google Maps Geocoding + Distance Matrix
  11. Loyalty Program Cost Analysis

    2026 · Complete · decision pending

    I analysed.I reproduced a loyalty vendor's numbers from raw data, then found the real cost.

    CREDITS

    Engineering
    Denandro Yusuf — analysis and recommendation, reproduced the vendor's figures from raw data, built the daily sync, wrote the vendor letter setting the study terms
    Go deeper

    A daily API sync plus the analysis it enabled: independently reproducing a loyalty vendor's own numbers from raw data, then showing that the licence fee on the invoice was the smaller part of what the program actually cost — and that the vendor's headline ROI figure was circular. The figures themselves are one retailer's unit economics and one vendor's commercial terms, so they are described, never published.

    Status: Complete: analysis delivered, holdout test recommended, decision pending

    What it does

    Two halves. The engineering half is a small, dependency-free daily sync that pulls every customer, order and points-activity record into a dated snapshot, writes a member balance export, renders a dashboard page and prunes itself. The analysis half is the actual deliverable: a renewal decision on a loyalty platform, built by reproducing the vendor's figures from raw data, taking apart the causal claim underneath them, and writing both the internal recommendation and the letter to the vendor specifying the study design in advance. The in-house alternative that decision would need is a separate entry, the First-Party Loyalty Platform: in shadow mode and not launched.

    • Python 3 (standard library only — urllib, csv, json)
    • Loyalty platform REST API (vendor not named)
    • Shopify order data (cross-check)
    • Generated static HTML dashboard
    • cron
  12. First-Party Loyalty Platform

    2026 · Shadow mode · not launched

    I built.A loyalty program's points, tiers and rewards, rebuilt in-house and reconciled against its vendor.

    CREDITS

    Engineering
    Denandro Yusuf — design, directing Claude Code, the migration ledger and its reconciliation gate, the daily backup pipeline, the cutover runbook
    Go deeper

    A first-party Shopify app that rebuilt a loyalty vendor's points program in-house: an append-only points ledger, earning rules, tiers, rewards issued as single-use Shopify discount codes, a storefront loyalty page, redemption at checkout and a customer-account page. Beside it, a backup of the vendor's data is scheduled every morning, and the app is reconciled against that backup in shadow mode. It was built while the decision on that vendor is still open, and it has not been launched. The cost analysis that frames the decision is a separate entry.

    Status: Not launched; in shadow mode since 31 Aug 2026. A backup of the loyalty vendor's data is scheduled for every morning; it runs on a laptop, so it skips any day the machine is off. Each complete copy is imported into the new ledger and reconciled against the vendor's own balances, tiers and statuses, and all 33 reconciliations up to 9 Oct 2026 passed the cutover gate. That proves the import, not the points engine: the import's corrections make the ledger agree with the backup by design. The app has never been connected to the store, so no live order, webhook or shopper has reached it, and its storefront, checkout and customer-account extensions are not deployed. Before any cutover the points-expiry rule has to be rebuilt, because on 4 Sep 2026 it was found to work differently from the vendor's. The vendor decision itself is still open.

    What it does

    A direct-to-consumer apparel brand runs its points program inside a third-party loyalty vendor. Leaving would mean moving every account's points, tier and history with nothing lost and nothing counted twice, and the only source is an API that exposes customers, orders, points activity and claimed rewards, but not the program's own settings. The work has two parts. A backup pipeline takes an immutable, checksummed snapshot of everything that API exposes, validates it, compares it with the previous day and keeps an off-site copy. A first-party Shopify app rebuilt the program's points, tiers and rewards in-house: an append-only points ledger, earning rules, rewards issued as single-use Shopify discount codes, a storefront loyalty page, redemption at checkout, a customer-account page, and an embedded admin with reconciliation, audit and health pages. In shadow mode the app imports each complete snapshot and is reconciled against the vendor's own figures. The hard part is the migration. Every ledger entry carries an idempotency key, so a re-run changes nothing. The vendor's full activity history is replayed with its original timestamps, and where that history cannot reproduce the vendor's own balance, an opening-balance correction tied to that snapshot closes the gap without rewriting history. A reconciliation then compares every account's balance, tier and status with the vendor's and puts each disagreement in one of five classes: expected, timing, rule difference, migration error or unknown. Only the first two can pass the cutover gate. Because the corrections make the import agree with the snapshot by design, a clean reconciliation proves the import, not the snapshot and not the points engine. That is why the snapshot is checked on its own first: sanity floors fail an implausibly small export, and a day-over-day comparison raises an alert.

    • TypeScript 5.9
    • React Router 7 (Shopify's React Router app template)
    • React 18 + Shopify App Bridge
    • Polaris web components
    • Prisma 6 (SQLite in development, PostgreSQL production schema)
    • Shopify checkout and customer-account UI extensions (Preact)
    • Liquid theme app extension behind an app proxy
    • Shopify Admin GraphQL API
  13. Agent Session Observability

    2026 · In daily use

    I built.What each local AI coding session is doing, how far along, and its cost.

    CREDITS

    Engineering
    Denandro Yusuf — design, build contract, implementation, directing AI coding agents
    Go deeper

    A local macOS dashboard for every Claude Code session on the machine — what each one is doing right now, how far along it is, roughly how long it needs, and what it would have cost on API pricing. Zero runtime dependencies, nothing instrumented, nothing leaves the machine.

    Status: Working tool, installed and in daily use locally

    What it does

    Reads only what is already on disk under ~/.claude plus the macOS process table, and derives a live view of every session: state, current activity, running background agents with what each was asked to do and what it is doing now, progress, an ETA range with a confidence chip, and per-model cost attribution. The core runs under plain `node src/server.js` with no dependencies at all — Electron is only the window. The server binds 127.0.0.1 and has no auth surface because it is never reachable off-host.

    • Node 22 (ESM, zero runtime dependencies)
    • Electron 34 (CJS shell only)
    • HTTP + Server-Sent Events on 127.0.0.1
    • Vanilla JS/CSS UI, no framework, no build step
  14. JobStreet CV Harvester

    2026 · Working tool

    I built.Downloads every applicant CV from a hiring portal into per-job folders.

    CREDITS

    Engineering
    Denandro Yusuf — design, implementation
    Go deeper

    A Playwright automation that downloads every applicant CV from a SEEK hirer employer account into per-job folders with a CSV index — built to survive a GraphQL API with introspection disabled that the vendor reshapes without notice.

    Status: Working tool, used against a live employer account

    What it does

    The unified SEEK hirer portal that JobStreet employer accounts now live on has no bulk 'download all resumes' button, and SEEK deletes applicant attachments 180 days after an ad closes — a constraint the README states before anything else. This drives the portal to recover them. Four commands: sign in yourself, teach it your portal, harvest, report. The script never sees or stores credentials.

    • Node.js (ESM)
    • Playwright 1.62 (sole dependency)
    • SEEK hirer GraphQL API (undocumented)
    • CSV manifest
  15. DENAVUM

    2026 · Live since 30 Sep · single-user

    I built.Replaced its first version and the Bridge tracker with one live, private app.

    CREDITS

    Engineering
    Denandro Yusuf — architecture, directing Claude Code, row-level security on every table, deployment
    Go deeper

    A private, single-user app, with Today, Life, Money, Creator and Work behind one quick-capture box, rebuilt in September 2026 to replace its first version and Bridge. It keeps money, journal and health records online, so the database enforces ownership on every table, sign-in needs an authenticator code, and AI runs only on my own Mac.

    Status: Live since 30 Sep 2026 as a private, single-user app. The September rebuild runs on Vercel with a Supabase Postgres database; it has one account, sign-ups are switched off and sign-in needs an authenticator code. A worker on my Mac runs the AI jobs, the Instagram sync and nightly backups. It replaced its first version, on Vercel since 1 Aug 2026, which was retired at the cutover with its tables moved aside rather than deleted, and my local tracker Bridge, retired the same day. Checked on 9 Oct 2026: the sign-in page answers and the worker is running.

    What it does

    Daily planning was split across two tools that each did half the job: a first version of DENAVUM with three spaces, and Bridge, a laptop-only tracker that had grown an Instagram dashboard and a content studio. The rebuild is one installable app with five spaces behind a single capture box. Today is a daily deck drawn from the other spaces. Life holds tasks, habits, check-ins, a journal, goals, people, documents and reading. Money is a rupiah-first ledger with accounts, budgets, bills, clients, currency rates and CSV import. Creator carries over Bridge's Instagram dashboard, studio, trend research and profile. Work keeps Bridge's three rules: every project has a next action; blocked needs a reason and waiting needs a person; nothing is marked shipped until five handover items are filled in. These rules are enforced in validation, in the domain logic and as database constraints. The capture box reads a typed line, in English or Indonesian, as a spend, income, task, idea, follow-up, date or note, and shows how it will be filed before anything saves; on a phone it also captures offline and sends reminders. The hard part is that one person's money, journal, health and people records live online, so no single check is trusted. There is no way to sign up, and every sign-in needs an authenticator code. Requests pass a host guard and a per-request content security policy. Row-level security is forced on every table: the site's own database role can read nothing until a request's verified identity is set inside its transaction, and a test fails if any table is left unprotected. AI never runs on the server. The site queues jobs in Postgres, and a worker on my Mac claims them and runs them through the Claude Code command line, with schema-checked output, under a database role that cannot reach the money, journal, people or health tables at all. Going live was handled as the migration of a running system: production was backed up, the migration was rehearsed on a copy built from that backup, and the first version's tables were moved into their own schema with a written rollback rather than dropped.

    • Next.js 16 (App Router, Server Components + Server Actions)
    • React 19
    • TypeScript 6 (strict, noUncheckedIndexedAccess)
    • Tailwind CSS 4
    • Zod 4
    • Supabase Auth (@supabase/ssr) with required TOTP
    • Supabase Postgres 17
    • Row Level Security, forced on every table
  16. Bridge

    2026 · Retired · replaced by DENAVUM

    I built.A local-first work tracker built around one question: what is the next action?

    CREDITS

    Engineering
    Denandro Yusuf — design, implementation, directing Claude Code for its September 2026 additions
    Go deeper

    A single-user, local-first work tracker built around one question: what is the next action, and if there isn't one, why not. Stalled work is made to look wrong rather than blank, and a project cannot be marked shipped until five handover items are ticked.

    Status: Retired on 30 Sep 2026, when DENAVUM replaced it, and kept as a read-only archive

    What it does

    Ran on the laptop, bound to 127.0.0.1 only, and kept everything in one folder you could copy. In its July version it was explicitly not a team tool: no accounts, no cloud sync, no notifications, no LLM. By its retirement it also used Claude in five specific places and synced Instagram, work DENAVUM has taken over. Today, List and Board views over projects with markdown notes, subtasks, links, file attachments, an append-only activity timeline, tags, people, a handover checklist and a status-report generator.

    • Next.js 15 (App Router)
    • React 19
    • TypeScript
    • SQLite (better-sqlite3)
    • Drizzle ORM + drizzle-kit
    • Zod 3
    • Tailwind CSS 4
    • Vitest
  17. Customer Birthday Backfill

    2026 · Complete

    I built.Moved birthday months from free-text customer notes into structured Shopify metafields.

    CREDITS

    Engineering
    Denandro Yusuf — implementation, operational run
    Go deeper

    A re-runnable Shopify migration that reads birthday months out of free-text customer notes into structured metafields — with a keyword guard so 'customer ordered in March' never becomes a birthday, and a dry run that is the default.

    Status: Complete — run against the live store, report retained

    What it does

    Years of customer birthdays and mobile numbers had been typed into the free-text `note` field by staff. This walks every customer, extracts what can be extracted safely, and writes it into `custom.birthday_month` and `custom.mobile_number` metafields so the store can actually use it — producing a per-customer CSV report of exactly what it did or would do.

    • Python 3
    • Shopify Admin GraphQL API (2026-04)
    • Client-credentials OAuth grant
    • requests
    • CSV audit report
  18. Storefront Search Relevance Fix

    2026 · Live

    I built.Stops storefront search returning products that never mention the query.

    CREDITS

    Engineering
    Denandro Yusuf — diagnosis, implementation, release QA
    Go deeper

    Two Liquid snippets that stop Shopify's semantic search returning products that share only a theme with the query — because a search for 'lobster' returning a product with the word nowhere on it reads as a broken site.

    Status: Live on the production storefront

    What it does

    Shopify's storefront search expands a query semantically, so /search returned products that only share a theme with it — 'lobster' brought back OH CRAB!, MARLED CRAB V and KEY WEST, and 'pumpkin' brought back WONDERFUL CHRISTMAS, none of which carry the word in their title, tags, description or variants. (MAUI V and CROP BOYFRIEND also come back for 'pumpkin', and stay: a pumpkin colourway is in their variant names, so they are genuine matches.) This adds a post-filter over the search results so an item is only shown if it genuinely carries every word the customer typed, while leaving deliberate tag matches intact. Shipped alongside a jersey-placement product add-on and a banner extension.

    • Shopify Liquid
    • Shopify Online Store 2.0 theme architecture
    • Theme settings schema
  19. ANATOMY

    2026 · Handed over

    I built.A from-scratch Shopify theme for ANATOMY, a personal project with Stephanie Monique.

    CREDITS

    Engineering
    Denandro Yusuf — theme development, handover
    With
    Stephanie Monique
    Go deeper

    A personal project with Stephanie Monique: a Shopify theme built from scratch for ANATOMY, the minimal knitwear label the two of us founded — 24 sections, a named brand palette in the settings schema, no framework and no build step.

    Status: Complete, packaged for handover

    What it does

    A complete editorial storefront theme: hero, marquee, brand intro, philosophy, material values, founders, category grid, collection feature, featured products, CTA banner, newsletter and a cart drawer, on top of the full set of main-* page templates. Colours, type and spacing are exposed as named brand tokens in settings_schema.json (color_ink and friends) so the merchant edits the brand rather than the CSS.

    • Shopify Liquid
    • Online Store 2.0 JSON templates
    • Theme settings schema
    • Vanilla JS (single theme.js)
  20. Systems in Motion (Portfolio V1)

    2026 · Superseded

    I built.The first version of this portfolio: a scroll-driven WebGL sequence over real DOM.

    CREDITS

    Engineering
    Denandro Yusuf — design, implementation
    Go deeper

    The first version of this portfolio — a scroll-driven cinematic sequence over a single WebGL corridor with every word in real DOM underneath it, and a documented editorial rule set forbidding any metric the source repositories do not actually measure.

    Status: Superseded — archived at tag v1.0-production; the portfolio has since been rebuilt

    What it does

    Nine scenes in sequence over one persistent WebGL canvas, six case studies statically generated at /work/[slug] each with a rendered architecture diagram, an Instagram read model, and a three-script QA harness. The genuinely reusable artefact is its editorial rule set: a written document that traces every claim on the site back to a specific repository and bans invented numbers outright.

    • Next.js 16 (App Router)
    • React 19
    • TypeScript
    • Tailwind CSS 4
    • three.js + react-three-fiber + drei
    • GSAP + ScrollTrigger
    • Lenis
    • axe-core
  21. Company Web & Favicon Pack

    2026 · Delivered

    I built.A web and favicon asset pack derived from one supplied brand image.

    CREDITS

    Engineering
    Denandro Yusuf — design, delivery
    Go deeper

    A web asset pack derived from a single supplied brand JPG — transparent PNG exports at three widths, a dark-UI variant, a traced SVG and a full favicon set, with a README mapping each file to the slot it belongs in.

    Status: Delivered

    What it does

    Takes an existing brand mark that only existed as a JPG and produces everything a website actually needs from it, without redesigning the mark. The README is explicit that the SVGs are traced approximations and that true-vector production would need the original Illustrator/EPS source — which is the useful part: it states its own limitation rather than passing a trace off as a master.

    • Raster export pipeline
    • SVG tracing
    • Favicon / PWA / Apple touch icon formats
  22. CV Screening Pipeline

    2026 · Foundation shipped · not piloted

    Built with me.Scores every CV against its job description; a person makes every decision.

    CREDITS

    Engineering
    Prada Dipa, Denandro Yusuf
    Technical & product direction
    Denandro Yusuf
    Go deeper

    An LLM screening service that reads and scores every CV against the pinned job description it was submitted under, redacts identifiers twice before anything crosses a border, and never makes an accept or reject decision — the ranked shortlist and the reasoning go to a person.

    Status: Foundation shipped as a separate service: sync, intake and extraction, scoring and push-back, merged and tested. On 2 Sep 2026 the screening moved into the HR platform's own recruitment module, one application on one database, with CVs in private cloud storage. HR's first production screening there, on 24 Sep 2026, failed before its result was saved, so no piloted end-to-end run is recorded yet.

    What it does

    Removes the repetitive half of hiring — reading and sorting every CV by hand — while keeping every accept/reject with a person. As first built, it polled the HR platform for candidates at the right stage, extracted CV text, scored it against a hash-pinned snapshot of the job description, and pushed a ranked shortlist with per-criterion reasoning back onto the /hiring pages recruiters already use. It was deliberately one-directional: the HR app never called out to it, so /hiring kept working when it was down. Since 2 Sep 2026 the same screening runs inside the HR platform's recruitment module. Every LLM step reads one editable markdown file with frontmatter (id, version, model, temperature, inputs, output schema), so changing how candidates are read is a prompt edit with a recorded version and hash, not a deploy.

    • TypeScript (Node 22, ESM)
    • PostgreSQL + Prisma 6
    • OpenAI gpt-4o-mini (schema-constrained)
    • unpdf + mammoth (PDF/DOCX extraction)
    • Zod 4
    • @google-cloud/storage
    • Vitest + embedded-postgres
    • Python (draw.io diagram generators)
  23. Order Intake Trial Dataset

    2026 · Complete

    I designed.A three-day hiring trial that grades how a candidate responds to seeded defects.

    CREDITS

    Engineering
    Prada Dipa, Luthfi Aditya
    Assessment design & direction
    Denandro Yusuf
    Go deeper

    A three-day technical hiring trial built as an engineering artefact: a deterministic generator that renders synthetic phone-photographed wholesale order forms with seeded defects across 14 classes, and an answer key that grades the correct RESPONSE to each defect, not just the correct value.

    Status: Complete — run against a live candidate in August 2026

    What it does

    The role being hired for is 'someone else's messy process, data nobody labelled, and a definition of working that belongs to a non-technical person in another department'. Rather than test that with a whiteboard question, this builds the messy process: a fictional wholesale knitwear brand whose sales reps photograph paper order forms, and a pipeline the candidate has to design over three days. The generator is deterministic, so the answer key is exact and reproducible.

    • Python 3
    • Pillow (PIL) — form rendering + photo degradation
    • Seeded deterministic generation
    • CSV answer key / manifest / filename cross-reference
  24. Ads Review Automation

    2026 · Production pipeline

    I directed.Rules flag each pause; a model explains, and may argue to keep.

    CREDITS

    Primary engineering
    Prada Dipa
    Maintenance
    Hady Satya
    Technical & product direction
    Denandro Yusuf
    Go deeper

    A weekly Meta ads keep/pause pipeline. A deterministic rules engine flags every pause; an LLM explains each one, and may argue to keep a flagged ad as a labelled override, but can never pause one. Product coverage is printed beside each pause and withholds nothing.

    Status: Production pipeline (report path live; Streamlit UI intentionally dormant)

    What it does

    Replaces a manual weekly Ads Manager review of dozens of ads across three funnel stages. Pulls insights per stage with its own date window, rolls up to one row per ad, evaluates each ad against a pluggable rules engine loaded from a single JSON config, then calls an LLM once per ad-set bucket to write the human-readable recommendation — and emits the same report as Markdown, self-contained HTML (Google Docs–friendly), JSON and PDF. A Streamlit dashboard with ad cards, filters and notes exists in the repo but is deliberately not active; the report is the shipped output.

    • Python
    • pandas
    • facebook-business (Meta Marketing API)
    • OpenAI gpt-4o-mini
    • Streamlit
    • Plotly
    • Playwright / Chromium (PDF)
    • gspread + google-auth
  25. Slow Seller Decision Engine

    2025 · Internal production

    I directed.Keep, sell out, mark down or drop: one recommendation per style, from live sales.

    CREDITS

    Primary engineering
    Kirei Kharisma Handayani
    Technical & product direction
    Denandro Yusuf
    Go deeper

    One button that reads a merchandising spreadsheet, pulls the store's own last 14 and 30 complete days of Shopify sales, matches style-and-colour across two naming systems, runs a decision tree with four possible actions or a deliberate blank, and writes three columns back into a sheet people are actively using.

    Status: Internal production — the 2.0 running on Cloud Run since its 29 Aug 2026 release

    What it does

    Replaces a manual Google Apps Script workflow for the seasonal merchandising sheet. Reads the product sheet, fetches real Shopify sales in the store's own timezone, resolves each sheet row to a Shopify style+colour, applies a faithful port of the team's flowchart, and writes back the two sales columns and a Web Suggestion sentence — plus the window dates into row 11, where the sheet already kept them by hand. Standard runs write in one step; a custom-threshold run stops at a preview and needs an explicit, confirmed apply. The README carries a warning block at the top: the app has no sign-in, so whatever sits in front of it is the only thing protecting the sheet.

    • Next.js 15 (App Router)
    • React 19
    • TypeScript
    • Zod 3
    • Shopify Admin GraphQL API
    • Google Sheets API (google-auth-library)
    • Radix UI + Tailwind
    • Vitest
  26. Commerce Data Warehouse & Dashboard

    2026 · Production

    I directed.One nightly-synced warehouse and dashboard instead of five admin UIs.

    CREDITS

    Engineering
    Prada Dipa, Luthfi Aditya
    Technical & product direction
    Denandro Yusuf
    Go deeper

    A nightly-synced PostgreSQL warehouse behind an internal dashboard, built so that 'what did we sell yesterday, through which channel, at what ad cost, and are customers happy about it' is one page instead of five admin UIs and an argument about whose spreadsheet is right.

    Status: Production — six nightly source syncs, a role-scoped internal dashboard and an on-demand report runner, all in one deployable service

    What it does

    Consolidates a storefront, an analytics property, three ad platforms and a review platform into one warehouse, and puts a dashboard on top of it. The genuinely hard part was never the charts — it was that the same figure had previously been computed three different ways and disagreed with the storefront's own report three different ways. So the platform commits to one written definition of a product sale, holds it in a database view, and makes every endpoint read that view. The dashboard's own conventions are written down as requirements for any new page: a date range with previous-period deltas, a daily/monthly toggle wherever there is a time series, CSV export on every card, live data only, and a colourblind-safe categorical palette assigned by slot rather than picked by hand.

    • FastAPI (Python)
    • PostgreSQL 18
    • React 19 + Vite + TypeScript
    • Shopify Admin API
    • GA4 Data API
    • Judge.me API
    • Meta Marketing API
    • Google Ads API
  27. Product Page Automation

    2026 · Production

    I directed.Builds complete storefront product pages from the internal source-of-truth workbooks.

    CREDITS

    Engineering
    Luthfi Aditya
    Technical & product direction
    Denandro Yusuf
    Go deeper

    Builds and updates storefront product pages from the internal source-of-truth workbooks. One or more style-colours go in; a complete page comes out — title, SEO, variants, size chart, per-size weights, SKUs, barcodes, tags, prices, per-location inventory, images and a per-style description.

    Status: Production — five production types on both the create and the update path, driven manually, from a daily returns grid, or from a small internal interface

    What it does

    Removes the click-by-click assembly of a product page. The season's data already exists — weights in the style master workbook, size charts and prices in a master data sheet, stock and image references in a working sheet, SKUs and barcodes in a UPC list — and a page is a deterministic function of all of it. The tool builds the page from the sheets, so a correction is made at the source rather than in the storefront admin. That is also its sharpest edge, and the README says so plainly: an update re-sends the handle, SEO, description, tags and images, so a hand edit made in the admin will be overwritten. Five production types share one skeleton and differ only in flags, stock location and size filtering — placeholder inventory for a style not yet made, real per-warehouse quantities for one that is, sample sizing for a sample.

    • Python 3.9
    • Shopify Admin GraphQL productSet mutation
    • Shopify Admin REST (inventory + metafields)
    • Shopify webhooks (HMAC-verified)
    • gspread / Google Sheets API
    • pandas + openpyxl
    • Streamlit (internal interface)
    • Docker Compose + nginx
  28. Master File Report Pipeline

    2026 · Superseded · folded in

    I directed.Merged Shopify, Salesforce and returns data into the tabs operations read.

    CREDITS

    Engineering
    Prada Dipa, Luthfi Aditya
    Technical & product direction
    Denandro Yusuf
    Go deeper

    The consolidation job underneath the reporting stack: Shopify orders, Salesforce orders and Redo returns merged into the four spreadsheet tabs that operational workflows read directly — first as notebooks, then behind a small multipage interface so someone other than a developer could run it.

    Status: Superseded as a standalone repository — folded into the data platform, where it is documented as the legacy sheets path that gets no new investment and must not break

    What it does

    Keeps four shared spreadsheet tabs — orders, returns, master data and a summary — as the operational source of truth they had already become, but fills them from APIs instead of by hand. The honest part of this project is its documentation: an architecture note in the team's own working language, a runbook, three numbered decision records, and a guide for adding a report. One of those decisions exists to record that a merge function does a date-overlap replace rather than the per-order upsert its docstring claims — the kind of thing that is a trap until it is written down. Its second act was an interface: the pipeline had no CLI and ran only from notebooks, so a multipage app was put in front of it and containerised, which is the moment it stopped being a developer's tool.

    • Python 3.11
    • Jupyter
    • Streamlit (multipage interface)
    • Shopify Admin GraphQL
    • Salesforce
    • Redo Returns API
    • gspread / Google Sheets API
    • pandas
  29. Style Master Data Entry

    2026 · Delivered

    I directed.Moved a season's style master from a spreadsheet into one edit screen.

    CREDITS

    Engineering
    Prada Dipa, Luthfi Aditya
    Technical & product direction
    Denandro Yusuf
    Go deeper

    A small web app that replaced a season's style-master spreadsheet with a normalised database and one edit screen — colourways and their yarn bill of materials, knit details, prices, packaging, shipping and per-size measurements on a single page instead of across a few hundred columns.

    Status: Delivered as a self-contained office install — two commands to set up, one to run, one file to back up

    What it does

    Takes the season's style master out of a spreadsheet where every attribute is a column and puts it into tables that match what the data actually is. Office staff add a style, search by season, style, colour or size, and edit everything about it on one screen. Two details make it survivable on an office machine rather than a developer's: dropdown lists teach themselves, so a yarn or a packing method typed once is offered next time and spelling stays consistent, and the whole database is a single file — the backup instruction is 'copy this file', and the restore instruction is 'put it back'.

    • Node 22 (node:sqlite — no native dependencies)
    • Express + multer
    • React + Vite
    • SQLite
    • Python (one-off spreadsheet extractor)
  30. Sheets-Native Attendance System

    2026 · Superseded by PeopleOS

    I directed.Turned one shared spreadsheet into a permissioned, audit-logged timekeeping app.

    CREDITS

    Engineering
    Prada Dipa, Luthfi Aditya
    Technical & product direction
    Denandro Yusuf
    Go deeper

    A Google Apps Script system that turns one shared spreadsheet into a per-employee-permissioned, audit-logged timekeeping app — with real per-row range protections, cross-midnight shifts, and a monthly payroll recap generated rather than re-typed. The legacy system PeopleOS later replaced.

    Status: Superseded by PeopleOS; kept as the reference implementation of the confirmed company rules

    What it does

    Staff clock in and out through a web app form; admins and HR manage sheets and generate payroll recaps from a custom Sheets menu. Every source comment and UI string is in Indonesian. Deployed against a bound spreadsheet with clasp; there is no build, no tests, and no local way to run it — every execution happens in Apps Script. This is also the archaeological source for PeopleOS's CONFIRMED company rules: MAX_OT = 78, REGULAR_DAYS = 7, the Saturday 5-hour shift credited as 7, and the 30-minute late grace all came from reading this code and the payroll workbook formulas.

    • Google Apps Script (V8)
    • Google Sheets API
    • HtmlService (single-file web app)
    • clasp
    • Installable + time-based triggers
    • LockService
    • Range Protections