Skip to main content
Ashley Childress

How I decide.
Filed, dated, and searchable.

249 on file

Filed notes

  1. Decisions · August 2026

    Told the presentation was the problem, I changed the data instead

    Every card carried a blurb and a fact — a summary of a summary — and the blurb was the half being indexed and shown. I deleted the blurb, put the fact itself on the tile, and cut 103 cards that turned out to be describing rather than deciding.

  2. Principles · August 2026

    I traded stronger copy for accurate credit on every award I shared with a team

    A rewrite turned my award cards from passive voice into first person and, in doing so, claimed sole ownership of work my team did. I pulled sixteen of them back to shared credit. The hedged verbs read weaker than the version that overclaimed, and that is the accurate one.

  3. Principles · August 2026

    Forty-one cards publish under my own word floor because the alternative was inventing the reason

    My rules set a thirty-word minimum on every card. Forty-one could not reach it without me supplying a rationale the original decision never recorded. I left them short and live rather than pad them. A rule I wrote loses to a claim I cannot support.

  4. Practice · August 2026

    Guardrails and model cross-review replaced the code reading I gave up

    Instead of reading every generated path on Carbon Trace, I leaned on strict guardrails, Claude Opus self-review, Codex implementation support, and occasional Antigravity input. That let me pick up the frontend stack and the UX decisions in context rather than stopping to study them first.

  5. Practice · August 2026

    Every Save the Sun commit was signed, attributed, and scanned in CI

    On Save the Sun I enforced provenance in the pipeline: signed atomic commits, AI-attribution footers on every commit, and secret-scanning plus CodeQL running in CI on every push.

  6. Awards · August 2026

    Save the Sun won Best Google AI Usage in the June Solstice Game Jam 2026

    I submitted Save the Sun as The Oracle and the Wolf and won Best Google AI Usage in DEV's June Solstice Game Jam 2026. Gemini plays a live deduction rival on top of a deterministic engine that never hands it the answer.

  7. Principles · August 2026

    Every automated gate replaced a review I stopped doing by hand

    The pipeline started as tests and that was not enough. I added Sonar, then Lighthouse CI, then Playwright, keeping each tool that caught something the others did not. If I am not reading every diff myself, something has to be doing that job instead.

  8. Architecture · August 2026

    I put a database between the AI and the search index so I could review before publishing

    I wanted the AI writing cards, and the Algolia MCP was still in beta. I built a server that writes to Supabase instead, so I could read every card before pushing to Algolia. Algolia is production; Supabase is where AI output waits for me.

  9. Decisions · August 2026

    I containerized to Cloud Run because DEV added embed support and suggested it

    DEV added support for embedding a live Cloud Run service and recommended it when the New Year challenge opened. I containerized the Next.js app instead of taking the one-click Vercel deploy, because the embed was worth more than the convenience and I already knew GCP.

  10. Decisions · August 2026

    The card writer was built for OpenAI first and converted to MCP when I added Claude

    The first version targeted ChatGPT specifically, before the Codex app existed. When I started using Claude I rewrote it as an MCP server so either model could write cards, which meant giving up anything OpenAI-specific I had leaned on.

  11. Architecture · August 2026

    Cards are soft-deleted so Supabase migrations do not force an Algolia reindex

    I soft-delete cards instead of removing rows, because hard deletes would mean tearing down and rebuilding the Algolia index on every Supabase migration. Algolia documents soft deletion as the pattern for keeping an index stable across upstream schema changes, so deleted cards stay as rows with a timestamp.

  12. Decisions · August 2026

    I split the Lighthouse budgets and let mobile score lower

    Mobile failed the shared budget and I did not understand mobile performance well enough to fix it properly. I split the configs and set mobile lower rather than block the pipeline on something I could not yet diagnose. It has been climbing since.

  13. Decisions · August 2026

    Ruled out an AI ATC-audio pipeline after confirming OpenSky already broadcasts callsign

    I researched fine-tuning Whisper on ATC audio to resolve flight callsigns for Metal Birds Feed, assuming the ADS-B feed only carried icao24. OpenSky's live state-vector API returns callsign alongside icao24 already, so I dropped the audio pipeline rather than build GPU inference and LiveATC permission requests for data already available.

  14. Decisions · August 2026

    ICAO hex is a nullable enrichment field, never a required key

    The ICAO 24-bit address is a Mode-S allocation separate from registration, so a national register carries it only when it derives it from a public formula, as the FAA does. I keep hex nullable rather than a join requirement, so a source that omits it still onboards.

  15. Awards · July 2026

    I put the data validation and end-to-end testing hours in before a platform pilot launched

    A platform pilot was heading for launch. I contributed the data validation and end-to-end testing hours ahead of it rather than trusting the design to hold on its own, and the team's launch came off clean. The Home Depot gave me a Homer Award on July 15, 2026.

  16. Architecture · July 2026

    The object store is the job state; there is no status database

    I refused a second datastore. The rendered object in the public bucket is the ready state, and the metadata file is written last, so a crash leaves a job retryable and never leaves a job marked ready whose output does not exist.

  17. Decisions · July 2026

    I queued the work rather than pay Cloud Run for idle CPU

    Detaching the job from the request requires Cloud Run's no-cpu-throttling, which charges for a container doing nothing. I ran the pipeline on a Cloud Tasks worker request that calls back into the service, so a user can close the tab. Background execution bills instance time; a worker request bills work.

  18. Decisions · July 2026

    A prompt example that is the right answer gets parroted back

    My prompt examples described a plausible commit arc, and the model reproduced them rather than reading the evidence in front of it. I rewrote the examples to describe an arc no real repository could have, so they teach shape without supplying a conclusion.

  19. Practice · July 2026

    A weekend has no room to discover a project-killer on Sunday

    Before building anything I proved two things: that a stored procedure could reach the GitHub API through an external access integration, and that Cortex ran in my region. Either failing would have ended the project, and I had two days to find out.

  20. Principles · July 2026

    The 500-commit ingest cap prints itself on the output

    I stop ingest at five hundred commits to bound the bill, and when a repository is longer the output says so. Reporting a slice as a whole life is false, and the honest fix is to admit the boundary on the artifact. A cap you hide is a lie.

  21. Decisions · July 2026

    A function clicked into existence in a UI does not live in your git history

    Cortex AI Function Studio was the fastest way to prototype the narration call, but the object it creates exists only inside the account. I rewrote it as a hand-written UDF over AI_COMPLETE so the function ships in the repo and deploys with the CLI.

  22. Principles · July 2026

    A repo with no real story gets told so, and the model never runs

    I gated every storyline on a fifteen-commit floor: below it the card reads "no story here" and no model is called at all. A tool that admits when there is nothing to say is the one you believe when it says otherwise.

  23. Principles · July 2026

    The model reads the arc of a commit history and never the motive behind it

    I let the model interpret the arc — the gaps, the hours, the returns — but forbade it from asserting intent or feeling. The moment the output guesses at why, nothing else it claims is trustworthy. Timestamps are evidence; motivation is invention.

  24. Principles · July 2026

    A twenty-thousand-commit repo bills the same as a two-hundred-commit one

    Narration cost cannot grow with repository size, so I budgeted the model's evidence at a quarter of the commit lines, floored at twenty and capped at one hundred forty. The demo and a production run are the same architecture — there is no rewrite waiting between them.

  25. Decisions · July 2026

    A profile aggregates unrelated work into noise; a repository has an arc

    The original plan read a developer's entire year. I cut it to a single repository because a repo has a clean shape — commits start, cluster, pause, restart, or stop — and that shape is what gets narrated. A year of a profile is several unrelated shapes overlaid.

  26. Decisions · July 2026

    Two reads of the same repo were handing the model different commits

    I ordered evidence by distance from the pivot and then by timestamp, which is not a total order — rebased and batch-pushed commits share an authored time. SHA became the last tiebreak, because a narrator that changes its evidence between runs cannot be trusted about any of it.

  27. Decisions · July 2026

    The model wrote "fifty-six commits after midnight" about forty-seven

    Handed every fact as a single object, the model read the total commit count and printed it as the night count. I split each fact into its own labelled argument and barred the model from emitting digits at all. The renderer composes every number now.

  28. Decisions · July 2026

    I raised the model's temperature from 0 to 0.4 and moved the guardrails into SQL

    Temperature zero is the responsible number and it wrote nothing worth reading. I turned the sampler up until the writing had a pulse, then paid for it with thirteen SQL reject checks, so the warehouse rather than the sampler decides what reaches a card.

  29. Architecture · July 2026

    SQL picks the story in a commit history; Cortex only writes the sentence

    I put the narrative decision in plain SQL and left the model only the prose. Six detectors score the history and one wins deterministically, so the same repo always yields the same story and the only non-deterministic step is a sentence.

  30. Awards · July 2026

    I built the daily results summary that made platform testing output readable

    A platform testing effort the team was running produced system-behavior output nobody outside it could read. I built the system-behavior report and the daily results summary in a format the whole team could use without translation. The Home Depot gave me a Homer Award on July 1, 2026.

  31. Decisions · June 2026

    I tore out the live voice session and rebuilt it as push-to-talk

    I scrapped my first voice approach — a continuous live audio session — and rebuilt it as push-to-talk: hold, speak, release, transcribed server-side through the exact same pipeline as the text box. Voice and typing became identical, and the key never reached the browser.

  32. Architecture · June 2026

    I matched each AI role to a model tier instead of using one model everywhere

    Rather than run one model for everything, I matched each AI role to a tier: a full-strength model where reading the player's words exactly mattered, and a lighter, cheaper one for the rival so he'd play looser — plus cheap tiers for speech-to-text and text-to-speech.

  33. Architecture · June 2026

    I made the text-to-speech endpoint speak only server-owned lines

    I built the text-to-speech endpoint so it never voices client-supplied text — only server-owned lines by known ID or opaque per-session lookup. That keeps it from being abused as a free speech service or an injection surface, and it is rate-limited per session on top.

  34. Architecture · June 2026

    I sized my own rate limits to trip before the vendor's quota

    I put a per-surface rate limiter in front of every AI call, keyed by client and session and sized so a whole game never throttles — but abuse trips my limiter before the vendor's quota, with the billing cap as the hard spend stop.

  35. Architecture · June 2026

    I kept the public layout and the hidden answer on two unlinkable seeds

    In my AI deduction game I drew the public board layout and the hidden answer from two separate random seeds that can never be linked — otherwise the visible arrangement could leak the answer. The secrecy guarantee lives in the engine, not in the shuffle.

  36. Practice · June 2026

    I built in dependency order and tested each layer before the next

    I built the game in strict dependency order — core engine first, then the language layer, then the opponent — and each module shipped with its own test suite before the next began. Nothing was layered onto untested foundations.

  37. Architecture · June 2026

    I shipped with no database and still made a refresh resume the round

    I deliberately shipped with no database or accounts — sessions live in memory behind an opaque cookie. A refresh still resumes the exact round, with the same answer, board order, and progress, so the simpler architecture cost the player nothing.

  38. Practice · June 2026

    I set coverage floors by blast radius, not a blanket number

    On this game project I tiered CI test-coverage gates by risk, not one flat number: the core engine carried a 100% bar because a bug there means an unfair round, and the UI a far lower one. Config could only raise a threshold, never weaken it.

  39. Decisions · June 2026

    I built the debug view as on-stage proof that the engine owns truth

    To prove my game's AI couldn't cheat, I built a live debug log that tags every result as either deterministic engine truth or AI inference. It lets me show, in real time, that code owns every fact and the AI only supplies the voice.

  40. Decisions · June 2026

    I scope with explicit non-goals and documented rejected options

    I scope by writing down what a project will NOT do, in versioned milestones, and I record rejected options with the reason they lost. Documenting the roads not taken keeps a solo, deadline-driven build honest.

  41. Decisions · June 2026

    I let the AI play the opponent and rejected a scripted one on purpose

    For my deduction game's AI rival, I rejected a scripted opponent — a predictable one is beatable like a puzzle and wastes the model. I let the AI reason live instead, with a weighted-random fallback only when it errors. Its capacity to misjudge is the feature.

  42. Decisions · June 2026

    I treated accessibility and graceful degradation as CI gates, not polish

    I treated accessibility as a build gate, not polish: missing WCAG 2.1 AA contrast or the Lighthouse accessibility bar fails the build. The game stays fully playable with every visual and audio flourish off — a feature that can't degrade cleanly doesn't ship.

  43. Decisions · June 2026

    I capped the AI opponent at a 12-year-old's reasoning so it would feel human

    I deliberately weakened my game's AI opponent so it would feel human: I ran him on a lighter, cheaper model tier and prompted him to reason one clue at a time and guess when 'sure enough.' A fallible opponent feels more alive than a flawless one.

  44. Architecture · June 2026

    I kept the API key in a secrets manager and let a test enforce the masking

    I kept the Save the Sun API key only in a secrets manager, masked it at the logging layer, and wrote a test that asserts it never leaks. The masking is enforced by the suite, not by remembering to mask each new log line.

  45. Practice · June 2026

    I test the engine's leash, not the model's brain

    On an AI game project, my testing rule was 'test the engine's leash, not the model's brain.' I never assert what the AI chooses — only that illegal moves are rejected, the hidden answer never leaks, and a deterministic fallback always catches AI failures.

  46. Architecture · June 2026

    I made the engine the only source of truth and never let the AI see the secret

    Save the Sun is a browser deduction game where you race an AI to a hidden answer. I gave the AI four roles — reading questions, playing the rival, transcribing speech, voicing lines — but kept all truth in a deterministic engine that never lets it see the secret.

  47. Decisions · June 2026

    DATA_LICENSES.md collapsed from 1,844 lines to 273

    I cut DATA_LICENSES.md from 1,844 lines to 273 by moving verbatim agency replies, recon notes, license-drift logs, and roughly 150 awaiting-reply entries into the source-onboarding-checklist and license-matrix. The public file now answers a downstream consumer's questions, and nothing more.

  48. Decisions · June 2026

    Attribution docs publish copy-paste-ready example text, not rule descriptions

    I reframed the Attribution section to publish the exact text a downstream consumer pastes into their UI, not a description of what attribution rules require. Fabricated boilerplate like “this material has been changed” was stripped from every block once I noticed it came from neither the agency nor the license.

  49. Practice · June 2026

    Every source promotion ends with a stale-reference grep sweep

    After every source-status change I grep for the prior label and prior URL across DATA_LICENSES, README, license-matrix, and source-onboarding-checklist and confirm zero hits. Three promotions in a row had left stale Personal-use references and outdated register URLs hiding in one file or another.

  50. Practice · June 2026

    Stopped guessing agency emails after two same-day bounces

    I stopped using pattern-guessed agency emails after Lebanon and Dominican Republic both bounced on the same day. Every address now gets verified against a real contact page before a draft is written. Verified channel first, drafted body second.

  51. Decisions · June 2026

    Offline agency sites get shelved, not chased and not excluded

    Morocco DGAC's entire transport ministry was port-443-refused at both the DGAC subdomain and the parent ministry. I marked the source deferred with a weekly recheck rather than chasing postal, phone, or parent-ministry channels. Dead site rarely beats moving to the next reachable agency.

  52. Decisions · June 2026

    A statutory fee citation can drive exclusion without an agency reply

    I graduated Egypt ECAA to Excluded on the strength of Law No. 93 of 2003 alone — a statutory per-record fee for civil aviation services. The pattern already matched Romania, Japan, Israel, and the UK. Law 93 settled Egypt before anyone wrote back.

  53. Architecture · June 2026

    The monthly refresh cron undershoots four of five live sources

    My cadence audit found the project's own records wrong on its busiest sources. FAA refreshes daily, Transport Canada updates same-day, ILT is weekly, and only CASA Australia matches the monthly cron 0 6 1 * *. I now treat the schedule as a known gap, not an assumption to keep.

  54. Decisions · June 2026

    Stated and observed refresh cadence are both stored, and the faster one wins

    Thailand CAAT stated 'twice yearly, January and June' but the file history showed roughly annual mid-year drops. I now store stated and observed cadence side by side and refresh on whichever is faster, because aspirational cadence in the docs is a worse downstream signal than the historical pattern.

  55. Practice · June 2026

    Ministry-level Personal-use terms turned out softer than they read

    Singapore CAAS, Brazil ANAC, and Switzerland FOCA all flipped from Personal-use to Open after the dataset team waived their parent ministry's default site-wide terms. Three reclassifications in a row told me ministry boilerplate is a starting posture, not a final answer.

  56. Practice · June 2026

    Register pages are read for license posture, not contact details

    I shifted research to scan register and portal pages for license-posture language in the same pass that finds the contact email. The earlier contact-then-classify approach produced wasted draft cycles when the page already declared 'personal and non-commercial use' verbatim, as ANAC Algeria did mid-batch.

  57. Architecture · May 2026

    Convergence math is a pure deterministic Kotlin function, not a model call

    I converge the three lens reads in a deterministic Kotlin function: two-of-three agreement yields CONSENSUS, a single survivor CANDIDATE, full disagreement AMBIGUOUS, and a Skeptical conflict over an otherwise-consensus surface CONSENSUS_WITH_CONFLICT. The model proposes the reads. The verdict layer does not consult the model.

  58. Decisions · May 2026

    FP16 numerical drift between CPU and GPU produces different tag sets for the same prompt

    I tested twelve entries and six produced different tag sets across CPU and GPU on the same model, prompt, and seed. The cause is FP16 numerical drift selecting different argmax tokens where the top two logits are close. Per-backend stability still holds, so production pins one backend.

  59. Principles · May 2026

    Privacy is enforced at build time, not only at runtime

    Beyond the runtime network gate I added a verifyNoTelemetry Gradle task that runs four independent scans — classpath, manifest, APK, host list — and fails the build if any trip. Receipts upload as a CI artifact every run, so the claim is checkable at construction time.

  60. Architecture · May 2026

    Recurrence is built by the app first and only judged by the model

    Recurrence is the one extraction surface I did not let the model decide alone. The app builds a deterministic candidate from prior entries, then asks the model only whether the current entry actually repeats it or merely shares a clock time. The model never emits a pattern ID.