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
| Rule | Why |
|---|---|
| Never write files directly to the FUSE-mounted project path | FUSE 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 there | Files 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 commit | TS6133 unused-variable errors = Cloudflare Pages build failure |
| Parent SHA = last pushed commit SHA — hardcode it | Using 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 trees | Catches truncated files before they land in git |
App.tsx lives at src/App.tsx — not src/components/App.tsx | main.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
| Item | Path |
|---|---|
| Project root (FUSE) | E:\Claude_Projects\sonan-trackers\lawncare-portal\ |
| Bash mount path | /sessions/.../mnt/sonan-trackers/lawncare-portal/ |
| Worker API | worker/index.ts |
| Frontend entry | src/main.tsx → src/App.tsx |
| Global state | src/context/AppContext.tsx |
| Admin shell + login | src/components/AdminApp.tsx |
| Admin views | src/components/admin/*.tsx (10 files) |
| User guide (static) | public/user-guide.html (served as /user-guide.html) |
| CSS | src/index.css |
| D1 migrations | migrations/0001_init.sql, 0002_settings_seed.sql, 0003_extras.sql |
| D1 ID | f029e545-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
| Gotcha | Fix |
|---|---|
unable to unlink tmp_obj_* warning during hash-object | Harmless FUSE permission warning — blob IS written. Verify with git cat-file -t <sha> |
| Truncated .tsx file — build passes locally but Pages fails | source_file_audit catches truncation before commit. Fix: rewrite to /tmp and re-hash. |
pack-objects returns 0 bytes | Objects already in loose store from hash-object calls — harmless. git push picks them up. |
| jsPDF import causes TypeScript error | Import as: import jsPDF from 'jspdf' — not a named export |
| Cloudflare Pages build output dir | Must be set to dist with no trailing space in the Pages dashboard |
| JWT_SECRET missing → Worker returns 401 on all requests | Run: 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.