{
 "notes": [
  {
   "id": "group-ai-environment",
   "title": "AI environment",
   "group": "AI environment",
   "body": "The working environment I built: memory, rules, hooks, agents, verification.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "group-ai-features-shipped",
   "title": "AI features shipped",
   "group": "AI features shipped",
   "body": "AI features that shipped to customers inside the platform.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "group-ml-and-models",
   "title": "ML and models",
   "group": "ML and models",
   "body": "Model work: training, benchmarks, pipelines.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "group-integrations",
   "title": "Integrations",
   "group": "Integrations",
   "body": "Vendor and API integrations I built or rebuilt.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "group-platform-engineering",
   "title": "Platform engineering",
   "group": "Platform engineering",
   "body": "Auth, product data, testing, data accuracy.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "group-website-and-brand",
   "title": "Website and brand",
   "group": "Website and brand",
   "body": "The company website, brand kit, video, and SEO work.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "group-infrastructure",
   "title": "Infrastructure",
   "group": "Infrastructure",
   "body": "Local stack, cloud, cost discipline.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "group-decisions",
   "title": "Decisions",
   "group": "Decisions",
   "body": "Decisions I recommended and delivered.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "group-personal-builds",
   "title": "Personal builds",
   "group": "Personal builds",
   "body": "Software a small business and a household run on.",
   "links": [],
   "tags": [],
   "url": null,
   "kind": "hub",
   "when": ""
  },
  {
   "id": "case-memory-system",
   "title": "Building the memory system from scratch",
   "group": "AI environment",
   "body": "Solentrex · April 2026\n\n Building the memory system from scratch\n\n Every AI session used to start from zero. I rebuilt the memory as five layers that load themselves, save themselves, and never ask me to explain the same thing twice.\n\n Five layers9 hooks, 6 events2,211 documents137 consecutive logsLocal only\n\n What it was\n\n An AI session forgets when it ends. I would explain the codebase, the vendors, the decisions we had already settled, and then the window closed and it was gone. The next morning I explained it again.\n\n My first attempt at a fix was a folder of 116 files with nothing connecting them. It stored things. It did not compound.\n\n What I did\n\n On April 3, 2026 I rebuilt it as five layers, each with one job.\n\n A personality file. How the assistant behaves and who it works for. Rarely changes.\n\n Core context. What the business is, the current priorities, the conventions, who is on the team.\n\n One living document per project, updated in place as things change. This is the part that compounds.\n\n An append-only daily log. The safety net for anything that did not fit a project file. Never edited after the fact.\n\n Structured records for decisions, discoveries, and failures, so a settled question is not re-argued and a failure is not repeated.\n\n The shape is not original and I never claimed it was. I read six open source memory projects and two tutorials, took what worked in each, and assembled it for one person doing engineering work.\n\n Then I made the loading automatic, because a memory that depends on remembering to use it is not a memory. Nine small scripts run on six events in the tool's lifecycle. Context is injected at session start and routed by which folder I am working in. A save marker is written before the context window is compacted. The session is reconciled back into the vault when it stops, and a staleness check runs at the end. If nothing has been written for fifteen minutes of active work, the session gets a warning. Each session has to open with an agreed short phrase; if the phrase is missing, I know the context was skimmed and not read.\n\n What may be stored is written down and narrow. No keys, tokens, passwords, customer names, or raw API responses, ever. Patterns and placeholders instead of real values. It lives on one machine as plain text, syncs to no cloud service, and the owner can read, edit, or delete all of it in a minute.\n\n What it produced\n\n MeasureCount\n\n Documents in the memory2,211\n\n WordsAbout 4.4 million\n\n Written rules for AI agents129\n\n Consecutive working days logged137\n\n Hook scripts, and lifecycle events they run on9 scripts, 6 events\n\n Hook fires stamped and logged since August 3321\n\n The practical result is that I stopped re-explaining. A session opens already knowing the state of every workstream and the mistakes we have already made. Work done on a Tuesday is available on Friday without me carrying it.\n\n The same pattern now runs a second, much smaller vault for personal projects, loaded by the same hook and kept separate from work. That is the evidence it is a method and not one lucky folder. One addition came from a failure: a session ran out of context and lost two days of decisions, so I added a summary writer that reads the raw transcript and records the session itself. Keeping the memory current no longer depends on the AI choosing to do it.",
   "links": [
    "group-ai-environment",
    "build-a-second-memory-vault"
   ],
   "tags": [
    "case"
   ],
   "url": "/work/memory-system/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "case-two-tools-one-brain",
   "title": "Two AI tools, one brain, and a written cost policy",
   "group": "AI environment",
   "body": "Solentrex · July to August 2026\n\n Two AI tools, one brain, and a written cost policy\n\n Two vendors' coding agents, one rulebook and one memory between them, and a spending policy written the day a run got expensive.\n\n One canonical fileRound trip verifiedModel and cost policyStanding verifierNon-engineer adoption\n\n What it was\n\n I run two vendors' coding agents, Claude Code and OpenAI Codex. Each reads its own instruction file and keeps its own memory. Left alone, that gives you two versions of the rules that slowly disagree, work done in one tool that the other never learns about, and a memory that belongs to whichever vendor you are using this month.\n\n Cost was the other open question. An agent can spread work across many parallel workers, and nothing stops that from getting expensive.\n\n What I did\n\n There is one canonical instruction file, AGENTS.md, the filename several tools already look for. The Claude Code filename is a symlink to it, so it is the same file and not a copy. The rules live in one folder that the file points at rather than repeats. Both tools read the shared memory at session start and write findings back into it, and a meaningful change gets a line in the daily log so the next tool picks it up. Wiring in a new tool later is one link.\n\n I verified the round trip on July 5, 2026 instead of assuming it. Codex read the required start files, checked that my local environment was isolated, wrote its answer into the shared record, and appended its own daily log entry. The other tool read that entry in its next session.\n\n The policy came out of a bad day. A research run fanned out to 105 agents that inherited the main session's model and used 3.4 million tokens in a single run. The rules are written down.\n\n RuleWhy\n\n The main session's model is never inherited by a fan outInheritance is how one search becomes 105 agents on the most expensive option. Every agent is given its model explicitly.\n\n Model per seat, effort per laneJudgment work gets the strong model. Research runs at high effort and implementation lower, because implementation agents over engineer at high effort.\n\n Never the smallest modelIt is cheap and it invents things.\n\n A standing adversarial verifierOne seat exists to contradict the managing agent. In a seven worker, fourteen hour run it refuted four of the manager's own claims.\n\n Getting non-engineers into the tools\n\n The same principle works for people who do not write code. Give them one written rule that decides the hard case. The roof labeling that trains our geometry model is done by the people with the product context to judge the edge cases, not by an engineer. They work from a written guide with one rule at the top: a saved correction has to be a real correction. Open the model's guess, save it unchanged, and all you have taught it is that it was right.\n\n The website copy worked the same way. The people who own the message wrote the words, and the pipeline treated their file as the source of truth.\n\n What it produced\n\n The memory is not locked to a vendor. Both tools work from the same rules and leave findings in the same place, and a third is one link away. The spending rules are a page anyone can read and hold me to, rather than a habit in my head. And the people with the most context and the least code do real work in these tools without an engineer beside them.",
   "links": [
    "group-ai-environment",
    "entry-model-and-effort-policy"
   ],
   "tags": [
    "case"
   ],
   "url": "/work/two-tools-one-brain/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "case-brand-kit-and-content",
   "title": "The brand kit and the content system",
   "group": "Website and brand",
   "body": "Solentrex · April to July 2026\n\n The brand kit and the content system\n\n An AI tool writes generic copy until you tell it who it is writing as. So I wrote that down first, in a plain file the tools read before they write a word.\n\n One plain fileAlways and never listsFive source sweepAdversarial review\n\n What it was\n\n Ask any AI tool to write something for a company and you get copy that is competent and anonymous. It does not know the colors, it does not know the words the company refuses to use, and it does not know who the reader is. Everything it produces has to be rewritten, which costs more than writing it yourself. The tool is not the problem. Nobody told it who it works for.\n\n What I did\n\n I wrote the company brand kit in April 2026 as a plain text file, not a slide deck for designers. It is written to be followed by a machine and read by a person.\n\n Color with roles. One blue for everything interactive, one dark for headings, one orange used sparingly as a single accent moment.\n\n Image style, including what is banned. Editorial photographic realism, daylight, real objects, generous space. No glowing holograms, no neon, no posed stock photo people, no cartoon suns.\n\n Voice. Short declarative sentences. Concrete numbers and real workflows instead of adjectives. Speak to the reader directly.\n\n An always list and a never list. Always back a claim with a number or a step. Never use hype words or corporate jargon, never invent a statistic, a quote, or a customer story, and never name the data vendors behind the product in public copy.\n\n In July I added the content layer for the AI writing tools. I built it from a sweep of five internal sources: the brand kit, the business and strategy files, the live site copy, the approved copy decisions, and the writing conventions. Then I ran an adversarial review over the result, checking every claim against something we could point to. Only the claims that survived that pass are allowed in generated content, and any number that was not settled was banned outright rather than guessed at. The file also names the audience, which is installers, sales teams, and finance partners, never homeowners.\n\n The same month, the words for the new website were written in house rather than generated. I took that copy as the source of truth, kept a verbatim record of it, and fed it through the AI build pipeline that produced the pages. The relaunch went live in one week, with new hosting, DNS, certificates, and analytics on all eleven pages.\n\n What it produced\n\n Since April, the brand kit has been the first file any AI content tool reads. Generated drafts come back in the company's voice and inside its rules, so the editing pass is about substance rather than tone. The banned list is the part that earns its keep: a tool that cannot invent a statistic or name a vendor cannot quietly publish either one.\n\n It is also portable. This site has the same artifact in the same shape, a design and voice file every session that built these pages read first. You can read it at the brand kit page. That file is the first thing I would write for a company rolling out AI tools, before choosing a single tool, because it is the difference between output you use and output you rewrite.",
   "links": [
    "group-website-and-brand",
    "case-two-tools-one-brain",
    "case-memory-system"
   ],
   "tags": [
    "case"
   ],
   "url": "/work/brand-kit-and-content/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "case-platform-takeover",
   "title": "Taking over a platform from an agency",
   "group": "AI environment",
   "body": "Solentrex · March 2026\n\n Taking over a 14-repo platform from an agency\n\n When engineering moved in house I took over the whole platform as the only in-house engineer.\n\n 14 repositoriesSpend down ~95%~800 merged PRsLocal isolated stack\n\n What it was\n\n Solentrex is a solar CRM platform spread across fourteen repositories: two Angular apps, seven .NET services, two Python AI services, and the marketing site. An outsourced agency built it and ran it. I joined in December 2025 doing QA, which meant I spent my first months reading the product against the code.\n\n By February the company was weighing whether to keep engineering outsourced or bring it in house.\n\n What I did\n\n I wrote up an evidence-based picture of the platform: what each part of the product actually did, checked against the running application and the source, with the file and the behavior written next to every finding. I took it to leadership in March 2026.\n\n Engineering moved in house that month, and I took over the platform as the only in-house engineer.\n\n The first thing I changed was not a feature. It was how work gets proven. I built a local stack that runs the whole product on one machine, isolated from production, so a change can be tested in seconds instead of waiting on a shared pipeline. Then I wrote down what counts as evidence: drive the real app, judge by network calls and database rows rather than by a screenshot, and trace the cause to a file and a line before calling anything fixed.\n\n AreaBeforeAfter\n\n Engineering spendOutsourcedIn house, down about 95%\n\n Testing a changeShared cloud pipeline, about five minutes per runFull workflow on an isolated local stack\n\n Definition of doneManual reviewSignals measured, cause traced to file and line\n\n What it produced\n\n In the first three weeks alone I pushed more than 280 commits across six repositories, mostly to keep the platform moving while the handover settled. Across the five months since, the four core repositories took roughly 1,800 commits and about 800 merged pull requests from me, while the product kept shipping: financing, energy analysis, and design.\n\n Engineering spend fell by about 95% and stayed there. That is the number I would defend first, because it is the one the company can check on an invoice.",
   "links": [
    "group-ai-environment",
    "entry-ending-the-outsourced-development-contract",
    "tool-angular-net-core-sql-server",
    "entry-fully-isolated-local-production-mirror"
   ],
   "tags": [
    "case"
   ],
   "url": "/work/platform-takeover/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "case-lender-certification",
   "title": "Lender certification, and the money math",
   "group": "Integrations",
   "body": "Solentrex · 2026\n\n Lender certification, and the money math\n\n I rebuilt the financing integration, passed the lender's certification at 100 of 100 addresses, and built the checks that catch pricing and savings errors before a customer sees them.\n\n 100 of 100Rebuilt end to endAudited on real dataOne shared savings engine\n\n What it was\n\n Every proposal we send a homeowner is a financial document. It quotes a system size, a monthly payment, and twenty five years of projected savings, and a lender underwrites a loan against those numbers. I inherited the integration that produces them.\n\n Two things had to be true at once. The lender had to certify our design output as accurate enough to fund, and the money on the page had to be right.\n\n What I did\n\n I rebuilt the financing integration end to end: the design and pricing payload we send, the webhooks that come back, and the loan states in between. Then I ran the lender's certification set and passed it at 100 of 100 addresses.\n\n The math was the harder half. I stopped reading the code and started checking the numbers against real production data, because that is the only way to know what a system actually produces.\n\n What I builtHow it is proven\n\n One savings engine instead of several copiesThe escalation schedule runs through a single shared function, verified live in production against the contract terms rather than against my reading of the code. Landed across 14 merged pull requests.\n\n An approved-loan auditEvery approved loan in production is checked against its own design record, so the system size on the loan and the system size on the design have to agree.\n\n Adder pricing checksMulti-adder pricing is reproduced against real production proposals, so the loan amount has to match the quote.\n\n Guards on the lender's limitsValues are validated against the lender's published limits before they are sent, so anything out of range is caught on our side.\n\n A standing sweep over the financing surfaceA parallel agent audit runs across live proposals and reports anything whose displayed state does not match its own record.\n\n What it produced\n\n The integration is certified and live, and the savings math now runs through one shared function instead of several copies, which let me delete about a thousand lines of dead code.\n\n The part I care about is the order of events. The checks run on production data before a homeowner ever signs, which is the only point where finding something is still cheap.",
   "links": [
    "group-integrations"
   ],
   "tags": [
    "case"
   ],
   "url": "/work/lender-certification/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "case-bill-reader",
   "title": "An in-house utility bill reader",
   "group": "AI features shipped",
   "body": "Solentrex · July 2026\n\n An in-house utility bill reader\n\n I replaced a vendor model we could not inspect with a two-stage vision pipeline that costs about a cent a bill and shows its work.\n\n ~1 cent per billLocate then cropDual read votingLive 2026-07-23\n\n What it was\n\n A solar proposal starts with a utility bill. A homeowner uploads a PDF or a photo, and the numbers read off it set the usage, the rate, and every savings figure that follows. Everything downstream inherits whatever the reader says.\n\n The previous reader was a vendor model we did not own and could not inspect, and it could not tell us why it produced a given number. I wanted the first number in the funnel to come from something we could open and read.\n\n What I did\n\n I built the reader in house and kept it deliberately simple. There are three ideas in it.\n\n Locate, then crop, then read. The first pass finds where on the bill the value lives. The second pass reads only that crop. A model given a small, correct region makes far fewer mistakes than a model given a whole page.\n\n Two reads have to agree. Each value is read independently more than once, and the answer only stands if the reads match. Disagreement is a flag, not an average.\n\n A plausibility gate. A value that clears both reads still has to be possible for a residential bill. If it is not, the pipeline says so instead of passing it along.\n\n I scoped version one with leadership to extract the current month only and let the existing rate engine model the rest of the year, rather than trying to read a full history off one document. Then I verified it against 28 real billing cycle observations, preserved exactly as they were, and compared field by field. It went live on July 23, 2026.\n\n What it produced\n\n The reader costs about one cent per bill. That is the whole reasoning behind the build. A hosted GPU model charges by the hour whether bills arrive or not, so the cost is fixed and the ceiling is the hardware. Paying per bill means the cost tracks the business, and at our volumes it is a fraction of the alternative.\n\n Ownership was worth more than the cost. The prompt, the crop, the voting rule, and the gate are all ours and all readable. When a number looks wrong I can open the pipeline and see which stage produced it, which was impossible before. One more external dependency went away with it.",
   "links": [
    "group-ai-features-shipped"
   ],
   "tags": [
    "case"
   ],
   "url": "/work/bill-reader/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "case-vendor-decision",
   "title": "A roof-data vendor decision",
   "group": "Integrations",
   "body": "Solentrex, August 2026\n\n A roof-data vendor decision, proven that afternoon.\n\n I sat on the vendor call, recommended replacing our roof-data provider outright, and had running code against their test environment before the end of the day.\n\n Buy vs buildVendor decisionRoof geometryProof of conceptOne day\n\nWhat it was\n\nEvery solar proposal our platform produces starts with the shape of a roof. Planes, pitches, directions, and obstructions decide where panels fit, how much energy the system makes, and what the customer pays. If the roof data is wrong, everything downstream is wrong in a way nobody catches until a crew is standing on the roof.\n\nWe had two ways to get that data. We were building our own roof model in house, and we were buying measurements from commercial vendors. I had already put months into the in-house side and benchmarked our own model against vendor output on real homes. That is why the decision that followed was informed rather than a guess. I knew where our model held up, where it fell apart, and what a vendor would have to beat.\n\nWhat I did\n\nOn 2026-08-12 I sat on an intro call with a commercial roof-data provider. I did two things that were not on the agenda. I put the limits of our in-house roof model on the table up front, with the mitigations already in place, rather than leave a vendor to find them. Then I staked the product position we would hold: 2D measurements as the standard path, 3D only on demand.\n\nThe recommendation on the table was a hybrid. Keep our geometry, layer the vendor on top. I recommended against it and called for a full swap of the roof-data provider, with the designer rebuilt around the vendor's geometry instead of translated into ours. Half a swap leaves you maintaining two models and reconciling them forever.\n\nThen I proved it the same afternoon. I wrote a standalone bench against the vendor's test environment and pulled real measurement data through it. Not a slide deck. Running code, on the day of the call.\n\nWhat it produced\n\nA written decision with the reasoning attached, made in a day instead of a quarter, and a bench that has been running since. It parses all three of the vendor's measurement formats. Recomputing roof areas from their raw geometry matches their own reported numbers to 0.00% across 16 real homes. It draws wireframes over aerial imagery, packs panels with live count, kilowatts, and expected production, and times an outline-grade instant design lane at three to eight seconds.\n\nThe useful part is not the speed. It is that the decision was testable before it was expensive. Nothing was committed on a vendor's claim.",
   "links": [
    "group-integrations"
   ],
   "tags": [
    "case"
   ],
   "url": "/work/vendor-decision/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "case-identity-rebuild",
   "title": "Identity rebuild: buy WorkOS, ship internally",
   "group": "Platform engineering",
   "body": "Solentrex, August 2026\n\n Identity rebuild: buy WorkOS, ship internally.\n\n I bought the identity layer instead of building it, kept the work in house, and cut production over in two days.\n\n WorkOSBuy vs buildAuth cutoverMulti-tenant securityProduction\n\nWhat it was\n\nThe platform ran on two applications with two separate user stores, and identity needed a single authority. Bringing in an outside team for stage one of a rebuild was estimated at three to four months.\n\nIdentity is a bad place to be inventive. It is a solved problem with a market of vendors who do nothing else, and every hour spent inventing our own version is an hour not spent on the product customers actually buy.\n\nWhat I did\n\nI locked four decisions before anyone wrote code.\n\nBuy, not build. WorkOS owns authentication. We own the record of who a person is inside our own database, so removing someone archives their history instead of orphaning it.\n\nNo authorization engine in version one. Rewriting permissions at the same time as authentication doubles the blast radius. One authority for identity first, permissions second.\n\nSSO deferred until a customer asks for it. It carries an ongoing cost and ongoing maintenance, and we were not going to carry it ahead of the ask.\n\nHard no on cross-tenant visibility. No company sees another company's data, with no convenience exceptions.\n\nI delivered it in house instead. Production cutover ran on 2026-08-13 and 08-14: seven pull requests, including an auth layer touching 318 files. Before anything went near production I made sure every production database had long-term backup retention and a fresh export in hand, so the rollback path existed before it was needed.\n\nTwo days later I ran a cross-tenant read and write security sweep across production and shipped hardening from it. On 08-18 the cross-app auth bridge went live, so both applications sign a person in once.\n\nWhat it produced\n\nOne login, one authority on access, and a rollback path on every production database. The work landed in days, from inside the company. The scope also held, which is the part I care about: no authorization engine got built, and no single sign-on was built ahead of a customer asking for it. Both are written down as deferred, with the trigger that reopens them.",
   "links": [
    "group-platform-engineering"
   ],
   "tags": [
    "case"
   ],
   "url": "/work/identity-rebuild/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "build-a-rental-app-for-a-family-member",
   "title": "A rental app for a family member",
   "group": "Personal builds",
   "body": "A family member runs a ~50-property rental portfolio, mostly from a phone. I built the app that runs it. It tracks expected rent against received rent, leases and tenant history, move-in and move-out photos, improvements, and bills. It never moves money. Payments stay on the rail they were already on, and the app tracks and flags.\n\nTwo AI pieces, both fenced. The importer reads a messy spreadsheet and proposes rows, and a human approves every row before anything is saved. The assistant answers questions over the ledger and may never invent a number, because every figure it says comes from the database rather than the model.\n\nCost is fenced too. Market rent data comes from RentCast behind a cache with a self-enforced monthly ceiling that is checked before every outbound call, and the remaining quota is visible on screen. The rule I designed to is that no button on screen can cost money. Access uses Clerk, scoped by organization, so the identity provider authenticates and our own database decides what that person can see. It runs on a single Cloudflare Worker with D1, R2, and a monthly cron job that writes to an audit table.\n\nThe vendor work mattered as much as the code. Three data vendors were rejected on license terms rather than capability, RentCast was kept under a caching design its license permits, and Street View imagery was dropped once it was clear the license did not allow storing the image. It is live at myonealproperties.com.",
   "links": [
    "group-personal-builds",
    "entry-a-rental-app-for-a-family-member",
    "entry-cost-fences-and-vendor-licensing-on-a-personal-budget",
    "entry-identity-and-access-on-the-personal-side"
   ],
   "tags": [
    "personal"
   ],
   "url": "/personal/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "build-a-household-finance-assistant",
   "title": "A household finance assistant",
   "group": "Personal builds",
   "body": "A private finance app for our household: balance sheet, cash flow, recurring payments, receipts, and a chat assistant. A deterministic engine doing integer-cent math owns every number on screen. The model explains and navigates. It does not calculate.\n\nThe privacy rule is enforced by the operating system, not by instructions. The language model runs locally under its own isolated OS user account, so real financial data never reaches a cloud model. Sign-in is passkeys and Face ID. The bank and brokerage connectors sit behind a gate that fails closed, so missing configuration means it refuses to connect rather than quietly falling back. It has a self-deploying release pipeline, so fixes ship without me sitting at a terminal.",
   "links": [
    "group-personal-builds"
   ],
   "tags": [
    "personal"
   ],
   "url": "/personal/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "build-a-second-memory-vault",
   "title": "A second memory vault",
   "group": "Personal builds",
   "body": "The AI memory system I built for work is a method, so I run it twice. The personal vault holds 25 documents, loaded by the same nine hook scripts and routed by folder, with a firewall between work and personal context so neither loads in the other. It writes its own session summary at the end of every session.",
   "links": [
    "group-personal-builds",
    "entry-a-second-memory-vault",
    "entry-nine-lifecycle-hook-scripts-across-six-events"
   ],
   "tags": [
    "personal"
   ],
   "url": "/personal/",
   "kind": "case",
   "when": ""
  },
  {
   "id": "brand-visual-theme-and-atmosphere",
   "title": "Brand kit: Visual Theme and Atmosphere",
   "group": "Website and brand",
   "body": "Quiet, warm, exact. The site is a record, not a pitch. Warm paper background, near-black ink, one deep green accent used sparingly. Big serif display type set at calm sizes; a humanist sans for reading; a monospace for small labels and captions. Generous vertical space between sections and hairline rules instead of boxes. One animated element only: the knowledge-base graph. Everything else is still.\n\nIf a line of copy sounds impressive, cut it and let the fact carry it. If a component looks like a template, remove the decoration until it does not.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "brand-agent-prompt-guide",
    "brand-color-palette-and-roles"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "brand-color-palette-and-roles",
   "title": "Brand kit: Color Palette and Roles",
   "group": "Website and brand",
   "body": "Light (default):\n\n| Token | Value | Role |\n|---|---|---|\n| --paper | #FAF8F4 | page background |\n| --paper-2 | #F3F0EA | framed panels, code, figure backgrounds |\n| --ink | #14181C | headings, primary text, primary button fill |\n| --ink-2 | #2B323A | body copy |\n| --muted | #5B6470 | captions, secondary text, labels |\n| --rule | #E4E0D8 | hairlines, borders |\n| --rule-2 | #D6D1C7 | stronger borders, ghost button border |\n| --accent | #0F5C4A | eyebrows, links inside prose, one emphasis per section, graph nodes |\n| --accent-soft | #E6F0EC | accent tint for callouts (rare) |\n\nDark:\n\n| Token | Value |\n|---|---|\n| --paper | #0F1214 |\n| --paper-2 | #151A1D |\n| --ink | #ECE8E1 |\n| --ink-2 | #D3CEC5 |\n| --muted | #98A1AA |\n| --rule | #242A2F |\n| --rule-2 | #30373D |\n| --accent | #7FD1B4 |\n| --accent-soft | #12251F |\n\nRules: text on paper meets WCAG AA (ink 15.5:1, ink-2 11.9:1, muted 5.4:1 on --paper). Accent is never used for body text. Never introduce a second hue. No gradients. No gold, no amber, no neon.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "brand-component-stylings",
    "brand-typography-rules"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "brand-typography-rules",
   "title": "Brand kit: Typography Rules",
   "group": "Website and brand",
   "body": "- Display: Fraunces (variable), weight 400, optical size 96, letter-spacing -0.015em, line-height 1.05. Used for h1, h2, big numbers, and the lead sentence. Italic 400 for one emphasized phrase per page at most.\n- Body: Inter, 17px, line-height 1.6, color --ink-2. Max measure 64ch.\n- Labels and captions: system monospace, 11 to 13px, letter-spacing 0.06 to 0.12em, uppercase for eyebrows only.\n- Scale: h1 clamp(40px, 6.2vw, 72px); h2 clamp(30px, 4vw, 46px); lead clamp(20px, 2.2vw, 26px); h3 19px semibold; small 14px; caption 13px.\n- Numbers use Fraunces at display size with a smaller muted suffix (%, /100, M).\n- No bold display type. No all-caps headings. No text shadows.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "brand-layout-principles",
    "brand-component-stylings",
    "brand-responsive-behavior"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "brand-component-stylings",
   "title": "Brand kit: Component Stylings",
   "group": "Website and brand",
   "body": "- Buttons: pill, 12px by 20px, 15px semibold. Primary: --ink fill, --paper text. Ghost: transparent, --rule-2 border, --ink text. Hover lifts 1px. Never more than two buttons together.\n- Cards: 1px --rule border, radius 10px, 22px padding, paper fill. Big display number top-left, h3, 14.5px muted description, small accent \"Read\". Hover: border to --rule-2, 2px lift, soft shadow.\n- Number tiles: grid with shared 1px --rule borders, radius 10px on the outer frame only. Display number, then 14px muted label.\n- Plain lists: no bullets; 1px --rule dividers; bold lead-in in --ink, rest in --ink-2.\n- Eyebrow: mono 12px uppercase --accent, 14px below it.\n- Framed figure: --paper-2 fill, 1px --rule, radius 14px, soft shadow; caption bar with mono text and a top rule.\n- Callout (rare aside): --paper-2 fill, 1px --rule, 3px --accent left rule, radius 10px.\n- Tags: mono 12px, pill, --rule-2 border, --muted text.\n- Nav: sticky, 60px, translucent paper with blur, hairline bottom rule, brand in Fraunces 19px, links 14px muted.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "brand-depth-and-elevation",
    "brand-layout-principles"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "brand-layout-principles",
   "title": "Brand kit: Layout Principles",
   "group": "Website and brand",
   "body": "- Max content width 1120px, gutters clamp(20px, 4vw, 48px).\n- Sections padded clamp(64px, 9vw, 120px) top and bottom, separated by a hairline rule.\n- Two-column sections use a 5/7 split: short display heading left, prose or figure right. Collapse to one column under 860px.\n- One idea per section. Heading, at most two paragraphs, then one component.\n- Prose pages: single column, max 68ch, h2 in Fraunces 28px.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "brand-depth-and-elevation"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "brand-depth-and-elevation",
   "title": "Brand kit: Depth and Elevation",
   "group": "Website and brand",
   "body": "Almost flat. Two levels only: the page, and framed figures or hovered cards, which get one soft shadow: 0 1px 2px rgba(20,24,28,.05), 0 18px 40px -24px rgba(20,24,28,.18). No inner shadows, no glass panels, no layered blurs except the sticky nav.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "brand-do-s-and-don-ts"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "brand-do-s-and-don-ts",
   "title": "Brand kit: Do's and Don'ts",
   "group": "Website and brand",
   "body": "Do: write in first person, short sentences, numbers over adjectives; give every claim a date or a count; keep the accent to one job per screen; respect prefers-reduced-motion; keep pages fast (no frameworks, no heavy libraries).\n\nDon't: em dashes; emojis; superlatives; quotes from executives or vendors; dollar figures for company spend (percentages only); private hostnames or IPs; family or friend names; stock photos; icons for their own sake; hero slogans; second accent colors; dark mode as an identity (it is a preference); anything addressed to a specific employer.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "brand-agent-prompt-guide",
    "brand-responsive-behavior"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "brand-responsive-behavior",
   "title": "Brand kit: Responsive Behavior",
   "group": "Website and brand",
   "body": "- Breakpoints: 860px (grids to one column), 760px (nav collapses to a menu button; number tiles two-up).\n- Type scales with clamp(); nothing below 14px except captions at 13px.\n- Figures keep aspect via a fixed height clamp(360px, 52vw, 560px); labels reposition by percentage.\n- Tables scroll inside their own container; the page never scrolls sideways.\n- Touch targets at least 34px.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "brand-agent-prompt-guide"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "brand-agent-prompt-guide",
   "title": "Brand kit: Agent Prompt Guide",
   "group": "Website and brand",
   "body": "Paste this with the file:\n\n\"Build the page using DESIGN.md. Use the tokens as CSS custom properties exactly as named. Fraunces for display, Inter for body, monospace for labels. Warm paper background, one green accent, hairline rules, no boxes unless the component list says so. First person, plain English, no em dashes, no superlatives, no dollar spend figures. Every case page follows: What it was, What I did, What it produced. Verify at 1440 and 390 wide, and check prefers-reduced-motion. If a design choice is not covered here, choose the quieter option.\"",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "entry-brand-kit-and-design-system"
   ],
   "tags": [
    "brand-kit"
   ],
   "url": "/brand/",
   "kind": "brand",
   "when": ""
  },
  {
   "id": "tool-claude-code",
   "title": "Claude Code",
   "group": "Tools",
   "body": "The operating environment itself: 9 lifecycle hooks, 41 project directories, 274 sessions, the worker fleet, golden testing, deep-research workflows",
   "links": [
    "entry-usage-footprint",
    "tool-claude-design",
    "tool-claude-haiku"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-claude-opus",
   "title": "Claude Opus",
   "group": "Tools",
   "body": "Judgment seats: management, security, architecture, review, and implementation at medium effort on the access rebuild",
   "links": [
    "entry-model-and-effort-policy",
    "tool-claude-fable",
    "tool-claude-sonnet"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-claude-sonnet",
   "title": "Claude Sonnet",
   "group": "Tools",
   "body": "Subagent fan-outs of up to about 105 agents, bounded implementation, creative concept generation",
   "links": [
    "entry-model-and-effort-policy",
    "tool-claude-design"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-claude-fable",
   "title": "Claude Fable",
   "group": "Tools",
   "body": "Design and interface work, mockups, screen-to-data archaeology, verifier seats; never in security lanes by policy",
   "links": [
    "tool-claude-design",
    "tool-claude-haiku"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-claude-haiku",
   "title": "Claude Haiku",
   "group": "Tools",
   "body": "Banned by written policy, listed here because the policy is the point",
   "links": [
    "tool-claude-design",
    "tool-claude-sonnet"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-claude-design",
   "title": "Claude Design",
   "group": "Tools",
   "body": "Website mockup rounds and the brand design system page",
   "links": [],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-openai-codex-cli",
   "title": "OpenAI Codex CLI",
   "group": "Tools",
   "body": "Independent reviewer pinned to an exact staged tree, transcript extraction in a read-only sandbox, primary builder of two personal apps, second reader of the same shared memory",
   "links": [
    "tool-azure-openai-and-document-intelligence",
    "entry-standing-adversarial-verifier-seats"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-openai-o3-gpt-5-gpt-4-1-gpt-4o",
   "title": "OpenAI o3, GPT-5, GPT-4.1, GPT-4o",
   "group": "Tools",
   "body": "Bill chart reading, compared head to head on one corpus; realtime voice and the importer in the rental app",
   "links": [
    "tool-azure-openai-and-document-intelligence",
    "entry-head-to-head-against-a-vendor-derived-baseline"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-azure-openai-and-document-intelligence",
   "title": "Azure OpenAI and document intelligence",
   "group": "Tools",
   "body": "Inherited bill extraction and image analysis paths inside the company tenant",
   "links": [
    "tool-azure"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-google-gemini-and-its-speech-models",
   "title": "Google Gemini and its speech models",
   "group": "Tools",
   "body": "Tree-shading validation in the production designer, image upscaling, narration for every ad and promo",
   "links": [],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-local-qwen-via-llama-cpp-and-ollama",
   "title": "Local Qwen via llama.cpp and Ollama",
   "group": "Tools",
   "body": "The only model allowed near real financial data, under an isolated OS user; a vision model in an early personal prototype",
   "links": [],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-pytorch-hugging-face-transformers-mask2former-dinov2-sam2-gr",
   "title": "PyTorch, Hugging Face transformers, Mask2Former, DINOv2, SAM2, GroundingDINO, YOLO",
   "group": "Tools",
   "body": "Roof segmentation training and gating, geometry model experiments, and the shipped designer stack run locally",
   "links": [
    "entry-roof-face-segmentation-model-and-its-training-flywheel"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-blender-blenderproc-cgal-cadquery",
   "title": "Blender, BlenderProc, CGAL, CadQuery",
   "group": "Tools",
   "body": "Synthetic roof generation and parametric CAD",
   "links": [
    "entry-synthetic-roof-generator-and-a-geometry-model"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-fiftyone-shapely-scikit-image-opencv-laspy-rasterio-numpy",
   "title": "FiftyOne, shapely, scikit-image, OpenCV, laspy, rasterio, NumPy",
   "group": "Tools",
   "body": "ML observability, geometry, elevation benches, deterministic image masks",
   "links": [],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-model-context-protocol-servers",
   "title": "Model Context Protocol servers",
   "group": "Tools",
   "body": "API workspace, three browser-automation servers, mail, meeting transcripts, identity control plane, custom local-stack bridge",
   "links": [
    "group-ai-environment"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-playwright",
   "title": "Playwright",
   "group": "Tools",
   "body": "Golden testing, request-counting measurement, throttled performance harnesses, proof video and marketing capture, never visual judgment",
   "links": [
    "entry-end-to-end-and-measurement-harnesses",
    "entry-golden-testing"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-postman",
   "title": "Postman",
   "group": "Tools",
   "body": "Source of truth for lender and tariff vendor requests before anything is tested live",
   "links": [
    "entry-lender-rate-handling-root-caused-and-shipped"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-angular-net-core-sql-server",
   "title": "Angular, .NET Core, SQL Server",
   "group": "Tools",
   "body": "The platform: two front ends, seven services, one production database of record",
   "links": [
    "entry-fully-isolated-local-production-mirror",
    "group-ai-environment"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-python-fastapi-celery",
   "title": "Python, FastAPI, Celery",
   "group": "Tools",
   "body": "AI services, benchmarks, audit scripts, extraction pipelines",
   "links": [],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-azure",
   "title": "Azure",
   "group": "Tools",
   "body": "Production estate: clusters, registry, static hosting, DNS, functions, storage, monitoring, key management, SQL, cost management, identity",
   "links": [
    "entry-hosting-and-dns-migration"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-cloudflare-workers-d1-r2-pages-queues-access",
   "title": "Cloudflare Workers, D1, R2, Pages, Queues, Access",
   "group": "Tools",
   "body": "Personal builds, the executive review site, and this site",
   "links": [
    "entry-marketing-site-rebuilt-in-a-week"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-workos-and-clerk",
   "title": "WorkOS and Clerk",
   "group": "Tools",
   "body": "Identity: WorkOS for the platform, Clerk for the rental app",
   "links": [
    "entry-tool-servers-wired-into-the-agents",
    "build-a-rental-app-for-a-family-member"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-hubspot-and-salesforce",
   "title": "HubSpot and Salesforce",
   "group": "Tools",
   "body": "Marketing platform migrated off and kept for CRM use; Salesforce used daily in an earlier sales coordination role",
   "links": [],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-docker-and-colima",
   "title": "Docker and Colima",
   "group": "Tools",
   "body": "The local stack, eight containers on pinned ports",
   "links": [],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-launchd-tmux-syncthing-private-mesh-networking",
   "title": "launchd, tmux, Syncthing, private mesh networking",
   "group": "Tools",
   "body": "Keeping agent seats and services alive across reboots, mirroring memory, reaching the workstation safely",
   "links": [],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-three-js-cesium-photorealistic-3d-tiles",
   "title": "three.js, Cesium, photorealistic 3D tiles",
   "group": "Tools",
   "body": "The homepage 3D roof hero, the roof engine viewer and rendering proofs of concept",
   "links": [
    "entry-3d-roof-viewer-proofs-of-concept",
    "entry-animated-3d-roof-hero"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-hyperframes-remotion-ffmpeg",
   "title": "HyperFrames, Remotion, ffmpeg",
   "group": "Tools",
   "body": "Ads, narrated promos and the parked programmatic video pipeline",
   "links": [
    "entry-programmatic-video-pipeline-parked"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "tool-fellow-and-todoist",
   "title": "Fellow and Todoist",
   "group": "Tools",
   "body": "Meeting transcript extraction into decisions and action items; the action index that mirrors the memory vault",
   "links": [
    "entry-meeting-transcript-pipeline"
   ],
   "tags": [
    "tool"
   ],
   "url": null,
   "kind": "tool",
   "when": ""
  },
  {
   "id": "entry-the-brain-shared-memory-every-agent-reads-and-writes",
   "title": "The brain: shared memory every agent reads and writes",
   "group": "AI environment",
   "body": "Every AI session at work loads the same plain markdown vault at startup and writes findings back before it ends. It holds 2,211 files and about 4.4 million words: one living document per project (508), append-only daily logs (140), 1,119 discovery notes, 34 decision records, 48 failure post-mortems, a manifest, redaction rules and a one-page status board. I built it on April 3, 2026 after diagnosing the previous setup as 116 files with no connections between them, and the operating spec every agent reads is 425 lines with a 2,326-line conventions file under it.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "entry-a-second-memory-vault",
    "build-a-second-memory-vault",
    "month-apr-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026 to now"
  },
  {
   "id": "entry-nine-lifecycle-hook-scripts-across-six-events",
   "title": "Nine lifecycle hook scripts across six events",
   "group": "AI environment",
   "body": "About 916 lines of shell and Node do the context work that a model cannot be trusted to do for itself. They inject the right memory based on the working directory so work and personal context never mix, print a handshake phrase that proves the context was read rather than skimmed, save state before the session runs out of room, log every file access as JSONL, and warn when 15 minutes pass with no write back to memory. 321 hook fires are stamped in the log since August 3, 2026.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "case-identity-rebuild",
    "entry-a-second-memory-vault",
    "month-apr-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026 to now"
  },
  {
   "id": "entry-the-manager-and-worker-playbook-17-non-negotiables",
   "title": "The manager and worker playbook, 17 non-negotiables",
   "group": "AI environment",
   "body": "A written protocol for running many AI coding sessions as one team: an orchestrator that talks to me, managers that gate and decide, workers that execute. State passes through append-only files watched by event-driven monitors, about a three second handoff, and every gate is on intent, with the worker posting its diff and pausing rather than assuming approval. 177 state-file channels and 77 kickoff prompts exist; one manager agent ran seven workers for about 14 hours on August 1, 2026.",
   "links": [
    "group-ai-environment",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-standing-adversarial-verifier-seats",
   "title": "Standing adversarial verifier seats",
   "group": "AI environment",
   "body": "Every multi-agent program keeps one permanent seat whose only job is to attack the load-bearing claims, including the manager's own. In a single day it refuted four of the manager's claims, which produced the standing rule that a manager who is never contradicted is not being verified. Code that touches money gets three reviews in parallel, standard, blind and adversarial, with a blocking versus non-blocking severity gate, and a second vendor's CLI reviews the same staged tree pinned to its exact hash.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "May to August 2026"
  },
  {
   "id": "entry-reboot-proof-agent-fleet",
   "title": "Reboot-proof agent fleet",
   "group": "AI environment",
   "body": "A 236-line reconciler under the operating system service manager keeps declared agent seats alive from a manifest naming each seat, its kickoff prompt, whether it is enabled, and which model it runs. It reconciles every 120 seconds, restarts dead seats, caps crash loops at four an hour, writes an atomic status file, and holds the machine awake only while seats are running. 21 seats are registered, with six of six dry-run receipts before it went live. It took about two hours to build.",
   "links": [
    "group-ai-environment",
    "case-vendor-decision",
    "entry-the-brain-shared-memory-every-agent-reads-and-writes",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "July to August 2026"
  },
  {
   "id": "entry-golden-testing",
   "title": "Golden testing",
   "group": "AI environment",
   "body": "A named QA method. Drive the real running application, judge by network calls, database rows and API responses rather than screenshots, trace the fault to a file and a line, then hand it to a fresh adversarial agent to verify. The agent captures its own login headlessly, so no human hands it a token, and the loop stays in fix mode until the measurement changes. A worker who had never seen the problem shipped a real fix end to end from the written method alone.",
   "links": [
    "group-ai-environment",
    "case-identity-rebuild",
    "entry-end-to-end-and-measurement-harnesses",
    "month-jun-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "June 2026 to now"
  },
  {
   "id": "entry-129-written-rules-for-ai-agents",
   "title": "129 written rules for AI agents",
   "group": "AI environment",
   "body": "120 feedback rules plus reference and project rules cover model policy, agent conduct, production safety, environment isolation, review gates, and how results are reported to someone who does not read code. An index file is injected into every session so the rules load before work starts. They live in the shared vault rather than one tool's private memory, which is what lets a second vendor's CLI obey the same rules.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "case-two-tools-one-brain",
    "case-vendor-decision",
    "entry-one-instruction-file-two-ai-tools-one-memory",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to August 2026"
  },
  {
   "id": "entry-model-and-effort-policy",
   "title": "Model and effort policy",
   "group": "AI environment",
   "body": "Judgment seats such as management, security, architecture and review run the largest models. Wide research fan-outs run a cheaper one, and the smallest model is never used. Effort is set per lane, high for research and medium for implementation, because implementation agents over-engineer at high effort. The policy exists because two fan-outs of about 105 agents inherited an expensive model and consumed 3.4 million tokens in a day.",
   "links": [
    "group-ai-environment",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "July to August 2026"
  },
  {
   "id": "entry-one-instruction-file-two-ai-tools-one-memory",
   "title": "One instruction file, two AI tools, one memory",
   "group": "AI environment",
   "body": "A single canonical instructions file is symlinked under each tool's expected filename, so two vendors' coding CLIs read the same rules and write findings back into the same vault. I verified it end to end: the second tool produced the handshake phrase while citing a rule that exists only in the vault, and the two tools exchanged tokens through a relay file with no private channel between them. A remote-control daemon keeps the second CLI available on the workstation, started at login and rechecked every five minutes, with no network listener exposed.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "case-two-tools-one-brain",
    "case-vendor-decision",
    "case-identity-rebuild",
    "entry-a-second-memory-vault",
    "month-jul-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-deep-research-runs-with-adversarial-claim-checking",
   "title": "Deep research runs with adversarial claim checking",
   "group": "AI environment",
   "body": "Multi-stage runs of about 100 agents: searchers, fetchers, claim extractors, three independent refuters per claim, then synthesis. I used it four times, including 105 agents over 23 sources on memory-system design, where 19 claims were confirmed three votes to zero, and 102 agents on procedural roof generation, where 24 claims were confirmed. The failure modes are written down next to the results, including one run whose synthesis had to be recovered from the run journal.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "case-vendor-decision",
    "entry-height-fusion-program-reported-as-negative-results",
    "month-jul-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-the-worker-prompt-preamble",
   "title": "The worker prompt preamble",
   "group": "AI environment",
   "body": "Every worker prompt opens with the same preamble, distilled from the published system prompts of six coding agents and from my own incidents. It requires loading memory first, grepping every caller and reading every return path before editing, labeling contract changes, naming constants with their units, running the code instead of reading it, and quoting actual against expected. Each rule cites the real failure that produced it.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "month-apr-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026 to now"
  },
  {
   "id": "entry-reporting-standards-for-ai-output",
   "title": "Reporting standards for AI output",
   "group": "AI environment",
   "body": "Rules for how agents report to a decision maker who does not read code: results as visual artifacts served on one address, plain English, only a finished result or a real decision surfaced, numbered action boxes, and a one-line state stamp at the end of every turn. They exist because dense engineering updates are the main reason work stalls at review.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "month-jul-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026 to now"
  },
  {
   "id": "entry-tool-servers-wired-into-the-agents",
   "title": "Tool servers wired into the agents",
   "group": "AI environment",
   "body": "Agents reach outside systems through Model Context Protocol servers rather than one-off scripts: an API workspace that is the source of truth for request shapes, three parallel browser-automation servers, mail with read and draft only, meeting transcripts, the identity vendor's control plane, and a custom bridge to my local stack. Seven or more integrations, each with a rule attached, the main one being to read the API collection before guessing a parameter.",
   "links": [
    "group-ai-environment",
    "tool-model-context-protocol-servers",
    "case-memory-system",
    "case-platform-takeover",
    "case-vendor-decision",
    "case-identity-rebuild",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to August 2026"
  },
  {
   "id": "entry-meeting-transcript-pipeline",
   "title": "Meeting transcript pipeline",
   "group": "AI environment",
   "body": "Three Python scripts, 285 lines, plus an agent skill. One returns the most recent meetings correctly sorted, another pulls a transcript to disk so 37,000 characters never enter a chat window, then a fixed prompt runs it through a read-only sandboxed CLI and returns decisions, action items, blockers, engineering items and money and people, each with an owner. One run returned seven action items and five decisions from a single meeting.",
   "links": [
    "group-ai-environment",
    "month-jul-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-status-board-parked-ledger-task-sync",
   "title": "Status board, parked ledger, task sync",
   "group": "AI environment",
   "body": "Every session reads a one-page status board first and rewrites its own block before it ends, and a mechanical end-of-session hook files a note if the daily log gets more than 30 minutes ahead of the board. A parked ledger holds suspended work so nothing is dropped quietly. The task manager was reset in July 2026: 61 stale tasks closed and 53 synced with stable IDs by a 425-line script that pulls, pushes, renders, logs and verifies against a registry.",
   "links": [
    "group-ai-environment",
    "case-memory-system",
    "entry-the-brain-shared-memory-every-agent-reads-and-writes",
    "entry-paid-social-campaign-board",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "July to August 2026"
  },
  {
   "id": "entry-usage-footprint",
   "title": "Usage footprint",
   "group": "AI environment",
   "body": "The environment is used daily, not demonstrated once. 41 project directories, 274 top-level sessions and 2,584 transcript files including subagents, 940 logged session starts across 140 daily logs, and 425 prompt and state files, measured on August 18, 2026.",
   "links": [
    "group-ai-environment",
    "entry-the-brain-shared-memory-every-agent-reads-and-writes",
    "entry-a-second-memory-vault",
    "month-aug-2026"
   ],
   "tags": [
    "measured"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to August 2026"
  },
  {
   "id": "entry-in-house-utility-bill-reader",
   "title": "In-house utility bill reader",
   "group": "AI features shipped",
   "body": "A solar proposal starts with a utility bill, so the first number in the funnel decides every number after it. I replaced a vendor model we could not inspect with a pipeline that locates the chart on two votes, refuses anything that is not a usage chart, reads the bars with three concurrent votes and accepts the first agreeing pair, then puts the result through a plausibility gate and returns dated observations that were actually verified rather than a whole year or nothing. About 137 commits across the AI service and the CRM shipped it, and one live project shows 28 deterministic billing-cycle observations spanning 2022 to 2024.",
   "links": [
    "group-ai-features-shipped",
    "case-bill-reader",
    "case-vendor-decision",
    "entry-bill-reader-model-bake-off-and-generalization-gauntlet",
    "entry-bill-reader-latency-program",
    "month-jul-2026"
   ],
   "tags": [
    "live in production since July 23, 2026"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-server-side-energy-profile-engine",
   "title": "Server-side energy profile engine",
   "group": "AI features shipped",
   "body": "Four copies of energy math living in the browser became one tested server engine with two doors: an address door that builds a seasonal curve from the tariff vendor, and a bills door that reconciles the reader's output onto twelve cells with per-cell provenance. A deterministic constrained fit, written as a pure-Python solver, makes the curve integrate over every real bill window to the exact printed total. 49 to 55 unit tests cover it, cost always comes from the vendor rather than rate times usage, and it was verified live end to end.",
   "links": [
    "group-ai-features-shipped",
    "case-bill-reader",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-automatic-designer-speed-progress-and-equipment-awareness",
   "title": "Automatic designer: speed, progress and equipment awareness",
   "group": "AI features shipped",
   "body": "About 200 commits made the automatic solar designer feel like a product: per-phase progress events relayed from the Python pipeline to an animated modal, roof geometry precomputed at imagery time, a reliable imagery cache with awaited uploads and a completion manifest, and a tree-validation call hoisted off the critical path to remove 10 to 15 seconds with byte-identical output as the acceptance rule. The dialog was rebuilt around approved equipment combinations, with a DC optimizer picker that appears only when the chosen inverter requires one and a battery-size recommendation. A test bench replays placement against captured roofs so a change can be judged before it ships.",
   "links": [
    "group-ai-features-shipped",
    "case-memory-system",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "March to August 2026"
  },
  {
   "id": "entry-design-insights-panel",
   "title": "Design insights panel",
   "group": "AI features shipped",
   "body": "Performance metrics on top, design specifications below, inverter metrics behind a caret with within-limit, balanced and clipping states, an efficiency ratio, carbon savings as a real quantity, a guard against dividing by zero on offset, per-metric hover explanations, a raw-numbers toggle and a copy-all that pastes into a spreadsheet as two columns. The hardcoded three-option optimizer dropdown became a data-driven per-inverter picker, so the options on screen and the values we send the lender both come from the inverters each design actually supports.",
   "links": [
    "group-ai-features-shipped",
    "case-brand-kit-and-content",
    "case-lender-certification",
    "month-jun-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "June 2026"
  },
  {
   "id": "entry-standalone-designer-api-validated-against-15-metered-systems",
   "title": "Standalone designer API, validated against 15 metered systems",
   "group": "AI features shipped",
   "body": "Address in, panel placement out, with system size, annual and monthly production, per-panel output, shading factors, roof segments and an interactive interface, running in about 1.2 seconds per address. Validated against 15 real metered systems it produced a mean bias of plus 1.0% and a mean absolute error of 6.6%, against 6.8% for the market-leading tool and 4.7% for the public reference model, and the exercise found and removed a double-counted temperature derate that had been under-predicting by 12.4%. It was superseded in June by the clean rebuild, and its 110-address validation set was reused.",
   "links": [
    "group-ai-features-shipped",
    "case-vendor-decision",
    "month-apr-2026"
   ],
   "tags": [
    "built in about three hours, later superseded"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026"
  },
  {
   "id": "entry-incentives-on-proposals",
   "title": "Incentives on proposals",
   "group": "AI features shipped",
   "body": "Incentives display for every financing type and are never applied to price, because who claims an incentive, the installer or the homeowner, is not knowable from the software. I built the engine and admin configuration in house because the vendor path did not cover battery deals, and the total is presented as a possible total with the qualification attached. It shipped across a dozen pull requests and unblocked an installer in a new state.",
   "links": [
    "group-ai-features-shipped",
    "case-platform-takeover",
    "case-lender-certification",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-every-us-state-live-in-the-incentives-admin",
   "title": "Every US state live in the incentives admin",
   "group": "AI features shipped",
   "body": "I took the incentives admin's utility coverage from a single state to all 56 states and territories. I shipped the state mapping, migration, filtered unique indexes and committed-only progress counters on the API side and a state-filtered dropdown in the admin, then ran the backfill from the tariff vendor's utility API in two passes with a hardened resume for the twelve largest states. It ended at 2,997 utilities and 3,226 state-to-utility links, and the sync button was specced asynchronous after I proved a synchronous one dies at the 30 second gateway timeout.",
   "links": [
    "group-ai-features-shipped",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-battery-only-deals-backup-planner-and-battery-as-design",
   "title": "Battery-only deals, backup planner and battery as design",
   "group": "AI features shipped",
   "body": "Battery-only became a first-class deal: a battery-only proposal page composed from shared components, a reserve dial that recomputes savings per position, an edit dialog rebuilt as a backup planner that turns duty-cycled appliance loads into one honest backup-time number, expansion-pack editing, and in August every battery option as its own design row with its own proposals. About 140 commits, with six pull requests clearing a four-agent hostile review with zero blockers. The storage dispatch work mattered most: the reserve dial now moves savings against the applied time-of-use plan rather than a default one.",
   "links": [
    "group-ai-features-shipped",
    "case-lender-certification",
    "month-aug-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "March to August 2026"
  },
  {
   "id": "entry-project-editing-model-and-the-cost-of-opening-a-project",
   "title": "Project editing model and the cost of opening a project",
   "group": "AI features shipped",
   "body": "I measured what opening one project actually did before changing anything: 28 endpoints and 161 calls in three minutes, much of it repeated fetches of data that had not changed. The fix was structural: proposals open, edit and switch in place inside one tab, a two-phase instant paint, inline rename with collision-safe naming, a prompt before closing the designer with unsaved work, and cache-first reads so a warm precompute row means no vendor call at all.",
   "links": [
    "group-ai-features-shipped",
    "case-vendor-decision",
    "entry-ground-cost-model-per-project",
    "month-aug-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-financing-details-tab-and-loan-status",
   "title": "Financing details tab and loan status",
   "group": "AI features shipped",
   "body": "Loan status refresh with eligibility traffic lights, an itemized system price breakdown, optimistic action buttons, an activity log with real display names, a webhook-driven timeline and case document upload with live updates. About 35 commits. A version banner became a mandatory update modal pushed over the live connection at deploy, so nobody is working from a stale bundle after a release.",
   "links": [
    "group-ai-features-shipped",
    "case-memory-system",
    "case-lender-certification",
    "month-may-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "March to May 2026"
  },
  {
   "id": "entry-deleting-what-nobody-opened",
   "title": "Proof-first deletion",
   "group": "AI features shipped",
   "body": "I removed unused code in about 30 commits, spanning screens, components, scheduler manifests and leftover assets, and proved before each deletion that nothing still called it. Deleting code you inherited is the cheapest reliability work there is, as long as you prove first that nothing calls it.",
   "links": [
    "group-ai-features-shipped",
    "entry-the-legacy-admin-application-measured-module-by-module",
    "month-aug-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "July to August 2026"
  },
  {
   "id": "entry-dark-mode-for-the-platform-measured-before-written",
   "title": "Dark mode for the platform, measured before written",
   "group": "AI features shipped",
   "body": "Before quoting the work I measured it: 233 stylesheets and 52,503 lines, of which 3,332 color declarations, about 81%, already route through one palette file, leaving 769 hardcoded across 70 files. The lever is a single variable pass in that one file, and the one real blocker is 169 color-function call sites plus a component library sitting on a prebuilt theme. The result was a five-phase plan and an honest estimate rather than a guess.",
   "links": [
    "group-ai-features-shipped",
    "month-aug-2026"
   ],
   "tags": [
    "plan only, no code"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-roof-face-segmentation-model-and-its-training-flywheel",
   "title": "Roof face segmentation model and its training flywheel",
   "group": "ML and models",
   "body": "A segmentation model fine-tuned to label roof pixels by face orientation, then turned into faces and vectorized into the editor's format, with the architecture chosen with the patent landscape in mind. Gates were harsh: labels verified before use, about a third of homes held out, and promotion only on a human decision after a multi-metric check. Held-out face agreement climbed 0.256 to 0.537 to 0.807 across the first three rounds and settled near 0.74 by round 12 on a corpus of 119 to 139 hand-traced homes, with the largest single gain coming from label quality rather than volume.",
   "links": [
    "group-ml-and-models",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "R&D, 24 gated retraining rounds"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-benchmark-against-certified-survey-wireframes",
   "title": "Benchmark against certified survey wireframes",
   "group": "ML and models",
   "body": "I scored the promoted model against certified aerial-survey wireframes on 16 real homes with the pass thresholds committed before any scoring. The verdict was 0 of 16 usable as drawn, 8 fixable with edits, 7 not usable, and mean edge error of 1.91 ft in one direction against 3.39 ft in the other, with 71 predicted faces against 221 true faces. It replaced a gut estimate of about 20% usable and ruled that the instant designer would be built on purchased certified data.",
   "links": [
    "group-ml-and-models",
    "case-memory-system",
    "case-vendor-decision",
    "entry-classical-line-extraction-bake-off-ten-variants",
    "month-aug-2026"
   ],
   "tags": [
    "R&D, published internally as a stop signal"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-head-to-head-against-a-vendor-derived-baseline",
   "title": "Head to head against a vendor-derived baseline",
   "group": "ML and models",
   "body": "On the same 27-home held exam, through a reconstruction conveyor whose own round-trip scored 0.979 so the comparison was near lossless, our model reached a median face agreement of 0.748 against 0.576 for planes derived from a large vendor's data, winning 20 of 27 homes, with corner accuracy of 0.750 against 0.549. The conclusion was narrow and useful: that particular ingredient is not a competitor's moat.",
   "links": [
    "group-ml-and-models",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "R&D, concluded"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-pre-registered-retrain-probe-with-a-statistical-test",
   "title": "Pre-registered retrain probe with a statistical test",
   "group": "ML and models",
   "body": "A retrain recipe was written down and adversarially reviewed before any compute was spent: a scale ladder plus horizontal flip and a 24-epoch schedule against the current recipe, five runs each, judged by a Welch test. The treatment beat the blessed model on all four frozen gates and doubled the score on the hardest estate home, 0.7276 against 0.7139 for the incumbent and 0.7149 for the control, one-sided p of 0.044.",
   "links": [
    "group-ml-and-models",
    "entry-running-the-ml-program-like-a-lab",
    "month-jul-2026"
   ],
   "tags": [
    "R&D, treatment adopted"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-classical-line-extraction-bake-off-ten-variants",
   "title": "Classical line-extraction bake-off, ten variants",
   "group": "ML and models",
   "body": "Ten pipeline variants were built and scored on the same 16-home answer key with the same grader: outline and straight skeleton, height-model mass splitting, edge and line detection, fusion, and multi-view confirmation. None produced a usable-as-drawn roof, and recall was the wall, with the detector seeing 124 of 220 certified faces. Lanes given a certified outline came back fixable 14 times out of 15 while model-outline lanes failed 7 of 8, which identified the building outline as the binding constraint and produced a three-tier product strategy.",
   "links": [
    "group-ml-and-models",
    "case-bill-reader",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "R&D, concluded"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-height-fusion-program-reported-as-negative-results",
   "title": "Height fusion program, reported as negative results",
   "group": "ML and models",
   "body": "A research pass over 23 sources with 25 claims put to a three-vote check, 17 confirmed and 8 killed, chose one fusion design, then the experiment killed it: seven-channel height scored 0.700 against a six-channel control at 0.733. A geometric solver constrained by roof laws lost 1 to 26 against the model's own interior carve. Two ablations scored identically to 16 digits, which proved height physics bought nothing except through structural choices, and that is written down where the next person will find it.",
   "links": [
    "group-ml-and-models",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "R&D, closed"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-triangulation-bench-and-hand-drive-roof-editor",
   "title": "Triangulation bench and hand-drive roof editor",
   "group": "ML and models",
   "body": "A geometry core with no machine learning in it: known camera positions, per-view homographies from vendor anchors, rays, crossings. Measured end to end at a median 3D error of 0.05 m with reprojection around two pixels at a ground sample distance near 3 cm. Then a draw-first editor where a person chains points on the overhead image and sets height by clicking guide lines in obliques, with auto-traced faces, pitch and azimuth and an isometric preview. Automated line detection and the hand-clicked face agreed on the same roof slope, and the browser math matches the Python to 13 decimal places under a 17-assertion suite.",
   "links": [
    "group-ml-and-models",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "R&D prototype on real vendor imagery"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-gpu-training-node",
   "title": "GPU training node",
   "group": "ML and models",
   "body": "A desktop GPU machine became a train-step-only node so the heavier dual-input recipe could train at all: the workstation builds the corpus and scores, the GPU machine trains, weights come back. Gradient checkpointing was mandatory to fit it in 8 GB, since the workstation could not run the backward pass for this model. Parity was proven before adoption at 0.6332 against 0.6282, and a full round went from 20 to 40 minutes down to about 12.",
   "links": [
    "group-ml-and-models",
    "entry-three-machine-compute-fleet",
    "month-jul-2026"
   ],
   "tags": [
    "R&D infrastructure"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-synthetic-roof-generator-and-a-geometry-model",
   "title": "Synthetic roof generator and a geometry model",
   "group": "ML and models",
   "body": "A frozen vision backbone with height as surface normals, one stage for orientation and one for interior structure, trained partly on procedurally generated roofs: real building footprints, dormers with cheek walls, labeled ridges, hips and valleys, rendered in Blender with surface-model noise fitted to real data and only permissively licensed assets. I reviewed the output card by card, one round scoring 26 acceptable, 1 bad and 1 questionable. It was parked in July once measurement showed it barely moved the product, with the synthetic data banked rather than thrown away.",
   "links": [
    "group-ml-and-models",
    "case-lender-certification",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "R&D, banked and parked"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-labeling-tools-training-bench-and-a-roof-database",
   "title": "Labeling tools, training bench and a roof database",
   "group": "ML and models",
   "body": "The unglamorous half of an ML program: geometry-keyed labels that record which human drew them, race-safe saves, a keyboard-fast labeler driven by gaps in the corpus, a dataset cockpit showing predictions, heatmaps, polylines and scores with edit links, and a SQL layer with dual-write then database-first reads, 129 canonical homes backfilled and nightly backups. Without this the model numbers elsewhere in this section would not be reproducible.",
   "links": [
    "group-ml-and-models",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to July 2026"
  },
  {
   "id": "entry-line-detector-with-a-labeling-interface-for-a-non-engineer",
   "title": "Line detector with a labeling interface for a non-engineer",
   "group": "ML and models",
   "body": "A wireframe detector fine-tuned on aerial and elevation crops, paired with a browser CAD-style correction tool with snapping, auto-split and endpoint dragging so a company executive could label, with save-correction, retrain and hot reload behind it. The onboarding document I wrote for him carries the lesson that made the loop work: a saved correction must differ from the detector's raw output or it adds no signal, which we learned when training on accepted detections made the model fire about 1,000 edges on a house that should have about 40.",
   "links": [
    "group-ml-and-models",
    "month-jul-2026"
   ],
   "tags": [
    "R&D, parked"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to July 2026"
  },
  {
   "id": "entry-3d-roof-viewer-proofs-of-concept",
   "title": "3D roof viewer proofs of concept",
   "group": "ML and models",
   "body": "A viewer fusing public LiDAR, vendor imagery and solar data with plane fitting, intersection snapping, 5 cm welding, wall extrusion and texture draping produced usable meshes for 6 of 8 addresses at a pitch error of 1 to 2 degrees. I then researched five ways to render a designed system on a real roof, built and discarded three proofs of concept, got photorealistic 3D tiles rendering four addresses, and ruled server-side meshes as the production path after a 20-address batch generated every mesh at 9.1 seconds each.",
   "links": [
    "group-ml-and-models",
    "case-memory-system",
    "case-vendor-decision",
    "month-apr-2026"
   ],
   "tags": [
    "R&D, three discarded, one path chosen"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026"
  },
  {
   "id": "entry-bill-reader-model-bake-off-and-generalization-gauntlet",
   "title": "Bill reader model bake-off and generalization gauntlet",
   "group": "ML and models",
   "body": "Four vision models were compared on the same corpus with the same truth, and the best read usage charts at about 1.0 kWh per day mean absolute error against 1.4 and 1.9 for the next two, with one model unusable because it snapped bars to gridlines. I then built a never-tuned validation set of 430 bills: 190 official public sample bills from about 90 utilities across five regions, including 62 with no chart at all, plus 240 synthetic bills with truth exact by construction. Wrong-series answers are weighted ten times heavier than misses, because a bill the reader refuses is safe and a bill it reads wrong is not.",
   "links": [
    "group-ml-and-models",
    "case-bill-reader",
    "entry-bill-reader-latency-program",
    "month-jul-2026"
   ],
   "tags": [
    "R&D that shipped into production"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-bill-reader-latency-program",
   "title": "Bill reader latency program",
   "group": "ML and models",
   "body": "Two passes against a hard ceiling. Corpus median went 163 s to 91 s, then the second pass put the 95th percentile at 55.0 s with no bill over 60 seconds, and a lower-effort model setting proved about twice as fast with accuracy unchanged. The speed work introduced one honesty regression, a dense 24-bar chart read as 12 flat bars on one run in three, which the full-corpus gate caught before release and a flatness backstop fixed, verified across six consecutive runs.",
   "links": [
    "group-ml-and-models",
    "case-bill-reader",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-per-bar-reading-contract-and-dense-chart-color-separation",
   "title": "Per-bar reading contract and dense-chart color separation",
   "group": "ML and models",
   "body": "A bill showing ten readable months was yielding nothing, because each of three parallel votes discarded partial reads before the votes were compared. Shipping per-bar two-of-three corroboration moved usable real months from 36 to 95 on the corpus while the legacy output field stayed byte-identical under a golden snapshot. A deterministic color-separation mask handles multi-year charts where three years of bars overlap, and half the months that persist land within 4.5% of truth with 85% within 10%.",
   "links": [
    "group-ml-and-models",
    "case-bill-reader",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-per-house-energy-baseline-experiment",
   "title": "Per-house energy baseline experiment",
   "group": "ML and models",
   "body": "I tested making the first energy estimate house-specific instead of an area average: passing building area and vintage from the property-data vendors into the tariff vendor's baseline call, in three variants from bare to fully filled, measured on about 20 real addresses with ground-truth bills. It is a small experiment with an outsized effect, because that first estimate sets the system size a customer is quoted.",
   "links": [
    "group-ml-and-models",
    "case-bill-reader",
    "case-vendor-decision",
    "month-jun-2026"
   ],
   "tags": [
    "benchmarked"
   ],
   "url": null,
   "kind": "entry",
   "when": "June 2026"
  },
  {
   "id": "entry-image-super-resolution-feasibility-answered-no",
   "title": "Image super-resolution feasibility, answered no",
   "group": "ML and models",
   "body": "Five upscaling models were benchmarked for 4x enlargement of aerial captures. The finding that ended it was that generative upscalers hallucinate: on a rooftop benchmark, false positives went from 7 to 24. Since the imagery is used for annotation and measurement, invented detail is worse than blur, so I recommended plain browser zoom and spent nothing.",
   "links": [
    "group-ml-and-models",
    "case-vendor-decision",
    "month-apr-2026"
   ],
   "tags": [
    "benchmarked, not adopted"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026"
  },
  {
   "id": "entry-literature-sweep-and-design-around-analysis",
   "title": "Literature sweep and design-around analysis",
   "group": "ML and models",
   "body": "Verified research on the public roof-wireframe datasets and their licenses, geometry priors, active learning and the current state of the art, with one confirmed gap: no complexity-stratified public roof benchmark exists, so any claim of accuracy has to name its own exam set. A two-layer patent review, explicitly engineering analysis rather than legal advice, established which approaches are clear and which are claimed, and that analysis is why the model was designed around plane fitting instead of the obvious alternative.",
   "links": [
    "group-ml-and-models",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to July 2026"
  },
  {
   "id": "entry-running-the-ml-program-like-a-lab",
   "title": "Running the ML program like a lab",
   "group": "ML and models",
   "body": "Probes were pre-registered before compute was spent, adversarial reviews ran against my own results, seven in one night on one program, and the scoreboard is append-only so a number cannot be quietly improved. No home was ever dropped from an exam set to make a score look better. Training seats run under the same reboot-proof service as the rest of the fleet, so a battery of runs survives a restart.",
   "links": [
    "group-ml-and-models",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-lender-financing-integration-rebuilt-from-scratch",
   "title": "Lender financing integration, rebuilt from scratch",
   "group": "Integrations",
   "body": "When engineering moved in house I rebuilt the third-party-ownership integration end to end: loan creation, qualification, disclosures, system design and utility information uploads with per-roof-segment arrays and real shading, savings schedules, a milestone tracker matching the lender's own portal, case and stipulation uploads, agreement send, cancel and resubmit, and an authenticated webhook endpoint streaming status into the CRM live. Phases one to four deployed between March 13 and April 3, 2026 across about 280 commits, and 181 commits in the API carry the lender tag. It passed the lender's certification on 100 of 100 addresses.",
   "links": [
    "group-integrations",
    "case-memory-system",
    "case-platform-takeover",
    "case-lender-certification",
    "case-vendor-decision",
    "entry-production-was-pointed-at-a-sandbox",
    "month-aug-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "March to August 2026"
  },
  {
   "id": "entry-change-order-contract-established-by-testing-rather-than-rea",
   "title": "Change-order contract, established by testing rather than reading",
   "group": "Integrations",
   "body": "I fired real change-order calls against the vendor sandbox on a live test loan and established that exactly five change types are valid for third-party ownership and a sixth is rejected, that one amount field is a rate rather than a dollar total, and that rate changes must be bundled with a savings schedule, a rule that appears in no documentation. The bench script and runbook are re-runnable. Outside-in discovery then found six mutating endpoints with no caller in our interface and produced a seven-of-seven milestone lock truth table and a build spec for a deal-level record frozen at signing.",
   "links": [
    "group-integrations",
    "case-memory-system",
    "case-lender-certification",
    "case-vendor-decision",
    "entry-production-was-pointed-at-a-sandbox",
    "month-aug-2026"
   ],
   "tags": [
    "tested and specified"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-utility-tax-layer",
   "title": "Utility tax layer",
   "group": "Integrations",
   "body": "I found why the tariff vendor's cost figure can understate a real bill: it omits state, county and city taxes and franchise fees, which for one Arizona utility compose to 11.394%. I built a per-jurisdiction multiplier on the cost path with a cache version bump and screen and document parity, held the merge until the lender confirmed in writing that schedules should be tax-inclusive, and shipped it with a sandbox proof showing the lender's savings validation flip from rejected to accepted.",
   "links": [
    "group-integrations",
    "case-lender-certification",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-post-solar-rate-on-a-net-avoided-basis",
   "title": "Post-solar rate on a net-avoided basis",
   "group": "Integrations",
   "body": "When the lender replaced verbal guidance with written guidance, I reworked the post-solar utility rate to taxed net-avoided cost divided by net-avoided kilowatt-hours, with fixed fees excluded and tiers and time-of-use reflected, at both the cache-write and fallback sites. Before-and-after sandbox proofs were quoted to the cent and the change was verified on real proposal traffic after deploy. Written vendor guidance beats a remembered phone call, which is why this one waited for the document.",
   "links": [
    "group-integrations",
    "case-lender-certification",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-lender-rate-handling-root-caused-and-shipped",
   "title": "Lender rate handling, root-caused and shipped",
   "group": "Integrations",
   "body": "The rate that goes to the lender now comes from one verified source and is checked against the lender's limit before it is sent. I traced the original path through every reference in the codebase rather than trusting the field name, tested the replacement against real production values instead of assuming it was right, and confirmed how the limit actually applies before shipping. Two pull requests.",
   "links": [
    "group-integrations",
    "case-lender-certification",
    "month-may-2026"
   ],
   "tags": [
    "shipped in two pull requests"
   ],
   "url": null,
   "kind": "entry",
   "when": "May 2026"
  },
  {
   "id": "entry-tariff-and-energy-vendor-hardening",
   "title": "Tariff and energy vendor hardening",
   "group": "Integrations",
   "body": "This is the highest-traffic integration in the platform and about 230 commits went into it: consumption uploads carrying twelve monthly readings rather than one annual figure, parallel post-solar tariff comparison with automatic selection of the cheapest, likelihood and grandfathered labels, storage optimization on eligible time-of-use plans, an eleven-position precompute cache, cache-first proposal reads, on-demand compute for closed plans, and a bill-window matcher that reproduces a bill's exact period cost. 21 pull requests landed in three days for a net reduction of about 2,000 lines, precompute went from 15 analyses per run to 9, and one shared energy editor with a single write path replaced five legacy surfaces.",
   "links": [
    "group-integrations",
    "case-bill-reader",
    "case-vendor-decision",
    "entry-tariff-pipeline-repair",
    "month-aug-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "March to August 2026"
  },
  {
   "id": "entry-tariff-pipeline-repair",
   "title": "Tariff pipeline repair",
   "group": "Integrations",
   "body": "I hardened the tariff pipeline in three merged pull requests. The vendor returns 25 plans per page unless a page size is sent, so I set the page size explicitly and verified that a utility's full list of residential plans comes through to the comparison. I fixed time-of-use detection to read the marker at the level of the response where the vendor actually returns it, and verified it working in production. And I replaced three inconsistent pickers with one vendor-approved policy for what may be shown and what may be computed.",
   "links": [
    "group-integrations",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "three pull requests merged"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-roof-data-vendor-evaluation-and-swap",
   "title": "Roof data vendor evaluation and swap",
   "group": "Integrations",
   "body": "I took the partnership call with the CEO, disclosed the platform's weakest component and the mitigation, staked the product position of 2D as standard with 3D on demand, then overruled the recommended dual-vendor path in favor of a full swap with the designer rebuilt around the new geometry. The proof of concept was running against the vendor sandbox that afternoon. I scraped five API specifications and stood up two sandbox applications the same week, and the bench measured a caveat worth knowing: the vendor's own building outline and its own imagery disagree by 2 to 3 m, which demotes the outline from geometry to a hint.",
   "links": [
    "group-integrations",
    "case-memory-system",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "ruled and validated"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-ground-cost-model-per-project",
   "title": "Ground-cost model per project",
   "group": "Integrations",
   "body": "Before negotiating I measured what the platform actually pays each vendor per project and design, fixed against variable, from contracts, invoices, call patterns and test volumes. Almost all of the marginal cost sat with one imagery vendor, one large vendor did not move the per-project figure at the volumes we modelled, and a scenario table from 250 to 20,000 projects a year showed per-project cost falling roughly thirtyfold with a crossover near 8,300 designs a year. Five lessons from it shaped the deal structure: usage-based, purchases gated by funnel stage, and no exclusivity that would foreclose a future license.",
   "links": [
    "group-integrations",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "shipped as negotiation preparation"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-address-and-property-data-moved-server-side",
   "title": "Address and property data moved server-side",
   "group": "Integrations",
   "body": "Address-to-coordinate resolution now happens on the server before any row is written, with a precision bar, a distinction between a bad address and a vendor outage, and a timeout, plus a geocoder fallback for projects created from the popup. Square footage and year built persist at lead creation from the property-data vendors. Coordinates written after the fact are coordinates nobody trusts.",
   "links": [
    "group-integrations",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-approved-equipment-list-enforcement",
   "title": "Approved equipment list enforcement",
   "group": "Integrations",
   "body": "The lender's approved vendor list became real data: a program and part-number schema, an admin module, installer and dealer toggle screens bounded by that installer's ceiling, and an eligibility endpoint doing a three-way intersection of financier program, installer catalog and product, consumed directly by the automatic designer. Part numbers rather than product names now go to the lender. The rework to part-number-first identity shipped as three coordinated pull requests in dependency order with test suites at 154 of 154, 101 of 101 and 24 of 24, and a reported 411 W versus 410 W discrepancy was traced to a dual-source seam rather than argued about.",
   "links": [
    "group-integrations",
    "case-lender-certification",
    "case-vendor-decision",
    "case-identity-rebuild",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to July 2026"
  },
  {
   "id": "entry-vendor-usage-and-cost-metering",
   "title": "Vendor usage and cost metering",
   "group": "Integrations",
   "body": "One handler on every HTTP client factory meters all 33 sends to the tariff vendor, 17 lender sites, and the e-signature, address, property and mapping vendors with no call-site edits, attributing each call to installer, dealer, branch, person and project through ambient context. An append-only event table joins a dated vendor rate card at record time, and a vendor with no rate is reported unpriced rather than zero. 30 files, 3,618 lines, one migration, 60 new tests and 18 mutation proofs. Adding a vendor is now a configuration entry.",
   "links": [
    "group-integrations",
    "case-lender-certification",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "merged"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-competitive-and-market-research-feeding-product-decisions",
   "title": "Competitive and market research feeding product decisions",
   "group": "Integrations",
   "body": "Research that changed what we built rather than filling a deck: a check across four competing platforms showed none enforces a hard margin gate, which changed what we planned to build; a four-track study proved no live storage-incentive feed exists since a public database was retired at the end of 2025, so curated manual entry is the industry norm rather than a shortcut; and a competitor's account model, mined from 29 of its own help articles, informed the identity rebuild.",
   "links": [
    "group-integrations",
    "case-identity-rebuild",
    "month-jul-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "March to July 2026"
  },
  {
   "id": "entry-auth-cutover-one-sign-in-reaches-both-applications",
   "title": "Auth cutover: one sign-in reaches both applications",
   "group": "Platform engineering",
   "body": "Overnight on August 17 I cut the admin application over to the CRM's session cookie and retired its separate login, with the admin service gaining a cookie scheme, a role gate and identity taken from the resolved caller. I wiped the old admin database to a clean slate, 27,625 rows across 31 tables, with deletion order derived at runtime from declared foreign keys, then the real provisioning endpoints re-created both companies and both people so the two databases carry identical identifiers. The faults that surfaced at cutover were mostly caught by the automated suite, and I traced each one link further down the chain instead of patching at the symptom.",
   "links": [
    "group-platform-engineering",
    "case-identity-rebuild",
    "month-aug-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-product-database-rebuilt-inside-the-crm",
   "title": "Product database rebuilt inside the CRM",
   "group": "Platform engineering",
   "body": "Creating a product took 230 fields across 9 equipment types and a 4-step wizard in the old admin application against 2 fields in the CRM. The CRM's own tables had carried the full specification since 2024, 51 columns for panels, 61 for inverters and 68 for batteries, over a library of 6,825 modules, 3,537 inverters and 1,260 batteries with no screen above it. Widening the write path took 2,218 lines and no migrations, the screens took 2,381, and product enablement moved to the CRM after nine adversarial review rounds found a disable path that could leave a product active. 137 of 139 fields round-trip and 11,567 live rows are read by every design.",
   "links": [
    "group-platform-engineering",
    "month-aug-2026"
   ],
   "tags": [
    "shipped, remaining equipment types open"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-the-legacy-admin-application-measured-module-by-module",
   "title": "The legacy admin application, measured module by module",
   "group": "Platform engineering",
   "body": "16 modules and 88,499 lines measured by file type: 62,862 lines have no equivalent in the CRM and must move, 15,029 are already replaced and can die, 4,502 are dead now, and 6,106 are unclear. 4,562 dead lines are deleted so far. The pass also caught an error of my own program's making, where a 2,064-line module rated unreachable in fact had three live registered routes, because unreachable through the menu is not the same as cannot render.",
   "links": [
    "group-platform-engineering",
    "month-aug-2026"
   ],
   "tags": [
    "measurement complete, execution under way"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-tenant-isolation-across-the-api",
   "title": "Tenant isolation across the API",
   "group": "Platform engineering",
   "body": "A central company filter on tenant-owned entities plus a read-guard primitive, then a sweep through the API applying it consistently to project reads, dashboards, documents and contracts, with webhooks and system requests scoped and project reads following the acting company. About 60 commits. An adversarial lane with executable proving harnesses overturned the first diagnosis and pinned the real scope of the fix, and it merged only after an independent pass re-derived the patch identity with four instruments and five controls.",
   "links": [
    "group-platform-engineering",
    "case-memory-system",
    "case-identity-rebuild",
    "month-aug-2026"
   ],
   "tags": [
    "shipped to production"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-end-to-end-and-measurement-harnesses",
   "title": "End-to-end and measurement harnesses",
   "group": "Platform engineering",
   "body": "About 25 commits of test infrastructure: a browser harness with its own auth fixture and storage encryption matching the application, trace and network capture, a request-counting mode that measures how many calls a workflow really makes, golden specs for incentives and savings, an API-level stress harness for multi-section saves, a secure admin sign-in harness with network-escape checks, and an exploratory agent that drives the app looking for breakage. A researched architecture for the next tier of this, using trace assertions, front-to-back correlation and property-based tests, is written down rather than guessed at.",
   "links": [
    "group-platform-engineering",
    "case-lender-certification",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to August 2026"
  },
  {
   "id": "entry-getting-the-backend-test-suite-to-run-at-all",
   "title": "The backend test gate",
   "group": "Platform engineering",
   "body": "I turned the backend unit suite into a real gate: 1,044 tests in 23 seconds behind one plain command. I made the build reproducible from a fresh checkout first, then verified every database-backed test runs on an in-memory engine, so the suite needs nothing but the command itself. On its first runs it exposed three real product bugs, including one in a data path that needed hardening. Catching them at the gate, before a change ships, is the point of the work.",
   "links": [
    "group-platform-engineering",
    "case-memory-system",
    "month-jun-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "June 2026"
  },
  {
   "id": "entry-independent-verification-harness-for-pull-requests",
   "title": "Independent verification harness for pull requests",
   "group": "Platform engineering",
   "body": "Each pull request is verified against a pristine baseline rather than trusted. On one front-end change the baseline ran 1,663 of 1,663 specs with 19 failures and the change ran 1,810 of 1,810 with the same 19, so 147 tests were added and nothing new broke, with failing names compared byte for byte. The count of executed tests is grepped out of the log rather than inferred from an exit code, because the runner exits zero after running nothing when the browser binary is missing.",
   "links": [
    "group-platform-engineering",
    "month-aug-2026"
   ],
   "tags": [
    "in use"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-fifteen-copies-of-the-same-calculation",
   "title": "One shared savings calculation",
   "group": "Platform engineering",
   "body": "I ran a four-agent fork map over the 25-year savings figure to find every place it was computed, then condensed a calculation that had been duplicated across the backend and the browser into one shared function feeding screen, document, e-signature and raw endpoints. The consolidation deleted 1,056 lines, and the number a customer sees on screen, in the document and in the e-signature flow now comes from the same code.",
   "links": [
    "group-platform-engineering",
    "case-lender-certification",
    "month-jul-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to July 2026"
  },
  {
   "id": "entry-drift-detector-across-three-production-databases",
   "title": "Drift detector across three production databases",
   "group": "Platform engineering",
   "body": "A select-only script guarded by database name compares people, companies and roles across three production databases and reports disagreement as one number, with an append-only log where a missing row is itself a signal. The first version was scrapped because it scaled with headcount, so version two headlines only terms whose healthy value is zero. It also caught a comparator bug inside itself, where a duplicate key silently overwrote a row and was counted nowhere. It is a detector and never a reconciler, on purpose.",
   "links": [
    "group-platform-engineering",
    "month-aug-2026"
   ],
   "tags": [
    "live, read-only"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-readiness-assessment-for-the-ceo",
   "title": "Readiness assessment for the CEO",
   "group": "Platform engineering",
   "body": "When the CEO asked whether a real installer could sell and finance through the platform that day, I answered with a measured readiness figure, an estimate of three to four focused weeks, and a named list of the remaining work. Later I reconciled a ten-item go-to-market list from a sweep of about 100 internal documents plus email and meeting records, and set the phasing: land the features first, then one deliberate stabilization pass.",
   "links": [
    "group-platform-engineering",
    "case-lender-certification",
    "month-jun-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to June 2026"
  },
  {
   "id": "entry-production-was-pointed-at-a-sandbox",
   "title": "Configuration over labels",
   "group": "Platform engineering",
   "body": "I read the production container configuration for the financing integration line by line instead of trusting the names on the deployments. Pinning down which vendor environment each deployment actually talked to let me classify its traffic correctly and reframe what production readiness for that integration means. Environment facts come from configuration, not from labels.",
   "links": [
    "group-platform-engineering",
    "case-lender-certification",
    "month-apr-2026"
   ],
   "tags": [
    "discovery"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026"
  },
  {
   "id": "entry-variable-dictionary-and-codebase-maps",
   "title": "Variable dictionary and codebase maps",
   "group": "Platform engineering",
   "body": "A 381-line mandatory-read map of every key variable in the CRM: where it is set, where it is updated, where it is read, which fields are dead, which names collide, and the priority order of rate sources. Eight component maps, five workflow compasses and a platform map sit alongside it. It is the artifact that lets an agent, or a new engineer, change a calculation with every setter and reader already on the map, instead of discovering them one at a time.",
   "links": [
    "group-platform-engineering",
    "case-brand-kit-and-content",
    "month-apr-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026 onward"
  },
  {
   "id": "entry-reproducible-build-for-the-segmentation-service",
   "title": "Reproducible build for the segmentation service",
   "group": "Platform engineering",
   "body": "I made the boundary-segmentation service build reproducible, in 21 commits over 9 days: pinning the environment, removing a build-time hardware assertion that failed on machines without a GPU, fixing the container entry point and adding a pipeline definition. A model service you can rebuild is a model service you can fix.",
   "links": [
    "group-platform-engineering",
    "month-mar-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "March 2026"
  },
  {
   "id": "entry-marketing-site-rebuilt-in-a-week",
   "title": "Marketing site rebuilt in a week",
   "group": "Website and brand",
   "body": "The website became the top priority on July 6 and it was live one week later: 12 hand-written self-contained pages with no framework, about 540 KB of HTML in total. The direction was set first, six homepage directions to choose a blend from, then a design system, then code, then more than 20 review rounds on navigation order, copy and vendor naming. The site routes by audience rather than by feature, and the interactive proof at the centre of it is the one workflow gate competitors do not have.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "case-vendor-decision",
    "entry-marketing-site-ownership-and-the-vendor-naming-rule",
    "tool-claude-design",
    "month-jul-2026"
   ],
   "tags": [
    "live since July 13, 2026"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-hosting-and-dns-migration",
   "title": "Hosting and DNS migration",
   "group": "Website and brand",
   "body": "The site moved off a marketing platform onto static hosting with valid certificates on both hostnames, extensionless URLs with permanent redirects from every legacy path, and analytics confirmed firing on every content page. The DNS zone was rebuilt as a faithful copy including the mail authentication records. I caught a group of service hostnames missing from the first cut, proved the backends healthy by resolving them directly, restored the records, and documented the gateway listener list as the authoritative source for what hostnames exist.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "month-jul-2026"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-animated-3d-roof-hero",
   "title": "Animated 3D roof hero",
   "group": "Website and brand",
   "body": "The homepage takes one real address, draws the roof lines on the aerial image, morphs them into a 3D model, raises walls, drops trees, runs an irradiance heatmap, places the array, fills a production chart and then lets the visitor orbit it. It is fed by the in-house roof engine through an address-to-hero pipeline and is entirely self-hosted with no third-party content requests. An eight-agent feasibility spike came first, and the multi-home generator was proven on roofs from 8 to 30 planes.",
   "links": [
    "group-website-and-brand",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-interactive-product-tour",
   "title": "Interactive product tour",
   "group": "Website and brand",
   "body": "About 2,000 lines of self-contained JavaScript recreate the real three-step workflow as running animated screens with a scripted cursor, narration beats, click-a-step-to-play, an explore mode and a scrolling mini proposal with a finance drawer and signature. Every label was verified against production source, 22 of them locked, and the fictional numbers were reconciled by script so the arithmetic on screen is internally consistent. The first build was rejected after ten versions and rebuilt over 22 rounds in one day, then given a mobile camera that keeps the desktop screens intact inside a phone viewport and zooms per beat, verified at 375 pixels.",
   "links": [
    "group-website-and-brand",
    "month-jul-2026"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-homepage-performance-rebuild",
   "title": "Homepage performance rebuild",
   "group": "Website and brand",
   "body": "Measured on production under a throttled network and CPU, before and after: total weight 5,561 KB down to 1,389 KB, requests 62 down to 32, load event 68.6 s down to 18.3 s, and the hero alone 2,887 KB across 25 requests down to 192 KB in one. Tree models were compressed 3,890 KB to 705 KB with vertex counts checked at 7,718 against 7,704 to prove only exact duplicates were removed, and a head-script gate keeps a static poster on slow links. A later recurrence was root-caused to cache revalidations being read as a 3 KB per second connection, which is the kind of thing you only find by watching frames.",
   "links": [
    "group-website-and-brand",
    "month-aug-2026"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "July to August 2026"
  },
  {
   "id": "entry-automated-blog-publishing-pipeline",
   "title": "Automated blog publishing pipeline",
   "group": "Website and brand",
   "body": "The content vendor fires a signed webhook, a cloud function verifies it, pulls the truth from their API, washes the HTML, mirrors images to storage, stores canonical JSON and serves the blog, its posts and the sitemap, with a 15-minute timer that self-heals anything missed. About 1,285 lines of publisher code, 9 to 12 articles live, publish to live in about two minutes and an edit live in about 15 seconds, with a person choosing what goes into it. I negotiated four API capabilities from the vendor, including brand-kit injection, and they shipped the same day. I added a loop guard on the webhook so a misbehaving caller cannot run the publisher repeatedly.",
   "links": [
    "group-website-and-brand",
    "case-memory-system",
    "case-brand-kit-and-content",
    "case-vendor-decision",
    "month-jul-2026"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-brand-kit-and-design-system",
   "title": "Brand kit and design system",
   "group": "Website and brand",
   "body": "A real logo, wordmark and favicon bundle, a written brand file and prompt file, and a full design system page with color roles, a type scale, logo clear space and component samples, rebuilt once after I caught an approximated logo being used instead of the real asset. On top of that sits a content brand kit for AI tools: voice, image style, always and never rules, locked claims and a vendor-name scrub list, which the content vendor injects into every generation. Design decisions were informed by a research pass where 25 agents reviewed 105 hand-verified sites and returned 26 adopt, 64 adapt and 15 avoid.",
   "links": [
    "group-website-and-brand",
    "case-memory-system",
    "case-brand-kit-and-content",
    "case-vendor-decision",
    "tool-claude-design",
    "month-jul-2026"
   ],
   "tags": [
    "in use"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to July 2026"
  },
  {
   "id": "entry-narrated-vertical-promo",
   "title": "Narrated vertical promo",
   "group": "Website and brand",
   "body": "43 seconds at 1080 by 1920 and 60 frames per second, with generated narration, 103 word-level caption timestamps and a licensed music bed at about negative 14.6 loudness units, checked against 18 composition snapshots and a contact sheet before sign-off. It was rebuilt from scratch after I found an earlier revision using draft fragments and invented interface labels, so the delivered cut records only the live deployed page through a reproducible recorder with the provenance of every frame frozen.",
   "links": [
    "group-website-and-brand",
    "month-jul-2026"
   ],
   "tags": [
    "delivered"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-paid-social-campaign-board",
   "title": "Paid social campaign board",
   "group": "Website and brand",
   "body": "Four portrait ads under one campaign line, 12.0 to 13.5 seconds each, approved from a package containing the visual board, the exact scripts, muted-viewing copy and a source-and-claim ledger before any composition existed. Every product claim was verified against shipped behavior and six claim categories were excluded as unsupported. A shared 3.8-second silent brand intro renders with motion resolved by 2.0 seconds.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "month-jul-2026"
   ],
   "tags": [
    "one delivered, three in review"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-print-advertising",
   "title": "Print advertising",
   "group": "Website and brand",
   "body": "The first gallery of eight concepts was rejected as web-style price tiles, so I re-read the brief, wrote a creative brief and rebuilt six full-page editorial concepts on a real print canvas at 300 dpi, then expanded to 21 through three agents with every page re-rendered and inspected. The chosen concept was finished by measuring the overlay's own signature colors to solve the background position mathematically rather than nudging it by eye.",
   "links": [
    "group-website-and-brand",
    "month-jul-2026"
   ],
   "tags": [
    "delivered"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-programmatic-video-pipeline-parked",
   "title": "Programmatic video pipeline, parked",
   "group": "Website and brand",
   "body": "Browser specs captured real interactions with painted fictional data and injected cursor motion, and a React-based video framework assembled a self-assembling logo intro, count-ups, four interaction scenes and a live calculation scene. Three versions, about 43 and 37 seconds. I parked it because the gap was creative direction rather than tooling, which is worth writing down: more automation would not have fixed it.",
   "links": [
    "group-website-and-brand",
    "month-jul-2026"
   ],
   "tags": [
    "three versions, parked"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-booking-flow",
   "title": "Booking flow",
   "group": "Website and brand",
   "body": "Every call to action on the site reads one constant, with progressive enhancement, an inline embed with native auto-height on desktop and a full-screen popup on mobile. It was verified by running the real site script against a headless DOM shim, 22 assertions of 22, rather than by clicking around.",
   "links": [
    "group-website-and-brand",
    "month-jul-2026"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "July 2026"
  },
  {
   "id": "entry-smaller-brand-and-marketing-assets",
   "title": "Smaller brand and marketing assets",
   "group": "Website and brand",
   "body": "Partnership announcement graphics exported in both companies' colors at two aspect ratios; an email signature system with an avatar tuner, a per-person copy installer and a play-once animated logo chosen from 20 evaluated styles; a custom award badge drawn as four self-contained vector variants and repositioned in flow after it overlapped on a real screen; five embeddable animated step widgets later superseded by the product tour; and complete listing copy for two software directories.",
   "links": [
    "group-website-and-brand",
    "case-brand-kit-and-content",
    "month-aug-2026"
   ],
   "tags": [
    "mixed"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-fully-isolated-local-production-mirror",
   "title": "Fully isolated local production mirror",
   "group": "Infrastructure",
   "body": "The whole platform runs on one workstation: five application services plus eight backing containers including an emulated SQL Server, storage emulator, document renderer, message broker, cache, document store and the Python AI service with its worker. The local database is seeded at production scale, about 183,000 rows and more than 200 migrations, so real logins work end to end, and the only thing the stack touches outside the machine is authentication. Isolation is proven rather than assumed, with zero cloud storage calls and zero imagery credits consumed during a full workflow run.",
   "links": [
    "group-infrastructure",
    "case-identity-rebuild",
    "entry-message-bus-repointed-in-production",
    "month-aug-2026"
   ],
   "tags": [
    "live and in daily use"
   ],
   "url": null,
   "kind": "entry",
   "when": "April to August 2026"
  },
  {
   "id": "entry-three-machine-compute-fleet",
   "title": "Three-machine compute fleet",
   "group": "Infrastructure",
   "body": "A workstation and two GPU machines run as one pool with content-addressed job directories that are safe to retry, a register-or-refuse rule per job, a fleet gate that fails closed where any actor may tighten it and only I may loosen it, and a 200 W GPU power cap added after a silent power-class failure. Measured rather than assumed: the workstation retrains the lighter recipe in about 8 minutes a round on CPU, which disproved the assumption that all training had to leave the machine.",
   "links": [
    "group-infrastructure",
    "case-memory-system",
    "month-aug-2026"
   ],
   "tags": [
    "live internally"
   ],
   "url": null,
   "kind": "entry",
   "when": "July to August 2026"
  },
  {
   "id": "entry-workstation-migration-and-access-topology",
   "title": "Workstation migration and access topology",
   "group": "Infrastructure",
   "body": "All local workloads moved from an unstable PC to a 16-core workstation with 64 GB: about 200 GB inventoried with 150 GB identified as regenerable, an emulated database because no native image exists, a fixed folder layout, and the knowledge vault cut over to a git repository with one writer and receive-only mirrors. Remote development keeps my old machine as the interface. Access runs over a private mesh with per-device keys and no port forwarding, internal tools are served only inside that mesh, and unused tunnels were removed. A freed drive became an encrypted external volume with recovery on both operating systems, plus nightly backups and tiered storage.",
   "links": [
    "group-infrastructure",
    "case-memory-system",
    "case-identity-rebuild",
    "month-jul-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to July 2026"
  },
  {
   "id": "entry-availability-monitoring",
   "title": "Availability monitoring",
   "group": "Infrastructure",
   "body": "I shipped availability monitoring across production: 14 tests across 13 hosts plus the marketing site, every five minutes from two locations, with a host-down alert that reaches a person. When weekend alerts fired, I root-caused them to the cluster's own maintenance window rather than writing them off as noise. An earlier design for a two-audience status page, covering 54 internal and vendor targets plus a public component view, is written down and partly realized by this suite.",
   "links": [
    "group-infrastructure",
    "case-vendor-decision"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "2026"
  },
  {
   "id": "entry-message-bus-repointed-in-production",
   "title": "Message bus repointed in production",
   "group": "Infrastructure",
   "body": "I repointed the cross-service message bus in production, restarted the rollout and verified published rows and end-to-end employee sync between the two applications. Before touching anything I traced the messaging layer end to end and established exactly which store the outbox wrote to and which cluster carried it alongside the Python services, so the repoint was made on verified topology rather than on assumption.",
   "links": [
    "group-infrastructure",
    "month-jul-2026"
   ],
   "tags": [
    "live in production"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to July 2026"
  },
  {
   "id": "entry-ending-the-outsourced-development-contract",
   "title": "Moving engineering in house",
   "group": "Decisions",
   "body": "I wrote the technical assessment of the delivered work that informed the decision at the March 9 executive meeting to move engineering in house. At the time I was most of the way through rewriting the pre and post-solar calculations. Monthly engineering spend fell about 95%, and I became sole developer on March 13.",
   "links": [
    "group-decisions",
    "month-mar-2026"
   ],
   "tags": [
    "executed"
   ],
   "url": null,
   "kind": "entry",
   "when": "March 2026"
  },
  {
   "id": "entry-the-platform-mission-one-app-one-login-one-database-of-recor",
   "title": "The platform mission: one app, one login, one database of record",
   "group": "Decisions",
   "body": "I set the platform mission and hold it: one interface, role-driven abilities, one access authority and one database of record, with the second application deleted at the end. The platform spanned two applications and two databases, and I measured the gap myself before proposing anything, down to field counts for the same task in each application. The charter names what does not count as progress so the program cannot drift into cleanup work.",
   "links": [
    "group-decisions",
    "case-identity-rebuild",
    "month-aug-2026"
   ],
   "tags": [
    "ruled"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-the-test-environment-call-made-twice",
   "title": "The test environment call, made twice",
   "group": "Decisions",
   "body": "I measured the QA environment myself rather than trusting the estimate, proved production did not depend on it, and retained it in July because it was the only place to gate the auth rewrite. In August, once production had cut over and the remaining case for the environment did not survive measurement, I ruled it deleted and rebuilt as a mirror of production. Changing a decision when the evidence changes is cheaper than defending the first one.",
   "links": [
    "group-decisions",
    "case-memory-system",
    "month-aug-2026"
   ],
   "tags": [
    "ruled, reversed, ruled again"
   ],
   "url": null,
   "kind": "entry",
   "when": "July to August 2026"
  },
  {
   "id": "entry-compliance-framing-held-against-the-room",
   "title": "Compliance framing held against the room",
   "group": "Decisions",
   "body": "I held that the system must not present equipment selections as automatically approved for domestic content rules without lender confirmation, against the initial view in the room. The team adopted my position. A software product that tells a salesperson something is compliant is making a claim the company has to stand behind.",
   "links": [
    "group-decisions",
    "case-memory-system",
    "case-lender-certification",
    "month-aug-2026"
   ],
   "tags": [
    "position adopted"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-executive-work-inventory-and-commitment-tracking",
   "title": "Executive work inventory and commitment tracking",
   "group": "Decisions",
   "body": "A portfolio-level classification of every program as live and verified, live but needing proof, built locally, or waiting on a decision, which put every open commitment in one visible place. Closing open items was mechanical once they were visible, including a partner onboarding item verified closed with 25 real accounts created. Product rulings came out of the same lane, including dropping a bill-upload gate after a sales leader showed it would block his real workflow.",
   "links": [
    "group-decisions",
    "case-bill-reader",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "June to August 2026"
  },
  {
   "id": "entry-marketing-site-ownership-and-the-vendor-naming-rule",
   "title": "Marketing site ownership and the vendor-naming rule",
   "group": "Decisions",
   "body": "I own the company's primary sales surface: the workflow of directions first and copy second, audience routing, the interactive proof at the centre, the hosting and DNS calls, and the rule that reconciled conflicting internal asks, which is that partner names appear in one place on the site and nowhere else. I also built a promotional campaign on request and then ordered its removal when it had run its course.",
   "links": [
    "group-decisions",
    "case-memory-system",
    "case-brand-kit-and-content",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "owned"
   ],
   "url": null,
   "kind": "entry",
   "when": "February to August 2026"
  },
  {
   "id": "entry-the-designer-as-an-embeddable-product",
   "title": "The designer as an embeddable product",
   "group": "Decisions",
   "body": "I argued in April that the solar designer should eventually be a standalone embeddable component other platforms drop into their own products, with all computation on our backend and a public API alongside it. Every architectural decision since has kept the designer separable rather than entangled with the CRM, which is the cheapest time to make that choice.",
   "links": [
    "group-decisions",
    "case-vendor-decision",
    "month-apr-2026"
   ],
   "tags": [
    "direction set"
   ],
   "url": null,
   "kind": "entry",
   "when": "April 2026"
  },
  {
   "id": "entry-energy-input-decisions-and-tooling",
   "title": "Energy input decisions and tooling",
   "group": "Decisions",
   "body": "I presented a four-tier decision tree for energy data, from hourly interval down to distribution averages, and the resulting rulings shaped the product: monthly input rather than annual, actual bill costs with a modeled fallback, no averaged month, and support for full 8,760-point interval files for battery proposals. In the same period I made the case for the AI tooling tier I work in as a force multiplier, which is the reason a one-person engineering function is possible at all.",
   "links": [
    "group-decisions",
    "case-bill-reader",
    "month-mar-2026"
   ],
   "tags": [
    "shipped"
   ],
   "url": null,
   "kind": "entry",
   "when": "February to March 2026"
  },
  {
   "id": "entry-a-rental-app-for-a-family-member",
   "title": "A rental app for a family member",
   "group": "Personal builds",
   "body": "A family member's roughly 50-property rental portfolio runs on it, mostly from a phone, with 32 properties and 38 units modeled: expected against received rent, leases and tenant history, move-in and move-out photos, improvements and bills. 99 commits in the first 10 days. It never moves money, the AI importer proposes rows that a human approves one at a time, and the assistant may not invent a number because every figure it reports comes from the database. One definition per field generates the form, the API validation and the importer schema, so the three can never drift apart.",
   "links": [
    "group-personal-builds",
    "case-bill-reader",
    "month-aug-2026"
   ],
   "tags": [
    "live at myonealproperties.com"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-identity-and-access-on-the-personal-side",
   "title": "Identity and access on the personal side",
   "group": "Personal builds",
   "body": "The rental app runs hosted identity with organizations from day one, so it is multi-tenant before it has a second tenant, on a custom production domain with tokens verified on the server for every API route. The identity provider authenticates and our own database decides what a person may see, which means removing someone archives them instead of orphaning the history they created. With the work-side rollout that is two hosted identity vendors configured end to end.",
   "links": [
    "group-personal-builds",
    "case-vendor-decision",
    "case-identity-rebuild",
    "month-aug-2026"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-cost-fences-and-vendor-licensing-on-a-personal-budget",
   "title": "Cost fences and vendor licensing on a personal budget",
   "group": "Personal builds",
   "body": "Market rent data sits behind a cache with a self-enforced monthly ceiling checked before every outbound call, with remaining quota visible on screen, on the rule that no button on screen can cost money. Three data vendors were rejected on license terms rather than capability, including one whose product terms carve out real-estate applications, and imagery was dropped once it was clear the license did not permit storing it. The repository history was rewritten before the first push to remove 25 real addresses and 17 policy numbers.",
   "links": [
    "group-personal-builds",
    "case-memory-system",
    "case-vendor-decision",
    "month-aug-2026"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "entry-a-second-memory-vault",
   "title": "A second memory vault",
   "group": "Personal builds",
   "body": "The work memory system is a method, so I ran it a second time. The personal vault holds 25 documents and about 24,100 words with 18 daily logs, loaded by the same nine hook scripts routed by folder, with a firewall so neither vault loads in the other's directories, and shared across two AI tools through one instruction file. It writes its own session summary at the end of a session, which I added after a context loss cost two days of decisions.",
   "links": [
    "group-personal-builds",
    "case-memory-system"
   ],
   "tags": [
    "live"
   ],
   "url": null,
   "kind": "entry",
   "when": "2026"
  },
  {
   "id": "entry-an-agent-tournament",
   "title": "An agent tournament",
   "group": "Personal builds",
   "body": "Twenty creator agents produced 60 concepts and 40 reviewer agents each ranked 20 of them against a shared commercial baseline under a written protocol, with robustness checks confirming the winner survives removing any single reviewer, family, stance or packet. 84 artifact files. The winning prototype was built, playtested and killed on product grounds, so nothing shipped. The machinery is the point: generate, score, verify, and keep the final call with a person.",
   "links": [
    "group-personal-builds",
    "month-aug-2026"
   ],
   "tags": [
    "method exhibit"
   ],
   "url": null,
   "kind": "entry",
   "when": "August 2026"
  },
  {
   "id": "about",
   "title": "About Hayden Gross",
   "group": "About",
   "body": "I set up the AI environment a company works inside, then use it to ship. Those are two halves of one job.\n\nThe environment is the part most people skip. It is a shared memory every AI session loads at startup and writes back to when it ends, so nothing has to be explained twice: 2,211 documents, about 4.4 million words, pulled in automatically by 9 hook scripts across 6 lifecycle events, with 129 written rules loaded before any work starts. A correction I give once becomes a rule that applies from then on. There is a brand kit so the AI sounds like the company rather than like AI, and a model and cost policy that governs what runs where.\n\nThen I use it to ship real product. I joined Solentrex in December 2025 doing QA, taught myself the stack through that environment, and by March 2026 I was the only in-house engineer on a 14-repository platform. In the five months after that I shipped about 1,800 commits and about 800 merged pull requests. I bought the identity layer rather than building it and cut production over in two days. I built the utility bill reader that runs at about one cent per bill. Engineering spend fell about 95% after the platform moved in house. I have sat on the weekly executive committee since January 2026.\n\nBefore this: three years of banquet operations at Wedgewood Weddings, which taught me pace and what a tool has to survive on the day, and logistics analytics at Amazon on a network moving 62 million units a year. My degree is a B.B.A. from Arizona State University. I am in Goodyear, Arizona.",
   "links": [
    "group-ai-environment",
    "group-ai-features-shipped",
    "group-ml-and-models",
    "group-integrations",
    "group-platform-engineering",
    "group-website-and-brand",
    "group-infrastructure",
    "group-decisions",
    "group-personal-builds",
    "live-solentrex-com",
    "live-myonealproperties-com",
    "live-resume-pdf",
    "self-who-hayden-is",
    "self-strengths-shipping-and-ownership",
    "self-how-hayden-works-day-to-day",
    "self-hard-situations-hard-decision"
   ],
   "tags": [
    "about"
   ],
   "url": "/",
   "kind": "hub",
   "when": ""
  },
  {
   "id": "month-mar-2026",
   "title": "Mar 2026",
   "group": "Timeline",
   "body": "What shipped or changed in Mar 2026: Reproducible build for the segmentation service, Ending the outsourced development contract, Energy input decisions and tooling.",
   "links": [],
   "tags": [
    "month"
   ],
   "url": null,
   "kind": "hub",
   "when": "Mar 2026"
  },
  {
   "id": "month-apr-2026",
   "title": "Apr 2026",
   "group": "Timeline",
   "body": "What shipped or changed in Apr 2026: The brain: shared memory every agent reads and writes, Nine lifecycle hook scripts across six events, The worker prompt preamble, Standalone designer API, validated against 15 metered systems, 3D roof viewer proofs of concept, Image super-resolution feasibility, answered no, A financing environment configuration audit, Variable dictionary and codebase maps, The designer as an embeddable product.",
   "links": [
    "month-mar-2026"
   ],
   "tags": [
    "month"
   ],
   "url": null,
   "kind": "hub",
   "when": "Apr 2026"
  },
  {
   "id": "month-may-2026",
   "title": "May 2026",
   "group": "Timeline",
   "body": "What shipped or changed in May 2026: Financing details tab and loan status, Lender rate handling, root-caused and shipped.",
   "links": [
    "month-apr-2026"
   ],
   "tags": [
    "month"
   ],
   "url": null,
   "kind": "hub",
   "when": "May 2026"
  },
  {
   "id": "month-jun-2026",
   "title": "Jun 2026",
   "group": "Timeline",
   "body": "What shipped or changed in Jun 2026: Golden testing, Design insights panel, Per-house energy baseline experiment, Backend test suite, Readiness assessment for the CEO.",
   "links": [
    "month-may-2026"
   ],
   "tags": [
    "month"
   ],
   "url": null,
   "kind": "hub",
   "when": "Jun 2026"
  },
  {
   "id": "month-jul-2026",
   "title": "Jul 2026",
   "group": "Timeline",
   "body": "What shipped or changed in Jul 2026: One instruction file, two AI tools, one memory, Deep research runs with adversarial claim checking, Reporting standards for AI output, Meeting transcript pipeline, In-house utility bill reader, Server-side energy profile engine, Incentives on proposals, Every US state live in the incentives admin, Head to head against a vendor-derived baseline, Pre-registered retrain probe with a statistical test, Height fusion program, reported as negative results, GPU training node, Synthetic roof generator and a geometry model, Labeling tools, training bench and a roof database, Line detector with a labeling interface for a non-engineer, Bill reader model bake-off and generalization gauntlet, Bill reader latency program, Per-bar reading contract and dense-chart color separation, Literature sweep and design-around analysis, Utility tax layer, Post-solar rate on a net-avoided basis, Tariff pipeline repair, Approved equipment list enforcement, Competitive and market research feeding product decisions, One shared savings calculation, Marketing site rebuilt in a week, Hosting and DNS migration, Animated 3D roof hero, Interactive product tour, Automated blog publishing pipeline, Brand kit and design system, Narrated vertical promo, Paid social campaign board, Print advertising, Programmatic video pipeline, parked, Booking flow, Workstation migration and access topology, Message bus repointed in production.",
   "links": [
    "month-jun-2026"
   ],
   "tags": [
    "month"
   ],
   "url": null,
   "kind": "hub",
   "when": "Jul 2026"
  },
  {
   "id": "month-aug-2026",
   "title": "Aug 2026",
   "group": "Timeline",
   "body": "What shipped or changed in Aug 2026: The manager and worker playbook, 17 non-negotiables, Standing adversarial verifier seats, Reboot-proof agent fleet, 129 written rules for AI agents, Model and effort policy, Tool servers wired into the agents, Status board, parked ledger, task sync, Usage footprint, Automatic designer: speed, progress and equipment awareness, Battery-only deals, backup planner and battery as design, Project editing model and the cost of opening a project, Deleting what nobody opened, Dark mode for the platform, measured before written, Roof face segmentation model and its training flywheel, Benchmark against certified survey wireframes, Classical line-extraction bake-off, ten variants, Triangulation bench and hand-drive roof editor, Running the ML program like a lab, Lender financing integration, rebuilt from scratch, Change-order contract, established by testing rather than reading, Tariff and energy vendor hardening, Roof data vendor evaluation and swap, Ground-cost model per project, Address and property data moved server-side, Vendor usage and cost metering, Auth cutover: one sign-in reaches both applications, Product database rebuilt inside the CRM, The legacy admin application, measured module by module, Tenant isolation across the API, End-to-end and measurement harnesses, Independent verification harness for pull requests, Drift detector across three production databases, Homepage performance rebuild, Smaller brand and marketing assets, Fully isolated local production mirror, Three-machine compute fleet, A paid GPU that was not being used, The platform mission: one app, one login, one database of record, The test environment call, made twice, Compliance framing held against the room, Executive work inventory and commitment tracking, Marketing site ownership and the vendor-naming rule, A rental app for a family member, Identity and access on the personal side, Cost fences and vendor licensing on a personal budget, An agent tournament.",
   "links": [
    "month-jul-2026"
   ],
   "tags": [
    "month"
   ],
   "url": null,
   "kind": "hub",
   "when": "Aug 2026"
  },
  {
   "id": "live-solentrex-com",
   "title": "solentrex.com, the live company website",
   "group": "Website and brand",
   "body": "The company website, live at www.solentrex.com. I rebuilt it from the ground up and relaunched it in one week: hosting, DNS, certificates, HubSpot and analytics, a blog and SEO pipeline. Built and shipped by me, and I own it end to end.",
   "links": [
    "group-website-and-brand",
    "entry-marketing-site-rebuilt-in-a-week",
    "entry-homepage-performance-rebuild",
    "entry-marketing-site-ownership-and-the-vendor-naming-rule",
    "about"
   ],
   "tags": [
    "live",
    "shipped"
   ],
   "url": "https://www.solentrex.com/",
   "kind": "case",
   "when": "Live since April 2026"
  },
  {
   "id": "live-myonealproperties-com",
   "title": "myonealproperties.com, the rental app, live",
   "group": "Personal builds",
   "body": "A rental app a family member runs a real business on, live at myonealproperties.com. Cloudflare Worker, D1, R2, Clerk auth, RentCast and OpenAI integrations, and a human-approved AI import. It manages a family member's ~50-property rental portfolio.",
   "links": [
    "group-personal-builds",
    "build-a-rental-app-for-a-family-member",
    "entry-a-rental-app-for-a-family-member",
    "about"
   ],
   "tags": [
    "live",
    "shipped"
   ],
   "url": "https://myonealproperties.com/",
   "kind": "case",
   "when": "Live"
  },
  {
   "id": "live-resume-pdf",
   "title": "The one-page resume",
   "group": "About",
   "body": "Hayden's one-page resume as a PDF. The site is the depth behind it: the work section holds the case pages and the catalog holds the full record.",
   "links": [
    "about",
    "case-platform-takeover",
    "case-memory-system"
   ],
   "tags": [
    "resume"
   ],
   "url": "/Hayden-Gross-Resume.pdf",
   "kind": "case",
   "when": ""
  },
  {
   "id": "self-who-hayden-is",
   "title": "Who Hayden is",
   "group": "About",
   "body": "I am Hayden Gross, Director of Technology at Solentrex, based in Goodyear, Arizona. Each job on my way here taught me something I still use. Three years in banquet operations at Wedgewood Weddings taught me pace, seasonality, and what a tool has to survive on a wedding day. Logistics analytics at Amazon taught me to turn data from a 62 million unit per year network into reports leaders acted on, and to train fulfillment crews on a new process. I joined Solentrex in December 2025. Since March 2026 I have been the only in-house engineer on its 14-repository platform. I taught myself the job by building the AI working system I use every day. I have sat on the weekly executive committee since January 2026. My degree is a B.B.A. from Arizona State University.",
   "links": [
    "about",
    "case-platform-takeover",
    "case-memory-system",
    "live-resume-pdf"
   ],
   "tags": [
    "about"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-who-hayden-is-what-drives-him",
   "title": "What drives Hayden",
   "group": "About",
   "body": "What drives me is helping a company and the people in it actually use AI. I have twice made a new process stick with frontline staff who did not ask for it. At Wedgewood Weddings I trained new staff with a checklist they could follow on a busy Saturday. At Amazon I trained fulfillment center crews on site during a national change in dock management. At Solentrex I build the environment first: shared memory, written rules, and a brand kit, so the AI sounds like the company and people who do not write code do real work in the tools. Colleagues with the most product context label the data that trains our models, and every AI output that touches a customer or a dollar gets a checkable expected result. Adoption is the number I watch.",
   "links": [
    "about",
    "case-two-tools-one-brain",
    "case-brand-kit-and-content",
    "entry-line-detector-with-a-labeling-interface-for-a-non-engineer"
   ],
   "tags": [
    "about"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-how-hayden-works-day-to-day",
   "title": "How Hayden works day to day",
   "group": "About",
   "body": "Every session I start loads the same memory: 2,211 documents, about 4.4 million words, injected automatically by 9 hook scripts across 6 lifecycle events. I never re-explain context. What I learn on a Tuesday is already loaded on Friday. The rules are written down too: 129 of them, loaded into every session before work starts. A correction given once becomes a written rule, so it applies from then on. I wrote the model and cost policy the day a 105-agent research run used 3.4 million tokens, and it has governed every run since. Sessions write their findings back into the memory when they end, and an append-only daily log has run for 137 consecutive working days.",
   "links": [
    "about",
    "group-ai-environment",
    "case-memory-system",
    "entry-129-written-rules-for-ai-agents",
    "entry-nine-lifecycle-hook-scripts-across-six-events",
    "entry-model-and-effort-policy",
    "case-two-tools-one-brain"
   ],
   "tags": [
    "about"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-how-hayden-works-verify-before-trusting",
   "title": "How Hayden verifies before trusting",
   "group": "About",
   "body": "I check what a system actually does before I build on it. My QA method, golden testing, drives the real running application, judges by network calls and database rows rather than screenshots, and traces a cause to a file and a line. Any figure that reaches a customer gets checked against real production data first, and I have caught and corrected savings math that way before it was ever shown to anyone. I measure before I propose. I costed a cloud test environment against what it was actually used for and proved production did not depend on it before making a call on it. I counted the fields needed to create a product in each of our two applications before proposing that they be consolidated, so the proposal rested on a measurement rather than an opinion. When we evaluated a new roof-data provider, I had running code against their test environment that same afternoon.",
   "links": [
    "about",
    "entry-golden-testing",
    "case-lender-certification",
    "entry-the-test-environment-call-made-twice",
    "entry-the-platform-mission-one-app-one-login-one-database-of-recor",
    "case-vendor-decision",
    "entry-dark-mode-for-the-platform-measured-before-written"
   ],
   "tags": [
    "about"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-how-hayden-works-plan-and-ship",
   "title": "How Hayden plans and ships",
   "group": "About",
   "body": "I write findings down the moment I have them. The shared memory holds 1,119 discovery notes, 34 decision records, and 48 failure post-mortems, each written when the work happened, so nothing depends on my recall. Decisions carry their reasoning, which means a settled question stays settled. Plans come before code: the identity rebuild locked four decisions before any code was written, and the rollback path existed before the production cutover ran. Then the work ships in small reviewable pieces. The savings schedule work landed as 14 merged pull requests. The production auth cutover was 7. Five months of platform work came to about 1,800 commits and about 800 merged pull requests, small changes landing steadily rather than one large release.",
   "links": [
    "about",
    "group-decisions",
    "entry-the-brain-shared-memory-every-agent-reads-and-writes",
    "case-identity-rebuild",
    "case-lender-certification",
    "case-platform-takeover"
   ],
   "tags": [
    "about"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-working-with-people-plain-english",
   "title": "How Hayden reports to executives",
   "group": "About",
   "body": "I have been the only technology voice on the company's weekly executive committee since January 2026, and I report in plain English. I own technical direction, vendor strategy, and hiring, and the decisions get written down. When the CEO asked whether a specific installer scenario could run through the platform that day, I answered directly: a plain percentage, the gaps named, and three to four focused weeks to close them. The same standard is written into my AI environment: agents report in plain language, as results a decision maker can look at and act on. A code change is explained as what it does, why, and what could go wrong. Every AI output that touches a customer or a dollar gets a checkable expected result.",
   "links": [
    "about",
    "entry-reporting-standards-for-ai-output",
    "entry-readiness-assessment-for-the-ceo",
    "entry-129-written-rules-for-ai-agents"
   ],
   "tags": [
    "about"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-working-with-people-non-engineers",
   "title": "How Hayden brings non-engineers into AI tools",
   "group": "About",
   "body": "I get people outside engineering doing real work in AI tools. The roof labeling that trains our geometry model is done by a company executive, using a guide I wrote and a correction tool I built for him. The guide leads with the rule that makes each label count. The company website took the same approach: the copy was written in house, and the AI build pipeline treated it as the source of truth. I learned to train people before I learned to code. I am grateful to Wedgewood Weddings, where I trained new staff with a checklist they could follow on a busy Saturday, and to Amazon, where I helped train fulfillment center staff during a national change in dock management. I have twice made a new process stick with frontline staff who did not ask for it.",
   "links": [
    "about",
    "case-two-tools-one-brain",
    "entry-line-detector-with-a-labeling-interface-for-a-non-engineer",
    "case-brand-kit-and-content"
   ],
   "tags": [
    "about"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-strengths-shipping-and-ownership",
   "title": "What Hayden is good at",
   "group": "About",
   "body": "I learn fast and prove it by shipping. I joined Solentrex in December 2025 doing QA, taught myself the stack through the AI environment I built, and by March 2026 I was the only in-house engineer on a 14-repository platform. In the five months after that I shipped roughly 1,800 commits and about 800 merged pull requests across the four core repositories. I own systems end to end: the identity rebuild ran from the buy decision to a production cutover I directed myself, including an auth layer touching 318 files, with backup retention on all five production databases in place first. Nearly three years of banquet operations at Wedgewood Weddings taught me the pace of a live event, where the date does not move, and I still work with that calm on a deadline.",
   "links": [
    "about",
    "case-platform-takeover",
    "case-identity-rebuild",
    "case-memory-system"
   ],
   "tags": [
    "about",
    "strengths"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-strengths-verification-and-cost",
   "title": "Hayden's verification and cost discipline",
   "group": "About",
   "body": "I check every number against real data before I trust it. Financing math is verified against production data before a homeowner signs, and I have corrected savings figures at that stage rather than after. My QA method judges by network calls and database rows, not screenshots, and my AI agents include a standing seat whose job is to contradict the manager. I treat cost the same way: measure first, then cut with proof. Engineering spend fell about 95% after the platform moved in house. I costed a cloud environment against its actual use, proved production did not depend on it, and retired it. I trace every line that moves to a cause: one trace tied almost all of an imagery charge to a single inactive account, and the renewal became a downsize. The utility bill reader I built runs at about one cent per bill.",
   "links": [
    "about",
    "case-lender-certification",
    "entry-golden-testing",
    "entry-standing-adversarial-verifier-seats",
    "entry-the-test-environment-call-made-twice",
    "case-bill-reader"
   ],
   "tags": [
    "about",
    "strengths"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-hard-situations-hard-decision",
   "title": "How Hayden runs a hard decision",
   "group": "About",
   "body": "When the call is hard, I put written evidence in front of the room and let the facts carry it. Before engineering moved in house in March 2026, I wrote a technical assessment of the delivered work, checked against the running application and the source. That assessment was the evidence at the March 9 executive meeting, and I took over the platform as the only in-house engineer. Engineering spend fell about 95%. I also make the same call twice when the facts change. I kept a test environment in July 2026 because it was the only place to gate the auth rewrite, then ruled it replaced by a mirror of production in August once production had cut over. New evidence gets a new decision.",
   "links": [
    "about",
    "entry-ending-the-outsourced-development-contract",
    "entry-the-test-environment-call-made-twice",
    "case-platform-takeover",
    "group-decisions"
   ],
   "tags": [
    "about",
    "how I work"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-hard-situations-production-incident",
   "title": "How Hayden handles a problem under pressure",
   "group": "About",
   "body": "I run a problem to its root rather than patching the symptom, and I leave the system better watched than I found it. When a cross-service message path stopped delivering, I did not stop at the first plausible cause. I traced it one link at a time, followed it to the database cluster actually carrying the store, repointed the message bus, restarted the rollout, and then proved it was fixed rather than assuming: published rows checked, and employee sync confirmed end to end across both applications before I called it done. The same rule held through the auth cutover, where every failure got traced one link further down instead of being patched where it surfaced. Then I built the watching in, so the next one does not need me to notice it: availability monitoring runs 14 tests across 13 hosts and the marketing site, every five minutes from two locations, with a host-down alert that reaches a person.",
   "links": [
    "about",
    "entry-message-bus-repointed-in-production",
    "entry-auth-cutover-one-sign-in-reaches-both-applications",
    "entry-availability-monitoring"
   ],
   "tags": [
    "about",
    "how I work"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-hard-situations-holding-a-line",
   "title": "How Hayden holds a line under pressure",
   "group": "About",
   "body": "Anything that touches money or compliance ships on written confirmation from the party that owns the answer. I built a per-jurisdiction utility tax layer after measuring that state and local taxes move a real Arizona bill by about 11.4%, held the merge until the lender confirmed in writing that savings schedules should be tax-inclusive, and shipped it with a proof showing the lender's validation passing. I reworked the post-solar rate math when the lender's guidance arrived as a written document, quoting before-and-after proofs to the cent, because a written document beats a remembered phone call. I also held that software must never present a regulatory determination as settled without written confirmation from the party responsible for it, and that position was adopted. Holding a line is calm work: state the rule, show the evidence, wait for the document.",
   "links": [
    "about",
    "entry-utility-tax-layer",
    "entry-post-solar-rate-on-a-net-avoided-basis",
    "entry-compliance-framing-held-against-the-room",
    "case-lender-certification"
   ],
   "tags": [
    "about",
    "how I work"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-governance-values-data",
   "title": "How Hayden handles data in an AI program",
   "group": "About",
   "body": "I keep AI memory clean by rule, and the rule is written down. The vault every AI session loads holds patterns and placeholders, never keys, tokens, passwords, customer names, or raw API responses. It is plain text on one machine, never synced to a cloud service, and the owner can read, edit, or delete all of it in a minute. Company data belongs in the company's own tenant. At Solentrex the utility bill reader I built runs inside our own stack, so every stage of that pipeline is ours to inspect. At home I hold the same line: the household finance assistant runs its language model locally under an isolated OS user, so real financial data never reaches a cloud model.",
   "links": [
    "about",
    "case-memory-system",
    "case-bill-reader",
    "build-a-household-finance-assistant"
   ],
   "tags": [
    "values"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-governance-values-accountability",
   "title": "How Hayden keeps AI accountable",
   "group": "About",
   "body": "I hold every AI output that touches a client or a dollar to a checkable expected result. That standard runs in practice: the financing math is verified against production data before a homeowner signs, and in the rental app I built, the AI importer proposes rows and a person approves every one before anything is saved. The program itself has to earn its keep in numbers: hours saved, adoption, and cost, reported so the person paying can judge it and stop it if it is not working. Honesty lives in the wording. Claims carry dates and counts, the brand kit bans invented statistics, quotes, and customer stories, and a number that is not settled is banned from generated content rather than guessed at.",
   "links": [
    "about",
    "case-lender-certification",
    "build-a-rental-app-for-a-family-member",
    "case-brand-kit-and-content"
   ],
   "tags": [
    "values"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-what-i-dont-discuss",
   "title": "Where Hayden draws the line on an employer's internals",
   "group": "About",
   "body": "I talk about method freely. I do not talk about an employer's internal problems, their numbers, or the quality of anyone else's work.\n\nSafe to share: how I build, what I measure, techniques that would work anywhere, and my own mistakes.\n\nNot mine to share: incidents and what they touched, customer data of any kind, internal defect counts, vendor terms, and the architecture of a production system in enough detail to rebuild it.\n\nOn an arrangement that was replaced, I talk about scope, cost per unit of work, and delivery cadence. Never about competence. The decision belonged to the business. I supplied the assessment.\n\nWork product built for a company is the company's: the code, the prompts, the configurations, the evaluation harnesses. What travels with me is general technique, which is how I structure an environment, how I design an evaluation, how I write a guide. My personal vault is a separate store and holds none of a company's material. When the line is unclear I ask, and the default is that it belongs to the company.\n\nIf you ask me something on the second list, I will tell you I do not discuss it rather than give you a vague answer.",
   "links": [
    "about",
    "self-governance-values-data",
    "self-governance-values-accountability",
    "case-memory-system"
   ],
   "tags": [
    "values",
    "governance"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-2190-commits-across-ten-repositories",
   "title": "2,190 commits across ten repositories",
   "group": "Platform engineering",
   "body": "I wrote about 2,190 of the 2,459 non-merge commits across ten repositories between 1 March and 18 August 2026, which is about 89 percent of them. The peak month was August 2026 at 887 commits. The number comes from git log across the repos, not from memory, so anyone can recount it. I have been the sole developer on the 14-repo platform since 13 March 2026 and I work almost entirely through AI-assisted engineering. The commit line is the plainest measure I have of what one person plus a well set up agent environment lands in five months.",
   "links": [],
   "tags": [
    "git",
    "commits",
    "output",
    "sole developer",
    "AI-assisted engineering"
   ],
   "when": "March 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-32-pull-requests-in-one-day",
   "title": "32 pull requests in one day across four repos",
   "group": "Platform engineering",
   "body": "On 14 August 2026 I landed an access-layer rebuild as 32 pull requests in a single day across four repositories. Product code came down by about 17,900 lines and test code went up by about 8,300. Slicing it that way was deliberate. Each pull request was small enough to review on its own and to revert on its own, and the four repos moved together so nothing was left half migrated overnight. The shape of the result matters more than the count: less product code to maintain, and more of it held in place by tests.",
   "links": [],
   "tags": [
    "access layer",
    "pull requests",
    "refactor",
    "tests",
    "multi-repo"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-one-pm-seven-workers-fourteen-hours",
   "title": "One PM, seven workers, fourteen hours",
   "group": "AI environment",
   "body": "On 1 August 2026 I ran the largest single multi-agent session I have run: one PM seat coordinating seven worker seats for about fourteen hours. It worked because the protocol is written down. Workers and the PM talk through append-only state files with START and END markers, and each side watches the other's file, so a handoff takes about three seconds. Across the program there are 177 state-file channels and 77 kickoff prompts, so a new seat starts from a written brief rather than from a conversation. The PM gates on intent: a worker posts its diff and pauses instead of merging on its own judgement.",
   "links": [],
   "tags": [
    "multi-agent",
    "PM-worker",
    "orchestration",
    "state files",
    "kickoff prompts"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-mission-control-dev-stack-dashboard",
   "title": "Mission Control, a zero-dependency dashboard for the dev stack",
   "group": "Infrastructure",
   "body": "I built Mission Control, a roughly 6,500-line Node dashboard with zero dependencies that monitors and controls my whole development stack: 8 Docker containers, 5 app serves, the AI services, the designer engines, the compute fleet and the worker seats. It does start, stop, restart and force kill, with a timeout, a cancel and a watchdog on every control job. It has health hysteresis so a service does not flap, stuck-state detection after 90 seconds, server-sent event heartbeats, energy logging with a daily rollup, an MCP bridge so agents can read it, and an HTTPS install so I can open it on my phone. It also holds the fleet gate, where only my own click can loosen access.",
   "links": [],
   "tags": [
    "Node",
    "dashboard",
    "Docker",
    "PWA",
    "MCP",
    "monitoring"
   ],
   "when": "June 2026 to July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-smart-project-id",
   "title": "Smart Project ID, minted at a single insert seam",
   "group": "Platform engineering",
   "body": "I gave projects a VIN-style identifier in the shape YY-CCCC-DDD-TTTT, minted atomically at the one place a project is inserted, with the internal integer key kept and immutable behind it. The smart ID is what appears in the Project 360 URL, on the print route and on the PDF cover. Alongside it I set a rule that applies to every repo: no raw sequential entity id renders on any human-facing surface, because a sequential number tells an outsider how many records you have. I proved the minting with a 27 of 27 audit harness that included a 10,000-domain bijection check and a 25-way concurrent create burst with zero duplicates.",
   "links": [],
   "tags": [
    "identifiers",
    "concurrency",
    "audit harness",
    "URLs"
   ],
   "when": "July 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-one-create-project-popup",
   "title": "One create-project popup for every company type",
   "group": "Platform engineering",
   "body": "I replaced the separate create paths with one popup that works for every company type. It has Google address autocomplete with a satellite preview, tiles for the project type, and a duplicate-address dialog that shows how old the existing projects are and flags anything created in the last 30 days. Address data is fetched once on arrival rather than repeatedly, and an address that cannot be resolved is refused before the first insert, so a bad address never becomes a row. About 55 commits went into it.",
   "links": [],
   "tags": [
    "address validation",
    "UX",
    "duplicate detection",
    "autocomplete"
   ],
   "when": "July 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-proposal-pdf-generation-rebuild",
   "title": "Proposal PDF generation, rebuilt through four approaches",
   "group": "Platform engineering",
   "body": "I brought proposal PDF generation into the platform and worked through four approaches before settling. First PuppeteerSharp, then a full QuestPDF rebuild with Montserrat embedded so it rendered the same inside Docker and Kubernetes, then html2canvas with jsPDF, and finally Gotenberg capturing the DOM of the print route, with one path serving both Download and Email or Share. The document gained an incentives breakdown, and one source feeds the 25-year savings figure on screen, in the PDF and in the eSign copy. PDF reads are scoped to the tenant. About 40 commits across the four approaches.",
   "links": [],
   "tags": [
    "PDF",
    "Gotenberg",
    "QuestPDF",
    "PuppeteerSharp",
    "tenant scoping"
   ],
   "when": "March 2026 to July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-roof-model-confidence-layer",
   "title": "A confidence score that says which drawings a human must fix",
   "group": "ML and models",
   "body": "I added a confidence layer on top of the roof model so the product could tell a safe drawing from one a person has to correct. The score comes from the model's own softmax, calibrated against hand-traced truth, and I judged it by AUC, which went from 0.914 to 0.936 across versions. What I shipped it against is a harder bar than accuracy: at zero confidently wrong drawings, auto-accept went from 56 percent to 59 percent. The gate threshold moved from 0.870 to 0.635 as calibration improved. In the editor it appears as a drawing assist with a dial, and the default position is the one that produces no confidently wrong output.",
   "links": [],
   "tags": [
    "confidence",
    "calibration",
    "AUC",
    "human in the loop",
    "roof model"
   ],
   "when": "July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-certified-roof-report-parser",
   "title": "Parsing certified roof reports into geometry",
   "group": "ML and models",
   "body": "I wrote a parser that turns certified roof reports into geometry my own tools can use, and I checked it rather than trusting it. Every deliverable polygon closed: 71 of 71, 49 of 49, 69 of 69, and the 3D area matched the vendor's own figure to 0.00 percent. I then parsed 16 real XML reports into a geometry.json each, with an affine fit residual of 0.11 to 0.14 feet. Doing that turned up something useful for the product: per-facet TSRF ships as a number in those reports, so shading per face does not have to be recomputed. The parser fed a standalone mini-designer with panel packing in both 2D and 3D.",
   "links": [],
   "tags": [
    "parser",
    "geometry",
    "certified truth",
    "XML",
    "validation"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-straight-skeleton-written-from-scratch",
   "title": "A straight skeleton written from scratch to keep the license clean",
   "group": "ML and models",
   "body": "For the fast 2D roof lane I needed a straight skeleton, the operation that turns a building outline into ridge and hip lines. The available implementation was LGPL, which would have followed us into the product, so I implemented the algorithm myself instead of vendoring it. It sat in a pipeline that took a building outline, split the mass using an aerial surface model, detected lines and fused oblique views, and it produced an outline in 3.4 to 8.6 seconds. Writing it rather than importing it cost a few days and left the whole lane free of a license I did not want to argue about later.",
   "links": [],
   "tags": [
    "straight skeleton",
    "licensing",
    "LGPL",
    "geometry",
    "2D pipeline"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-outline-is-the-binding-constraint",
   "title": "Finding the binding constraint before choosing a roof strategy",
   "group": "Decisions",
   "body": "I tested whether a seconds-fast 2D lane could stand in for a certified wireframe, and graded seven variants against certified truth instead of arguing about it. The answer was clean: when the lane started from a certified building outline, 14 of 15 results were fixable, and when it started from an outline the model produced itself, the results did not hold up. Everything downstream was the same, so the outline is the binding constraint. On clean references the median system-size error was 2.4 kW. That result is what let me write a three-tier product strategy, instant, certified and own-the-engine, with each tier chosen for what the outline source can actually support.",
   "links": [],
   "tags": [
    "benchmark",
    "product strategy",
    "constraint",
    "roof",
    "evidence"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-plane-first-roof-engine",
   "title": "Plane-first roof engine and a measured alignment warp",
   "group": "ML and models",
   "body": "I built a roof engine on FastAPI and Three.js that takes an address, pulls USGS 3DEP LiDAR through an EPT octree plus commercial ortho and surface data, with Google Solar as a fallback, and shows a photo-draped point cloud. Aerial sources do not line up perfectly, so I fitted a height-band-stratified phase-correlation warp with a guard that refuses any warp that makes the fit worse, and a residual heatmap to look at. Median roof residual went from 0.37 m to 0.06 m on one home and 0.39 m to 0.17 m on another. It loads 106,416 LiDAR points in about 6 seconds. From there the engine fits a RANSAC plane per face, derives line types where planes meet, and builds watertight 3D with step walls.",
   "links": [],
   "tags": [
    "LiDAR",
    "DSM",
    "RANSAC",
    "alignment",
    "Three.js",
    "FastAPI"
   ],
   "when": "June 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-byte-identical-acceptance-rule",
   "title": "Byte-identical output as the acceptance rule for a speed change",
   "group": "AI features shipped",
   "body": "The auto-designer uses Gemini 2.5 Flash to validate tree overhang for shading, and that call blocked the pipeline for 10 to 15 seconds. I moved it to launch the moment imagery exists so it overlaps segmentation and panel placement instead of waiting its turn. The rule I set for accepting the change was that the output had to be byte identical to before, not merely similar. That is the whole trick with a performance change on a path that produces a customer number: if you cannot state an equality condition up front, you are guessing about whether you changed the answer. It shipped on 21 May 2026 and removed 10 to 15 seconds from the run.",
   "links": [],
   "tags": [
    "Gemini",
    "latency",
    "acceptance criteria",
    "concurrency",
    "designer"
   ],
   "when": "May 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-site-survey-requirements-research",
   "title": "Site survey requirements, distilled from eight evidence groups",
   "group": "Decisions",
   "body": "Before writing anything for site surveys I read what the field already agrees on. I pulled eight evidence groups from NABCEP, the Department of Energy, a real authority-having-jurisdiction example, two industry site-survey tools, and mapped them against how the platform handles documents today. The proposal that came out of it is a common core of survey items that every job collects, plus versioned conditional requirements that switch on by jurisdiction or equipment, and a mobile architecture built for unreliable uploads in the field. It is written up as a plan with the sources attached, so the next person to build it can check my reading rather than take my word.",
   "links": [],
   "tags": [
    "site survey",
    "standards",
    "research",
    "requirements",
    "mobile"
   ],
   "when": "July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-reference-file-per-vendor-api",
   "title": "A written reference file for each vendor API",
   "group": "Integrations",
   "body": "For each vendor API the platform depends on I keep a reference file generated from the source, with call sites and payloads, plus a standing rule that any API-touching change updates the reference. It runs to 28 files and about 5,000 lines. I wrote the Gemini one after timing the real calls: about 4 seconds of wait actually exposed to the user, and about 16 seconds for local calls. There is a matching file for the OpenAI and Azure AI work that the bill reader uses. Writing these down is cheap and it removes a whole class of repeated investigation, because the next session opens the file rather than re-instrumenting the call.",
   "links": [],
   "tags": [
    "documentation",
    "vendor APIs",
    "Gemini",
    "measurement",
    "reference"
   ],
   "when": "May 2026 to July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-api-reference-library-and-postman-collections",
   "title": "An API reference library for every outside service",
   "group": "Integrations",
   "body": "I keep one reference document per outside service, generated from our own source so it lists the real call sites and payloads rather than the vendor's marketing page. There are 28 files and about 5,000 lines. The energy tariff vendor's runs to 905 lines and the lender's to 736. The set also covers address, property, mapping, imagery, weather, model and email providers, plus our own internal services. The standing rule is that any change touching an API updates the reference in the same pass, so it never drifts. Alongside it I keep Postman collections and environments, including live Run Analysis, Create Storage and Upload Consumption Profile calls, and I treat those as the source of truth before anything gets tested against a running system.",
   "links": [],
   "tags": [
    "integrations",
    "documentation",
    "postman",
    "apis"
   ],
   "when": "March 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-nrel-domain-migration-audit",
   "title": "A vendor domain move, scoped 16 days early",
   "group": "Integrations",
   "body": "NREL announced a domain move for 30 April 2026. I swept all 14 repositories for anything pointing at the old domain and found exactly two live references, a PVWatts v8 GET and an NSRDB typical-year URL, both in the Python service. That made it a two line fix, scoped and written down 16 days before the deadline. The point of the sweep was to turn an open question into a number. Once I knew it was two lines and not two hundred, the work stopped being a risk and became a task someone could do in an afternoon.",
   "links": [],
   "tags": [
    "integrations",
    "audit",
    "apis"
   ],
   "when": "April 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-roles-as-data-abilities-per-endpoint",
   "title": "Roles as data, abilities per endpoint",
   "group": "Platform engineering",
   "body": "I rebuilt authorization so that roles are data rather than code. Four business tier roles live as records, and every endpoint declares the ability it requires. The map from policy names to abilities fails the build if it is incomplete, so an endpoint cannot ship without an answer. I ran the gates in audit mode first, watched what real traffic asked for, then flipped them to enforcing. The admin service holds one canonical capability authority plus named action grants, and the membership contract was frozen as a version 3 shape across the identity, admin and CRM services on 31 July 2026 so all three agree on what a person is. Making the rules data meant a new role became a row, not a release.",
   "links": [],
   "tags": [
    "authorization",
    "access-control",
    "platform",
    "design"
   ],
   "when": "July 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-three-services-released-in-order",
   "title": "One change, three services, released in a fixed order",
   "group": "Platform engineering",
   "body": "Equipment identity moved to being keyed on the manufacturer SKU rather than the product name, which meant the same change had to land in three services at once: the admin service, the CRM API and the admin UI. I shipped it as three coordinated pull requests released in a set order, 8180 then 8181 then 8179, as releases 345, 104 and 63. Each had its own green gate: 154 of 154 tests, 101 of 101, and 24 of 24. Writing the order down first is what makes a multi service change boring. Anyone can follow the runbook, and no step depends on a shape that has not been deployed yet.",
   "links": [],
   "tags": [
    "release",
    "equipment",
    "coordination",
    "platform"
   ],
   "when": "July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-system-pricing-screen",
   "title": "The System Pricing screen, priced on the server",
   "group": "Platform engineering",
   "body": "I rebuilt how a system is priced on screen. The edit page is three columns, and every preview is priced by the server rather than by the browser, so what the salesperson sees is the same number the contract will carry. It shows an itemized system price, an add-ons price-per-watt breakdown, escalation, graduated battery pricing and Powerwall 3 pack pricing, over a reworked contractor floor filter and rate table. When pricing finishes, the server pushes a ready signal over SignalR instead of the page polling for it. The rule I held to throughout was that one place computes the money and every surface reads that one place.",
   "links": [],
   "tags": [
    "pricing",
    "product",
    "angular",
    "platform"
   ],
   "when": "March 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-lender-relationship-and-validation-proposals",
   "title": "Running the lender relationship, question by question",
   "group": "Integrations",
   "body": "I ran a standing check-in with the lender's team from 21 May 2026 and used it to close the questions an API document cannot answer. I sent written technical questions on the zero percent escalator, time-of-use post-solar rates, negative base price per watt, and what battery state of charge means in contract terms, including whether a reserve is backup or energy shift and where partial reserve sits. Their written answer was that backup pricing applies from a five percent reserve. I also mapped the path to production credentials, which is ten validation proposals they review. Getting answers in writing, from the people who own the rules, is faster than inferring them from behaviour and it survives staff changes on both sides.",
   "links": [],
   "tags": [
    "financing",
    "vendor-management",
    "integrations"
   ],
   "when": "April 2026 to July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-second-lender-spec-captured-early",
   "title": "Capturing a second lender's spec before we needed it",
   "group": "Decisions",
   "body": "The strategy was one lender working perfectly before adding a second. That does not mean waiting to learn anything about the second. While we were still deep in the first integration I captured a second lender's partner documentation as input: a 30 page third-party-ownership API specification, their milestone state machine, 12 PDFs and their approved vendor list data. Having a real second example on hand is what tells you which parts of your design are genuinely lender-neutral and which parts are shaped around one lender's assumptions. It cost a day of collection and it made every later architecture argument concrete instead of hypothetical.",
   "links": [],
   "tags": [
    "financing",
    "research",
    "integrations",
    "decisions"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-imagery-licences-read-before-training",
   "title": "Reading the imagery licences before training anything",
   "group": "Decisions",
   "body": "I read the signed imagery contract and the mapping platform's terms of service myself, with the clause numbers written down, before the roof model program went further. Both prohibit training machine learning models on their imagery and elevation data. So I set the posture in writing: research only on that imagery unless and until we get a written amendment, an amendment request sent to the account rep, and derivative works confirmed to survive termination. I also ruled that the new partner's data lane stays completely separate so the two datasets never mix. A model trained on data you are not licensed to train on is not an asset, so this is cheaper to settle first than to unpick later.",
   "links": [],
   "tags": [
    "licensing",
    "ml",
    "governance",
    "decisions"
   ],
   "when": "July 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-vendor-dependency-inventory",
   "title": "An inventory of 21 outside services the platform depends on",
   "group": "Infrastructure",
   "body": "I wrote down every external service the platform depends on and, for each one, what breaks without it, how it authenticates, whether its credentials can be rotated while it is live, whether it has a sandbox, and what it costs. That came to 21 services covering energy and tariff data, address and property data, lending, email and e-signature, mapping and imagery, model providers, identity, meetings and bot protection. On one page it becomes something you can plan against: an ownership column per service, a rotation order, and an answer to the question of what happens if any one of them goes away. It is the kind of document that takes a day and then gets used in every architecture conversation after it.",
   "links": [],
   "tags": [
    "vendors",
    "inventory",
    "operations",
    "infrastructure"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-domain-services-with-build-enforced-perimeters",
   "title": "Domain services with build-enforced write perimeters",
   "group": "Platform engineering",
   "body": "I directed a contractor through establishing canonical domain services for projects, clients, proposals, leads, documents and appointments, so there is one intended way to write each kind of record. The part that makes it hold is the enforcement: Roslyn analyzers SLX1005 through SLX1013 fail the build when code writes outside the perimeter. A convention written in a document decays. A convention checked by the compiler does not, and it does not depend on me being in the review. The same program added service-to-service authentication across the CRM API, admin, notification and scheduler services and both Python services. It ran to about 100 commits across 8 repositories.",
   "links": [],
   "tags": [
    "architecture",
    "analyzers",
    "dotnet",
    "platform"
   ],
   "when": "April 2026 to June 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-email-service-release-gates",
   "title": "Release gates and replay guards on the email service",
   "group": "Platform engineering",
   "body": "I worked the email service until sending an invitation is a governed operation rather than fire and forget. It has an explicit dispatch state policy, replay guards so the same invitation cannot go twice, and receipt-proof invariants so a claim that mail went out has evidence behind it. The release path gates fail closed and are scoped to exact stages, so an unclear pipeline result blocks rather than passes. Locally it uses a mail provider with no network access, and the key vault never defaults to production. I also removed AutoMapper so what maps to what is readable in the code. 32 commits in August 2026.",
   "links": [],
   "tags": [
    "email",
    "ci",
    "release",
    "platform"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-reversible-catalog-change",
   "title": "Making a catalog change reversible before making it",
   "group": "Platform engineering",
   "body": "Before changing anything in the equipment catalog I made the change reversible. That meant 14 JSON snapshots plus a full database backup, and an 11,705 line per-row audit CSV so every row that would change could be read and checked by a human before it did. Then the change ran in phases rather than as one sweep. Out of that work came a SKU model specification, which became the SKU-first identity the platform now uses for equipment. The discipline is simple: if I cannot describe how to undo a data change, I am not ready to make it.",
   "links": [],
   "tags": [
    "data",
    "equipment",
    "method",
    "platform"
   ],
   "when": "June 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-commit-record-across-ten-repositories",
   "title": "The commit record across ten repositories",
   "group": "Timeline",
   "body": "From 1 March to 18 August 2026 the ten repositories took 2,459 non-merge commits and 874 merge commits. About 2,190 of the non-merge commits are mine, roughly 89 percent. By repository: the CRM API 999, the CRM UI 867, the admin service 183, the admin UI 142, extraction 110, identity 60, email 46, the segmentation service 31, scheduler 13 and notifications 8. By month: 366 in March, 130 in April, 202 in May, 252 in June, 613 in July and 887 in August through the 18th. I count these because they are the one measure of output nobody has to take my word for.",
   "links": [],
   "tags": [
    "metrics",
    "git",
    "timeline"
   ],
   "when": "March 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-website-research-25-agents-105-sites",
   "title": "The research run that set the website's design language",
   "group": "Website and brand",
   "body": "Before designing the marketing site I ran 25 agents across 105 sites and made them return verdicts rather than opinions: 26 things to adopt, 64 to adapt, 15 to avoid. What came out was a design language I could actually build to. Instrument-grade restraint. A solar blueprint line vocabulary. Orange rationed to exactly three jobs. One signature motion moment per section and no more. No purple gradients and no glassmorphism. It also produced a build specification: IntersectionObserver with the Web Animations API for motion, self-hosted fonts, and WebGL confined to the hero. Doing the research first meant the design arguments later were about evidence, not taste.",
   "links": [],
   "tags": [
    "research",
    "design",
    "website",
    "multi-agent"
   ],
   "when": "July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-seo-baseline-across-the-site",
   "title": "The SEO baseline across every page",
   "group": "Website and brand",
   "body": "I set the same baseline on every page of the site: a unique title and description, a self-referencing canonical, exactly one H1, Open Graph and Twitter cards, Organization and WebSite structured data, Article structured data on blog posts, a sitemap, a robots file and an llms.txt. When the CEO changed the title and H1 to The Next Generation in Solar Sales and Design, I mirrored it in the first-paint skeleton so the rendered page and the markup say the same thing from the first frame. None of this is clever. It is a checklist applied to every page without exceptions, which is the part most sites skip.",
   "links": [],
   "tags": [
    "seo",
    "website",
    "structured-data"
   ],
   "when": "July 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-exec-review-site-behind-access",
   "title": "An executive review site, one branch per round",
   "group": "Website and brand",
   "body": "To get the website reviewed by executives I ran it on Cloudflare Pages behind Zero Trust Access, with a separate preview branch for each review round. That gave every round a stable link that only invited people could open, and it meant feedback attached to a specific version instead of to a moving target. Over 20 review rounds went through it, covering navigation order, partner naming, the tone of the finance page and hero copy. Reviewers could compare round three against round four themselves. The setup took an hour and removed most of the arguing about what had changed.",
   "links": [],
   "tags": [
    "website",
    "review",
    "cloudflare",
    "process"
   ],
   "when": "July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-four-vendor-api-capabilities-negotiated",
   "title": "Four API capabilities asked for, shipped the same day",
   "group": "Integrations",
   "body": "To automate blog publishing I needed four things the content vendor's API did not yet do: headless publish, lifecycle events, control over the URL pattern, and the ability to inject our brand kit into generation. I wrote them up as four specific capability requests with the reason for each, sent them, and they shipped all four the same day. Asking for named capabilities with the integration already designed around them works far better than asking a vendor to improve their API. They could see exactly what to build and how small it was.",
   "links": [],
   "tags": [
    "vendors",
    "apis",
    "content",
    "integrations"
   ],
   "when": "July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-directing-a-contractor-esign-build",
   "title": "Directing a contractor through the e-signature build",
   "group": "Integrations",
   "body": "The move to a hosted e-signature service was built by a contractor under my direction, and I set the shape before he started. It goes behind a provider seam, so the signing vendor is a swappable implementation rather than something threaded through the product. The webhook is HMAC verified. Envelope assembly, embedded signing and template resolution sit behind that seam, per-recipient status moves through five states and streams into the CRM over SignalR, and the deal advances to Contract Signed off that signal. About 110 commits went into it. I did the hardening pass on the parts that touch money and identity myself.",
   "links": [],
   "tags": [
    "esignature",
    "docusign",
    "contractor",
    "integrations"
   ],
   "when": "June 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-deal-backbone-frozen-at-signing",
   "title": "A deal record frozen at signing",
   "group": "Decisions",
   "body": "I wrote the build specification for a deal-level backbone with three rules. The deal record freezes at signing, so what was agreed stays readable no matter what changes afterwards. The gate that decides whether a change is allowed sits below the controllers, so it cannot be bypassed by adding a new endpoint. Lender rules live as configuration rather than as code branches, so a second lender is data. I got there by working the API from the outside in first: mapping which endpoints mutate state, which fields only the originating application can trigger, and building a truth table of what each milestone locks. Change orders are common enough in this business that I raised them to a launch blocker.",
   "links": [],
   "tags": [
    "architecture",
    "financing",
    "change-orders",
    "design"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-financing-product-sync",
   "title": "The financing product sync between admin and the CRM",
   "group": "Integrations",
   "body": "Financing products come from the lender and have to land correctly in our admin service and then in the CRM. I rewrote that sync to match on offer identifier rather than on anything that can be renamed, added a manual sync button so an admin can trigger a run on demand, and guarded it so a run returning zero products leaves the existing set alone. Deactivation persists, so a product an admin has deliberately switched off stays off through the next sync. I also added a paired reconciliation and republish endpoint, so when two sides disagree there is a defined way to compare them and push the truth again rather than guessing.",
   "links": [],
   "tags": [
    "financing",
    "sync",
    "admin",
    "integrations"
   ],
   "when": "March 2026 to May 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-seo-baseline-and-lighthouse-100",
   "title": "SEO baseline that scored 100 of 100 in an outside audit",
   "group": "Website and brand",
   "body": "I built the site wide SEO baseline by hand: unique titles and descriptions, self canonicals, one H1 per page, Open Graph and Twitter cards, Organization and WebSite JSON-LD, Article JSON-LD on posts, plus sitemap, robots.txt and llms.txt. An independent growth consultant audited it on 19 July 2026 and measured Lighthouse SEO 100 of 100 on the home, demo and pricing pages, with accessibility at 91 to 92. The audit task list is what drove the performance work that followed. I like this kind of check because the score is produced by someone else's tool, not by me.",
   "links": [],
   "tags": [
    "SEO",
    "lighthouse",
    "structured data",
    "audit"
   ],
   "when": "July to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-award-badge-svg",
   "title": "Award badge drawn as hand built SVG",
   "group": "Website and brand",
   "body": "When the company was named Top AI Solar Design Software 2026, I drew the badge instead of pasting an image. Four self contained SVG variants, no external assets, so it stays sharp at any size and costs nothing to load. The executives picked the laurel version and I deployed it, then moved it into the normal page flow after checking it on a real viewport rather than only in a mockup. Small piece of work, but it follows the same rule as the rest of the site: build the asset so it is part of the page, and verify it on the screen a visitor actually uses.",
   "links": [],
   "tags": [
    "SVG",
    "brand",
    "website"
   ],
   "when": "July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-one-command-local-stack-and-preflight",
   "title": "One command bring up and a 415 line preflight check",
   "group": "Infrastructure",
   "body": "I wrote the scripts that start, stop and check the local development stack. Bring up is idempotent with per port and per process guards, and stop targets processes by path so it never signals something else that happens to hold the same port. doctor.sh is a 415 line read only preflight with 10 sections: auth config, CORS, a no token 401 smoke test, repositories on main, port identity, a data floor, a host leak scan, roof JSON integrity, backup freshness and encrypted volume state. It changes nothing, it only reports. Anyone can run it before starting work and know in seconds whether the environment is sound.",
   "links": [],
   "tags": [
    "developer environment",
    "shell",
    "automation",
    "verification"
   ],
   "when": "June to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-self-healing-config-generators",
   "title": "Config generated from git, with a mutation tested guard",
   "group": "Infrastructure",
   "body": "I made local config regenerable instead of hand edited. The self heal script rebuilds the front end environment and proxy files straight from the committed version at HEAD, overrides exactly five host values, and exits with code 3 naming any unexpected host it finds. I mutation tested the guard, which means I deliberately broke the input and confirmed the script caught it, rather than trusting that it would. The API side uses the same idea: settings restored from a pinned canonical file with a keys only diff. The result is that config is derived rather than typed, and a wrong host fails loudly instead of quietly pointing somewhere it should not.",
   "links": [],
   "tags": [
    "configuration",
    "automation",
    "testing",
    "developer environment"
   ],
   "when": "June to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-disk-reclaim-backup-first",
   "title": "Reclaimed about 299 GB by backing up before deleting",
   "group": "Infrastructure",
   "body": "I ran a disk cleanup on my build machine in a fixed order, backups first. All six Docker volumes were dumped and gzip verified before anything was touched. Then a census of 1,016 worktree entries, a tarball of every dirty tree at 207 tarballs and 11 GB, and detached HEADs pinned so nothing became unreachable. Only then did I delete, and only directories I could prove were regenerable, which came to about 299 GB across two disks. I moved the worktree root to the external SSD and added a garbage collector that reports by default instead of deleting. An independent audit confirmed all 12 SQL backup members were byte identical and restored clean.",
   "links": [],
   "tags": [
    "storage",
    "backups",
    "git worktrees",
    "verification"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-least-privilege-onboarding-playbook",
   "title": "A least privilege onboarding playbook for new engineers",
   "group": "Infrastructure",
   "body": "I wrote the playbook for giving a new engineer cloud and DevOps access. It uses a two identity model, names the five role assignments that are actually needed and nothing more, and documents the first sign in behavior that otherwise looks like a broken account. The value is that access grants stop being improvised per person. Someone follows the list, the new engineer can work on day one, and the set of things they can reach is written down and reviewable later. Least privilege only holds if the correct grant is easier to apply than the lazy one, so I made the correct one a checklist.",
   "links": [],
   "tags": [
    "access",
    "onboarding",
    "documentation",
    "cloud"
   ],
   "when": "April to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-homelab-uptime-measured",
   "title": "Measured the home server before rebuilding it",
   "group": "Personal builds",
   "body": "I read the logs on my always on home server before touching the hardware, and found 21.5 days of continuous uptime and zero unexpected shutdowns in 45 days. What people were describing as random crashes was the network dropping, not the machine restarting. That changed the plan. I inventoried the box, an i7-7700 with 32 GB and two 8 TB drives mirrored, then specified a rebuild on Unraid with Docker rather than replacing hardware that was working fine. Measuring first cost an evening and saved the wrong fix.",
   "links": [],
   "tags": [
    "homelab",
    "measurement",
    "unraid",
    "docker"
   ],
   "when": "June to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-local-llm-privacy-broker-design",
   "title": "Designed a local model as a privacy broker in front of the cloud",
   "group": "Personal builds",
   "body": "I designed a split where a local Qwen class model sits between my sensitive data and any cloud model. The local model is the only thing holding sensitive access. It reads what it needs and passes a distilled result upward, so the cloud model gets the shape of the question and never the raw material. Every action path has a never auto act gate, meaning the system can propose and cannot execute on its own. This is the same principle I use elsewhere: decide where data is allowed to travel first, then build the assistant inside that boundary rather than adding rules to it afterwards.",
   "links": [],
   "tags": [
    "local LLM",
    "privacy",
    "architecture",
    "Qwen"
   ],
   "when": "June to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-contractor-working-model",
   "title": "How I scoped work for a contractor",
   "group": "Decisions",
   "body": "I brought on a part time 1099 contractor and wrote down the working model before he started. He gets outcome level tickets plus the reference material, and he decomposes his own sub issues. I do not hand a senior person a task list, because the decomposition is most of the value I am paying for. He worked with full access, and his campaign of 23 pull requests across nine services was independently audited read only afterwards. That combination is the point: wide autonomy on how the work is done, and an independent check on what landed, so trust is granted up front and verified after.",
   "links": [],
   "tags": [
    "contracting",
    "management",
    "code review",
    "scoping"
   ],
   "when": "April to July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-one-lender-first-sequencing",
   "title": "One financing partner working perfectly before adding others",
   "group": "Decisions",
   "body": "I argued for finishing one financing partner completely before onboarding a second. The goal was to become their recommended vendor, which only happens if the integration is right rather than broad. In practice that meant working through 10 validation proposals to earn production credentials with the first partner while deliberately deferring the rest. Breadth looks better on a slide and costs more to maintain, because every partner adds its own pricing rules and its own failure modes. Depth first gave us one path a salesperson could actually rely on, and a reference relationship worth more than a longer list.",
   "links": [],
   "tags": [
    "strategy",
    "partners",
    "financing",
    "sequencing"
   ],
   "when": "March to June 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-hiring-bar-identity-verification",
   "title": "Set an identity verification bar for engineering hiring",
   "group": "Decisions",
   "body": "I wrote the job description for a senior full stack contract to hire role, posted it on 13 May 2026, and ran the screening across seven applicants myself. Then I wrote down the standing bar so the next round does not depend on my instincts: live video with government identification on camera, live screen share coding, LinkedIn must match the resume, and references only through a channel the company controls. Each of those is cheap to run and hard to fake. Writing them down as a fixed bar matters more than any single call I made, because it means the same checks apply whether or not anything about an application looks unusual.",
   "links": [],
   "tags": [
    "hiring",
    "verification",
    "process"
   ],
   "when": "May 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-field-registry-one-definition",
   "title": "One field definition generates the form, the validation and the importer",
   "group": "Personal builds",
   "body": "In the rental app I put every field in a single registry. One definition generates the form control, the API validation rules and the importer schema. Adding a field is one edit in one place, and the three surfaces cannot disagree with each other, because they are all produced from the same source. Most data quality bugs in a small app come from exactly that drift: the form accepts something the API rejects, or the spreadsheet importer writes a shape the form cannot display. Generating all three removes the class of bug instead of fixing instances of it.",
   "links": [],
   "tags": [
    "architecture",
    "landlord app",
    "validation",
    "code generation"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-human-approved-spreadsheet-import",
   "title": "A spreadsheet importer where nothing lands without a human approving it",
   "group": "Personal builds",
   "body": "The rental app imports messy spreadsheets with AI, in three steps: parse, human review per row, commit. Nothing is written to the database until a person approves that row, so the model is doing extraction and the human is doing the deciding. A full import costs about two cents. I built it this way because the alternative, trusting a model to write records directly, means every later number in the app inherits an error nobody saw. Reviewing rows takes a few minutes once. A wrong ledger takes months to notice.",
   "links": [],
   "tags": [
    "AI",
    "import",
    "human in the loop",
    "landlord app"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-helper-assistant-cannot-invent-a-number",
   "title": "An assistant that reads the ledger and cannot invent a number",
   "group": "Personal builds",
   "body": "The rental app has an assistant called Helper. It reasons over the deterministic ledger data and it cannot produce a number of its own. Every figure it says is one the ledger computed, so a question about what is owed this month is answered by the same code that renders the screen. I added voice through OpenAI Realtime over WebRTC, with the session tokens minted server side and short lived so no key ever reaches the browser, and I validated it running as a progressive web app on iOS. The constraint is the feature here. An assistant that can talk about money and cannot make money up is one you can hand to someone else.",
   "links": [],
   "tags": [
    "AI assistant",
    "voice",
    "OpenAI Realtime",
    "landlord app"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-os-enforced-ai-data-boundary",
   "title": "An operating system boundary keeps financial data away from cloud models",
   "group": "Personal builds",
   "body": "In my household finance system the rule is that real financial data never reaches a cloud model. I did not implement that as a prompt or a policy document. The whole stack runs under an isolated macOS user account, so the boundary is enforced by the operating system and holds even if I write bad code. The repository and its tests contain synthetic data only. Real provider connections sit behind a fail closed production gate and an encrypted credential broker, meaning the default state is no access. A local model handles conversation inside that boundary. If a rule matters, make something other than intention enforce it.",
   "links": [],
   "tags": [
    "privacy",
    "local LLM",
    "security",
    "household assistant"
   ],
   "when": "July to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-deterministic-engine-owns-every-number",
   "title": "Deterministic integer cent code owns every number",
   "group": "Personal builds",
   "body": "The household assistant is a TypeScript monorepo on Node 20 where ordinary deterministic code owns every number, held in integer cents so nothing rounds by accident. That code produces the balance sheet, a 30 day cash flow anchored to the coverage I have actually verified with each provider, the separation of real purchases from transfers between my own accounts, and recurring detection that carries an explicit unverified state instead of guessing. The model layer sits on top and explains. It never calculates. When the engine does not know something it says so, which I prefer to a confident wrong figure.",
   "links": [],
   "tags": [
    "finance",
    "determinism",
    "TypeScript",
    "household assistant"
   ],
   "when": "July to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-receipt-capture-local-ocr",
   "title": "Receipt capture with on device OCR and deterministic matching",
   "group": "Personal builds",
   "body": "I added receipt capture to the household assistant from my phone. The text extraction runs through Apple Vision on the device, so the photo of a receipt never goes to a service to be read, and the extracted line items are matched to the existing charge by deterministic rules rather than by asking a model which one it probably is. The dashboard cards and charts on top are declarative, so I add a new view by describing it rather than writing another component. Same pattern throughout: local where the data is sensitive, deterministic where a number is involved.",
   "links": [],
   "tags": [
    "OCR",
    "Apple Vision",
    "privacy",
    "household assistant"
   ],
   "when": "July to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "build-closet-app-confidence-gated-indexing",
   "title": "Closet app: AI indexing with confidence gated human review",
   "group": "Personal builds",
   "body": "I built a private digital closet as a progressive web app on Cloudflare. Photos go into a persistent upload queue, a Worker and queue consumer index them, and Cloudflare Queues with a dead letter queue give at least once processing so nothing is silently dropped. Workers AI vision extracts a record that must pass a schema, and anything below the confidence threshold routes to human review instead of being written as fact. Metadata, batches, jobs, per field model and prompt provenance, confidence and review state all live in D1. Originals sit in private R2 and are only served through authenticated routes. Cloudflare Access is the boundary and the Worker revalidates the Access token itself, so the deployment is fail closed by design.",
   "links": [],
   "tags": [
    "Cloudflare Workers",
    "queues",
    "Workers AI",
    "human in the loop"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-cross-machine-agent-control-over-mcp",
   "title": "An agent on one machine driving an application on another",
   "group": "Personal builds",
   "body": "For a game prototype I had Codex running on my Mac drive Roblox Studio on my Windows PC through Studio's MCP server. The agent lived where my tooling and memory were, and it acted inside an application on a different machine that only runs there. Around it I used Rojo for syncing source into Studio, rokit for toolchain pinning, and selene and stylua for linting and formatting, so the generated code went through the same checks I would apply to anything else. It is a small demonstration of a general thing: MCP lets an agent reach an application it cannot be installed next to.",
   "links": [],
   "tags": [
    "MCP",
    "automation",
    "Codex",
    "cross-machine"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "build-moonboard-cad",
   "title": "A parametric climbing wall study in CadQuery",
   "group": "Personal builds",
   "body": "I modelled a transportable 40 degree climbing wall in CadQuery on OpenCascade, parametric so the dimensions follow the room rather than a fixed drawing. I sized it against a Gaussian splat scan of the actual loft taken with Scaniverse, which meant the fit study used the real space instead of assumed measurements. Outputs were STEP and GLB files, five Blender renders, a site fit study labelled not for fabrication, a Next.js viewer for spinning the model in a browser, and a printable package PDF. It is paused pending field measurements, because a scan is good enough to design against and not good enough to build from.",
   "links": [],
   "tags": [
    "CAD",
    "CadQuery",
    "Blender",
    "3D"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-audit-first-reset-on-cad-work",
   "title": "Audit first, then continue",
   "group": "Personal builds",
   "body": "On the CAD project I checked the geometry the AI had produced and found it would not build. My response was not to fix that one model. I set aside the batch and re-audited before continuing. I declared everything produced up to that point untrusted and imposed an audit first reset: open each artifact, check it against what it claimed to be, and carry forward only what passes. The audit rejected the frame design as non buildable, which was the correct answer and would have cost real money to learn later. This is the habit I rely on with agents. When one output turns out to be confidently wrong, the assumption that the others were checked no longer holds.",
   "links": [],
   "tags": [
    "verification",
    "AI oversight",
    "CAD",
    "process"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-vault-session-end-auto-summary",
   "title": "Automatic session summaries in the personal vault",
   "group": "AI environment",
   "body": "My personal vault writes its own record. A script runs on two lifecycle events, session end and pre compact, and calls Claude in headless mode to summarise what the session decided and did, then files it into the daily log. It has three self trigger guards, because a summariser that invokes Claude can easily invoke itself in a loop. A separate hook injects the right context at session start by routing on the working directory, so a personal repository loads personal memory and nothing else. The effect is that decisions get written down without me remembering to write them down, which is the only version of note keeping that survives a busy week.",
   "links": [],
   "tags": [
    "hooks",
    "memory",
    "automation",
    "Claude Code"
   ],
   "when": "July to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-commit-activity-across-ten-repos",
   "title": "Commit activity across ten repositories, measured",
   "group": "Platform engineering",
   "body": "I benchmarked my own output rather than describing it. Across ten repositories between March and August 2026 I authored roughly 2,190 commits. The number comes from git log in each repository with a date filter, so it is reproducible by anyone with access rather than a figure I estimated. It includes commits I produced working with AI agents, which is how I work and how the number should be read. I keep this measured because throughput claims are easy to make and easy to check, and I would rather publish the check than the claim.",
   "links": [],
   "tags": [
    "metrics",
    "git",
    "throughput"
   ],
   "when": "March to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-wedgewood-weddings-banquet-and-sales-coordination",
   "title": "Banquet operations and sales coordination at Wedgewood Weddings",
   "group": "Timeline",
   "body": "I worked at Wedgewood Weddings from September 2021 to May 2024 as a Banquet Operations and Sales Coordinator. It is where I learned how a business runs on the day itself: the handoffs between sales and operations, what a promise to a customer costs to keep, and how much of the work is coordination rather than execution. That grounding is why I start any project by sitting with the people doing the work instead of with the tooling. I still use the habits from that job. Write the plan down, confirm it with the people who have to deliver it, and check the result yourself.",
   "links": [],
   "tags": [
    "wedgewood weddings",
    "operations",
    "hospitality",
    "early career"
   ],
   "when": "September 2021 to May 2024",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-amazon-outbound-logistics-and-trailer-pool-reporting",
   "title": "Outbound logistics and data analytics at Amazon",
   "group": "Timeline",
   "body": "At Amazon Corporate in Tempe, Arizona I worked in business logistics and data analytics, starting in May 2024. The scope was outbound shipment of more than 62 million units a year, plus reporting on the US trailer pool. That job taught me to trust measurement over opinion. At that volume nobody can eyeball whether something is working, so you build the number, then you check the number against reality before anyone acts on it. It is the same discipline I use now when I audit a cost line or a savings calculation. Get the real figure, show the method, and let the figure carry the argument.",
   "links": [],
   "tags": [
    "amazon",
    "logistics",
    "data analytics",
    "reporting"
   ],
   "when": "From May 2024",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-business-degree-from-arizona-state",
   "title": "Business degree from Arizona State",
   "group": "Timeline",
   "body": "I hold a Bachelor of Business Administration from Arizona State University. I came into technology from the business side, and that shapes how I work. I report in hours, dollars and correctness, and I am comfortable making a buy or build call in front of a finance-minded room because I can show the arithmetic behind it. The engineering came after the degree, learned by shipping production software and writing down what worked.",
   "links": [],
   "tags": [
    "education",
    "arizona state university",
    "bba",
    "business"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-joining-solentrex-in-a-qa-capacity",
   "title": "Starting at Solentrex in a QA capacity",
   "group": "Timeline",
   "body": "I joined Solentrex in December 2025 in a QA and testing capacity. My first meeting invite was 16 December 2025 and I sent a QA feedback document to the COO on 26 December 2025. Starting on testing was useful. Before I wrote a line of production code I had read the platform from the outside, driven it as a user would, and written down what I found. My first production commit came on 28 February 2026. By then I already knew the product from the user's side, which is why the first months of building went fast.",
   "links": [],
   "tags": [
    "solentrex",
    "qa",
    "start date",
    "testing"
   ],
   "when": "December 2025",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-the-only-technology-voice-in-the-executive-room",
   "title": "The only technology voice in the executive room",
   "group": "Decisions",
   "body": "Since January 2026 I have been the only technology voice on the weekly executive operations committee. I do not receive tickets. I set technical direction, handle vendor negotiations, and make the buy or build and staffing calls. That changes how I work. Every recommendation has to survive being questioned by a finance-minded room, so I bring the measurement with me rather than the opinion, and I write the decision down afterwards with the reasoning and the alternatives that lost. If a call I made turns out wrong, the record shows what I knew when I made it.",
   "links": [],
   "tags": [
    "executive committee",
    "technical direction",
    "governance",
    "solentrex"
   ],
   "when": "January 2026 to now",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-five-months-of-shipping-in-numbers",
   "title": "Five months of shipping, in numbers",
   "group": "Platform engineering",
   "body": "Since 1 March 2026 I have authored 1,928 commits across the four core repositories and merged 799 pull requests to main. The organisation's pull request numbering advanced by about 895 over the same stretch. Between 13 March and 4 April 2026, my first three weeks working solo, that was more than 280 commits across six repositories. Volume is not quality on its own, so I pair it with a written testing standard and verification by a separate agent before I call anything done. But the throughput number answers a fair question from anyone hiring: can one person actually carry a platform, and for how long.",
   "links": [],
   "tags": [
    "commits",
    "pull requests",
    "throughput",
    "delivery",
    "repositories"
   ],
   "when": "March 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-the-append-only-daily-engineering-log",
   "title": "An append-only engineering log, 137 days running",
   "group": "AI environment",
   "body": "I keep an append-only engineering log, one file per working day, and it has run for 137 consecutive working days. It holds around 684,500 words and 940 logged session starts. Alongside it sit 1,119 discovery notes and 34 structured decision records. Append-only matters. Nothing is edited after the fact, so the log is a record of what I knew on the day rather than a tidied version written later. Every AI session I run writes into it and reads from it, which is why I never have to re-explain a project. The log is the reason a question about work from three months ago has an answer with a date on it.",
   "links": [],
   "tags": [
    "daily log",
    "memory",
    "documentation",
    "discovery notes",
    "decision records"
   ],
   "when": "April 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-one-page-current-state-of-every-workstream",
   "title": "One page that holds the current state of every workstream",
   "group": "AI environment",
   "body": "On top of the daily log I maintain a single current-state file: 1,314 lines covering 39 workstream blocks. The daily log tells you what happened. This file tells you where everything stands right now, in one place, in one pass. Any agent I start reads it before it does anything else, and so do I on a Monday morning. Keeping it to one page forces a decision on every workstream: it is either live, in progress, parked, or finished. There is no room for a block that has quietly meant nothing for six weeks.",
   "links": [],
   "tags": [
    "live status",
    "workstreams",
    "status reporting",
    "memory"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-heartbeat-instrumentation-for-the-hooks",
   "title": "Heartbeat instrumentation that proves the hooks fired",
   "group": "AI environment",
   "body": "Every hook fire in my AI environment is stamped and logged to a file, and a verify script reads it back. Since 3 August 2026 that log has recorded 321 fires. On top of that I use a handshake: the agent has to repeat a specific phrase back to me at the start of a session, which proves the context file was actually read rather than skimmed past. Automation you cannot see is automation you are trusting on faith. Both checks are cheap, and together they mean I can answer the question \"did the memory system load\" with a timestamp instead of an assumption.",
   "links": [],
   "tags": [
    "hooks",
    "instrumentation",
    "verification",
    "logging",
    "heartbeat"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-session-summaries-written-by-a-script-not-a-model",
   "title": "Session summaries written by a script, not a model",
   "group": "AI environment",
   "body": "I took the AI out of the critical path of its own memory. A shell script reads the raw session transcript and writes the summary itself when a session ends or before context is compacted. It is incremental, so it only processes what is new, and it carries three separate recursion guards so it can never trigger itself. The reason is simple. If the model has to be healthy to save the record, then the record fails exactly when you need it most. A script that reads the transcript off disk keeps working when the session does not.",
   "links": [],
   "tags": [
    "hooks",
    "summaries",
    "determinism",
    "memory",
    "reliability"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-a-seven-worker-parallel-session-run-as-a-study",
   "title": "A seven-worker parallel session, run as a study",
   "group": "AI environment",
   "body": "On 1 August 2026 I ran seven AI workers in parallel for fourteen hours on the authentication program, and I treated it as a controlled study rather than just a work session. The most useful result was that four of the managing agent's own claims were refuted by its workers. That produced a rule I now apply everywhere: a manager agent that is never contradicted is not being verified. It is the same principle as a real engineering team. If the status report always matches the plan, the reporting line is the thing to check, not the plan.",
   "links": [],
   "tags": [
    "multi-agent",
    "parallel agents",
    "verification",
    "pm worker",
    "study"
   ],
   "when": "1 August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-apollo-level-rigor-as-a-written-standard",
   "title": "Apollo-level rigor as a written standard",
   "group": "AI environment",
   "body": "I wrote a standard my agents work to and called it Apollo-level rigor. Four rules carry most of it. Testing is not reading, so running the thing beats reasoning about the code. Grep every caller before you change a shared function. Every magic number needs a written rationale. And never confuse \"the process completed\" with \"the product is correct\", because a green run and a right answer are different claims. Each rule is tied to something specific I learned once. Writing them down means the same lesson does not have to be learned twice, by me or by an agent.",
   "links": [],
   "tags": [
    "standards",
    "testing",
    "rigor",
    "verification",
    "magic numbers"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-mission-control-for-the-local-stack",
   "title": "Mission Control, a dashboard for the local stack",
   "group": "Infrastructure",
   "body": "I built Mission Control, a zero-dependency Node service that starts and monitors the whole local stack: five application services and eight Docker containers. It runs under launchd so it survives a reboot, binds only to a private mesh network rather than the open internet, and installs as a progressive web app so I can check state from a phone. I also wrote a 441-line tool server that exposes the same stack to my AI agents, so an agent can see whether a service is up instead of guessing. Zero dependencies was deliberate. The thing that tells you what is broken should not be able to break on its own.",
   "links": [],
   "tags": [
    "mission control",
    "dashboard",
    "node",
    "launchd",
    "pwa",
    "docker"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-the-only-gate-is-production",
   "title": "The only gate is production",
   "group": "AI environment",
   "body": "I worked out the authorization boundary for chained agents and wrote it down. Running agents in a chain raises a question nobody warns you about: how does an agent know it has permission when the permission came from a human two hops back. Authorization relayed through a file is not the same as authorization from the person at the keyboard, and the tooling correctly refuses external changes on that basis. So I drew the line in one sentence: the only gate is production, and local is never gated. Agents can move fast where mistakes are cheap and reversible, and a human stands in front of the one place where they are not.",
   "links": [],
   "tags": [
    "authorization",
    "agent safety",
    "policy",
    "local development",
    "governance"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-a-packaged-local-stack-for-new-hires",
   "title": "Packaging the local stack so a new hire can run it",
   "group": "Decisions",
   "body": "The whole platform runs on my desk with one command to start, one to stop, and a 415-line doctor script that checks every part of the health chain and says which piece is unhappy. On 14 August 2026 I ruled that this stops being my personal setup and becomes a packaged, repo-resident one, so a new engineer can clone and run rather than spend their first week wiring an environment. A stack that only works on the machine that built it is a liability. Moving it into the repository is what turns my setup into the team's setup.",
   "links": [],
   "tags": [
    "local development",
    "onboarding",
    "decision",
    "developer experience",
    "mac studio"
   ],
   "when": "14 August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-a-second-lender-certification",
   "title": "A second lender certification, passed over two months",
   "group": "Integrations",
   "body": "I took the platform through a second lender certification: a two-month LightReach certification with Palmetto, passed in August 2026. Two independent lenders certifying the same engine is worth more than one, because they check different things and they do not coordinate with each other. This one ran over a longer window and covered a different lender's process and paperwork end to end. For a pre-revenue platform, certifications are the closest thing to an outside audit you can get before customers arrive, so I treat them as the bar rather than as a formality.",
   "links": [],
   "tags": [
    "certification",
    "lightreach",
    "palmetto",
    "lender",
    "financing"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-standing-technical-liaison-to-the-lender",
   "title": "Standing technical liaison to the lender",
   "group": "Integrations",
   "body": "I am the standing technical contact between our platform and the lender, with recurring check-ins with their technical lead, and I led the platform demo to their integrator lead on 12 June 2026. Being the same face at every call is worth a lot on an integration. Their team knows who to ask, questions get answered in one hop instead of three, and when something needs to change on our side I can commit to it in the meeting. It also means the integration decisions and the relationship sit with the same person, so nothing gets promised that the code cannot do.",
   "links": [],
   "tags": [
    "goodleap",
    "lender",
    "partner management",
    "demo",
    "integration"
   ],
   "when": "May 2026 to now",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-verifying-contractor-onboarding-at-volume",
   "title": "Verifying contractor onboarding at volume",
   "group": "Integrations",
   "body": "I got contractor onboarding with the lender working ahead of the July deadline executives had set, and I verified it the way I verify anything: by creating 25 real users through the flow and confirming each one landed. Proving it at volume is the part that mattered. A single successful create tells you the endpoint responds. Twenty-five tells you the process holds up when the sales team starts using it every day. I would rather spend an afternoon running the same flow twenty-five times than report it done off one happy path and find out later.",
   "links": [],
   "tags": [
    "onboarding",
    "goodleap",
    "epc",
    "verification",
    "users"
   ],
   "when": "June to July 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-writing-the-engineering-job-description-and-screening",
   "title": "Writing the engineering job description and screening for it",
   "group": "Decisions",
   "body": "I wrote the Senior Full-Stack Engineer job description, posted it on 13 May 2026, and set the hiring criteria myself. The hard gate was verifiable honesty rather than a technology checklist. I can teach a stack. I cannot fix someone who describes work they did not do. I screened the first batch personally and checked claims against evidence before anyone was given access to anything. Screening for honesty is slower than screening for keywords, because every claim has to be traced back to something real. It is also the only gate that matters when one person is going to hold production credentials.",
   "links": [],
   "tags": [
    "hiring",
    "job description",
    "screening",
    "criteria",
    "team"
   ],
   "when": "May 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-features-first-then-one-stabilization-pass",
   "title": "Features first, then one deliberate stabilization pass",
   "group": "Decisions",
   "body": "On 22 June 2026 I gave the CEO my engineering sequencing for go to market: build the features first, then take one deliberate stabilization pass across all of them, with an eight-item priority list attached. The alternative, stabilising continuously as you go, sounds safer and is slower, because you pay the hardening cost on code that is still changing shape. One pass at the end, on a frozen surface, catches more for less. On 10 August 2026 leadership moved marketing execution off my plate so I could finish the platform for the 1 October go-live date.",
   "links": [],
   "tags": [
    "sequencing",
    "roadmap",
    "go to market",
    "prioritisation",
    "stabilization"
   ],
   "when": "June to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-real-time-pricing-and-project-360",
   "title": "Real-time pricing from about 25 live data points",
   "group": "AI features shipped",
   "body": "Pricing in the platform is assembled live from about 25 simultaneous API data points rather than from a cached table. A sales rep sees the real number for the real address at the moment they are looking, not a figure that was true last week. I also unified the proposal and battery workflows into one path, and shipped Project 360, a single view of a project with staged pending-change tracking so an edit can be seen and reviewed before it becomes the record. The theme across all three is the same: one place to look, and the number on screen is the current one.",
   "links": [],
   "tags": [
    "pricing",
    "apis",
    "project 360",
    "proposals",
    "workflow"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-battery-count-recommended-from-usage",
   "title": "Battery count recommended from a household's own usage",
   "group": "AI features shipped",
   "body": "The platform recommends how many batteries a home needs from that home's own usage data, staying inside the lender's soft limits, and calculates backup capability from a reserve level rather than a rule of thumb. Storage dispatch runs against the time-of-use plan actually applied to the project, which is what makes the savings figure specific to that household rather than generic. I verified that live, changing the applied plan and watching the number follow. A battery recommendation is a several thousand dollar suggestion to a homeowner, so it should come from their meter data, not from a default.",
   "links": [],
   "tags": [
    "battery",
    "storage dispatch",
    "time of use",
    "backup",
    "recommendation"
   ],
   "when": "June to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-costing-cloud-services-to-the-unit-rate",
   "title": "Costing cloud services down to the unit rate",
   "group": "Infrastructure",
   "body": "When I audit a cloud bill I take it down to the unit rate and the source. For the container registry that meant pricing storage per GiB-month and measuring the actual gigabytes held. For logging it meant measuring the real cost per gigabyte ingested and then tracing the spend back to the specific pipe producing the volume. A monthly total tells you nothing you can act on. A unit rate multiplied by a measured quantity tells you exactly which knob to turn, and it lets me put a defensible number in front of finance instead of an estimate. Every cost conversation I have starts from that arithmetic.",
   "links": [],
   "tags": [
    "cost",
    "cloud",
    "unit economics",
    "audit",
    "logging"
   ],
   "when": "August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-right-sizing-an-imagery-renewal",
   "title": "Right-sizing an aerial imagery renewal with usage data",
   "group": "Integrations",
   "body": "Ahead of an annual aerial imagery renewal I pulled the credit consumption apart account by account instead of taking the total at face value. That gave me a per-account picture of what the business actually consumes and why. With that in hand the conversation with the vendor changed direction: instead of buying a larger plan to match the headline usage, we could right-size the plan down to real need. Renewals are usually negotiated on relationship and on last year's number. They are much easier to negotiate when you bring your own usage data and can show where every unit went.",
   "links": [],
   "tags": [
    "vendor management",
    "renewal",
    "aerial imagery",
    "usage",
    "negotiation"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-backup-retention-across-production-databases",
   "title": "Long-term backup retention on every production database",
   "group": "Infrastructure",
   "body": "As part of the production authentication cutover on 13 August 2026 I put long-term backup retention on all five production databases. Before any planned reset I exported the data first: 53 tables and 89,205 rows written out and kept. Both steps are unglamorous and both are non-negotiable for me. A migration you cannot reverse is not a migration, it is a bet. Taking the export costs an hour. Not having one costs the company its record. I would rather be the person who took a backup nobody needed than the person explaining why there is not one.",
   "links": [],
   "tags": [
    "backups",
    "retention",
    "production",
    "databases",
    "data safety"
   ],
   "when": "13 August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-design-output-back-tested-against-metered-systems",
   "title": "Design output back-tested against metered systems",
   "group": "ML and models",
   "body": "I back-tested the production design model against real metered production data from installed systems and measured a variance of minus one percent. The comparison point in the same test came in at plus two percent. What I take from that is narrow and I state it narrowly: on that data set, the model under-predicted slightly, which is the safer direction to be wrong in when a homeowner is being shown a savings figure. One back-test is not a general accuracy claim, and I do not present it as one. It is a measurement on a specific set of metered systems, with the method written down next to it.",
   "links": [],
   "tags": [
    "accuracy",
    "back testing",
    "validation",
    "production model",
    "benchmark"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-manager-solution-delivery-dec-2025",
   "title": "Manager, Solution Delivery, from December 2025",
   "group": "Timeline",
   "body": "My first role at Solentrex was Manager, Solution Delivery and Technical Implementation. It ran from December 2025 to March 2026 and it was remote. I led algorithm validation, and drove measurable improvements in modeling accuracy through iterative testing and data analysis. The skills recorded against that role are software quality assurance and Google Apps Script. It was a delivery and correctness job before it was an engineering ownership job. In March 2026 I moved into Director of Technology at the same company.",
   "links": [],
   "tags": [
    "solentrex",
    "first role",
    "algorithm validation",
    "quality assurance",
    "google apps script"
   ],
   "when": "December 2025 to March 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-director-of-technology-mar-2026",
   "title": "Director of Technology, from March 2026",
   "group": "Timeline",
   "body": "I have been Director of Technology at Solentrex since March 2026. I lead engineering and technology. The work is platform modernization, third-party financing integrations, and data accuracy across a multi-service product stack. I translate product requirements into technical decisions, and I own the architecture, the development and the delivery of the platform end to end. I address issues at the root rather than working around them, and I build integrations with major financing partners that perform reliably in production. Before this I was Manager, Solution Delivery at the same company, from December 2025.",
   "links": [],
   "tags": [
    "solentrex",
    "director of technology",
    "platform",
    "integrations",
    "architecture"
   ],
   "when": "March 2026 to now",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-what-solentrex-builds",
   "title": "What Solentrex builds",
   "group": "About",
   "body": "Solentrex is an end-to-end AI-driven solar CRM, design and proposal platform for EPCs. It is B2B software and I work on it remote. The product covers the path from a customer record through a system design to a proposal, so the engineering spans a CRM, a design engine and financing integrations rather than one application. That shape explains most of my work there. It is a multi-service stack, and the same number has to be right in the CRM, in the design and on the proposal.",
   "links": [],
   "tags": [
    "solentrex",
    "solar",
    "crm",
    "b2b saas",
    "epc"
   ],
   "when": "December 2025 to now",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-five-months-of-commits-and-pull-requests",
   "title": "1,800 commits and nearly 800 merged pull requests in five months",
   "group": "Platform engineering",
   "body": "In five months as the only in-house engineer I shipped over 1,800 commits and nearly 800 merged pull requests. The platform is 14 repositories: Angular, .NET, SQL Server, Azure and Python AI services. Inside that run is the lender financing integration that passed certification on 100 of 100 addresses, with feedback rating it as accurate as the market leader. The counts are the honest way to describe the pace. I would rather give a number than an adjective, and those two are the numbers I would put in front of anyone asking what a single engineer got done in that window.",
   "links": [],
   "tags": [
    "throughput",
    "pull requests",
    "commits",
    "14 repositories",
    "solo engineer"
   ],
   "when": "March 2026 to August 2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-executive-committee-seat",
   "title": "The only technology voice on the weekly executive committee",
   "group": "Decisions",
   "body": "Since January 2026 I have sat on the weekly executive committee as the only technology voice. That means I bring the vendor, security and hiring calls into the room in plain English, and then I go and build what the room agrees to. Two examples. The roof-data vendor swap, where I built the proof of concept the same afternoon I recommended it. And the identity decision, where I recommended buying WorkOS rather than building, and then shipped it. The seat started a month after I joined and two months before I took the director title.",
   "links": [],
   "tags": [
    "executive committee",
    "vendor decisions",
    "security",
    "hiring",
    "solentrex"
   ],
   "when": "January 2026 to now",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-amazon-business-logistics-and-analytics",
   "title": "Amazon, Business Logistics and Data Analytics, May 2024 to December 2025",
   "group": "Timeline",
   "body": "I worked at Amazon in Business Logistics and Data Analytics from May 2024 to December 2025, full time and hybrid out of the corporate office in Tempe, Arizona. The job was solving operational problems and escalations with industry standard data analysis practice, to mitigate risk and improve network stability. Day to day I monitored the US-wide active trailer pool and turned raw data into same-day decisions for fulfillment centers. I left in December 2025 to join Solentrex. This is the role directly before the Solentrex one, and the two do not overlap.",
   "links": [],
   "tags": [
    "amazon",
    "logistics",
    "analytics",
    "tempe",
    "corporate office"
   ],
   "when": "May 2024 to December 2025",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-amazon-reporting-and-root-cause-template",
   "title": "Reporting workflows and a root cause template across a 62 million unit network",
   "group": "Timeline",
   "body": "At Amazon I helped build reporting workflows and an automated root cause analysis template. Both were aimed at escalations: get to the cause faster, in a format the next person can read. The network they served ships 62 million units a year. The reporting toolkit was adopted from site leaders through to regional directors, which is the part I care about, because a report nobody adopts is not a result. It is also where I learned to write an analysis for an operator rather than for an analyst.",
   "links": [],
   "tags": [
    "amazon",
    "reporting",
    "root cause analysis",
    "62 million units",
    "adoption"
   ],
   "when": "May 2024 to December 2025",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-project-harmony",
   "title": "Project Harmony, the national decentralization of dock management",
   "group": "Timeline",
   "body": "From October 2024 to March 2025 I supported Project Harmony at Amazon, the national decentralization of dock management. My part included on-site training of fulfillment center staff on the new process. That is the piece I would point at. It was a process change the frontline had not asked for, explained in person, in their language, with something they could follow on a busy shift. When Harmony ended in March 2025 I moved onto the Central Dock Risk Team.",
   "links": [],
   "tags": [
    "amazon",
    "project harmony",
    "dock management",
    "training",
    "rollout"
   ],
   "when": "October 2024 to March 2025",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-central-dock-risk-team",
   "title": "Central Dock Risk Team, from March 2025",
   "group": "Timeline",
   "body": "I joined Amazon's Central Dock Risk Team in March 2025, straight after Project Harmony finished, and stayed on it until I left the company in December 2025. The team's work sat on top of the same network data I already reported on. I monitored the US-wide active trailer pool and turned raw data into decisions fulfillment centers could act on the same day. It was escalation work, which means most of the value was in getting to the real cause quickly and then saying it plainly to the person who had to act.",
   "links": [],
   "tags": [
    "amazon",
    "central dock risk",
    "trailer pool",
    "escalations"
   ],
   "when": "March 2025 to December 2025",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-wedgewood-four-roles-with-dates",
   "title": "Four roles at Wedgewood Weddings, September 2021 to May 2024",
   "group": "Timeline",
   "body": "I spent two years and nine months at Wedgewood Weddings and Events, across four roles. Banquet Server from September 2021 to May 2022. Banquet Captain from May 2022 to May 2024 in Goodyear, Arizona. Restaurant Supervisor from October 2022 to April 2023 in Chandler, Arizona. Captain Coordinating Sales Admin, part time and on site, from October 2023 to May 2024. Some of those dates overlap, because I held more than one of them at a time. That is where I learned the pace and the seasonality of the business, and what a tool has to survive on a wedding day.",
   "links": [],
   "tags": [
    "wedgewood",
    "banquet captain",
    "restaurant supervisor",
    "sales admin",
    "dates"
   ],
   "when": "September 2021 to May 2024",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-wedgewood-sales-admin-and-salesforce",
   "title": "Coordinating sales in Salesforce at Wedgewood",
   "group": "Timeline",
   "body": "From October 2023 to May 2024 I worked part time as Captain Coordinating Sales Admin at Wedgewood, on site. I coordinated sales in Salesforce and managed vendor relationships. That gave me the inquiry-to-booking flow first hand: how a lead comes in, what the coordinator has to do with it, and where it stalls. Salesforce and HubSpot are both on my skills list now from the software side, but this is where I learned the flow from the desk that has to run it rather than from the schema.",
   "links": [],
   "tags": [
    "wedgewood",
    "salesforce",
    "sales admin",
    "inquiry to booking",
    "vendors"
   ],
   "when": "October 2023 to May 2024",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-training-frontline-staff-at-three-companies",
   "title": "Trained frontline staff on a new process at three companies",
   "group": "About",
   "body": "I have trained frontline staff on a new process at three companies: Wedgewood, Amazon and Solentrex. At Wedgewood I trained new staff on operations and systems, on site, in their language, with a checklist they could follow on a busy Saturday. At Amazon I trained fulfillment center staff in person during the dock management rollout. The pattern is the same each time. Go to where the work happens, use their words, and leave something written they can follow when I am not there. Twice now I have made a new process stick with people who did not ask for it.",
   "links": [],
   "tags": [
    "training",
    "adoption",
    "wedgewood",
    "amazon",
    "solentrex"
   ],
   "when": "September 2021 to now",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-education-and-where-hayden-is-based",
   "title": "Education and where Hayden is based",
   "group": "About",
   "body": "I live in Goodyear, Arizona. I hold a Bachelor of Business Administration from Arizona State University, in Business Administration and Management. Before that I went to Mission Viejo High School. The degree is a business degree, not a computer science degree. I taught myself the engineering by building an AI working system and then using it every day on a real software platform. The business side is not decoration either. It is why I would rather report a result in hours saved, adoption by team and dollars than in tickets closed.",
   "links": [],
   "tags": [
    "education",
    "arizona state university",
    "business administration",
    "goodyear arizona"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-how-to-reach-hayden",
   "title": "How to reach Hayden",
   "group": "About",
   "body": "I am in Goodyear, Arizona. Email is haydengross2002@gmail.com. LinkedIn is linkedin.com/in/haydengross. The one-page resume is on this site as a PDF. The resume is the short version and this site is the long one, so if you want the detail behind a line on the resume it is almost certainly a page here, with the dates and the counts attached. If you are trying to work out whether I have done a specific thing, searching this site will answer it faster than emailing me will.",
   "links": [],
   "tags": [
    "contact",
    "linkedin",
    "email",
    "goodyear arizona"
   ],
   "when": "",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-clerk-organizations-scope-every-query",
   "title": "Clerk organizations scope every API query in the rental app",
   "group": "Personal builds",
   "body": "In the rental app I run Clerk on the web side with @clerk/clerk-react and in the Cloudflare Worker with @clerk/backend inside Hono. Every API route verifies the Clerk session JWT before it does anything else. The active organization id comes out of the session claims and scopes every query, so one organization is one landlord's business and no query can reach across that line by accident. Tenant isolation lives in the data layer, not in the interface. Production is email-code sign-in only, sign-up is restricted, sessions last seven days, and the app runs on a custom domain. There are no webhooks in the setup.",
   "links": [],
   "tags": [
    "clerk",
    "multi-tenant",
    "cloudflare worker",
    "hono",
    "jwt",
    "rental app"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-idp-authenticates-database-authorizes",
   "title": "The identity provider authenticates, my database authorizes",
   "group": "Decisions",
   "body": "I wrote this one down as a recorded design decision so I would not drift from it. The identity provider owns access. My own users table owns identity, holding a clerk_user_id per organization. The reason is history. If the provider is the only record of a person, removing them orphans every change they ever made. With a local user row, removing someone archives the person and the change history stays readable. The same rule sets the boundary for future tenant logins: they come in through the occupants table, never through organization scope. Buying an identity provider does not mean handing it your authorization model.",
   "links": [],
   "tags": [
    "identity",
    "authorization",
    "clerk",
    "design decision",
    "audit history"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-custom-membership-screens-over-stock-ui",
   "title": "Custom members and invitations screens instead of the stock ones",
   "group": "Personal builds",
   "body": "I built my own members and invitations screen in the rental app, driven server side through the organizations API rather than using the stock components. Roles are editable there. Demoting the last admin is refused on the server, not hidden in the client, because a control that only disappears is not a rule. I left out the delete-account, leave-organization and create-organization controls, since none of them make sense for a landlord's staff list, and put a plain account chip in their place. The result is that an owner can add and remove people without ever meeting a screen that offers them a way to lock themselves out.",
   "links": [],
   "tags": [
    "clerk",
    "admin ui",
    "roles",
    "server-side checks",
    "rental app"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-passkeys-and-face-id-sign-in",
   "title": "Passkeys and Face ID sign-in for the household assistant",
   "group": "Personal builds",
   "body": "Sign-in for the household assistant is per member, with passkeys through @simplewebauthn/server and full registration and authentication flows. On an iPhone that shows up as Face ID, because a platform passkey is what Face ID actually is. The fallback passphrase is stored as a scrypt verifier and compared with a constant-time check. Credential records live in local SQLite under the isolated operating system user that runs the app. The secure origin comes from a real certificate on the private network, only the web entry point is exposed, and the API binds to loopback behind a same-origin proxy. I wrote the access model and the isolated runtime up as their own documents.",
   "links": [],
   "tags": [
    "webauthn",
    "passkeys",
    "face id",
    "scrypt",
    "local sqlite"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-rentcast-quota-ceiling-and-cache-first-client",
   "title": "A self-enforced monthly ceiling checked before every outbound call",
   "group": "Personal builds",
   "body": "I put my own monthly ceiling in front of the market rent and property records the rental app pulls from RentCast, because the vendor does not cap it for you. The client is cache first, with 30 day and 90 day time to live values depending on the data. The ceiling is checked before every single outbound call, not sampled and not checked nightly. Remaining quota is shown in the interface. No element on screen fires a request directly, which is the rule I care most about: no button on screen can cost money. A monthly scheduled refresh handles the rest and writes to a job runs table so I can see what ran.",
   "links": [],
   "tags": [
    "rentcast",
    "quota",
    "caching",
    "cost control",
    "rental app"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-ai-import-and-voice-in-the-rental-app",
   "title": "Spreadsheet import and voice, priced per conversation",
   "group": "Personal builds",
   "body": "Three OpenAI features run in the rental app. A spreadsheet importer that returns structured output, so an owner can drop in a rent roll and get rows instead of prose. A helper assistant inside the app. And realtime voice over WebRTC, where the browser never holds a long-lived key, because the server mints a short-lived secret for each session. A ten minute voice conversation costs about twenty cents, and I know that because I measured it rather than guessed it. Knowing the per-use price is what makes it reasonable to leave a feature like that switched on.",
   "links": [],
   "tags": [
    "openai",
    "structured output",
    "webrtc",
    "realtime voice",
    "cost per use"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-bank-and-brokerage-data-with-fallbacks",
   "title": "Three account data providers, with the free tier counted",
   "group": "Personal builds",
   "body": "The household finance assistant pulls account data from three providers. Plaid and Plaid Investments are primary, SimpleFIN is the bank fallback and SnapTrade is the brokerage fallback. Plaid's free tier allows ten items for the lifetime of the account, so the app tracks how many of the ten are spent. The interface compares cost and data freshness per institution, so I can see which provider is worth using for which bank. Every connection is read only, sits behind a fail-closed production gate, and takes its credentials from an encrypted secret broker rather than from the environment.",
   "links": [],
   "tags": [
    "plaid",
    "simplefin",
    "snaptrade",
    "fallbacks",
    "cost tracking"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-gmail-read-only-ingestion",
   "title": "Read-only Gmail ingestion with incremental sync",
   "group": "Personal builds",
   "body": "The household assistant reads Gmail to pick up statements and receipts. The scope is read only. The sync is history based and incremental, so after the first pass it asks for what changed rather than walking the mailbox again. The initial backfill goes two years back. Alongside it, documents are read with Apple Vision OCR locally, which costs nothing and never leaves the machine. Between those two, the app can build a picture of the month without a cloud service seeing the contents of anything.",
   "links": [],
   "tags": [
    "gmail",
    "oauth",
    "incremental sync",
    "backfill",
    "household assistant"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-local-model-is-the-only-model-near-real-money",
   "title": "The local model is the only model allowed near real financial data",
   "group": "Personal builds",
   "body": "In the household finance assistant, a local Qwen3.6-35B running under llama-server is the only language model allowed anywhere near real data. Real financial data never reaches a cloud model. The model also does not own any number. A deterministic engine works in integer cents and produces every figure, and the model's job is to explain and route, not to calculate. Local OCR handles documents on the same principle. That boundary is also why I can show the thing at all, because any demo I give runs on synthetic data.",
   "links": [],
   "tags": [
    "local llm",
    "qwen",
    "llama-server",
    "privacy boundary",
    "deterministic engine"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-self-deploying-release-pipeline",
   "title": "A self-deploying release pipeline driven from a chat app",
   "group": "Personal builds",
   "body": "The household assistant deploys itself. Telegram is the control surface: it handles the bootstrap and sends the notifications, so a release can be kicked off and reported on without opening a terminal. The app runs on my own machine under an isolated operating system user, so there is no hosted deploy service in the loop doing this for me. The point of building it was that the thing gets updated when I notice something rather than when I next sit down at the desk, and that every deploy tells me it happened instead of leaving me to check.",
   "links": [],
   "tags": [
    "telegram",
    "deployment",
    "release pipeline",
    "household assistant",
    "automation"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-confidence-gating-to-human-review",
   "title": "Confidence gating vision output to human review",
   "group": "Personal builds",
   "body": "For an image classification build I ran Workers AI with llama-3.2-11b-vision, and made the model switchable so Moondream could take the same job. Every response is validated against a schema before anything downstream sees it. Below a confidence threshold the item is routed to a person to confirm instead of being written as fact. The work runs through Queues with a dead letter queue, so a failed item lands somewhere I can look at rather than disappearing. Schema first, then a confidence floor with a human behind it. That is the pattern I reuse whenever a model output is going to be stored.",
   "links": [],
   "tags": [
    "workers ai",
    "vision model",
    "human review",
    "queues",
    "dead letter queue"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "entry-re-validating-the-gateway-token",
   "title": "Re-validating the gateway token inside the application",
   "group": "Personal builds",
   "body": "When I put an app behind Cloudflare Access, the Worker still re-validates the access assertion itself against the team JWKS using jose. The gateway already checked it. I check it again inside the application. The reason is that a service which trusts a header is only as safe as the assumption that nothing can ever reach it except through the gateway, and that assumption is one configuration change away from being wrong. Two independent checks cost a few milliseconds. I use the same thinking on the rental app, where an optional user allowlist sits on top of restricted sign-up, and the development sign-in path is compiled out of the production bundle and verified absent in the built output.",
   "links": [],
   "tags": [
    "cloudflare access",
    "zero trust",
    "jwks",
    "jose",
    "defense in depth"
   ],
   "when": "2026",
   "url": null,
   "kind": "entry"
  },
  {
   "id": "self-only-engineer-how-correctness-holds",
   "title": "Nobody reviews Hayden's pull requests. Here is what does.",
   "group": "About",
   "kind": "entry",
   "tags": [
    "review",
    "reviewer",
    "peer review",
    "pull request review",
    "testing",
    "verification",
    "quality",
    "risk",
    "sole engineer",
    "no reviewer",
    "code quality",
    "who checks"
   ],
   "when": "March 2026 to now",
   "body": "I am the only in-house engineer on the platform, so there is no senior developer reading my pull requests. That is a real gap and I did not leave it open. What stands in for a second pair of eyes is machinery, and it runs whether or not I feel like being careful that day.\n\nThe backend unit suite is a gate: 1,044 tests in 23 seconds behind one plain command, every database-backed test running on an in-memory engine so it needs nothing but the command. Golden testing drives the real running application and judges by network calls and database rows rather than screenshots, so a passing screen does not count as a passing feature. My agent fleet carries standing adversarial verifier seats whose job is to contradict the manager, and work of any size goes through a hostile review before it merges. Anything touching money or a customer gets a checkable expected result, verified against production data before a homeowner signs.\n\nVolume is the wrong thing to judge me on and I know it. About 800 merged pull requests means nothing on its own. The number I would rather be judged on is that the changes land in small reviewable pieces with tests attached, and that when something is wrong the harness finds it before a person does.",
   "links": [
    "about",
    "self-how-hayden-works-verify-before-trusting",
    "case-memory-system"
   ],
   "url": null
  },
  {
   "id": "self-continuity-and-handover",
   "title": "What happens to the work if Hayden leaves",
   "group": "About",
   "kind": "entry",
   "tags": [
    "continuity",
    "handover",
    "succession",
    "risk",
    "bus factor",
    "single point of failure",
    "what if he leaves",
    "key man",
    "dependency",
    "offboarding"
   ],
   "when": "",
   "body": "People ask about the risk of one engineer holding a 14-repository platform: the bus factor, the single point of failure, what happens if I get hit by a bus or take another job. It is a fair thing to worry about and I do not wave it away. The honest answer is that I have spent most of my effort making the work survive me rather than depend on me.\n\nNothing important lives only in my head. The shared memory every AI session loads holds 2,211 documents: 1,119 discovery notes, 34 decision records and 48 post-mortems, each written when the work happened rather than reconstructed later. Decisions carry their reasoning, which is the part that is expensive to recover. There are 129 written rules loaded before any session starts, so the standards are a file rather than a habit. An append-only daily log has run for 137 consecutive working days.\n\nThe same applies to the systems. Plans are written before code, rollback paths exist before a cutover runs, and the whole product can be brought up on one machine with a single command, so a new engineer can see it working on day one instead of waiting on a shared environment. The environment itself is plain text and scripts, not a service only I can operate.\n\nIt is not the same as having a second engineer, and I would not claim otherwise. It does mean a successor inherits a written record rather than an archaeology project.",
   "links": [
    "about",
    "case-memory-system",
    "self-how-hayden-works-plan-and-ship",
    "self-only-engineer-how-correctness-holds"
   ],
   "url": null
  },
  {
   "id": "self-what-this-does-not-prove",
   "title": "What Hayden's record does not prove yet",
   "group": "About",
   "kind": "entry",
   "tags": [
    "weakness",
    "weaknesses",
    "bad at",
    "worst at",
    "not good at",
    "shortcoming",
    "limitation",
    "limits",
    "gap",
    "gaps",
    "flaw",
    "downside",
    "criticism",
    "blind spot",
    "what he is not",
    "honesty"
   ],
   "when": "",
   "body": "Asked what my record does not prove, here is the straight answer. I would rather say it than have someone find it.\n\nI have not run a team. I have directed a contractor and worked alongside one other engineer, and that is not evidence that I can lead a multi-person engineering organisation. It is the gap I am most aware of.\n\nThe AI environment is proven for one person, not for a team. Everything in it, the shared memory, the hooks, the agent playbook, was built for and used by me. Packaging it so someone else can run it is work I have started, not work I have finished, and until a second person is living in it daily I would not claim it scales.\n\nCommit volume is not code quality. About 800 merged pull requests says I ship; it does not say the code is good. No outside engineer has reviewed my own contributions. The tests, the golden harness and the adversarial reviewer seats are what I put in place instead, and I would rather be judged on those than on the count.\n\nAnd the impact I can point to is cost avoided, correctness and readiness, not revenue. Those are real and checkable, but they are not the same as a number a customer paid.",
   "links": [
    "about",
    "self-only-engineer-how-correctness-holds",
    "self-continuity-and-handover"
   ],
   "url": null
  },
  {
   "id": "self-changing-my-mind",
   "title": "Times Hayden changed his own call",
   "group": "About",
   "kind": "entry",
   "tags": [
    "mistake",
    "mistakes",
    "got wrong",
    "wrong",
    "error",
    "regret",
    "changed my mind",
    "reversal",
    "would do differently",
    "lesson",
    "learned",
    "bad call",
    "misjudged"
   ],
   "when": "",
   "body": "I make the same call twice when the facts change, and I would rather be caught reversing myself than be consistent and wrong.\n\nI kept a cloud test environment in July because it was the only place I could gate the auth rewrite, then ruled it replaced by a mirror of production a month later once production had cut over. Same question, opposite answer, because what the answer depended on had moved.\n\nOn a personal CAD project I checked geometry an AI had produced and found it would not build. The lesson was not about that one model. I had been accepting output that looked finished without a check that it was, so I set the batch aside, re-audited from the start, and put a buildable-geometry check in front of the work rather than after it. That is now how I treat anything a model hands me: the output is a proposal until something mechanical agrees with it.\n\nThe pattern in both is the same. I do not defend a decision because I made it. I try to be specific about what would have to be true for me to be wrong, and then go and look.",
   "links": [
    "about",
    "self-how-hayden-works-verify-before-trusting",
    "self-hard-situations-hard-decision"
   ],
   "url": null
  }
 ],
 "generated": "2026-08-18"
}