AI does the drafting. I answer for what ships.
I use AI coding tools, and I am the author of record for everything that merges. This page is how I keep that work reviewable, measured and defensible.
Five rules
- Scripts compute, models judge. Any number a user will act on comes from deterministic code. A model is used for the part that needs reading comprehension, and it has to quote its evidence.
- Never list what you can’t defend. That is a law of the AI-Era Job Kit and it applies to my own code. If I cannot explain a line in review, it does not merge, whoever wrote it.
- Decide the failure mode first. The contact form’s spam filter fails open so no real message is lost. The job kit stops when live data is unavailable so no stale number is shown. Both are choices made before the happy path was written.
- Measure the non-deterministic step. When a model’s grading moved 5.1 points between sessions, I measured it over three rounds until it was inside target. See the eval.
- Same gates for every author. A change drafted by an AI tool goes through the same type check, lint, tests and review as one typed by hand.
Workflow
- Write the change as a small pull request. Conventional commits, enforced by commitlint, with a 72-character header.
- Use agent tooling for the repeatable parts. The J0DI3 repository carries Claude Code skills for adding an endpoint, a migration or an AI surface, and agents that audit a change before a pull request and verify production after a deploy.
- Let CI decide. On the web app that is strict TypeScript, Biome, about 635 Vitest cases and Playwright in two browsers with an accessibility scan. On J0DI3 it adds a 70% coverage floor, integration tests against a real database and a migration round trip.
- Own the commit. I am responsible for the code, so the commit carries my name. A tool that helped draft a change does not share the accountability for it.
Prompts and pinned models
Prompts live in the repositories next to the code that calls them, and they are reviewed in pull requests like any other source.
| System | Model | Where the prompt lives |
|---|---|---|
| Contact form classifier | gemini-2.5-flash, temperature 0, JSON schema. SDK pinned to an exact version. |
classify-contact.ts |
| Career guide generators | gemini-2.0-flash with Zod schemas |
scripts/generate-*.ts |
| Job kit skills and agents | Runs in the user’s own assistant | skills/ and agents/ |
| Hashflag Skills | Runs in Claude Code | skills/ |
| J0DI3 | gemini-2.5-flash and gemini-2.5-pro, set in one client module |
Private repository |
Model names are pinned to a model family. Pinning dated snapshots is on the list for J0DI3 v2.
Evals
- Grading consistency eval for the résumé scorer: three résumés, three independent sessions each, three rounds, with every disagreement traced to a cause. Summarized in the Hashflag Skills deep dive.
- METHOD.md in the job kit: the rules for sourcing every number and the grader agreement check, with a target of the same letter on 90% of qualifications.
- Classifier tests: 13 cases covering every fail-open path, the model default and override, and that no personal data is logged.
What is not evaluated yet is stated on each deep dive. The contact classifier and J0DI3 do not have labelled answer-quality sets, and I say that there instead of implying otherwise.
LLM data handling
This describes what the code does today.
- vetswhocode.io contact form. The name, email, subject and message are sent to Google Gemini once to classify the message. The application does not store submissions. It forwards them to the team in Slack. A test asserts that personal data is not written to logs.
- Career guides. Only public military job code descriptions are sent to a model. No personal data is involved.
- Job kit and Hashflag Skills. Scripts run on the user’s machine and refuse to read or write a résumé inside a git repository. The résumé is read by whichever AI assistant the user runs the skill in, under that assistant’s terms. Nothing is sent, posted or applied for on the user’s behalf.
- J0DI3. Learner input is sanitized and delimited before it reaches a prompt. Personal fields are encrypted at the field level with rotating keys, access to them is audit logged, and output guardrails check responses for personal data. Sampled response snapshots are kept for 30 days for incident replay.
Decisions
The tradeoffs behind each artifact are written up as short records in the decision records index.