<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Thinking — Oliver Newth</title>
  <link href="https://n3wth.com/thinking" />
  <link rel="self" href="https://n3wth.com/feed.xml" />
  <id>https://n3wth.com/feed.xml</id>
  <updated>2026-07-29T00:00:00Z</updated>
  <author><name>Oliver Newth</name></author>
  <entry>
    <title>Tell it as a place</title>
    <link href="https://n3wth.com/thinking/field-guide" />
    <id>https://n3wth.com/thinking/field-guide</id>
    <updated>2026-07-29T00:00:00Z</updated>
    <summary>A guide in three chapters: find the story, build the world, hand the playhead to the reader. The worked example is this site — every model, texture, and trick below is live on the homepage, and this page performs each technique as it explains it. Chapter one: the story spine, Kenn Adams's seven-line improv exercise, forces a shape onto a decade that 'responsible for' phrasing hides — feed it your own last ten years and notice where it snags, because the snag is the story. You are not the hero of your own site; a visitor arrives mid-problem wanting a guide, not an audience, which is why the homepage greets them with somewhere to go instead of a banner announcing greatness. Chapter two: 3D earns its bytes when the objects mean something, not as a flex — ten GLB models here each stand for something the owner made or loves, built with Blender, Hyper3D Rodin, FLORA, and a headless bpy script, and none of those tools decides what deserves to glow. That part stays yours. Chapter three: scroll runs on one of two clocks, the movie (a timeline fires on entering view, landing the same for everyone) or the flipbook (scrub ties progress to scroll position, so the reader conducts it, even backwards) — in GSAP that's toggleActions versus scrub, and a trigger only gets one. Find the event your resume is hiding, give it ground to stand on, then decide whose clock each scene runs on.</summary>
  </entry>
  <entry>
    <title>How this backlog got built</title>
    <link href="https://n3wth.com/thinking/this-page" />
    <id>https://n3wth.com/thinking/this-page</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>Every piece in this registry, including this one, went through the same process: pulled from something real, drafted in voice, built by an agent in its own isolated git worktree, checked, merged into one shared file. The backlog started as a markdown table — slug, topic, source, status — with one rule: no invented filler, every row traces to a shipped PR, a saved note, an actual git log. Where the source was thin, the instruction was to write around the gap, not manufacture specifics. Verification before merge was mechanical: typecheck, lint, tests, a build, plus one check specific to this site — grep every draft for AI product brand names, since a site tied to a job at a company with its own model can't name a competitor's. It caught something real: one of twenty drafts named a specific LLM by brand three times, carried straight through from source material that had quoted it verbatim, caught before merge. The one real integration failure: two independent agent batches, neither aware of the other, built the same four pieces at once — one on-disk name in lowercase, the other uppercase. Nothing in either worktree could see the other; it surfaced only when the second batch's file listing came back already full of work that looked like its own. The fix wasn't a smarter agent — it was a habit, check what's running before starting a new batch. The tracker says what was planned. The registry says what shipped. Believe the registry.</summary>
  </entry>
  <entry>
    <title>The PDF gotcha that cost an afternoon</title>
    <link href="https://n3wth.com/thinking/pdf-charspace" />
    <id>https://n3wth.com/thinking/pdf-charspace</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>A reportlab-based document generator started drawing right-aligned numbers past the end of their column, by exactly the amount you'd expect from a bug that isn't in the math at all. Values overshot a table's hairline rule by 10 to 25 points, but only in rows following a row with a letterspaced chip in it — rows without one aligned perfectly, and drawRightString looked correct on inspection. PDF has an operator, Tc, character spacing, that a chip sets to widen gaps between its own letters. The bug: Tc is graphics-state, not a property of the text object that set it. It survives past ET, the operator meant to end a text block, and keeps applying to every text object drawn after it on the same page, including the row total two lines down. reportlab's stringWidth, which computes where a right-aligned draw should start, has no idea any of this happened — it measures assuming zero spacing, so the start position is correct for a string about to render wider than that measurement says. The fix is one extra line: reset Tc to zero before the text object closes, not after — by the time you're back in the caller the state has already leaked into the next draw call. What caught it wasn't reading the source, it was rasterizing the page and measuring the rightmost dark pixel per row against the rule. The math was never wrong. An operator from two lines up was still switched on.</summary>
  </entry>
  <entry>
    <title>Working notes: 2026</title>
    <link href="https://n3wth.com/thinking/2026-goals" />
    <id>https://n3wth.com/thinking/2026-goals</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>Every year I rewrite one document: a single page listing what has to be true across career, money, and home life by December, plus the infrastructure underneath all three. The organizing idea is that a milestone in one column should leave something reusable in another. That's the theory. The plan quietly admits it doesn't hold evenly. The tell is in the section most planning documents skip: who actually checks on this, and how often. Career gets a fixed two-week sync, because someone else is waiting on the other end of it. Money gets a weekly script pruning drifted spend and a monthly pass against the plan, because drift there costs real money unchecked. The infrastructure underneath everything — hardware, the knowledge system — gets a quarterly review. Home life gets written down in the same document and then nothing: no cadence, no calendar entry, no second party expecting an update. That's a structural fact about which goals have external pressure attached, not an oversight. The category with no cadence is the one where I'm the only one keeping score, and I'm bad at scoring myself on a schedule I have to build in from outside. The fix isn't a better goal. It's giving the neglected category what the others already have — a date on a calendar not contingent on how busy the other three columns get, bolted onto the quarterly review that already exists. A goal with no scheduled check isn't a priority. It's a wish with better formatting.</summary>
  </entry>
  <entry>
    <title>A standing team with named desks</title>
    <link href="https://n3wth.com/thinking/agent-desks" />
    <id>https://n3wth.com/thinking/agent-desks</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>A desk on an org chart is a rectangle with a label in it until three more things are true. It fires on something other than a person remembering to click go. It's bounded — a specific list of what it can and can't touch. And it leaves a timestamp: a real, checkable answer to what it did last, without asking the person who set it up. Four generic desks here, kept categorical because the pattern matters more than the project behind it: a watcher checking every thirty seconds, only catching and correcting, never creating; a reviewer that runs before anything merges and can block but not approve its own work; a status board rewriting its own priority table on a timer; an inbox that files and closes but doesn't decide what matters. The desk I trust least is the one whose status a person types by hand — accurate the day it's written, a guess every day after. A board that rewrites its own table is different, not because the information is smarter but because the timestamp means something. Not every desk creates; the watcher's job is noticing another desk's output went missing, fast enough that it doesn't matter, which is why its cadence is the shortest. What none of them can tell you is whether they're still running — a cron job that stops doesn't announce it. A desk is real because it has a schedule, a boundary, and a timestamp that updates without you.</summary>
  </entry>
  <entry>
    <title>The toll-free verification maze</title>
    <link href="https://n3wth.com/thinking/twilio-compliance" />
    <id>https://n3wth.com/thinking/twilio-compliance</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>A toll-free number needs Twilio's sign-off before it sends a single text. The application reads like paperwork and behaves like a maze — you can't see a wall until you've walked into it, and the rejection notice is the only map you get. First submission was for one narrow job, an AI voice hotline sending a single follow-up text after a call. It came back rejected on business_website: the field pointed at a personal domain that redirects elsewhere, and the reviewer followed the redirect to a page matching nothing else in the application. Fix it, resubmit, and the wall moves — second rejection on opt_in_type, set to WEB_FORM when there's no signup form anywhere; the only real mechanism is VERBAL, a caller hearing a disclosure before anything gets sent, and Twilio's taxonomy doesn't treat 'close enough' as valid. One more field got changed in that same pass before it had the chance to reject anything on its own: business_type to SOLE_PROPRIETOR, the value that skips the EIN and registration-number fields a one-person operation registered only with the city doesn't have. Reading the enum definitions before submitting instead of after is the one wall you can route around blind. The application can only be edited while rejected, within a seven-day window that keeps a resubmission in the priority queue — miss it and the next attempt goes to the back of the line. A maze doesn't publish its map. You learn each wall by walking into it.</summary>
  </entry>
  <entry>
    <title>Kroots: mapping a graph nobody else could see</title>
    <link href="https://n3wth.com/thinking/kroots-map" />
    <id>https://n3wth.com/thinking/kroots-map</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>The page is called a map of content and looks like nothing: a title, a mission line, four short lists of links. It's the closest thing my kids'-media project has to an org chart. There's no team to chart — one person, and a folder of notes that had to behave like a company anyway, so the map stands in for departments that don't exist yet. The first cluster is the one a founding team would usually split three ways: a foundation document, a go-to-market strategy, competitor research, an accelerator application — four different jobs, one person, the same week. Nothing hides that; the map just lists the links side by side and lets you notice. The second cluster is where the map earns its name: notes on two established kids' properties studied for structure, a note applying a framework about how ideas spread, and one link away, the actual launch newsletter copy. Research and shipped material sit together because they're the same activity — there was never a separate research phase to split from production. The other two clusters are thinner by a full document each, and a folder wouldn't show you that; scrolling past three files feels the same as scrolling past four. The graph shows the gap as a gap. What the map is for isn't remembering where things are. It's seeing the shape of the work from above, including which cluster stays thin because I keep finding reasons to work on strategy instead of outreach.</summary>
  </entry>
  <entry>
    <title>A computed verdict can still be wrong</title>
    <link href="https://n3wth.com/thinking/hop-flights" />
    <id>https://n3wth.com/thinking/hop-flights</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>hop.flights answers one question — use points, or pay cash — and the pitch of the verdict engine is that the answer is computed from real loyalty balances and real cash prices, never generated. That's the moat: no hedge, no disclaimer, just a number. Which also means that when the number is wrong, it's wrong with the same flat confidence as when it's right. A solo search checks out. Add a second passenger to the identical search and the verdict can flip outright, points to cash or the reverse, while the page states it with total certainty. A DeepWiki audit surfaced it as the highest-priority open issue. Root cause: Seats.aero returns award pricing per seat, Duffel returns cash pricing as a total already summed across the party, and the search function divides one against the other without checking which is which — the Seats.aero client never reads the party-size parameter, so the number in the search box is invisible to half the calculation. The result inflates cents-per-point roughly by party size. The fix is arithmetic, not architecture: scale the award side by party size before it meets the cash side. It survived this long because the mock fixtures used in day-to-day dev don't scale by party size either, so the bug never got a chance to surface until real data. It's now one of four hard gates before billing turns on. A computed verdict is only better than a guess if the arithmetic checks out.</summary>
  </entry>
  <entry>
    <title>TypeScript at the edge</title>
    <link href="https://n3wth.com/thinking/edge-typescript" />
    <id>https://n3wth.com/thinking/edge-typescript</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>A request to a Cloudflare Worker doesn't reach a server. It reaches a V8 isolate — the same sandboxing tech that keeps browser tabs apart — spun up in under a millisecond wherever the request landed. A traditional serverless function boots inside a VM or container, and that boot cost is real time before your code runs. Workers skip it: no machine to boot, no container to schedule, execution starts on whichever edge location the request reached. TypeScript's job here isn't stylistic. A Worker's binding surface — the other services and models it's wired to — is a contract that has to hold at every edge location at once; compile-time types catch a broken binding before it ships everywhere. Isolates are disposable by design, which is fine until a request needs to remember something. Durable Objects answer that: stateful isolates with strongly consistent in-memory storage, addressable by an ID that always routes to the same instance. The same properties that make Workers cheap for stateless HTTP make them plausible as an agent runtime — lightweight enough that running thousands of distinct agent profiles, each waking only on a webhook, is a reasonable default instead of a scaling problem. Trace one request through the stack and the hops are always the same three: isolate, Durable Object, model binding, no home region in the middle. A V8 isolate isn't a fast server. It's compute cheap enough to hand one to every agent that needs one.</summary>
  </entry>
  <entry>
    <title>The toll booth is memory, not math</title>
    <link href="https://n3wth.com/thinking/llm-inference" />
    <id>https://n3wth.com/thinking/llm-inference</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>An inference request has two phases that behave nothing alike. Prefill processes the whole prompt at once, every token attending to every other in parallel, and it happens exactly once. Decode is sequential — one token out, fed back in, one token out again — and there's no parallelizing around it; token 200 can't start until token 199 exists. The instinct is to treat both as a compute problem, because that's how training works. Decode doesn't work that way. Every step has to reload the model's weights and the running KV cache out of memory before it can produce one token. The arithmetic is cheap. The trip to memory, repeated once per output token, is what the clock is actually measuring — decode is memory-bandwidth-bound, not FLOPs-bound. This isn't abstract on my own setup: a Mac mini M4 with 16GB of unified memory, memory-constrained enough that it dictates the whole design. Weights get quantized from 16-bit down to 4-bit, and a router sends simple prompts to the small quantized model to keep tokens-per-second high. The upgrade I've got planned, an M4 Pro with 48GB, buys room to run larger models locally, not raw speed. The same constraint shows up at enterprise scale wearing different clothes — a fleet problem optimizing for the same two numbers, plus cost multiplied across every concurrent request. Prefill is a sum you pay once. Decode is a toll you pay per token, and the toll booth is memory, not math.</summary>
  </entry>
  <entry>
    <title>What "autonomous" actually means in production</title>
    <link href="https://n3wth.com/thinking/autonomous-agents" />
    <id>https://n3wth.com/thinking/autonomous-agents</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>'Autonomous' gets applied to two different things and the word doesn't distinguish between them. One is a loop: an agent perceives, plans, acts, checks the result, and adjusts without a human approving each step. The other is a boundary: what that loop is allowed to touch. The loop is a property of the system; the boundary is a decision someone made. Confusing them turns 'the agent is autonomous' into 'nobody's watching,' which isn't the same claim at all. The loop itself is easy to describe — plan, execute in a disposable sandbox, read the error, revise, reach out through something like MCP to touch files or APIs. None of that should feel controversial; a sandbox is built to be wrong in. The interactive list in this piece walks through real actions an agent might take mid-task: half the loop just does, because being wrong costs nothing and the attempt gets discarded. The other half — merging to main, deploying, spending money, messaging a customer, deleting production data — need a person, because being wrong costs something that doesn't reset. A multi-agent setup makes the boundary easier to see, not harder, since it forces you to draw it per desk instead of once for 'the agent.' What makes a setup production-grade isn't a bigger or less supervised loop. It's a boundary drawn on purpose, before the fact, and better infrastructure doesn't move that line.</summary>
  </entry>
  <entry>
    <title>Compound engineering, applied to a personal site</title>
    <link href="https://n3wth.com/thinking/compound-engineering" />
    <id>https://n3wth.com/thinking/compound-engineering</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>Five real pull requests from this site's own git history, in the order they landed. The first Thinking piece shipped with two fixes already caught in review, not filed as bugs a week later — a redundant link grid trimmed, a camera sway toned down from 3x to 1.1x. The second PR wasn't a build, it was a review that caught drift before it spread to the other nineteen pieces: bordered cards fighting the site's flat rule, a synthetic model standing in for the real one, no spine to the argument. Two PRs later a raw hex color and a text-shadow glow slipped into one specimen anyway, both banned by the site's own flat rule — three lines, caught and reverted before it set a precedent. The pattern under those fixes — the magazine grid, the optional stage number, the optional margin note — moved into a shared component every later piece imports rather than redefines slightly differently, including this one. Two PRs after that, a compliance pass found copy naming specific AI products by brand, a hard constraint since I can't publicly endorse a competitor's model while working at Google. The rule got written once. The very next batch, four pieces built by parallel agents, caught its own violation with zero new instructions. A rule that has to be re-explained every time isn't a rule. It's a request.</summary>
  </entry>
  <entry>
    <title>Automation is arbitration plumbing</title>
    <link href="https://n3wth.com/thinking/home-automation" />
    <id>https://n3wth.com/thinking/home-automation</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>A house full of automation looks like magic from the doorway. From inside it's arbitration plumbing: sensors report, a hub decides, actuators act, and each of those three roles fails in its own way. My leak sensor sits under the kitchen sink on Z-Wave; when it detects moisture, my hub checks a rule and fires downstream. The real engineering problem isn't any one sensor — it's that Z-Wave, Zigbee, Hue, and Wi-Fi don't talk to each other natively, so the hub's actual job is translation. Mine is a Home Assistant Yellow, run local with no cloud dependency, tracking around 1,280 entities. The failure I keep coming back to is that same leak sensor's battery reading 20 percent. It hasn't missed a report yet, which is the problem — a dying sensor doesn't announce a threshold crossing, it just goes quiet one day with no error, and the hub can't distinguish 'no leak' from 'no sensor.' The reliability of the whole chain is bounded by whichever part is cheapest and least watched, not by the expensive hub sitting fine in a closet. I made one trade the other direction: pulled local voice control off the hub because it drained resources the rule evaluation actually needed. The next layer isn't more sensors. It's software that turns a stated intent into the right sequence across actuators that already exist.</summary>
  </entry>
  <entry>
    <title>A knowledge base becomes a graph once something else can query it</title>
    <link href="https://n3wth.com/thinking/personal-knowledge-graph" />
    <id>https://n3wth.com/thinking/personal-knowledge-graph</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>PARA and Zettelkasten pushed back on hierarchy, arguing notes should link the way ideas actually relate — in webs, not trees. The insight is obvious in retrospect; the problem is most tools claiming to support networked notes still treat linking as a cosmetic afterthought. My system splits across three backends by function: a core knowledge graph on Postgres holding structured knowledge across projects, meetings, people, and career history; a separate store for small atomic facts that need fast lookup; a third holding behavioral instructions for how my agents should act, not facts about the world. The core is a real graph, not markdown files that happen to link — pages connect via wiki-links into explicit nodes and edges, and the graph shape itself carries meaning. I enforce hygiene mechanically: no duplicate pages, orphan tracking for pages with no incoming links, bi-temporal facts so a changed role gets a time boundary instead of overwriting history. I publish a curated subset as a digital garden, deliberately exposing structure rather than polished standalone essays. The consequence that matters most is what happens when an agent reads and writes to it. Vector search tells you what sounds similar; it can't traverse three hops from a person to a project to a decision. A flat index gets fuzzy under pressure. The graph holds the error.</summary>
  </entry>
  <entry>
    <title>Live artifacts are three different architectures</title>
    <link href="https://n3wth.com/thinking/live-artifacts" />
    <id>https://n3wth.com/thinking/live-artifacts</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>'Live artifact' names three genuinely different architectures that share one surface feature — the output renders instead of displaying as text — and nothing else. The first: an AI chat platform renders generated HTML or React inside a sandboxed frame assembled from full context, and iterating republishes in place at a stable, versioned link. The second: a full Node environment compiled to WebAssembly runs entirely inside the browser tab — virtual filesystem, virtualized network, a real dev server, no remote VM — and a model operating here controls an entire environment, not just a rendered output. The third is generative UI: real React and component-library code, iteratively edited, optimized for production-realistic reuse rather than a sandboxed preview. The point isn't the preview, it's the code that ships. These diverge on security by design: OS-level sandboxing wraps every subprocess a coding agent spawns, a microVM-per-execution model gives each run its own machine, and a plain CSP iframe disables scripts and forms by default and re-enables them token by token. The underlying decision is how much control you hand the model against how contained the blast radius is when it's wrong. A full in-browser OS maximizes control and minimizes containment; a CSP iframe is the reverse. I picked the middle the same way anyone does — by deciding how much I trust the model, and how much I'd rather not find out.</summary>
  </entry>
  <entry>
    <title>Why every AI-generated UI looks the same</title>
    <link href="https://n3wth.com/thinking/ai-design-slop" />
    <id>https://n3wth.com/thinking/ai-design-slop</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>Every AI-generated UI I see lately looks roughly the same: a particular sans-serif, purple-to-blue gradient cards, a centered icon grid, low-contrast dark mode. One AI lab calls this distributional convergence — without explicit steering, a generative model samples from the safest, highest-probability region of its training distribution, the average of everything it's seen. I ran into this building hop, my flights side project, and on this site. An analysis of 1,590 Show HN vibe-coded submissions found 22 percent showed heavy slop, four or more recognizable patterns stacked together. No single fix solves it, but the countermeasures that work share one property: they're structural, not a better prompt. Grounding generation in a real component library, locked before generation starts, closes off the route into generic territory entirely. Token discipline helps almost as much — hand a model raw hex values and it hardcodes them, so I hide raw color primitives from the model and give it only semantic tokens, paired with deterministic linting to catch drift the moment it happens. Forcing an aesthetic-direction decision up front, grounded in the brief's own subject matter, is the third lever. On AI-judged design quality I stay skeptical — a model's critique correlates weakly with human judgment, and even trained designers disagree with each other. The leverage is constraints and process. Everything else is hoping.</summary>
  </entry>
  <entry>
    <title>The machine that runs my todo list while I sleep</title>
    <link href="https://n3wth.com/thinking/gtd-mini" />
    <id>https://n3wth.com/thinking/gtd-mini</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>A headless Mac Mini in a closet runs a cron job every fifteen minutes. Things 3 holds my task list, an LLM reasons about it, iMessage carries results to my phone, and none of it waits for me to be at my desk. I built it because manually triaging a backlog is slow and repetitive in exactly the way computers are supposed to handle. Things 3 has no API, so everything routes through AppleScript; the model reads current state, applies whatever context I've given it about priorities, and reorders, flags, or passes through unchanged. Most runs change nothing, and that's fine — the value isn't constant churn, it's not having to remember to check. I want a timeline scrubber next: a local interface that plays the day's automation back like a video, one cron run per frame, so I can see what the model saw at 9:15 and whether noon matched what I expected. Right now I read logs, which are fine but don't tell me whether the system is making decisions or just confirming the status quo. Nothing here is exotic — the intelligence is knowing what 'urgent' means relative to everything else on the list. What changes with an autonomous system is pressure: nothing depends on me checking in, and some days a task I'd have forgotten surfaces before it matters.</summary>
  </entry>
  <entry>
    <title>AI should be present, not summoned</title>
    <link href="https://n3wth.com/thinking/ambient-ai" />
    <id>https://n3wth.com/thinking/ambient-ai</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>The chat box is the wrong mental model. Every time I open one I stop what I'm doing and decide what to ask, and that pause is where most of the value gets lost. The agent literature makes a distinction worth taking seriously: an agent perceives its environment, decides, and acts — it doesn't wait to be addressed. Most shipped products ignore this. A sidebar or modal is a secretary you have to schedule, and for most people in most workflows that scheduling overhead is enough friction to kill the habit. Ambient presence is the alternative: not a persistent chatbot in the corner, but a layer that tracks what you're already doing and acts when useful instead of when prompted. The toggle in this piece makes it literal — same document, same paragraph being rewritten, but one state opens a modal over the work and pulls the cursor away, and the other leaves the work untouched and adds a small marker in the margin. The summoning pattern persists because it maps to how we used to think about software: invoke a tool, the tool does a thing. Agents that perceive and act don't fit that frame. Deciding what an agent should notice, what it shouldn't, and where the threshold sits between helpful and noisy is the actual product.</summary>
  </entry>
  <entry>
    <title>Trust is a runtime property</title>
    <link href="https://n3wth.com/thinking/trust-production" />
    <id>https://n3wth.com/thinking/trust-production</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>The trust question for AI systems gets asked at the wrong time — during design review, red-teaming, model evals — and by the time the system is live it feels settled. It isn't. Take a safety classifier: content passes through it before surfacing, and a flag routes to human review. The logic is sound; the failure is runtime. Chat has a 50ms latency budget, feed has 200ms, and those numbers mark where a response stops feeling like a response. If the classifier finishes inside that window you get synchronous review. If it misses, you've silently changed your policy — not in a document, at runtime — to either show flagged content anyway or hold it for async review. I built a slider to make this concrete: drag it and the classifier's latency crosses 50ms, and the review path flips from synchronous to async with no smoothing, because the flip is sudden in production too. This matters because latency gets treated as an infrastructure problem engineers optimize, not a policy variable the safety team signed off on. Cross-org model integration makes it worse — a classifier that runs at 40ms alone runs at 70ms waiting on an upstream service. The slider doesn't fix the gap. It just makes the tradeoff visible, which is where the honest conversation has to start.</summary>
  </entry>
  <entry>
    <title>Agents are an org design problem</title>
    <link href="https://n3wth.com/thinking/agents-org-design" />
    <id>https://n3wth.com/thinking/agents-org-design</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>The question I kept getting wrong for too long: is this a technical problem or an organizational one? I treated model choice and context windows as the lever. They weren't. What compounds is how work accumulates, and that's an org design question. I run a standing team of coding agents, each with a named desk and a real schedule. Agents own everything reversible — code, drafts, analysis, debugging. A human makes every call touching money, identity, or anything that can't be undone. That boundary doesn't move; the moment it moved once, it stopped being a rule and became negotiable, which makes it useless. The insight that took longest to land is about trails: an agent that solves a bug and leaves nothing behind isn't much better than a contractor who leaves. The bug has to become a document with enough context that the next agent treats it as a constraint, not a mystery to re-derive. Compounding happens in the trail, not the solving. Second insight: generation and review can't be one loop. The model that wrote the code is a poor judge of its own failure mode, so review has to be structurally separate — agents checking rules, humans making calls. Same models, different desk structure, and the output quality moves a lot.</summary>
  </entry>
  <entry>
    <title>What the night field broke</title>
    <link href="https://n3wth.com/thinking/night-field" />
    <id>https://n3wth.com/thinking/night-field</id>
    <updated>2026-07-22T00:00:00Z</updated>
    <summary>Five real bugs from building the homepage's 3D night field, in the order the pipeline actually hit them: generation, export, runtime, rigging, and then the process that caught all four. Rodin's default material renders black under a point light with no environment map — fixed by dropping metalness and raising roughness, two floats, invisible until you know what the exporter ships by default. A Blender export baked an off-origin transform into vertex coordinates, so the model orbited a ghost point instead of spinning in place. Suspense placed outside the Canvas remounted the whole WebGL context on every async load until enough of those chained together and the browser dropped it. A four-legged creature's feet skated because the animation ran without a rig, and nothing enforced ground contact between amplitude, frequency, and leg length. None of these got caught by one agent generating and shipping. It took a critique panel — composition, 3D-craft, anti-slop reviewers looking at renders separately, a fixer gated on typecheck and screenshot — capped at two or three rounds because quality converges by then. The homepage isn't a metaphor for that argument about production AI. It's the argument, running live, in front of you.</summary>
  </entry>
</feed>
