As The Geek Learns

The Geek

Tools and training for IT professionals. Join James Cruce—a systems engineer with 25+ years managing enterprise VMware infrastructure—for practical PowerCLI tutorials, automation tips, and lessons from the trenches. Much to learn, there always is. astgl.com

Episodes

  1. MAY 8

    ChatGPT Just Invented an Entirely Fake Version of My MCP Server

    I asked ChatGPT to tell me about my own MCP server. It returned about a thousand words of confident, beautifully formatted, completely fabricated nonsense. Tables. Comparisons. A made-up acronym. A "thinking substrate" that sits above data and below agents. None of it is real, and that's the part worth talking about. The Setup My project is called `mcp-astgl-knowledge`. It's an MCP server with 15 tools for searching my newsletter articles, backed by sqlite-vec and Ollama. The whole thing fits on a laptop. ASTGL stands for "As The Geek Learns," which is the name of this newsletter. I wrote it. I shipped it. There is a public GitHub repo and a public package.json. So when a friend asked me what the MCP server actually does, I figured I'd see how each big AI assistant explained it. ChatGPT was first up. I typed in "ASTGL MCP Knowledge" and hit enter. What I got back wasn't an answer. It was a hallucination wearing the suit of an answer. "ASTGL (Abstract Semantic Task Graph Layer) MCP Knowledge Server is an emerging MCP server focused on structured knowledge representation and reasoning... it turns knowledge into graph-based, machine-reasonable structures that agents can query and evolve." That paragraph alone has three fabrications: the acronym expansion (made up), the "graph-based, machine-reasonable structures" (the server stores text chunks with vector embeddings, no graph), and "evolve" (the index is static, refreshed every six hours by a cron job, agents do not edit it). Then it kept going. A four-row "MCP stack" table positioning ASTGL as "the thinking substrate" between data and agents. A comparison matrix against fictional products called "Totem" and "SwarmClaw" that don't exist. A capabilities list including "task decomposition" and "reasoning over structure." Use cases. "Real-world examples." A confident sign-off: "If AST-grep is about seeing code better, then ASTGL is about thinking better." Every word of it written with the calm, structured, lightly-emoji'd authority that makes ChatGPT sound right by default. As The Geek Learns is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber. What's Actually Going On When you ask an LLM about a topic it doesn't have indexed, it has two options: say "I don't know," or fill in the gap with something plausible. In practice, models default to the second one. They're trained to be helpful, and "I don't know" reads as unhelpful. So the gap gets filled. The result is what I'd call a fluency hallucination. The output has no factual grounding, but the writing is structured well enough that a casual reader can't tell. There are bullet points. There are tables. There's a "👉 In plain terms" callout. The rhetorical scaffolding looks like a real explainer because it's been pattern-matched to one. The contents underneath are pure fiction. This is a worse failure mode than search engines have. When Google doesn't know about you, you don't appear in results, and the user can see the gap. When an LLM doesn't know about you, the user gets a beautifully written description of someone the LLM made up, and your real work is still missing, but now there's a fake version sitting in front of it. For under-indexed creators (which, right now, is most of us), this is the default. Not the edge case. The Fix There's no quick patch for this on the engine side. The model isn't broken. It's doing what it was trained to do. The only handle I have is on my own side: make sure my real content reaches the retrieval surface, and measure whether it's working. So I built a citation tester. It's a small TypeScript script that hits Perplexity, Claude, and ChatGPT through their APIs, asks each one twenty target questions tied to articles I've already published, and parses the cited URLs from the response. If `astgl.ai` shows up, that's a hit. If it doesn't, that's the data. The point isn't that the floor is bad. I knew it would be. The point is that without a number, "improve our AEO" is a vibe, not a project. Every Monday at 9am the script runs again, writes a fresh row to a SQLite table, and tells me whether the floor moved. When it does move, I'll know which engine moved first, on which questions, and at what citation position. That's the actual feedback loop. Same root cause as the hallucination: my content isn't reaching the retrieval surface. Same fix: get it there. Different observability. Why This Matters If you write online and you care whether AI assistants represent you accurately, this is the thing to internalize: the alternative to being cited is not being silent. It's being replaced. Replaced by a confident summary of work you didn't do, opinions you don't hold, and product features you'd never ship. People who ask an LLM about your work and read its answer don't know they're reading fiction. They walk away with a model of you that you didn't write. The traditional AEO playbook talks about ranking, authority, and citation rate. All real, all worth measuring. But there's a tier underneath that, and it's the one most independent creators are stuck on right now: existence. Until your content is in the index, ranking doesn't apply. You aren't competing with anyone. You're competing with the LLM's imagination of you. Measurement is the cheapest part of fixing it, and it's the part most people skip. Quick Reference Four things that matter, in order: 1. Pick 20 questions your articles should answer. Tie each one to a specific URL on your site. 2. Hit each engine via API weekly. Perplexity returns a `citations[]` array. Claude returns search results in `web_search_tool_result` blocks. OpenAI returns `url_citation` annotations on `output_text` items. 3. Record the result to a small database, not a spreadsheet. You want trend data, not a snapshot. 4. Look at the floor first. Zero is a fine starting number as long as you're tracking it. The full script I'm using, including the gotcha where Node's `--env-file` silently dropped my Anthropic key on a fresh keypair, is in the repo. The article about the Anthropic key bug is coming separately. Found this useful? I share practical lessons from my systems engineering journey at As The Geek Learns Get full access to As The Geek Learns at astgl.com/subscribe

    8 min
  2. MAY 6

    The Ollama Model-Swap Death Spiral That Killed Every Cron at Once

    3 a.m. Every cron job on the Mac Studio failed inside the same 90-second window. No code changes. No model updates. No new jobs. Just a wall of timeout errors that lit up every channel I had wired to alerts. The culprit was hiding in plain sight: a fallback chain doing exactly what I told it to. The Setup One Mac Studio. One Ollama daemon. A handful of cron jobs each calling the local LLM for different tasks: code review, log summarization, doc indexing, a nightly digest. Each cron specified a preferred model. Each one inherited a "be resilient" fallback chain from the task router: try the preferred model, fall back to a smaller one, fall back to a tiny one if both fail. It looked clean on paper. Big model for the smart stuff, smaller model when the big one chokes, tiny model as a safety net. Classic graceful degradation. The kind of pattern you'd put in a "production-ready" checklist without thinking twice. The models on disk ranged from 4GB to 22GB. Loading the big one into VRAM took roughly 60 seconds cold. Generation, once warm, took 5 to 10 seconds. Guess which number I used to set the timeout. What's Actually Going On Here's the cascade. Cron A fires at 3:00:00 and asks for `qwen2.5-coder:32b`. The model isn't loaded. Ollama spends the entire 30-second timeout just paging the weights into VRAM. It never gets to generation. The request fails. The fallback chain kicks in and asks for `qwen2.5-coder:14b`. Ollama evicts the half-loaded 32b, starts loading the 14b. Another 30 seconds gone. Fallback again. Tiny model loads, finally generates. Cron A "succeeds" with degraded output. Meanwhile, Cron B fires at 3:00:15 expecting the 32b model that Cron A's first attempt was loading. Now there's a tiny model in VRAM instead. Cron B starts the same dance from a different starting point. Cron C lands on top of that. Within 90 seconds, every cron is waiting on a model swap that the next cron is about to invalidate. The fallback chain wasn't degrading gracefully. It was thrashing the VRAM and guaranteeing nobody finished. Every safety net I'd added was making the failure worse. As The Geek Learns is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber. The Fix Two changes. No clever code. Just operational discipline. First, pin one model in VRAM with `keep_alive: 24h`. This is a request-level option that tells Ollama to stop evicting the model after the response. Default behavior is to unload after 5 minutes of idle. That's the eviction that lets the next caller's load attempt thrash everything. # Pin model in VRAM with keep_alive curl -s http://localhost:11434/api/generate -d '{ "model": "qwen2.5-coder:32b", "prompt": "test", "keep_alive": "24h" }' Second, force every frequent cron to use that same pinned model. Kill the fallback chain for hot-path workloads. Fallback is fine for one-off scripts you run by hand. It's poison when three crons fire in parallel against shared VRAM. To make sure the model is loaded before any cron fires, I added a LaunchAgent that runs the warm-up curl on boot: Label com.local.ollama-warmup RunAtLoad ProgramArguments /usr/bin/curl -s http://localhost:11434/api/generate -d {"model":"qwen2.5-coder:32b","prompt":"warmup","keep_alive":"24h"} Load it with `launchctl load ~/Library/LaunchAgents/ollama-warmup.plist`. Now the model is hot before login completes. Every cron hits a warm model and finishes in the 5-to-10-second window the timeouts were designed for. Result: zero model-swap thrashing since the change. Crons that used to fail intermittently now run consistently. Why This Matters The lesson isn't about Ollama. It's about cold-load math. Anytime your "graceful degradation" path is slower than your timeout, every retry makes the next caller's situation worse. Fallback chains assume the fallback is fast. Model loads aren't fast. Database failovers aren't fast. Cold containers aren't fast. Operational discipline beats clever code here. One hot model, no swaps, every cron pointed at the same target. The "less resilient" design is actually more reliable because it removes the failure mode entirely. If you're running local LLMs on shared hardware, assume VRAM is a single resource that gets thrashed under parallelism. Pin what matters. Warm it before it's needed. Don't trust fallback chains during peak hours. Quick Reference * Cold model load on a 20GB+ model: roughly 60 seconds * Warm generation: 5 to 10 seconds * Default Ollama eviction: 5 minutes of idle * Pin a model: `keep_alive: 24h` in the API request body * Warm-up on boot: LaunchAgent (macOS) or systemd unit (Linux) * Hot path rule: one model, no fallback, same model across every concurrent caller * Reserve fallback chains for interactive, single-caller use If you found this article useful, you can find more articles like this at: As The Geek Learns Get full access to As The Geek Learns at astgl.com/subscribe

    7 min
  3. MAY 2

    I Killed OpenClaw and Built ClaudeClaw Mission Control

    Two months ago I wrote about ripping Notion out of my workflow and replacing it with OpenClaw—a self-hosted AI agent framework running on my Mac Studio. No cloud. No subscription. No black box. Last weekend I shut it down. Disabled 38 cron jobs. Moved 23 LaunchAgents into a _retired-openclaw/ quarantine folder. Killed the Ollama daemon. Archived the directory with a 30-day deletion timer. Everything in that original article still reads as true. Local-first is still right. Data ownership is still right. The critique of SaaS “well-enough” software is still right. What I got wrong was believing OpenClaw was the right vehicle for any of it. This is the post-mortem and the replacement: an agent OS I built on top of the Claude Agent SDK called ClaudeClaw Mission Control. Thirteen themed agents. One daemon. A scheduler I can actually see into. Zero silent failures slipping past me for a week before I notice. Let me explain how I got here. The Setup OpenClaw was doing real work. 38 cron jobs. Morning briefings. Evening summaries. A content pipeline that pulled research from web sources, structured it, scored it, and queued articles for ASTGL. An email triage pass. A model-usage monitor. A nerve-health monitor watching the other monitors. On paper: impressive. In practice: I had no idea if any of it was working. The system was so noisy that when something broke, I learned about it four days later when I noticed my morning briefing hadn’t arrived. Or I didn’t learn about it at all, because the cron job was exiting 0 while the script inside it was crash-looping. That last one is the killer. Let me show you what I mean. What’s Actually Going On Three failure modes hit me in a 48-hour window, and each one was invisible to the system watching the system. As The Geek Learns is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber. Failure one: successful exits, 100% broken payload. My content pipeline was ingesting URLs, and a regression introduced a trailing-slash bug that made example.com/foo and example.com/foo/ look like different URLs to the dedup layer. Every new article hit a UNIQUE constraint violation inside a subprocess. The outer wrapper caught the error, logged it to a file nobody was reading, and exited 0. For two weeks the cron appeared green while 100% of structurings were crashing. Failure two: PATH-resolved Node. I had the daemon running Node 24 (absolute path, explicit). A subagent it spawned inherited a PATH that fell through to Homebrew’s Node 25. One of the native modules (better-sqlite3) was compiled against 24, so every subagent invocation crashed with ERR_DLOPEN_FAILED and MODULE_VERSION mismatch. The smoke test I’d written passed because it ran from the daemon’s shell. The actual production path failed every time. Failure three: auth expiry with no escape hatch. OpenClaw stored some credentials in pass (the Unix password store). When my GPG key timed out, the daemon couldn’t start. Which meant the health monitor couldn’t start. Which meant the thing that would have told me about the outage was the thing that was out. OpenClaw had no watcher that lived outside the daemon it was watching. None of these are OpenClaw-specific bugs in the upstream sense. They’re pattern problems that emerge anywhere you have: 1. A monolithic daemon responsible for its own monitoring. 2. Flat-file state (HEARTBEAT.md, LEARNINGS.md) that gets appended to rather than queried. 3. Exit codes treated as truth when the real signal is in stderr. 4. No separation between “Did it run?” and “Did it work?” OpenClaw was built for a different job. It was a personal automation gateway—great at “kick off this script at 6:30 AM.” It wasn’t built to be an agent OS with observability. I was using a shovel to drive screws. I also couldn’t ignore the security posture. February’s disclosures—135,000 exposed instances, 15,000 vulnerable to RCE, the ClawHavoc plugin-registry incident, nine CVEs—had pushed me to patch hard and lock down. But every week I spent hardening OpenClaw was a week I wasn’t building what I actually wanted: themed agents that owned workstreams, could be reasoned about individually, and fail loudly. The Fix ClaudeClaw Mission Control is a Node.js daemon built on the Claude Agent SDK. It runs as a single LaunchAgent (com.claudeclaw.app), owns a SQLite store at store/claudeclaw.db, polls a scheduled_tasks table every 60 seconds, and dispatches due tasks to agents by ID. The interesting part isn’t the daemon. It’s the agents. I set up thirteen of them, themed after the small council of a certain fictional kingdom, because if I’m going to stare at this UI every day, I’d rather it amused me. Thirteen themed agents, each owning a workstream. STEWARD drives my mornings and evenings. MAESTER runs the ASTGL content pipeline. WATCHMAN watches the whole system from outside it. Each agent lives in its own directory at agents//, with an agent.yaml (model, personality, cwd, MCP servers) and a CLAUDE.md system prompt. A scheduled task carries an agentId column in the DB, and the dispatcher routes like this: if (shouldRouteViaAgent(task.agentId, listAgentIds())) { const result = await delegateToAgent(task.agentId, task.prompt, { fromAgent: SCHEDULER_FROM_AGENT, chatId: task.chatId, }); return result.text ?? '(empty response)'; } Adding a new agent is now: drop a folder under agents/, write a CLAUDE.md, run schedule reassign . No source changes. The dispatcher picks it up on next tick. That’s the piece I kept trying and failing to get with OpenClaw—modular ownership. In OpenClaw, everything was “the daemon.” In ClaudeClaw, MAESTER owning the content pipeline means if content alerts stop firing, the log line says maester: task failed instead of openclaw-gateway: subprocess exited nonzero. Attribution is free. Adding a new agent is now: drop a folder under agents/, write a CLAUDE.md, run schedule reassign . No source changes. The dispatcher picks it up on next tick. That’s the piece I kept trying and failing to get with OpenClaw—modular ownership. In OpenClaw, everything was “the daemon.” In ClaudeClaw, MAESTER owning the content pipeline means if content alerts stop firing, the log line says maester: task failed instead of openclaw-gateway: subprocess exited nonzero. Attribution is free. The Watchman probes WATCHMAN runs every hour at :05. It has seven probes, each targeting a failure mode that burned me on OpenClaw: 1. Failed tasks. status=’failed’ in the DB. Trivial. 2. Stuck tasks. status=’running’ AND last_run 3. Missed slots. status=’active’ AND next_run 4. Daemon liveness. launchctl print gui/$UID/com.claudeclaw.app—does launchd still have it? 5. Content-pipeline health. Tails the structured log file, parses the JSON, checks for crash shapes. 6. Hidden failures. Scans the last_result text column for ERR_DLOPEN_FAILED, MODULE_VERSION, Traceback, and other “the job exited zero but it sure didn’t work” signals. This is the probe that would have caught my trailing-slash bug in an hour instead of two weeks. 7. Delegation crashes. inter_agent_tasks WHERE status=’failed’ — on-demand agent invocations that blew up. On top of that, there’s a separate LaunchAgent running a healthcheck every 30 minutes that lives outside the main daemon and uses a keychain-backed Telegram token. If the daemon is dead, the healthcheck still delivers the alert. That’s the lesson from failure three: the watcher cannot share fate with the watched. Memory v2 OpenClaw’s memory was HEARTBEAT.md and LEARNINGS.md—flat files I appended to. Eventually they got long enough that the agent stopped reading them usefully, and I had no query surface to pull just the relevant bits. ClaudeClaw’s Memory v2 is a five-layer context stack: 1. Semantic recall—cosine similarity against stored memory embeddings, top 5 by score, chat-scoped. 2. Recent high-importance memories—memories with importance >= 0.7 written in the last 7 days. 3. Consolidation insights—a 30-minute loop that summarizes the short-term buffer into durable notes. 4. Cross-agent hive—stubbed for now; eventually lets MAESTER peek at something STEWARD noted this morning. 5. Conversation history—last N turns. Layers dedupe by memory ID. The whole thing is safe to drop into the SDK’s systemPrompt option. It’s not magic. It’s just queryable instead of append-only, which is the delta between “context I can use” and “a log file I’ll never re-read.” Forum-topic routing instead of bot-per-agent A small but satisfying piece. All thirteen agents post to one Telegram bot, into one supergroup, but each agent has a dedicated forum topic: Alerts → thread 22 (WATCHMAN) ASTGL → thread 23 (MAESTER) Council → thread 24 Steward → thread 25 Whisperers → thread 26 War Room - Security → thread 40 (WAR) One token. One chat. Threaded conversations per domain. The ergonomics are dramatically better than 13 separate bots with 13 separate tokens, which is the architecture I almost built before I remembered that Telegram supergroups have forum topics now. Why This Matters A few things I want to flag for anyone planning something similar. Build the rollback before you build the new thing. I wrote scripts/retire-openclaw.sh with explicit --rollback semantics before I disabled a single cron job. Plists get moved (not deleted) into _retired-openclaw/. Cron jobs get flipped enabled: false with a timestamped backup (jobs.json.bak.pre-retire-20260419). The OpenClaw directory sits untouched for 30 days with a calendar reminder to delete it. If ClaudeClaw had cratered on day two, I was one shell command away from being back on the old system in under a minute. Silent success is worse than loud failure. The design principle I pulled from this whole experience: every job in the system needs someone whose job it is to doubt that

    17 min
  4. FEB 17

    Cortex: An Event-Sourced Memory Architecture for AI Coding Assistants

    Cortex: Event-Sourced Memory for AI Coding Assistants Episode Summary Every time you close a session with an AI coding assistant, it forgets everything—the architecture it mapped out, the approaches it rejected, and the plan it was halfway through executing. You become the memory system. Cortex is an event-sourced memory architecture designed to fix that. In this episode, we walk through the problem, the research process that led to the solution, how the system works across four progressive tiers, and real-world A/B testing results that show an 84% reduction in cold start time and 80% fewer decision regressions. Suggested Episode Segments * 00:00—Intro: The amnesia problem with AI coding assistants * 01:22—What actually gets lost between sessions (five categories) * 03:26—Why event sourcing is the right foundation * 05:32—Three-layer extraction: structural, semantic, and self-reporting * 07:24—Projected briefings and decision immortality * 08:59—Progressive tiers: from 30-second setup to full MCP server * 12:13—A/B testing results and what surprised us * 12:43—Implementation war stories and gotchas * 13:03—The meta-irony of building this with a forgetful AI * 13:40—Tier 3: MCP server, git-tracked projections, branch alignment * 14:59—Getting started and what’s next The Problem AI coding assistants like Claude lose five categories of context at session boundaries: architectural understanding built through exploration (10-30 minutes of re-exploration per session), decision history including rejected approaches and reasoning, work state like multi-step plans and progress, tool and environment state, and conversational nuance—your preferences, priorities, and communication style. The cruelest part is that the better the AI performs within a session, the more painful the loss when that session ends. The Solution Cortex applies event sourcing—an immutable, append-only pattern borrowed from distributed systems—to capture and preserve context automatically. Three innovations make it work. First, three-layer event extraction. Layer 1 parses Claude Code hook payloads for objective structural data like file reads, commands, and code modifications—100% accuracy within its scope. Layer 2 pattern-matches response text for decision markers with confidence scoring. Layer 3 lets Claude self-report important context using [MEMORY:] tags, offering the highest accuracy through deliberate intent. These independent extractors compose through aggregation rather than requiring one monolithic system. Second, projected briefings. Raw event replay would blow through token budgets, so Cortex generates compressed, token-aware briefings that default to about 2,000 tokens—roughly 0.1% of the context window. Active decisions are immortal and never decay. Current plans show progress with step status. Recent knowledge fades over sessions based on tactical relevance. This separates retention (store everything) from representation (surface what matters). Third, progressive tiers. Tier 0 gives you basic context capture in 30 seconds. Tier 1 adds SQLite with full-text search. Tier 2 brings vector embeddings and anticipatory retrieval. Tier 3 enables an MCP server for mid-session queries, git-tracked projections, and branch-level context isolation. Results A/B testing across 18 Cortex sessions versus 11 baseline sessions showed measurable gains: an 84% reduction in cold start file re-reading, 80% fewer decision regressions where the AI re-suggested previously rejected approaches, and only 0.2% token overhead for the entire memory system. The system ships with 713 passing tests. Implementation Gotchas A few hard-won lessons from the build: every .get() call needs a default value, and every JSON parse needs a try/except because Claude Code hooks cannot afford to crash. Content-hash deduplication scopes to session ID to prevent over-deduplication. And the gitleaks GitHub Action requires paid licenses for org accounts, while the CLI stays free—a distinction that cost some debugging time. The Meta-Irony The entire project was designed and built using Claude Code—an AI assistant suffering from the exact problem being solved. Every session boundary during development was a visceral reminder of why this system needed to exist. Key Quotes * “The cruelest aspect: the better the AI performs within a session, the more painful the loss when the session ends. Excellence within a session amplifies the frustration at its boundary.” * “You can always trace back to ‘Why did the system think X?’ — that’s the power of event sourcing over CRUD.” * “Confidence as a first-class field enables downstream flexibility—briefing sorting, decay functions, user-tunable thresholds.” Links * Full article: https://astgl.com/p/cortex-event-sourced-memory-ai-coding-assistants * GitHub repository: https://github.com/As-The-Geek-Learns/cortex * As The Geek Learns: https://astgl.com Get full access to As The Geek Learns at astgl.com/subscribe

    18 min
  5. FEB 4

    The Ironclad Workflow That Prevents Technical Debt

    Welcome to the As The Geek Learns podcast. This is episode 5. My name is James Cruce and I am the Geek here at As The Geek Learns. No, this is not my voice, but I am glad you are here Listener’s Note: Due to how painful it is to listen to someone read code, I have removed it from this podcast recording. I am doing you a favor. Please refer to the article for the full code.… We aren’t sponsored… Yet, but we do have a new site where you can follow our software development progress. We have a Gumroad store where you can purchase ASTGL apps. Some free and some for dollars and absolutely no software subscriptions. Check it out at https://astgl.io. Our feature app this week is Substack Scheduler: Schedule your Substack Notes. Post at the perfect time, every time. Supports 1 or more substack sites. No subscription. ASTGL doesn’t do subscriptions. If you buy it, you own it. Now onto the episode… I was about to write my first line of Swift code when I stopped myself. Not because I didn’t know what to write. Because I realized I was about to make a mistake I’d made dozens of times before: writing code first, configuring quality tools later. That path leads to technical debt. Every time. The Familiar Pattern Here’s how most projects start: Day 1: “I’ll just get something working.” Day 30: 5,000 lines of code. No consistent style. Some files use tabs, others spaces. Import statements scattered randomly. Day 31: “I should really add a linter.” Day 32: SwiftLint reports 847 warnings. SwiftFormat wants to change every file. Day 33: “I’ll fix those later.” Day 365: Those 847 warnings are now 2,400. Nobody touches them. Sound familiar? As The Geek Learns is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber. The problem isn’t the linting tools. The problem is when you introduce them. Retrofitting quality enforcement into an existing codebase feels like punishment. Every commit triggers a wall of warnings. Developers start ignoring them. Eventually, someone disables the checks entirely. Ironclad Workflow: Setup Before Code The solution is simple but counterintuitive: configure all your quality tools before writing any application code. I call this the “Ironclad Workflow” because once it’s set up, it’s nearly impossible to introduce the kind of inconsistency that becomes technical debt. Ironclad Workflow Components: 1. Git—Version control from line one 2. Linting—Code quality rules (SwiftLint for Swift) 3. Formatting—Consistent style (SwiftFormat for Swift) 4. Pre-commit hooks—Enforcement before commits 5. CI/CD—Server-side verification When these are configured before code exists, every line you write conforms to the rules automatically. There’s never a “fix the linting” task because violations are caught immediately. Step-by-Step Implementation Here’s the order that works: Step 1: Initialize Git git init git add .gitignore git commit -m “chore: initial project setup” Start with version control. Every subsequent setup change gets its own commit. You can always see what changed when. Step 2: Configure SwiftLint # .swiftlint.yml disabled_rules: - line_length # Handled by SwiftFormat opt_in_rules: - empty_count - explicit_init included: - Sources - Tests SwiftLint catches code quality issues: force unwrapping, unused variables, and complex expressions. Configure it before writing code so you learn the rules as you go rather than fighting them later. Step 3: Configure SwiftFormat # .swiftformat --indent 4 --indentcase true --trimwhitespace always --importgrouping alphabetized --semicolons never SwiftFormat handles style: indentation, spacing, and import ordering. Consistent formatting speeds up code reviews because reviewers focus on logic, not style. Step 4: Add Pre-Commit Hooks #!/bin/sh # .git/hooks/pre-commit # Run SwiftFormat swiftformat --lint . if [ $? -ne 0 ]; then echo “SwiftFormat failed. Run ‘swiftformat .’ to fix.” exit 1 fi # Run SwiftLint swiftlint lint --strict if [ $? -ne 0 ]; then echo “SwiftLint failed. Fix violations before committing.” exit 1 fi Pre-commit hooks prevent violations from reaching the repository. You can’t commit code that fails the checks. This is enforcement, not suggestion. Step 5: Configure GitHub Actions # .github/workflows/ci.yml name: CI on: [push, pull_request] jobs: lint: runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: SwiftLint run: swiftlint lint --strict - name: SwiftFormat run: swiftformat --lint . CI/CD is the safety net. If someone bypasses pre-commit hooks (using --no-verify), CI catches it. Failed CI blocks merging. Why This Order Matters The sequence isn’t arbitrary: Git first because everything else depends on version control. You need commits to track configuration changes. Linting before formatting because linting rules affect code structure, while formatting just affects appearance. Configure the logic rules first. Hooks before CI because local enforcement is faster. You want violations caught in milliseconds on your machine, not minutes later on a build server. CI last because it’s your backup. CI catches anything that slips through local enforcement. The Psychological Shift When quality tools exist from the start, they feel like guardrails rather than gatekeepers. Retrofit mindset: “The linter is blocking my commit.” Setup-first mindset: “The linter is teaching me the patterns.” This isn’t just semantics. Developers who learn patterns during development internalize them. Developers who fight patterns during retrofit resent them. After a month of Ironclad Workflow, I found myself writing code that passed all checks on the first try. The patterns became muscle memory. Measuring Success How do you know Ironclad Workflow is working? Zero CI failures due to formatting. If CI ever fails because of a style issue, something’s wrong with your hooks. No “fix linting” commits. Every commit should already be clean. If you’re batch-fixing violations, your local enforcement isn’t working. Code reviews focus on logic. Reviewers shouldn’t comment on formatting or style. Those are automated. New developers onboard quickly. When they run the project for the first time, hooks enforce patterns automatically. No “read the style guide” onboarding step. The Investment Setting up Ironclad Workflow takes about two hours. That’s 15 minutes for Git and initial structure, 30 minutes for linting configuration, 30 minutes for formatting configuration, 30 minutes for pre-commit hooks, and 15 minutes for CI/CD. Compare that to dozens of hours retrofitting linting later, endless code review cycles about style, the cognitive overhead of inconsistent code, and the frustration of 2,400-warning projects. Two hours of setup prevents hundreds of hours of technical debt. The Lesson Technical debt is easier to prevent than fix. Most developers know this intellectually. Few act on it practically. The pull of “just get something working” is strong. But if you can resist that pull for two hours or just long enough to configure Git, linting, formatting, hooks, and CI, you’ll never need a “fix the linting” sprint again. Setup before code. Enforcement from line one. Quality by default. That’s Ironclad Workflow. James (The Geek) is a systems engineer building apps to fix real-world pain points. Drop your story in the comments. I read every one. James (The Geek) is a systems engineer building apps to fix real-world pain points… Drop your comments; we love them, everyone. I read every one. If you’re learning to build software while working a day job, subscribe to the As The Geek Learns podcast. I document the wins, the fails, and the “I wish someone had told me this earlier” moments. Until next time—stay curious, keep scripting, and remember: every expert was once a beginner who just refused to quit. I’m James, and this has been As The Geek Learns. One last thing: if you’re getting value from the show, drop me a rating on Apple Podcasts. Five stars keeps the lights on, but honestly? Even a review that says, ‘This guy talks about PowerShell too much,‘ helps the algorithm. I’ll take it. Get full access to As The Geek Learns at astgl.com/subscribe

    8 min
  6. JAN 13

    I Got Tired of Paying for Substack Notes Schedulers, So I Built My Own

    The Problem That Started It All If you publish on Substack, you’ve probably discovered their dirty little secret: you can schedule newsletter posts, but you can’t schedule Notes. Substack Notes, their Twitter/X competitor baked into the platform, requires you to be there, fingers on keyboard, posting at exactly the right moment. The market noticed this gap fast. Chrome extensions like Writestack and Finn Tropy’s Scheduler popped up, charging $25-50 for scheduling functionality. But here’s what drove me up the wall: every single one requires your browser to stay open 24/7. Why? Because they’re not calling APIs at all. They inject JavaScript into Substack’s web page, fill in the note composer programmatically, and literally click ‘Post’ for you at the scheduled time. Close your laptop lid, and your scheduled posts vanish into the ether. For someone who’s spent 25 years building enterprise automation, this felt like an affront to my professional sensibilities. There had to be a better way. Understanding the Existing Landscape Before writing any code, I needed to understand what I was dealing with. How Browser Extensions Actually Work The existing Chrome extensions all follow the same pattern: they inject JavaScript into the Substack Notes page, use your active browser session for authentication, store scheduled notes in Chrome’s LocalStorage, and then programmatically fill the composer and click ‘Post’ when the time comes. The browser has to stay open because they’re automating the UI, not calling APIs directly. That’s a fundamental architectural limitation, not a feature gap they forgot to address. The Substack “API” Discovery Substack has no official public API. That’s intentional. They want to control the platform experience. But their web application has to communicate with servers somehow, and that ‘somehow’ is a set of internal REST endpoints. The key insight came from digging into network traffic: these internal endpoints can be called directly if you have the right session cookie (connect.sid). This cookie gets issued when you log in and typically lasts weeks or months. As The Geek Learns is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber. The Terms of Service Question Before investing serious time, I needed to understand the legal landscape. Digging through Substack’s TOS and existing extension documentation surfaced one critical insight: “Substack terms and conditions prevent any system that would require you to disclose your credentials, so no external servers or services can be used for sending Notes.” This has major implications. Personal tools you build for yourself? Completely fine, since you’re using your own credentials. CLI tools that users run locally? Fine, because credentials stay on their machine. Desktop apps? Also fine, with credentials stored locally. A SaaS service where you handle user credentials on your servers? That’s where you run into problems. This shaped the entire product direction: a desktop app with local credential storage hits the sweet spot for commercial viability without TOS issues. Architecture Decision: Python CLI as Foundation Given my automation background and the goal of eventually building a commercial product, I went with Python for the core logic. Why Python? The syntax feels familiar coming from PowerShell. The HTTP libraries are excellent, and requests is perfect for this kind of work. It runs anywhere: Mac, Linux, Windows, Raspberry Pi, and cloud VMs. Quick iteration with no compile step. And the packaging story is solid. The same code can become a CLI tool, then a desktop app. The Evolution Path I Mapped Out Phase 1 would be a personal script, just Python and cron for my own dogfooding. No TOS risk. Phase 2, a packaged CLI tool I could sell as a one-time purchase for $20-50. Low TOS risk. Phase 3, a Tauri-wrapped desktop app, either one-time or subscription. Still low risk. Phase 4 would be a full SaaS service with cloud and browser automation and a subscription model but higher TOS risk. The decision became clear: build a desktop app where users keep their own credentials locally. This respects the TOS while providing a professional user experience. The Desktop Framework Decision: Tauri vs. Electron For wrapping a web UI into a desktop app, two main contenders exist: Tauri and Electron. Tauri produces binaries around 3-10 MB with lower memory usage since it uses the system webview. Faster startup, Rust backend. Version 2.0 is stable. Electron produces 150-200 MB binaries with higher memory usage because it bundles Chromium. Slower startup, Node.js backend, but extremely mature. Decision: Tauri 2.0 Size matters for distribution. 5 MB versus 150 MB affects download perception. And ‘lightweight & fast’ becomes a selling point for a utility app. The Rust backend would auto-start the Python scheduling daemon as a sidecar process. API Reverse Engineering: The Technical Foundation Before building anything, I needed to prove the core posting logic would actually work. Step 1: Cookie Extraction Getting the session cookie requires a one-time manual step. Open Chrome DevTools (Cmd+Option+I), go to your Substack publication’s Notes page, switch to the Network tab, post a test note manually, then find the POST request and copy the cookie string. Step 2: Endpoint Discovery Based on online research, I expected the Notes endpoint to be: POST /api/v1/notes. It isn’t. After network trace analysis, I discovered Notes are actually posted via: POST https://{publication-domain}/api/v1/comment/feed The naming makes sense once you understand Substack’s architecture: Notes evolved from their commenting system. They literally are ‘comments on your own feed.’ Step 3: Custom Domain Discovery If you have a custom domain (like resistandrise.blue or astgl.com), API calls go to that domain, not substack.com. Critical for multi-publication support. Step 4: Payload Structure Substack uses ProseMirror for content. The JSON structure wraps your text in a doc with a schema version, containing paragraphs with text nodes. There’s also a replyMinimumRole field set to ‘everyone.’ The Product Vision With research complete, I had a clear picture. Target User: Substack creators who want to schedule Notes without browser dependencies Core Value Proposition: Schedule Notes days, weeks, or months in advance. Runs in the background with no browser required. Works offline and posts when you’re back online. Multi-account support for creators managing multiple publications. Pricing Model (One-time purchase, tiered): Personal at $29 for 1 account. Creator at $49 for 3 accounts. Agency at $99 for unlimited accounts. Technology Stack: Python backend using FastAPI for the API and scheduling daemon. React frontend for the web-based UI. Tauri desktop wrapper to package everything together. Gumroad for payment and licensing. What’s Next With the research complete and architecture planned, Part 2 covers actually building the thing, from proof-of-concept CLI to polished desktop application. Thanks for reading As The Geek Learns! This post is public, so feel free to share it. Have you used an AI to help you research and build an app? I’d love to hear your experiences. Leave comments or questions. I will answer every one. — The Geek Get full access to As The Geek Learns at astgl.com/subscribe

    9 min
  7. JAN 9

    Database Schema Design Mistake That Broke My App

    TL;DR: My Johnny Decimal index app had a three-level database schema when the system requires four levels. I’d conflated folder containers with the files inside them—a fundamental data model flaw. One week of dogfooding caught what months of planning completely missed. Key lessons: use your own software early, match your data model to your mental model, and refactor before you accumulate data you’ll need to migrate. I’d been using JDex, my Johnny Decimal index application, for about a week when a fundamental database schema design flaw became impossible to ignore. The app worked beautifully. I could create areas and categories. I could track items with their XX.XX identifiers. The search was fast, the UI was clean, and I was feeling pretty good about what I’d built. Then I tried to organize real files. Somehow I had folders and the files inside them sitting at the same level in my database. My data model had three levels when Johnny Decimal requires four. Here’s how one week of dogfooding caught what months of planning completely missed and the four lessons I’m carrying into every future project. The Problem I Didn’t See Coming How Johnny Decimal Hierarchy Actually Works Johnny Decimal (johnnydecimal.com) is a system for organizing files and information. It has a clear four-level hierarchy: Area (00-09) └── Category (00, 01, 02...) └── ID Folder (00.01, 00.02...) └── Actual Files Level What It Is Number Format Example 1 Area 00 - 09 00-09 System 2 Category XX 00 Index 3 Folder XX.XX 00.01 JDex Database 4 Item XX.XX.XX 00.01.01 Main DB File My database had three levels: areas, categories, and... items. Where do the ID folders go? Why My Three-Level Database Schema Failed I’d conflated two fundamentally different things: Folder containers: the XX.XX numbered folders that hold files Actual files: the contents inside those folders In my original schema design, “00.01 JDex Database” (a folder) and the SQLite file inside it (an item) existed at the same level in the database. That’s like saying a filing cabinet drawer and the documents inside it are the same thing. Why Database Schema Mismatches Break Core Functionality This wasn’t just aesthetically wrong. The three-level data model broke features I needed: I couldn’t track what was inside a folder. The whole point of Johnny Decimal is knowing “everything about taxes lives in 12.04.” But my schema had no way to list what was actually in 12.04. The folder and its contents were peers, not parent and child. Folder-level properties were impossible. What if everything in a folder should be marked as sensitive? With my original design, I’d have to set that flag on every individual item. There was no way to say, “This container and everything in it shares this property.” The numbering didn’t work. Johnny Decimal items should be XX.XX.XX—three segments. Mine only went to XX.XX because I’d eliminated an entire level. When your data model doesn’t match the mental model of the system you’re building, you’ll keep running into walls. The database fights you instead of helping you. The Fix: Adding a Fourth Level The solution was straightforward conceptually but required rethinking the entire database schema: the old items table became folders. A new items table was created for the actual tracked objects inside those folders. The Refactored Database Schema Here’s what the corrected structure looks like: -- Folders (Level 3) - XX.XX format containers CREATE TABLE folders ( id INTEGER PRIMARY KEY, folder_number TEXT UNIQUE, -- “22.05” category_id INTEGER, name TEXT, sensitivity TEXT DEFAULT ‘standard’, created_at TIMESTAMP, updated_at TIMESTAMP, FOREIGN KEY (category_id) REFERENCES categories(id) ); -- Items (Level 4) - XX.XX.XX format objects CREATE TABLE items ( id INTEGER PRIMARY KEY, item_number TEXT UNIQUE, -- “22.05.03” folder_id INTEGER, name TEXT, sensitivity TEXT DEFAULT ‘inherit’, -- Can inherit from folder file_path TEXT, notes TEXT, created_at TIMESTAMP, updated_at TIMESTAMP, FOREIGN KEY (folder_id) REFERENCES folders(id) ); The inherit option for item sensitivity was a bonus insight that emerged from the refactor. Most items should just use their folder’s sensitivity level. Only exceptions need explicit overrides. That’s not something I could have implemented with the old three-level schema. Four Lessons from This Database Design Mistake Use Your Own Software (Dogfooding) The schema flaw was invisible in design documents and code reviews. It only appeared when I tried to organize real files with real data. A week of daily use caught what months of planning missed. There’s no substitute for using your own software to accomplish real tasks. You’ll discover conceptual mismatches that no amount of testing or review will reveal. If you’re building something, use it yourself as soon as it’s minimally functional. Match Data Models to Mental Models I knew Johnny Decimal had four levels. I could draw them on a whiteboard. Yet somehow my database had three. That mismatch was always going to cause problems—I just hadn’t encountered them yet. When you’re modeling a real-world system, count the levels. Write them down. Then count the levels in your database. Do they match? Containers and contents are different things, even if they both have names and locations. This seems like common sense now, but when I was building the original schema, I simply didn’t see the issue. Database Schema Changes Are Easier Early This refactor touched every database function and most UI components. It was significant work. If I’d caught this flaw after six months of data entry, migration would have been painful. I’d have needed to write migration scripts, test them against production data, handle edge cases, and pray nothing broke. Catching it in week one meant a clean restart. I dumped all the data because it was easier than trying to preserve and migrate it. That’s a luxury you only have early in a project. Keep Your AI Context Updated I’ve been bouncing ideas off Claude to see what might work and what definitely won’t. It’s been helpful for thinking through problems and catching issues. But here’s what I didn’t anticipate: my Claude Project configuration had the old three-level schema baked in. After the refactor, I had to update those instructions so the AI understood the new structure and why I changed it. The configuration is only as good as the context it contains. If your tools are working from outdated assumptions, they’ll give you outdated guidance. This applies to AI assistants, documentation, onboarding materials, and anything that encodes how your system works. I should have just let Claude help build the schema in the first place. But I was trying to learn as I went, and that’s the tradeoff. I’m not a coder by trade, just a systems engineer who likes to automate as much as possible. 📬 Learning to code while working full-time? You’re not alone. Subscribe to As The Geek Learns for weekly lessons from the trenches—the wins, the fails, and the “I wish someone had told me” moments. Database Design Checklist for Hierarchical Data Projects If you’re building something that models a hierarchy, run through this before you write code: Count the levels in the real-world system. Write them down. Then count the levels in your database schema. Do they match exactly? Distinguish containers from contents. Are there things that hold other things? Those need separate tables or entities, not just a “type” field. Define the numbering scheme. If your system has identifiers like XX.XX.XX, your schema needs to support generating and validating that format at each level. Identify inheritable properties. What attributes should cascade from parent to child? Design for that explicitly (like the sensitivity: inherit option I added). Plan for schema evolution. Your first model probably won’t be your last. How hard will it be to add a level later? To split a table? Design with migration in mind. What’s Next for JDex JDex is better now. The four-level hierarchical database design matches how Johnny Decimal actually works, and I can finally track what’s inside a folder. The most important bugs aren’t always in your code; they’re in your assumptions. The schema looked right. The code worked. But the underlying model was wrong, and no amount of polish would have fixed that. One week of real use revealed what months of planning couldn’t. James is a systems engineer building JDex as a learning project while documenting the journey. Follow along at As The Geek Learns for more lessons from the trenches of learning to code. Have you discovered a fundamental flaw hiding in your project’s design? How did you find it? Drop your story in the comments. I read every one. If you’re learning to build software while working a day job, subscribe to As The Geek Learns. I document the wins, the fails, and the “I wish someone had told me this earlier” moments. Get full access to As The Geek Learns at astgl.com/subscribe

    10 min
  8. 12/20/2025

    How I Built JDex with Claude AI—A Systems Engineer's Honest Take

    How I Built JDex with Claude AI—A Systems Engineer's Honest Take Part 2 of the Building JDex Series: What AI-assisted app development actually looks like in practice In Part 1 of this series, I shared my file organization disaster: years of digital chaos spread across four cloud services and two laptops. I discovered the Johnny Decimal system, a methodology that finally made sense, but I needed a tool to actually implement it across my sprawling reality. So I decided to build one. But I’m a systems engineer, not a web developer. I can write PowerShell scripts in my sleep, but building a proper web application? Different skill set entirely. This is where AI-assisted app development got interesting. From AI Answers to AI Collaboration I’d been using Claude, Anthropic’s AI assistant, for various work tasks. Technical documentation. Script debugging. Research. But I hadn’t explored what it would be like to build something with AI rather than just getting answers from it. I started describing what I wanted: a web-based index for managing Johnny Decimal systems across multiple storage locations. Something accessible from any device. Something that would help me track not just where files should go, but where they currently are as I work through the migration. What followed was genuinely collaborative. Not “AI writes code, human copies and pastes,” but an actual back-and-forth of designing, building, testing, and refining. I’d describe a feature. Claude would propose an implementation. I’d test it, find edge cases, and explain what wasn’t working. We’d iterate. Sometimes I’d have a half-formed idea, and Claude would help me think through implications before writing any code. I consider Claude a partner in this process. That’s not hype. It’s the most accurate description of how the work actually happened. What JDex Does: A Johnny Decimal Index App JDex is the Johnny Decimal index application that emerged from this collaboration. At its core, it lets you: Define your structure: Create areas and categories that match how you think about your information. Track locations: Note where files actually live, including which cloud service, which device, and which folder path. Manage the index: The heart of Johnny Decimal is the index, a master reference of what exists and where it belongs. Search and navigate: Find any ID quickly, see what’s in each category, and understand your system at a glance. JDex App The working application matters, but what I learned about building with AI matters more. 4 Lessons from Building an App with Claude AI 1. AI collaboration is a skill Like working with a human colleague, you get better at it. I learned to describe problems more clearly, break complex features into manageable pieces, and provide useful feedback when something wasn’t quite right. The quality of what Claude produced directly correlated with the quality of context I provided. 2. The human brings the context Claude doesn’t know my file organization history, my workflow, or my pain points. I had to bring that context to every conversation. The AI could suggest solutions, but only I could evaluate whether they fit my actual life. Domain expertise isn’t replaced—it’s leveraged. 3. It’s genuinely faster Things that would have taken weeks of learning web development frameworks happened in hours. Not because AI did all the work, but because it accelerated the parts I didn’t know while I focused on the parts I did. For a systems engineer exploring app development, this changes what’s possible. 4. The result feels like mine This surprised me most. JDex isn’t something Claude built that I’m using. It emerged from hundreds of small decisions I made, issues I identified, and features I prioritized. The AI was a capable partner, but the vision and direction were always mine. What's Next for JDex JDex is functional but not finished. I’m using it daily as I implement my own Johnny Decimal system, and real-world usage keeps surfacing improvements. I’ll be sharing more here on As The Geek Learns: How I structured my Johnny Decimal system (decisions and tradeoffs) Technical deep dives on JDex features Lessons from migrating years of digital clutter Honest assessments of where AI collaboration helps and where it doesn’t We’re at an interesting moment with AI tools. Underneath the hype, there’s something real: people with ideas and domain expertise can build things that previously required skills they didn’t have. I’m not a developer. But I had a clear problem, years of experience with organizational systems, and access to an AI that could help translate vision into working code. That’s a new kind of leverage worth exploring seriously. JDex is my first real experiment in that space. It won’t be my last. FAQ What is JDex? JDex is a web-based application for managing Johnny Decimal file organization systems. It lets you define your category structure, track where files are stored across multiple locations, and maintain a searchable index of your entire system. Do I need coding experience to use JDex? No. JDex is a user-facing application, not a coding tool. You interact with it through a web interface to manage your Johnny Decimal system. Can I build an app with Claude AI if I’m not a developer? Yes, with cautions. You need enough technical literacy to test, debug, and direct the process. My background as a systems engineer helped. I understood logic and structure even without web development experience. Complete beginners would face a steeper learning curve. Is JDex available to use? JDex is currently in development. Follow As The Geek Learns for updates on availability. James is a systems engineer, writer, and builder. He publishes As The Geek Learns at astgl.com and writes about surveillance technology and democratic resistance at Resist and Rise. Series: Building JDex Part 1: The File Organization Problem Part 2: How I Built JDex with Claude AI (You are here) Part 3: Coming soon Have you built anything with AI assistance? What was the collaboration actually like? Let me know in the comments. Get full access to As The Geek Learns at astgl.com/subscribe

    8 min
  9. I Had Files Scattered Across 4 Clouds. So I Built an App with AI to Fix It

    12/11/2025

    I Had Files Scattered Across 4 Clouds. So I Built an App with AI to Fix It

    Here’s something embarrassing. I’m a systems engineer. Twenty-five years managing enterprise infrastructure. And until recently? My personal file system was a complete mess. Files everywhere. iCloud, Dropbox, OneDrive, ProtonDrive. Two laptops. External drives with names like “Backup_OLD_FINAL_v2.” Folders buried 8 levels deep with names I made up in 2003 and haven’t thought about since. At work, I help manage around 5,500 virtual desktops running on VMware ESXi. Before that, I spent years on the server side; I’ve probably built and retired over 15,000 virtual servers throughout my career. I can script my way through complex VMware migrations without breaking a sweat. But ask me to find that PDF of my home insurance policy? Yeah. Good luck with that. If this sounds familiar, keep reading. The Problem Nobody Talks About Digital clutter sneaks up on you. It builds quietly, one “I’ll organize this later” at a time. Then suddenly you’re staring at a search bar, trying to remember which of four cloud services might have the document you need. Productivity gurus love to say, “Just pick a system.” Easy for them. They’re usually not dealing with: * Work files that absolutely must stay separate from personal stuff * Sensitive documents that need encrypted storage * Collaborative projects scattered across different platforms * Years of accumulated digital life that doesn’t fit neatly anywhere I tried everything. Color-coded folders. Strict naming conventions. Applied the “inbox zero” mindset to my files. Each approach worked for maybe three weeks. Then chaos won. It always does. That’s when I stumbled onto Johnny Decimal, on Reddit, of course. A System That Actually Made Sense Johnny Decimal is a file organization method created by Johnny Noble. The idea is simple: you get a maximum of 10 areas (your broad categories), and each area can hold a maximum of 10 categories (more specific groupings). Every single file gets a unique ID based on where it belongs. Instead of wading through /Documents/Work/Projects/2024/Client_ABC/Reports/Final/, you end up with something like 32.14 Client ABC Q3 Report. The constraints are the whole point. When you limit yourself to 10 areas and 10 categories per area, you actually have to think about how you organize things. Nothing goes deeper than two levels. Every file has a home. You can tell someone “it’s in 32.14” and they know exactly where to look. I read through Johnny’s documentation, and something just clicked. This wasn’t some organizational B.S. that would fade in six months. It’s a real framework for thinking about how information connects to other information. But I had a problem. The Gap Between Theory and My Reality Johnny Decimal works great when you’re starting fresh with one file system. I was looking at four cloud services, two machines, and years of accumulated chaos that all needed to be cataloged, cross-referenced, and migrated. I needed more than a methodology. I needed a tool. Here’s what I was looking for: * A central index to track where everything lives across all my storage platforms * A way to assign and manage Johnny Decimal IDs without going crazy * Something that could grow with me as I slowly brought order to the mess I searched for existing solutions. Some solid tools exist, but nothing quite fit my multi-platform, sprawling-digital-life situation. So I built it myself. With an unusual partner helping me along the way. Next time: What it actually looks like to build an app with AI as your collaborator—the process, the tool I created, and what I learned about this new kind of partnership. Have you tried Johnny Decimal? Struggled with multi-cloud chaos? I’d love to hear your experiences in the comments. Until next time—stay curious, keep scripting, and remember: every expert was once a beginner who just refused to quit. I’m James, and this has been As The Geek Learns. One last thing: if you’re getting value from the show, drop me a rating on Apple Podcasts. Five stars keeps the lights on, but honestly? Even a review that says ‘this guy talks about PowerShell too much’ helps the algorithm. I’ll take it. Get full access to As The Geek Learns at astgl.com/subscribe

    6 min
  10. 12/06/2025

    Much to Learn, There Always Is

    Welcome to the As The Geek Learns newsletter. As The Geek Learns is free today. But if you enjoyed this post, you can tell As The Geek Learns that their writing is valuable by pledging a future subscription. You won't be charged unless they enable payments. I am James Cruce and this is not my voice. I am glad you are here. Title: Much to Learn, There Always Is Subtitle: Why I'm starting As The Geek Learns after 25 years in IT I’ve been doing this for a while. Twenty-five years of systems engineering. Somewhere around 15,000 virtual machines under my watch (if you count VDI). Countless PowerShell scripts, automation workflows, and middle-of-the-night troubleshooting sessions. I’ve seen technologies come and go, watched VMware evolve from a curiosity to the backbone of enterprise infrastructure, and learned more from my mistakes than I’d care to admit. So why start writing now? Because I’m still learning. That might sound strange. After a quarter century, shouldn’t I have it all figured out? But here’s what I’ve discovered: the moment you think you’ve mastered something is usually the moment before it changes completely. The best engineers I know, the ones who’ve lasted, who’ve stayed sharp, are the ones who never stopped being students. That’s what this newsletter is about. What to expect I’m not here to pretend I have all the answers. Instead, I’ll share: What I’m learning — New tools, techniques, and approaches I’m exploring What actually works — Practical solutions tested in real production environments, not just lab demos What I’ve built — Apps and automation I’ve created to solve my own problems (and might solve yours too) What I got wrong — Because occasionally the best lessons come from failures If you work in IT, especially if you’re managing VMware infrastructure or trying to level up your PowerCLI skills, I think you’ll find something useful here. A bit about me I’m a systems engineer at a large healthcare organization in Florida, where I manage enterprise VMware infrastructure. I’ve spent years automating repetitive tasks, building tools to make my life easier, and figuring out how to do more with less (sound familiar?). Outside the day job, I’m working on training courses for IT professionals who want to master PowerCLI and VMware automation. I’ve also built a few apps, including KlockThingy—a time-tracking tool that came out of my wife’s frustration with what she calls ‘Clocky Math.’ It was vital that she log her hours more efficiently as a remote contract worker. She also coined the name. Why “As The Geek Learns”? The name says it all. I’m a geek. I learn things. I write about what I learn. There’s a Yoda quote I’ve always loved: “Much to learn, you still have.” I have adapted that quote to reflect my lifelong learning experience. “Much to learn, there always is” It’s a reminder that expertise isn’t a destination; it’s a direction. No matter how long you’ve been at this, there’s always something new to discover. I hope you’ll come along for the ride. What’s next In upcoming posts, I’ll be sharing: A deep dive into the PowerCLI automation techniques I use daily Behind the scenes of building and shipping my first app Practical tips for managing large-scale VMware environments And whatever else I’m learning along the way If any of that sounds interesting, you’re in the right place. Thanks for being here. Always a student. Sometimes a teacher. — James Get full access to As The Geek Learns at astgl.com/subscribe

    4 min

About

Tools and training for IT professionals. Join James Cruce—a systems engineer with 25+ years managing enterprise VMware infrastructure—for practical PowerCLI tutorials, automation tips, and lessons from the trenches. Much to learn, there always is. astgl.com