Skip to content

The Digital Homestead Blog

Thoughts from a technologist, father, and advocate of self-sufficiency. Tech insights to family wisdom.

Showing 1-5 of 49 posts
Writing8 min read

Guests, prototypes, and the price of attention

AI made the faith-based OSS prototype cheap. It did not make the maintainer's attention cheap. On guests, slop, empathy, and why "nope" is still the contract.

I opened a pull request once, enjoyed writing it, and watched it sit for three years. I am not complaining. I was a guest in someone else's house. That is the deal: you may offer, they may refuse, and "nope" is still the contract.

What changed is not that maintainers suddenly became colder. What changed is that I can now run the same experiment for a fraction of the cost.

That gap. cheap production, expensive attention, is the actual story of open source in the age of language models. Most arguments about "slop" and "empathy" are people standing on opposite sides of that gap and using moral language for an economic problem.

Two honest urges

On one side: I use the software. I hit a wall. The official path is "fork it" or "use this unofficial hack." Forks are fine if you have time, taste, and a build pipeline. A lot of people don't. They just want the tool to talk to the KMS they already pay for. For example...

So you build a prototype. Not because you think the maintainers owe you a merge, but because a working thing in a branch is a better conversation than another "+1 would be nice." Before language models, that prototype was an expensive, faith-based experiment. You could spend a weekend, show up with something shaped more or less like a solution, and anyone in the thread could say "nope." That was acceptable. That is the social contract.

On the other side: maintainers don't starve for ideas. They starve for hours. Every extra backend is a dependency, a security surface, a release note, a user who will file a bug in three years when the cloud vendor changes an API. A plugin system is not "yes, add my provider." It is "we will live with this interface after you have moved on."

Both urges are legitimate. They do not automatically produce a good thread.

What "slop" is pointing at

"Slop" is a lazy word, and also not a meaningless one.

It does not mean "a machine touched this." A lot of good code is typed with assistance now, the same way a lot of good code used to be typed with Stack Overflow open. I have shipped plenty of both. The tell is not the tool. The tell is when the artifact asks the reader to do the thinking the author skipped.

In a pull request that usually looks like this:

  • a large diff that arrives as a conclusion, not a question
  • prose that sounds confident about tradeoffs the project already rejected
  • tests that exercise the happy path and leave the scary path to the reviewer
  • a tone that optimizes for "look, it works on my machine" instead of "here is why this belongs in this tree"

People say "lacking empathy" when they mean that. They don't mean you failed a feelings exam. Some of my exes told me that, but that's another story. They mean the comment or the PR did not budget for the other person's attention.

That usage is loaded. It is also closer to the old engineering meaning of courtesy: don't make the next person hold the whole design in their head because you didn't want to cut the work into reviewable pieces.

I care about results. Does this introduce fewer problems than it solves? That test still matters. It is not the only test. In a shared repository, "it works" is the start of the argument, not the end.

The cost inversion

The old bottleneck was implementation. You had to understand the codebase well enough to change it, or you didn't ship.

The new bottleneck is absorption, the cost of taking a patch into the tree. Anyone can produce a plausible patch. Almost no one can cheaply decide whether that patch should become load-bearing infrastructure.

So the social contract needs a footnote, but not a rewrite.

It is still fine to say "nope."

It is still fine to prototype in public.

It is not fine to confuse "I reduced my cost of trying" with "I reduced your cost of reviewing." Those are different bills. AI pays the first one. It does not pay the second. Cheaper prototypes do not entitle you to a cheaper "nope." They entitle you to try again, smaller, or somewhere else.

This is why a proof of concept and a merge candidate are different objects. A POC can be a bit ugly. It can be one commit called wip: can we even do this. Its job is to resume a conversation. A reviewable change is small, motivated, and shaped to the house style. Mixing the two, dumping a conversation-opener and then being surprised that nobody wants to rubber-stamp it, is how threads go sideways.

I would rather be told "this is hard to engage with" than hear nothing at all. That sentence is useful data. It is not a verdict on whether the problem is real. A clear "nope" beats silence every time.

Empathy, without the sermon

If empathy means "anticipate how this lands," then both sides owe some of it.

Contributors can:

  • say what is a prototype and what is asking for review
  • point at the decision, not the novelty: in-tree hook versus out-of-tree plugin, protocol, key type, compatibility
  • cut the first patch until a tired person can read it on a phone
  • accept that "already possible, albeit hacky" is sometimes the correct official answer
  • stop narrating how much joy or pain went into the work. Effort is not a claim check.

Maintainers can:

  • close things. Silence is more expensive than a short no, and a short no is cheaper than a grudging merge
  • separate "we don't want this in core" from "you were wrong to want it"
  • keep the workaround visible, so the next person doesn't have to invent the same hack
  • remember that some of the volume they hate is downstream of years of no decision

Neither list requires anyone to become nicer in the abstract. They require people to treat attention as a limited reagent.

What I think is actually worth keeping

I like that models made the faith-based experiment cheaper. I like that someone who is not a full-time systems person can stand up a working backend and put it next to a three-year-old issue. I like that users who will never maintain a fork still get a chance to see a path.

I wrote about the artisan's Mk 45 recently: forty-five iterations on a chisel that is still not perfect yet. A prototype is allowed to be Mk 1. A merge request is the chisel you hand to someone else to swing. Different objects. Same shop, different job.

I do not like the emerging habit of using that cheapness as moral leverage: I produced a thing, therefore the project should metabolize it. That was never the contract. The contract was: you may offer, they may refuse, the software remains someone else's house.

The interesting future is not "AI PRs versus human PRs." It is whether projects grow an immune system that can tell a cheap experiment from a cheap mess.

A healthy version looks boring:

  1. Issue first. One paragraph on the problem, one on the constraint, one on the rejected alternatives.
  2. Prototype in a branch or a sidecar repo, labeled as such.
  3. If maintainers want it, a sequence of small patches that each survive a skeptical reading.
  4. If they don't, the prototype still exists for the people who need the unofficial path.

That last point matters. Official purity and user need can both be true. Documenting the hacky integration is often a better gift than arguing about whether core should grow another cloud vendor.

The golden age of forks... it might be another blog.

The part I had to write down so I wouldn't post it on the issue

English is not my first language. Words like "slop" and "empathy" arrive already hot. It is tempting to unpack them in the thread, explain the old PR, explain the model, explain that you would have done the same work by hand in 2022, explain that you are a heavy user and also an author of OSS, explain that you are only trying to help the people who cannot keep a fork alive.

Don't.

The future reader of that issue came for a key type and a protocol. They did not come for your theory of collaboration. If the theory is worth anything, it belongs in a separate place, where it can be wrong in public without blocking the patch.

The patch still has to answer the only question that gets code merged:

Does this solve more problems than it introduces — for the people who will still be here after the excitement dies?

If yes, make it small enough to check.

If not, leave the prototype on the porch and don't demand that the household rearrange the furniture around it. The door can stay closed. Nope is still a complete sentence.

If you built something useful, users will use your fork. Solve your own problems first. You might help some other vagrant user with the same one down the line.

Ciao!

Tech4 min read

Stealing 1613 shaders from the WebGPU CTS with a fake GPU

The best corpus of real-world WGSL is locked inside a test framework that refuses to hand it over. So I gave the framework a GPU that does not exist and wrote down everything it showed me.

A wireframe GPU stub catching streams of WGSL shader cards in neon mint green

If you are testing a WGSL backend, you need WGSL. Not ten shaders you wrote yourself; you need the strange ones, the ones written by people with different assumptions than you, exercising corners of the spec you did not know were corners. In the WebGPU world the obvious corpus exists: the WebGPU CTS, the conformance test suite, thousands of cases of shader code written by spec authors to poke at exactly the edges a compiler needs to survive.

There is one problem. The CTS is not a folder of shaders. It is a test framework: TypeScript, data tables, parameterized cases, plan files. The WGSL you want is buried inside per-case setup routines that only run when a real GPU is attached. There is no export button. The answer to "can I get the shaders" is, structurally, no.

So I built a GPU that does not exist.

The fake device

The harness runs the CTS's own code. It loads the real suite, transpiles it, and starts executing actual cases. Before a case runs, the framework asks the browser for a GPU: adapter, device, queues. My harness answers those requests with a stub. A fake adapter that always says yes. A fake device whose methods accept whatever they are handed and remember it. Then the case's setup runs, the framework composes the shader source it intends to submit, hands it to what it believes is a GPU, and my stub writes down every WGSL string that touches its palm.

No rendering. No GPU. No browser. The CTS never notices, because from its point of view it is talking to a perfectly agreeable, perfectly inert device.

Out the other end comes what nobody was meant to have: 1613 real, CTS-authored WGSL shaders, extracted deterministically, byte-reproducible from a pinned revision of the suite. Two runs, two identical corpora. That matters because a corpus you cannot reproduce is a benchmark you cannot defend.

Then the actual test

Each harvested shader takes the trip a real shader never takes backwards: WGSL to SPIR-V (naga does the converting; a tint fallback catches dialects naga cannot parse, like subgroups), SPIR-V through zioshade's WGSL backend, and the result into naga and tint for validation. Round-trip or bust. It is a brutal shape for a cross-compiler: you are not translating someone else's SPIR-V, you are regenerating WGSL from WGSL-derived IR and it has to still be WGSL.

The first run was ugly, and that was the point. 43 invalid cases across 11 distinct bug classes: null-constant folding that emitted the wrong literal, depth-texture forms, image-query signedness, depth-only fragments that returned a hardcoded 0.0, multiple-render-target type mismatches, functions that were reachable but never emitted, once-stored privates, phi-scope identifier collisions. Every one of those was a shader that would have compiled and computed the wrong thing. All fixed, each with a minimized regression test.

The scoreboard now: 1597 of 1613 round-trip valid, zero invalid, zero crashes. Two cases even the upstream converters cannot digest, and 14 refusals that are deliberate and named: the cross-function subgroup shapes no faithful WGSL spelling can express, integer-64, binding collisions, and point-size builtins. A refusal with a reason is a bug report you filed against the spec, not a failure.

The general trick

Strip the specifics and this is a reusable move: when the artifact you need is imprisoned in a framework, do not parse the framework, run it. Implement the smallest interface the framework demands, become the environment it trusts, and record what it hands you. It is the same move I described when I shipped stunt: a stunt double takes the hits so the star does not have to. Here the star is a real GPU and the double is a stub just agreeable enough to get the CTS to show its hand.

The prerequisite is licenses and honesty: the CTS is BSD-3-Clause, the vendored corpus carries its notice, and the pinned revision is in the manifest, so anyone can check that what I claim as a corpus is what the suite actually contains. A corpus you stole is only useful if you can prove where it came from.

Tech5 min read

The only shader cross-compiler that renders its own output

SPIRV-Cross checks golden files. naga asks you not to run generators. zioshade renders every backend* on a real GPU and pixel-diffs it. Here is the oracle stack, and the day the oracle was wrong.

Two GPU framebuffers side by side with a neon pixel diff scanner catching one mismatched pixel

When you test a shader translator, the tempting thing is to compile the output and check it against last week's output. That is what a golden file is: a snapshot of your own belief. It catches regressions against yourself. It says nothing about whether either version was right.

The industry knows this, which is why the incumbents are careful about what they claim. SPIRV-Cross's tests compare emitted text against golden files. naga's docs famously ask you not to run the output through other generators as a check, because the disagreements are noise. Both are defensible positions. Neither proves the output computes the right pixels.

That is the gap zioshade tries to close: the only shader cross-compiler I am aware of, at the time of writing, that renders its own output as part of verification. Every backend* is pixel-diffed before it ships.

zioshade is a young cross-compiler with one maintainer, and that changed the design constraint: I cannot ask anyone to trust me, so the output has to be judged by things that are not me, preferably things that would enjoy watching me fail. This is the stack, in the order it was built, each layer existing because the previous one let something through.

One oracle: render-diff against the incumbent

The base layer renders zioshade's MSL and the MSL from an independent glslang-plus-SPIRV-Cross pipeline on a real Metal GPU, and diffs the pixels. Fragments by framebuffer, vertex shaders by captured position, compute by output buffers. 1300+ shaders, zero divergences, one command to re-run.

A differential against one reference has a blind spot you can state exactly: if both compilers misread the spec the same way, they render identically wrong and pass. Any single-oracle differential has this hole. It is not a reason to skip the oracle; it is a reason to add a second one.

Two oracles: make them disagree

So the second layer adds naga as a second reference, with its own independent SPIR-V-to-MSL backend, unrelated to SPIRV-Cross. Now a shared misreading needs three implementations to coordinate the same mistake. Where all three render the same pixels, that is the strongest claim I know how to make short of a formal proof.

And the day the oracle was wrong

The row-major matrix work gave the whole stack its best moment. A row_major decoration describes how bytes sit in a buffer. It does not describe the matrix you are holding in a register after a load, but code paths kept treating it as if it did, and a whole-struct load followed by a member extract slipped past every compensation.

When I fixed it, one oracle flagged the fix as wrong: naga rejected the output. On inspection, naga itself was internally inconsistent on this exact shape: its own WGSL path transposed the loaded copy while its own Metal path did not. Two naga backends disagreed with each other, and the pixel diff, not the validator, settled which one was right. The fixed code renders identically to SPIRV-Cross and to naga's Metal on the GPU; naga's WGSL leg was the bug.

That is the argument for rendering over validating, in one anecdote. Validators check shape. Pixels check meaning. Same instinct as when I shipped stunt: fake the physics, never the plot.

Classification: not every diff is a bug

Once you render-diff at scale you meet a new problem: shaders that are supposed to be nondeterministic-adjacent. Mandelbrots whose escape condition sits on a knife edge, fract-of-sin hash chains, anything where reordering two multiplies moves a pixel by one ULP and the whole image avalanches. A naive harness reports these as failures. I have burned whole afternoons on Mandelbrot pixels that were never wrong, just dramatic. At some point you start talking to the harness. It does not answer back, which is probably for the best. That is how you learn to distrust the harness, and then how you learn to classify what it is actually telling you.

So divergences get classified, not just counted: deterministic miscompiles, single-ULP chaos amplified by the shader's own math, and proxy artifacts introduced by the check itself. The rule is that classification has to be provable. The five remaining diffs in the D3D12 WARP sweep, for instance, are all fp-contraction: recompile both sides with strict IEEE and they render pixel-identical. Benign, with a receipt, not benign because I was tired.

What is still not proven

MSL renders natively on Metal. HLSL renders on D3D12 WARP, which is a software rasterizer: real D3D12 semantics, no vendor hardware. GLSL and WGSL are compile-checked and verified through a Metal proxy, because I do not have a native GL or Vulkan leg wired yet. Those gaps are written on the front page of the docs, which is where gaps belong.

The verification does not make the compiler correct. It makes the failure modes visible, specific, and expensive to hide, which is the property you actually need before you trust a compiler with your pixels.

Tech7 min read

Why my terminal needed its own shader compiler

To draw its first frame on Windows, my terminal had to ship two C++ projects and spawn a subprocess per shader. Four months and 2400 commits later, that entire stack is one pure-Zig module. This is the origin story of zioshade.

Four-pane terminal with GLSL flowing into four shader backends as neon mint streams

I am building wintty, a Windows-native terminal based on Ghostty. Before it can draw a single frame it has to compile its shaders, and the industry answer to "compile shaders" is two C++ projects: glslang to turn GLSL into SPIR-V, and SPIRV-Cross to turn SPIR-V into whatever your GPU actually speaks. On Windows that meant shipping both, building them with CMake inside a Zig build, and either linking the C++ or spawning a subprocess per shader. A process spawn costs about 150 ms on Windows. Ten shaders, nearly two seconds of startup, before the first pixel.

So I did the reasonable thing, which is to say the thing that seemed small at the time: I wrote a little GLSL preprocessor in Zig to start eating that stack.

It did not stay little. The artisan's tools are never finished, only current; this one is on its Mk 2400 and counting.

What I mean by artisan tools

If the Mk 2400 bit landed without context: I wrote about that whole mindset in The artisan's tools. Worth a read if you want the longer version of what I mean when I say the tools are never finished, only current.

The slope, slid down

The preprocessor became a frontend. The frontend learned to emit SPIR-V. Then SPIR-V needed to become HLSL, because this is Windows, and once you have one backend the others start asking. Four months and 2400+ commits later it is zioshade: GLSL to SPIR-V to HLSL, MSL, GLSL, and WGSL, in one Zig module. 98k lines of Zig in the library, 53k lines of tests, zero lines of C++. The only C++ file in the repository is the Windows test harness that renders the output on D3D12 to prove it is correct.

There were stretches where I was sure I would not pull it off. SPIR-V semantics are a maze, HLSL has opinions about everything, and a green validator pass can still be lying to you. I kept researching, kept trying, kept adding oracles because I did not trust my own eyes. Then something shifted: the failures stopped feeling like verdicts and started feeling like a map. Each rejection, each pixel diff, each "naga says no" was telling me where the real boundary was. That kept me going long enough to merge it into wintty, then keep testing, keep tightening, keep widening scope a little at a time, until today.

wintty has been running on it in production since July. The pull request that deleted glslang and SPIRV-Cross from the build is the most satisfying diff I have ever merged.

For wintty the point was never a benchmark slide. A terminal that makes you wait two seconds to draw a frame feels broken before you type a character. Here is what swapping the stack actually bought:

MetricBeforeAfter
Toolchainglslang + SPIRV-Cross, CMake, C++ link or subprocessone Zig dependency
10-shader startup~1.8 s of process spawning~10 ms, in-process
Subprocess pipeline150-265x slower than zioshade732 µs to 1.2 ms per shader
Library vs librarySPIRV-Cross C API, in-process1.4-1.6x faster on the median shader

The honest caveat on the last row: most of the 150-265x is spawn overhead, not algorithmic genius. The library-vs-library number, where both sides run in-process, is the one that says anything about the compiler itself.

The part that was actually hard

Emitting plausible-looking HLSL is easy. I could show you output that compiles clean in every validator and still computes the wrong thing. Compile-clean is not render-correct, and a shader compiler that is subtly wrong is worse than one that crashes: your terminal renders, it is just quietly wrong.

So the real work became verification. The rule I settled on: the output is judged by the competitors' own tools, never by me.

  • Every SPIR-V byte goes through the Khronos validator, on a 2100+ fixture gate, on every commit.
  • The MSL backend is render-proven on a real Metal GPU: zioshade's output and an independent glslang+SPIRV-Cross reference both render, and the pixels must match. 1300+ shaders, zero divergences, plus a second independent oracle (naga) so a mistake the two of us share cannot hide.
  • The HLSL backend now has the same treatment on the D3D12 side: DXC compiles both outputs to DXIL, a WARP harness renders them, pixels diff. Full corpus: 1374 render-matches, 5 diffs, and all five are proven benign floating-point contraction (recompile both sides with strict IEEE and they render pixel-identical).
  • The WGSL backend is checked against the WebGPU CTS itself. This one is my favorite: the harness extracts the CTS's own per-case shaders by driving its framework with a faked GPU device, then round-trips them through zioshade and validates. 1613 real CTS-authored shaders, 1597 round-trip valid, zero invalid, zero crashes.
  • A structured fuzzer ran a million iterations clean.

And the principle underneath all of it: when zioshade cannot translate something faithfully, it refuses, by name. A rejected shader is a bug report. A silently miscompiled shader is a trap. The refusals are counted and published, because "we know exactly what we do not support" is a feature.

What it is not

It is not a Khronos drop-in. If you need the full GLSL ES surface, complete descriptor reflection, or SPIRV-Cross's twenty years of corner cases, use SPIRV-Cross; it is excellent and it is why I could verify against it. zioshade is deliberately narrow: the shading language real projects actually have (GLSL 330-460), to the four targets they actually need, with an in-process Zig module and no toolchain behind it.

It is also, so far, one maintainer. I think the answer to "why trust a one-person compiler" has to be the verification stack, not the author. Every claim above has a one-command reproduction in the repository. That was the design constraint: make checking the work cheaper than trusting the author.

Was this AI-built?

Heavily AI-assisted, and that is why the verification exists rather than a reason to discount the result. The workflow puts reference implementations in the judging seat: adversarial review on every change, then spirv-val, DXC, naga, tint, glslang, and two real GPUs rendering pixel diffs. The tools do the writing; the reference implementations do the judging. A one-maintainer compiler with this much external judging is, I would argue, more auditable than most, not less.

Try it

bash
1zig build cli
2zig-out/bin/zioshade hlsl examples/shader.frag -o shader.hlsl

Or as a dependency: zig fetch the v0.7.0 tarball, one import, done. MIT or Apache-2.0, your pick, C ABI included if you are not in Zig.

The homestead rule applies here too: the toolchain you need does not exist, so you build it, and you prove it with receipts before you ask anyone to trust it.

One more thing. Ghostty itself, upstream, still carries the same two C++ dependencies for its custom-shader feature today. I checked this morning. Same stack, same shape of problem. If that is interesting to anyone upstream: the tooling to prove the swap is safe now exists, and I am easy to find.

Tech6 min read

My first Show HN flopped on schedule

I predicted my first Show HN would flop and the internet obliged: two points. The thing I launched it for, a local stunt double for 95 APIs, does not care.

The stunt dashboard: the request inspector for a stripe-style sim, the adapter catalog in the sidebar

Yesterday I posted my first Show HN. It is at two points and two comments as I write this, and I wrote the ending in advance, in the post itself: "I am expecting to 'flop' in here since it's literally my first post. I know, I should feel ashamed!"

The internet read the script and obliged.

Clarifying: I meant ashamed for not having posted more after this many years.

Fine. Best launch I ever did on HN, mostly because it is the only one, and because the two points were never the point. The point was to finish the tool, point it at the world, and see what came back. What came back was one star, one thoughtful stranger, and a collaboration in embryo. We will get there.

First the tool. It matters more. It always does.

Your test suite should not need a Stripe account

I have spent twenty-odd years integrating other people's APIs: commodities trading and market data in London, fintech, payments, blockchain RPC infra. The test story never got better. Sandbox accounts you have to create, seed and babysit. Live secrets in CI, because someone was in a hurry once. Rate limits. Flaky suites, because the mock did not behave like the real thing, so you ended up testing the mock instead of your code.

The usual tools are not wrong. WireMock, Prism, MSW are great at static and schema-shaped mocks. What I always wanted was a runnable stand-in: create, list, mutate, webhooks, auth expiry, cursor pagination, state machines. A Stripe that is actually there when you turn around. Without writing it all myself, again, on every project.

A stunt double, not a stub

In film, the stunt double takes the hits so the star does not have to. That is the whole job description, and it is right there in the name: stunt. Local, stateful stand-ins for the public APIs your code talks to. Ninety-five adapters: Stripe, Twilio, GitHub, S3, Salesforce, and the long tail. Each one is YAML, sandboxed Starlark handlers, and synthetic fixtures, packed into a single Go binary.

brew install --cask stuntapi/tap/stunt, then stunt demo. You get a Stripe-style sim on a local port, and the curl it prints actually means it: create a charge, list it back (it is still there; state persists across requests and restarts), capture it, watch the signed webhook fire at a local sink. All synthetic, all offline, no Stripe account, no bills. Docs here.

And when you want your double to take a hit on purpose:

yaml
1version: 1
2rng_seed: 42
3network: { mode: port, base_port: 8000 }
4services:
5 example:
6 rules:
7 - match: { method: GET, path: /hello }
8 when: { chance: 20 }
9 respond: { status: 503, body: { inline: { error: boom } } }

One request in five gets boom. Deterministically, because the RNG is seeded. Chaos you can replay in CI, which is the only kind of chaos worth having.

The sandbox is the product

The clever part is not the adapters. It is where they run. Adapter logic executes in a Starlark VM with no host I/O and no network, with bounded execution. That is the property that makes it safe to stunt catalog add a stranger's adapter, the way you would install an npm package: third-party logic, zero trust required.

State lives in engine primitives, not in the scripts: SQLite-backed collections, KV, a blob store, HMAC tokens, a webhook emitter that signs with the real provider schemes. Everything is deterministic: an injectable clock, a seeded RNG (Random Numbers Generator), snapshot and restore. Each server gets a dashboard with a request inspector that does copy-as-curl and replay, so when a test fails you interrogate the crime scene instead of imagining it.

What belongs in the double

Jeremy, the one substantive commenter, asked which provider behaviours are worth promoting into reusable contracts. My answer, warmed over: anything a client cannot distinguish in a test suite belongs in the mock. State machines, pagination, signature schemes. The semantics stay provider-native. A Stripe refund and an Adyen reversal are not the same object, and abstracting them is how payment integrations get burned.

In other words: "fake the physics", never the plot.

Real SDKs keep the adapters honest

Any mock can pass tests you wrote yourself, in a language of nods and winks. The bar I care about is fooling the real client libraries. So CI drives the actual provider SDKs against the adapters: stripe-go, aws-sdk-go-v2 (real SigV4), go-github, twilio-go, go-shopify, and the Node trio of stripe, octokit and twilio, all through the real binary.

That harness caught real bugs in my adapters. Stripe money fields coming back as JSON strings, which typed SDKs simply reject. GitHub pagination Link headers pointing at production: octokit followed them straight to the real api.github.com, from inside a test that thought it was offline. Both bugs looked fine to my own tests. The SDKs were not fooled, and that is exactly the point of them.

The honest limits

  • Adapters are unofficial and fidelity-ranked, not API-complete (contributions welcome). I would rather nail the flows real integrations actually exercise than checkbox every endpoint.
  • Windows is a script install rather than the default winget source. Yet.
  • If you integrate an API that is missing, stunt adapter import openapi|har|proto scaffolds one. PRs welcome, genuinely.

Two points, one star, one collaborator

Back to the flop. Here is what two points actually bought:

One star, the first. One comment, from Jeremy, that engages with the sandbox design on its merits, points at an adjacent project of his own (Vyral: live-provider qualification, complementary rather than competing), and floats a collaboration. By the end of the thread we were, in my own words to him, "already collaborating in a way" because I dared to hand him a suggestion for his project. Hopefully it's going to be well received... that's the same "shipping" spirit I want to hold optimistically going forward.

My working theory of the flop, honestly held: a first post from an account with no history gets no lift, so almost nobody saw it; the title asks you to parse a metaphor before the payoff; and "95 adapters" in parentheses reads as a list nobody counts. I priced all of it in and posted anyway, because a launch is one hour of one day. The repo, the docs, and now this post are the durable artifacts. They keep working after the thread goes cold, and they are how the next hundred readers trickle in.

Also: I asked for fresh virtual tomatoes, and the internet sent one polite collaborator instead. Gentlest tomatoing imaginable.

But I need tomatoes to make a salad! I want criticism, ideas, opportunities for growth!

I wrote about the artisan and his Mk 45 chisel the other day: the tools are never finished, only current. stunt is v0.3.0, three hundred and fifty-seven commits in. In artisan terms that is Mk 357 wearing a modest name tag. It earns its keep in my own daily work or it does not; that is the only metric that was ever real.

Adapter #96 is taking nominations. wintty is probably the next shipped product from solo team yours truly.

Ciao!

Share𝕏LinkedInRedditWhatsApp