Long-Term Memory
MEMORY.md — persistent knowledge and standards
The vectrforce.ai v2 deploy was a mess because of three compounding mistakes. These rules are now baked into ISM's AI chat system prompt AND must be followed by all agents.
- jarvis-local (192.168.237.11) = app host. Runs services on internal ports. NO SSL here.
- dev-server (192.168.237.7) = app host for client projects. Same rules.
- public-facing-server (vpn.durhamchristian.com) = reverse proxy ONLY. ALL SSL termination happens here (Let's Encrypt).
- NEVER create SSL certs on jarvis-local or dev-server. Internal servers don't need SSL — the public-facing-server handles that.
- NEVER create nginx entries on jarvis-local for public services. The public-facing-server proxies DIRECTLY to
192.168.237.11:PORT. No intermediate nginx needed on the app host. - NEVER let certbot modify the default nginx site. Always target a specific server block with
-d domain.com. - When updating an existing domain, MODIFY the existing nginx entry — do NOT create a new one. Always check ISM for existing configs first.
- For a new public service, you need EXACTLY ONE nginx config: on the public-facing-server →
reverseProxyRemotetointernal_ip:port.
- An SSL cert was added to jarvis-local (wrong — internal server)
- Nginx entries were created on jarvis-local when the public-facing-server could point directly to the ports (redundant)
- Certbot on the public-facing-server added entries to the default nginx site instead of the vectrforce.ai block (broke everything)
- The fix was simple: just modify the existing vectrforce.ai nginx entry on the public-facing-server
- Build & start service on app host (jarvis-local or dev-server)
- Create/update ONE nginx config on public-facing-server →
reverseProxyRemotetointernal_ip:port - Provision SSL via certbot on public-facing-server (if new domain)
- Done. Nothing else needed.
- Services page: deploy button changed from ↑ to 🚀 emoji
- Services page: added "Group by Domain" toggle — groups services by root domain
- Nginx page: added "Group by Domain" toggle — groups nginx configs by root domain
- Nginx page title changed to "Nginx Sites"
- AI chat: added
remove_nginxaction (with mandatory user confirmation before removal) - AI chat: deployment architecture rules baked into system prompt
- ISM slug:
vectrforce-website - Live URL: https://vectrforce.ai
- Project dir:
/home/jarvis/projects/vectrforce-website - Service type: .NET | Port: 3500 | Systemd:
vectrforce-website - Build:
dotnet publish SalesWebsite.Web -c Release -o publish - Repo: https://github.com/fireballer/DurhamChristianSoftwareStudioWebsite.git (main)
- ISM managed: true — full lifecycle through ISM
- 🔓 POLICY CHANGE: Jarvis and Webster now have FULL ACCESS to make direct code changes to the website. The old "brief-only, no direct code changes" rule is RETIRED. Jason confirmed this on March 23, 2026.
- Still use Todoist for major changes that benefit from Jason's review.
These apply to EVERY coding task, whether Jarvis does it directly or delegates to Claude Code/Codex/any agent.
-
Exceptional error handling & messaging — Every app, every API call, every user-facing feature must have proper error handling with clear, descriptive error messages. No silent failures. No empty catch blocks. No "Something went wrong." Show what went wrong, where, and what the user can do about it.
-
Quality over speed — Jason does NOT care how fast it ships. He cares that it's done RIGHT. Take the time needed. No rush jobs. No cutting corners. No "we can fix it later." Do it properly the first time.
-
Clean, well-written, well-organized code — Modular where appropriate. Clear naming. Logical file structure. Separation of concerns. Code should be readable and maintainable by humans.
-
Future-proofed — Build with extensibility in mind. Structure code so future features and improvements can be added without rewriting. Think about what comes next.
-
Progress updates during builds — When Claude Code or any coding agent is running a long build, set up a cron job to send Jason status updates every 5 minutes until it's done. He should never have to ask "is it done yet?"
- Project: React Native (Expo) Android app at
/home/jarvis/projects/idea-pipeline-mobile/ - Serves:
https://pipeline.vectrforce.ai(same API as web app) - APK download:
https://apk.jarvis.durhamchristian.com/pipeline.apk - Login token:
pipeline-2026 - Android SDK: Installed at
~/android-sdk, JDK 17 at/usr/lib/jvm/java-17-openjdk-amd64 - nginx config:
/etc/nginx/sites-enabled/apk-site.confserves/var/www/apk/ - 6 APK builds before it worked. Every issue is documented below so we never repeat them.
These were learned the painful way during the Pipeline mobile app build (March 21, 2026). Apply them to EVERY future Android/mobile build.
- Android silently rejects self-signed certs. No popup, no error dialog — fetch just fails with a network error.
- Fix: Add
android:networkSecurityConfig="@xml/network_security_config"to<application>in AndroidManifest.xml - Create
res/xml/network_security_config.xmlthat references@raw/server_cert - Put the cert in DER format at
res/raw/server_cert.crt(convert withopenssl x509 -in cert.pem -outform DER -out cert.crt) - Only ONE file per resource name — having both
server_cert.crtandserver_cert.derinres/raw/causes "Duplicate resources" build failure expo prebuild --cleanWIPES the android/ folder — must re-add network_security_config.xml, server_cert.crt, and the AndroidManifest.xml attribute after every clean prebuild
- React Native's
fetchdoes NOT handle cookies like browsers. Set-Cookie headers may not be stored. Cookie headers may not be sent.redirect: 'manual'behaves unpredictably. - ALWAYS use Bearer token auth (Authorization header) for mobile apps. It's the industry standard and works reliably across all platforms.
- Server should accept BOTH cookies (for web) and Bearer tokens (for mobile) on all API routes.
- Store the token in AsyncStorage, send it as
Authorization: Bearer <token>on every request.
- Next.js middleware that redirects unauthenticated requests to /login will break mobile apps. The redirect returns HTML, which the app tries to parse as JSON → "Unexpected character: <"
- API routes (
/api/*) must ALWAYS return JSON responses — return{ error: 'Unauthorized' }with status 401, NEVER a 307 redirect to an HTML page. - Always validate Content-Type before parsing. Check
res.headers.get('content-type').includes('application/json')before callingres.json().
- Conditional
<Stack.Screen>rendering does NOT control routing in expo-router. expo-router is file-system based — it will always load the default route regardless of what screens you render. - Correct pattern: Create a root
app/index.tsxthat checks auth state and callsrouter.replace('/login')orrouter.replace('/(tabs)'). This is the entry point. - Add a guard in the tabs
_layout.tsxas well: if!isAuthenticated, redirect to/login. - NEVER rely on conditional screen rendering for auth flow.
- Never trust local storage alone. Android can persist AsyncStorage/app data across uninstalls (especially if "Auto Backup" is enabled).
- Always validate stored credentials against the server on app launch. Make an actual API call (e.g.
getStats()). If it fails, clear stored auth and send user to login. - Clean up stale keys from previous auth implementations on startup.
- Every
fetchcall needs a try/catch that classifies the error: SSL, network, auth, HTTP, parse. - Show the ACTUAL error message to the user — not "Something went wrong" or "Connection failed."
- Include the URL being attempted in error messages (helps debugging on-device).
- 401/403 errors should auto-redirect to login — don't just show an error card.
react-native-reanimatedhas C++ compatibility issues with newer React Native versions. If you hitUIManagerMountHooktype mismatches, replace with React Native's built-inAnimatedAPI.- First Gradle build on a fresh machine takes 30-60 minutes (downloads entire Android SDK, Kotlin compiler, all deps). Subsequent builds: 3-5 min.
- Gradle + React Native C++ compilation needs ~3GB RAM. On a 4GB machine, other processes (like the OpenClaw gateway) will get starved and crash. Kill unnecessary processes before building.
--no-daemonflag on Gradle prevents memory-hogging daemon processes from lingering after the build.
- Serve APKs via nginx on a subdomain (e.g.
apk.jarvis.durhamchristian.com) - Set
Content-Disposition: attachmentand proper MIME type (application/vnd.android.package-archive) - If using self-signed SSL, user needs to tap "Advanced → Proceed" in browser to download
- Include ALL of the above lessons in the prompt
- Specify Bearer token auth from the start
- Specify proper error handling from the start
- Specify the expo-router auth guard pattern from the start
- Tell it to handle self-signed certs if applicable
- Tell it to use
--no-daemonfor Gradle builds - Tell it to test with a real API call after building, not just compile
- Location:
business-development/idea-pipeline/ - System: 5-tier funnel (Tier 0–4) that pressure-tests ideas before execution
- Philosophy: Default state is KILL. Ideas must earn their way forward.
- Tier 0: Daily generation + Jason scores (≤5 dies, 6+ advances)
- Tier 1: Devil's Advocate — agents try to kill the idea (<24h)
- Tier 2: Unit Economics & Feasibility — financial stress test (<48h). Key rule from Jason: unfamiliar tech is NOT a blocker. He learns fast — flag time cost only.
- Tier 3: Validation Sprint — Jason pitches real humans. Need actual buying signal ("I'd pay for that"), not polite interest.
- Tier 4: Execution Commit — Jason picks ONE to build.
- Graveyard: Dead ideas archived with kill reason + "resurrect if..." condition.
- First batch entering Tier 1: AI Customer Retention Engine (8/10), CRA Notice Response Automation (7/10), IntelliDoc/RAG Knowledge Base (8/10)
- Standing rule: All daily ideas must include competition + demand analysis (since March 6)
- Cron instructions updated to note pipeline integration
- Setup complete. Projects: 📋 Review & Approve, 🚀 Business, 📊 Lead Generation
- Project IDs in todoist-config.md
- Labels: APPROVED (2183130144) | Approval Acknowledged (2183130298, green)
- Protocol: Add "Approval Acknowledged" label + read-receipt comment every time an APPROVED task is processed
- Drive files always go in "Workspace" folder (ID: 15OKNiXKiy1oEhZp_MjkjQjBdlnfeArp0)
- Endpoint: https://jarvis-webhooks.durhamchristian.com/hooks/todoist
- nginx on Jason's webserver injects the auth token
- Transform: ~/.openclaw/hooks/transforms/todoist.js
- Cron fallback: removed — webhooks fixed 2026-02-25. ⚠️ Do NOT recreate the cron fallback job.
- ✅ Webhooks working as of 2026-02-25 — Jason fixed the OAuth subscription issue. Cron fallback removed.
- Two separate legal entities:
- Durham Christian LLC (US) — Clients: Anesthetize.ai, Spry
- Vector Force Technology Studio Ltd. (Canada) — Client: Argyle Fox Inc, Durham Christian Bookstore
- Website domain: vectorforcetech.com (Vector Force only — no Durham Christian branding on site)
- Old domain: durhamchristiansoftware.com → redirect to vectorforcetech.com
- No Blazor anywhere — use Next.js for frontend
- Frontend includes iOS and Android (not just web)
- Contact/send email: jasonachin@durhamchristian.com (may change — TBD)
- Calendly: https://calendly.com/thechampeon (30 min meeting)
- SMTP: jasonachin@durhamchristian.com via heracles.mxrouting.net
- LinkedIn API: ~/.config/linkedin/credentials.json (OAuth, no password needed)
- HubSpot: hubspot-credentials.md
- Sent 2026-02-24 to info@cbmc.ca — "Local Whitby Business Owner - How to Get Involved?"
- Watching for reply
gog drive upload --jsonreturns{ "file": { "id": "...", "webViewLink": "..." } }— NOT{ "id": "..." }at root- Always parse via
d.get('file', d).get('id')or usewebViewLinkdirectly - Bug was in
scripts/todoist-post.sh— fixed 2026-02-26; Drive upload now works correctly
- ANY output requiring Jason's decision/approval/action MUST be posted as a Todoist task — not just mentioned in chat
- Chat is ephemeral. Todoist is the system of record.
- Most common miss: LinkedIn posts drafted by Taylor → always need an APPROVE task in Todoist
- Task title formats:
✅ APPROVE:,📤 SEND:,📧 APPROVE & SEND:,🌐 ACTION:,🔧 ACTION:,📞 ACTION:,🤔 DECIDE: - Rule baked into AGENTS.md and TOOLS.md
- Every time any agent posts a file to Todoist, attach it directly — NO Google Drive upload needed
- Universal script:
bash scripts/todoist-post.sh --title "..." --file "/path/to/file.md" [--screenshots-dir "/path/"] - This script does it all: uploads file directly to Todoist → creates task → attaches file as comment → attaches screenshots (if provided)
- Todoist file upload API:
POST /api/v1/uploadsmultipart; response hasfile_urlat root (not nested) - Todoist file attachment: post comment with
attachment: {file_url, file_name, file_type, resource_type: "file"} - Todoist screenshot attachment: post comment with
attachment: {file_url, file_name, file_type, resource_type: "image"} - Rule baked into workspace AGENTS.md and TOOLS.md
- 🔴 RECURRING MISTAKE (flagged by Jason 2026-03-20): I keep creating Todoist tasks with file paths in the description instead of actually uploading and attaching the files. NEVER just list a file path — always use
scripts/todoist-post.shor manually upload via the API and attach as a comment. Jason should be able to open the file directly from Todoist without SSHing into the server.
- OLD RULE (retired): No agent may make direct code changes to the website repo
- NEW RULE (2026-03-23): Jarvis and Webster have FULL ACCESS to the website code. Direct changes allowed. Website runs on local server via ISM (slug:
vectrforce-website). - Correct flow: write markdown brief → save to
agents/website/reports/→ post to Todoist → Jason approves → Claude Code implements - Rule is in main TOOLS.md AND agents/website/TOOLS.md
- Agent ID: website | Workspace: agents/website/
- Role: Visual audits via Playwright, content analysis, update briefs, Todoist posting
- Screenshot script:
node scripts/website-screenshot.js(playwright, 5 pages confirmed working) - Incoming queue: agents/website/incoming/ — other agents drop .md requests here
- Reports go to: agents/website/reports/
- Todoist post:
bash scripts/website-post-todoist.sh "Title" "Description" - Config: Added to openclaw.json agents list + main's allowAgents
- Transform: Todoist transform updated with website keyword routing → Webster
- Known pages: /, /services, /about, /contact, /portfolio (working); /work, /case-studies = 404
- Haytek → ✅ Authorized to name publicly (confirmed 2026-02-27). No longer "Client H".
- Haytek E-Commerce case study is LIVE on durhamchristiansoftware.com — confirmed by Jason 2026-03-04. Todoist task "🌐 ACTION: Publish E-Commerce Case Study" closed.
- Anesthetize.ai → TBD (confirm before naming)
- Posts 1–11: ✅ ALL LIVE — Jason has posted all of them himself
- Post 11 was posted by Jason on March 2, 2026 — do NOT flag as pending ever again
- Post 12: ✅ SCHEDULED — goes out automatically (March 5, 2026). Confirmed by Jason 2026-03-04.
- Posts 13–15: ready and queued
- Pattern: Jason posts LinkedIn content himself, OR schedules auto-publish. Never flag already-posted/scheduled content as pending.
- Strategic pivot: Killed the agency-style AI team (CMO Sarah, BDR Alex, Sales Mike, Content Emma, CEO). They produced deliverables but generated zero revenue conversations.
- Root cause: The model was a mid-market consulting firm playbook. Jason is a builder who sells through conversations, not funnels.
- What survived: Social (Taylor — LinkedIn posts working), Website (Webster), Knowledge (Atlas), Idea Pipeline (the actual growth engine)
- New model: Founder-led product pipeline. See
business-development/OPERATING-MODEL.md - Archived: All agent workspaces moved to
agents/_archived/(recoverable) - Cron jobs removed: CMO, BDR, Sales, Content, CEO check-ins all deleted
- Proposal template still available: business-development/proposal-template-DRAFT.md
- Outreach account: jasonachin@durhamchristian.com | SMTP/IMAP: heracles.mxrouting.net:587
- Script: scripts/send-email.py — ALWAYS use this (saves to IMAP Sent folder automatically)
- Bayview Metals correct email: sales@bayviewmetals.com (Michael Bureau, VP) — NOT info@bayviewmetals.com
- Jason will do all outreach himself — agents prep, Jason sends (or approves)
- Token expires periodically. Re-auth via manual curl OAuth exchange (gog's internal timeout is too slow for remote back-and-forth)
- Method: generate URL → Jason visits → paste redirect → extract code → curl POST oauth2.googleapis.com/token →
gog auth tokens import
- Anthropic API key: must be in env.vars.ANTHROPIC_API_KEY (NOT models.providers.anthropic.apiKey)
- Do NOT add custom models.providers.anthropic entry — conflicts with built-in registry, causes sub-agent 404s
- Do NOT add cacheRetention param to agents.defaults.models — causes sub-agent 404s
- Kimi K2: moonshot provider in models.providers.moonshot; heartbeat model = moonshot/kimi-k2
- Sub-agent failures with 404: usually means wrong API key location or cacheRetention param issue
- File: agents/cmo/target-market-strategy-FINAL.md (completed Feb 24)
- Focus: ICPs #1-3 (Professional Services, Healthcare, E-Commerce)
- E-Commerce = anchor ICP (only one with prior work + case study)
- Top action for Jason: Publish E-Commerce case study — single highest-leverage asset
- Jason's URL: https://www.linkedin.com/in/jasonchin1
- Approved headline: "I help Christian-owned businesses automate, scale & modernize with custom software | 27 Years | .NET, AI, Cloud"
- Post 7 = engagement template (AI + personal experience + provocative question format)
- Post 10 live Feb 24 8:30 AM (sequel to Post 7)
- Batch 1 LinkedIn connections sent Feb 23 (Joey Rhyne/Dow Smith, Tim Spiars/SPIARS Engineering, Jon DeLine/BETA Fueling, Ryne Johnson/Spirit Movers)
- Tier 2 research kicked off Feb 24 (6 companies)
- URL: https://jarvis.durhamchristian.com:8443
- allowedOrigins, trustedProxies (192.168.237.2), dangerouslyDisableDeviceAuth all configured
- Jason identified a new service offering: build AI agent teams for other businesses (like his own studio)
- This is now the #1 business priority
- GTM brief:
agents/cmo/ai-team-service-GTM-brief.md - Proposed product name: "Pilot AI by Durham Christian Software Studio"
- Packages: Audit ($500–$1,500) → Launch ($8K–$18K) → Managed ($2,500–$5K/mo) → Enterprise ($6K–$12K/mo)
- Year 1: $127K conservative → $480K optimistic (retainer math is the story)
- Brand names doc:
agents/cmo/ai-team-service-brand-names.md— 80+ options, Jason has not yet chosen - Top candidates: Steward, Covenant Ops, Vantage Ops, AgentOps, ApexOps
- Sarah's immediate actions: LinkedIn post, /ai-team website page, 20 personal conversations
- RAG-powered internal knowledge base for professional services (law, CPA, engineering)
- Jason scored 8/10 — one of his highest-rated revenue ideas
- Full plan:
business-development/rag-knowledge-base-execution-plan.md - Stack: .NET 9, PostgreSQL, pgvector, Next.js, Docker (all familiar)
- Build: ~8 weeks to MVP | Pricing: $3K–$8K setup + $800–$3,500/mo
- Year 1 target: 10 clients → ~$230K
- Week 1: name/domain, fake law firm demo docs, Loom demo, LinkedIn DMs, scaffold
- March 2 call happened — Gabe is focused on his own project (Anesthetize.ai), not hot on the first idea Jason pitched
- Open door: Gabe is willing to hear future ideas — "run ideas by him to see if he's interested"
- Strategy: don't push. Periodic idea drops only. If something clicks, he'll engage.
- No active co-build. Relationship maintenance mode.
- Next step: When a genuinely strong healthcare AI idea surfaces, pitch it to Gabe casually. Don't lead with partnership framing.
- ✅ Strong YES: Healthcare AI, RAG for professional services, e-commerce AI tooling, productized services
- ❌ Hard NO: Legacy .NET Framework work, retainers for existing clients who lack budget
- 🤝 Key partnership: Gabe Mullins (Anesthetize.ai) — explore co-building healthcare AI products
- Feedback tracked in:
memory/revenue-ideas-feedback.md
- Haytek AI Dashboard: v3 proposal SENT — $40K USD / $54,500 CAD, 4 phases, 18 weeks. Sent by Jason on March 1, 2026. Awaiting client response.
- Basketball Phenoms (phenomsbasketball.ca): Overhaul game plan v2 complete. Site on Argyle Fox. 3 live critical bugs. Phase 0+1: $6,250–$9,750 CAD. Proposal pending.
- Reality Engine: Payment in progress. $7,500 received 2026-03-04. Next ~$7,500 expected ~March 18. Remaining balance ~$21,500. Todoist task due Mar 18 — follow up if payment doesn't arrive. Original call script still in task description.
- Kynd E-Commerce Platform: Full spec generated (kynd-spec.md). Claude Code session
amber-kelpbuilding in/home/jarvis/projects/kynd/. Stack: .NET 9 + React + PostgreSQL + Stripe + CJ Dropshipping. - Nicole: Sustainable kitchenware dropshipping guide ready (PDF). Awaiting Nicole's contact info.
- Set
deliver: falseon Todoist hook mapping (Feb 26) — no more webhook announce messages - Agent still processes tasks and posts Todoist comments, but results don't auto-announce to Jason
- Transform bug fixed: was checking label ID
2183130144but API v1 uses label names (APPROVED)
- Job ID:
b6185b9a| Schedule:0 9 * * *America/New_York (9 AM EST daily) - Feedback file:
memory/revenue-ideas-feedback.md - Was misconfigured (ran at 2 PM instead of 9 AM) — fixed Feb 27
- Received $7,500 payment on March 4
- Jason requested another ~$7,500 in ~2 weeks (around March 18)
- Remaining balance after both: ~$14,000
- Todoist task updated with Mar 18 follow-up date
- Do NOT flag as overdue until March 18