Last updated: 2026-07-12

🤖
AI Session Handoff — Bernie's Backyard Lawncare Portal

Read this in full before touching any code. FUSE-safe commit rules are mandatory on every change — skipping them causes silent file truncation and GitHub push rejection.

Critical Rules

RuleWhy
Never write files directly to the FUSE-mounted project pathFUSE silently truncates large writes; both disk and git blob end up identically wrong so verification passes but the file is broken
Always write to /tmp/filename first, then hash from thereFiles in /tmp are not FUSE-mounted and write cleanly
Strip null bytes before hashing: raw.rstrip(b'\x00')Edit/Write tools append null bytes on FUSE mounts
Remove all unused TypeScript variables before commitTS6133 unused-variable errors = Cloudflare Pages build failure
Parent SHA = last pushed commit SHA — hardcode itUsing an intermediate failed commit as parent causes GitHub to reject the push (bad trees in the pack)
Run source_file_audit() on every .ts/.tsx file before building treesCatches truncated files before they land in git
App.tsx lives at src/App.tsx — not src/components/App.tsxmain.tsx imports from ./App at the src root
For first push of any new repo: delete lock, rewrite config, add remote, then push.git/config is always corrupted on FUSE and a .git/config.lock is left behind — follow the exact sequence below

Where Things Live

ItemPath
Project root (FUSE)E:\Claude_Projects\sonan-trackers\lawncare-portal\
Bash mount path/sessions/.../mnt/sonan-trackers/lawncare-portal/
Worker APIworker/index.ts
Frontend entrysrc/main.tsxsrc/App.tsx
Global statesrc/context/AppContext.tsx
Admin shell + loginsrc/components/AdminApp.tsx
Admin viewssrc/components/admin/*.tsx (10 files)
User guide (static)public/user-guide.html (served as /user-guide.html)
CSSsrc/index.css
D1 migrationsmigrations/0001_init.sql, 0002_settings_seed.sql, 0003_extras.sql
D1 IDf029e545-6c71-4762-8776-fd4f930264df (lawncare_db)

FUSE-Safe Commit Pattern (Summary)

# Full pattern — every code change follows these steps:

# 1. Write file to /tmp (never to FUSE path directly)
python3 -c "
content = r'''...full file content...'''
with open('/tmp/AdminApp.tsx', 'w') as f:
    f.write(content)
"

# 2. Hash from /tmp — strip null bytes first
python3 -c "
import subprocess
data = open('/tmp/AdminApp.tsx','rb').read().rstrip(b'\x00')
r = subprocess.run(['git','hash-object','-w','--stdin'],
    input=data, capture_output=True,
    cwd='E:\\Claude_Projects\\sonan-trackers\\lawncare-portal')
sha = r.stdout.decode().strip()
print('sha:', sha)
"

# 3. Audit: verify blob line count matches /tmp file
git -C lawncare-portal cat-file -p <sha> | wc -l
wc -l /tmp/AdminApp.tsx   # must match exactly

# 4. Run source_file_audit() on all .ts/.tsx/.css files in the commit

# 5. Build trees with Python ls_tree/mktree helpers (never shell printf/grep)
# 6. git commit-tree <tree> -p <LAST_PUSHED_SHA> -m "feat: ..."
# 7. Write new SHA to .git/refs/heads/main
# 8. Copy file from /tmp to FUSE path (shutil.copy) for disk consistency
# 9. Provide git push origin <sha>:refs/heads/main for user to run

New Repo First Push Sequence

Run these commands from Windows terminal in the project folder (not bash). The .git/config is always corrupted on FUSE and a .git/config.lock is always left behind:

del .git\config.lock
echo [core] > .git\config
echo     repositoryformatversion = 0 >> .git\config
echo     filemode = false >> .git\config
echo     bare = false >> .git\config
git remote add origin https://github.com/sonantechai/lawncare-portal.git
git push origin <SHA>:refs/heads/main

Known Gotchas

GotchaFix
unable to unlink tmp_obj_* warning during hash-objectHarmless FUSE permission warning — blob IS written. Verify with git cat-file -t <sha>
Truncated .tsx file — build passes locally but Pages failssource_file_audit catches truncation before commit. Fix: rewrite to /tmp and re-hash.
pack-objects returns 0 bytesObjects already in loose store from hash-object calls — harmless. git push picks them up.
jsPDF import causes TypeScript errorImport as: import jsPDF from 'jspdf' — not a named export
Cloudflare Pages build output dirMust be set to dist with no trailing space in the Pages dashboard
JWT_SECRET missing → Worker returns 401 on all requestsRun: npx wrangler secret put JWT_SECRET --env bernies

Current State (July 2026)

Portal is feature-complete and live. All migrations applied on remote D1. Worker deployed to bernies environment. Pages project lawncare-portal auto-deploys from GitHub main. Custom domain bernies.sonandigital.com active. Admin user seeded. Default password set to Admin@2026 — client should change on first login.