Skip to main content

How I forced Claude to finish the job

I

I do a lot of with Oracle APEX, and since April I run it out of a single folder. It is an Obsidian vault, a git repository and a Claude Code project at the same time. I call it my Brain, and Claude Code is the operator working inside it.

This article is not about prompts. It is about the less visible part: contracts, checklists, git discipline, hooks and a feedback loop, because that is what turned Claude from a clever intern into something I can hand a task to and walk away from. Almost every piece below was born from a mistake it made.


This is the simplified version. Every section below has more behind it than I can show here, and whole areas of the Brain (the parts that read my inbox, draft invoices, scrape LinkedIn or run my weekly audits) are left out on purpose. If you know Hermes Agent from Nous Research, the self-improving agent with its own skills and memory, this is kind of my version of it. Built from Markdown, git and hooks instead of a framework, and tuned for one Oracle developer.


One folder, three hats

The AGENTS.md (because I also use Codex and Copilot) at the root (CLAUDE.md is a symlink to it) tells any agent how to behave here, with skills exposed as slash commands.

No database (yes, an Oracle developer saying that with a straight face), no vendor lock-in, and small Python helpers do the heavy lifting so the model doesn't read hundreds of files just to count them. Rules live in one folder, decisions in another, then scripts, hooks, skills, logs, memory, each in its own. Projects and clients get a folder each for the notes, the tickets and the contracts, while the APEX and PL/SQL code stays in its own git repo that the Brain only governs. Two heavy folders (scraped content and my knowledge base) are git submodules, so a throwaway checkout doesn't drag four thousand files along.

The knowledge base is mostly Oracle: the APEX community blogs I follow, the Oracle 26ai docs and my own notes and snippets. A rule says Claude greps that archive before it answers anything about an APEX API from memory, because memory is where the invented parameters come from.


The git strategy

Everything else stands on git, so this is the part that took the most iterations. Every change has history. I have been pushing APEX apps and schemas into git for years (CI/CD for Oracle database & APEX developers is from 2021), so the Brain getting the same treatment was the easy decision; the hard part was the mechanics. There are three kinds of checkouts:

  • The canonical checkout is the folder Obsidian reads. I edit there by hand; agents never do.
  • Every agent session gets a throwaway worktree in a dot folder inside the repo, on its own branch, sparse-checked-out so binaries and dead folders never land in it.
  • Two permanent worktrees: one where every scheduled job runs and writes, and one that is the landing buffer for automated commits.

Worktrees are on sync ignore list, so I dont have large churn on my Dropbox.

Nothing develops on main. A hook refuses a write into the canonical checkout and any mutating git command on a protected branch, and the same guard covers the client APEX repos the Brain governs, so this is not a preference, it is physically blocked. Work lands through one path, the finish-merge: fold the branch churn, rebase onto origin main, fast-forward main, push through a gateway that verifies the result. A landing lock serializes it, so two sessions finishing in the same minute queue up instead of clobbering each other.

"Done" then has a definition I can check: the commit is reachable from origin main and from the canonical checkout's local main, and only then is it visible in my Obsidian vault. Not on a branch, not "pushed somewhere".

Around that core sit the safety nets. An autocommit job sweeps my hand edits in Obsidian into autosave commits every ten minutes. A worktree reaper merges finished branches a crashed session left behind and deletes what already landed. An off-Dropbox mirror with dated bundles answers "was this commit ever on main" even after a force-push, synced to OneDrive. And the two heavy folders are submodules, committed and pushed inside first, then the parent records the pointer.

That is the short version. The rule set behind git alone is a few dozen detail files, most of them written after something reached main the wrong way.


Two contracts: SOP and DOD

Every session loads two short files. The Standard Operating Procedure says how to start: orient yourself and print a banner that names the checkout and the branch. If I don't see the banner, the SOP was not read, and a hook stamps whether the file was actually opened.

WORKSPACE: BRAIN
WORKTREE: brain-setup-blog
BRANCH: claude/brain-setup-blog
REPO ROOT: ~/Dropbox/BRAIN/.claude/worktrees/brain-setup-blog


The Definition of Done is a checklist of 15 numbered items Claude walks before it is allowed to say "done": whole ask answered, only task files staged, everything committed and pushed, landed on main and confirmed on the canonical checkout (item 8 literally runs the merge-base check from the git section above), submodules first, tests reported, boards updated, correction ladder walked if it made a mistake, and so on. The result is printed as a delivery banner, roughly this shape:

COMMITS:
  main/8a7efd3e6 > blog: draft 230, how i forced claude code to finish the job
--
TESTS: none, content only
--
CHECKLIST: 12/15
N/A:
   9) Submodules first, none edited
  12) Changelog, project has none
  13) Kanban updated, no board touched


The trick is that a Stop hook parses this banner. The SHA has to match HEAD, the branch has to exist on origin, every file the session touched has to be committed and reachable from main. A "15/15" with a dirty file is rejected and the turn stays open. Claude can't talk its way out; it has to actually commit.

Before this, "done" meant "I wrote the file". Sometimes the file was in the wrong checkout. Sometimes the branch was ahead of main for days. Now I read one banner instead of a diff.

Projects and clients can add their own overlay (a project SOP, an extra checklist, a publish procedure, specific coding standards and more), and a release request has to print that file's banner before anything else happens. For a client's APEX schema that overlay is where the Oracle part lives: every touched object exported fresh from DEV and backed up before the first edit, zero invalid objects after a recompile before any pull request, and nothing deployed by the agent, ever; it builds the patch and hands it to me, and I run it. The contracts folder itself is locked: even Claude can only change a contract through one dedicated skill, never a direct edit, because otherwise the checklist quietly grows explanations nobody reads.


Rules are routers, not novels

One of the biggest challenges over these past months was to fight the AI bloat. You add something here and there and suddenly your token window is gone before you ask the first question.

Rules used to be one huge file. Now each topic has a short router (git, kanban, notes, dashboard, style) that points to detail files loaded on demand. Currently that is 34 routers and around 400 details, plus 36 decision records for the "why". The top-level instruction file says which router to load before which kind of action, and an unloaded rule is a broken rule.

Code has its own router, a coding standards file that says which Oracle file to load: the SQL and PL/SQL naming and formatting rules, the data model rules, or the APEX page checklist. Claude reads the standard for what it is about to write and nothing else, and a deviation in the code is fine as long as it carries a WHY comment.

Skills work the same way. About 100 of them, each a slash command with its own folder, loaded only when invoked. A dozen or so are pure Oracle: the strict SQL and PL/SQL formatters, a utPLSQL test writer, an APEX page review, a REST module checklist, the deployment flow with its patches and hotfixes, and the client ones that create a ticket branch or open the pull request. Nothing bulk-reads at session start, and a session price footer at the end of every task shows me what the context cost.


Hooks: rules that cannot be ignored

I love hooks. Prose rules drift all the time. Hooks don't. Whenever a behavioral rule fails twice, it gets promoted into a hook, so the roster keeps growing; today it is about 90 handlers behind one dispatcher, ordered by a single manifest file.

Some of what they do:

  • Before a tool call: refuse writes on main, writes to temp folders, sed and awk (they can't be allowlisted, so an unattended run would hang on a prompt), chained shell commands, and edits to a governed folder (a client's APEX repo, say) without a lock.
  • On every prompt: inject a small context reminder chosen by the topic of the prompt (git, notes, kanban), instead of loading everything always.
  • At the end of a turn: style lint, the DOD gate above, a check that a new lesson file has its mandatory sections, a check that a worktree branch is not sitting unpushed.
  • Below the agent, tracked git hooks reject a commit subject with the wrong shape or a board file that moves a card nobody edited.

There is also a sound on hand-back, which I wrote about in Mario sounds in Claude Code. The lesson from that one: a chime fires on everything its event carries, so gate it down to the single moment it exists for.


WTF, the post-mortem command

When Claude does something wrong, I don't argue in chat. I type /wtf followed by what annoyed me, and the skill runs a fixed procedure:

  1. Grep the lessons folder for prior incidents, because most of them are repeats.
  2. Reconstruct in one sentence what it did and what I wanted instead.
  3. Name the root cause, and if it is a repeat, why the previous prevention didn't hold (rule in a file nobody loaded, rule too soft, rule never wired to a hook).
  4. Present a plan: fix steps and prevention steps.
  5. Wait for my answer as clickable options, never as a sentence at the bottom of a wall of text.

On approval it fixes the files, patches the producer (the script, skill or hook that produced the bad output), and writes a lesson file. The lesson template has two mandatory headings, "Producer fix" and "Prevention rule", and the Stop hook refuses a lesson without them. A lesson that only says "I will be more careful" is a diary entry, and diary entries don't change behavior.


Lessons promote themselves

A lesson is raw material, not a destination. The enforcement ladder has three rungs: a hook or script gate (mechanical, zero tokens), a rule detail behind a router (for judgment calls), and the lesson text itself (the weakest one, since nobody reloads it).

Three strikes on the same failure and it is promoted to a rule automatically, without asking me, and the DOD has an item for it so the promotion happens at close and not "some day". Once the prevention lives in a rule or a hook, the lesson is archived; the active tail is capped at about twenty. The titles of the last five are injected into every session, and when a prompt matches an older lesson's topic, a pointer to it gets injected too. As I write this, the freshest lessons in it come from Oracle work: a test fixture that rebuilt the HR schema, lost five of its seven tables and reported success, 380 lines of generated PL/SQL asking a question the database already answers, and a test that could not fail.

One thing I had to say out loud: a lesson is for a mistake, not for a wish. If I ask Claude to remember a preference, that goes straight into a rule. Writing a lesson about it buries the real failures under paperwork.

The weekly insights run closes the loop from the other side. It reads the week's lessons and proposes a systemic fix when two share a theme. That is the workflow from Making Claude Code insights actionable, now feeding a board instead of a report.


The dashboard: watched, not searched

I don't want to search my Brain in the morning, I want to look at it. A custom Obsidian plugin renders a handful of panels from small data files that scheduled jobs refresh. The Today page is the calendar, recurring rituals, unread inbox count and open cards per board. Sorry, I decided not to show you the image (yet, stay tuned).

The top block is the system overview. Vault size, file counts, how many worktrees are alive and how much disk they eat (67 of them on that day, which is why the reaper exists). How many tokens the always-on context costs per session, next to the optional rules and the hooks, so I notice when the autoload creeps up. Skills, rules, active lessons. Guard denials, meaning how often a hook refused something today. Cron runs and cron errors. Open errors, decisions and insights on the system board. The two rows under it say when the last healthcheck and stats refresh ran, so a stale number looks stale.

The bottom block is money. One gauge per subscription with its reset time, then a stacked chart of the estimated spend per day and per project over the last eight days. The percentage above each bar is how much of the day's quota went; the one below zero is non-billable work. The three tiles at the end are the last 24 hours: commits, sessions, estimated cost. Yes, those numbers are real, and they are the reason token efficiency shows up in half of the rules.

A cron watchdog checks every scheduled job against a declared contract (cadence, expected time, grace window) and files a card when one drifts. The healthcheck does the same for the vault itself: notes without frontmatter, rules over their size cap, dangling links, stray files outside approved roots, job docs that drifted from the live schedule. And for fun there is a nifty menu bar pet that tails the Claude transcripts and shows a mood glyph, so I know from across the room whether it is working, waiting or stuck.

There are more panels I am skipping here: board previews, the life system, finances, a social feed, and 8 more columns. The dashboard alone would fill its own article. The theme all of this runs in is the one from I built an Obsidian theme.


The Kanban plugin: a card is the unit of work

Boards are plain Markdown files, one per area: projects, blog, business, and one for the system itself. The same plugin renders them as a Kanban view with drag and drop, inline editing and sticky card numbers, and it writes the Markdown back, so git sees every move. A client with JIRA gets no board at all: a /jira command with the ticket key resolves the client folder, its APEX repo and the ticket worktree, and from there the flow is the same as for a card.

Capture is one line: /kanban BLOG this article idea files a card at the top of the first column. Solving is another: /kanban-solver ADT 42 opens a worktree, takes a lock on the scope, loads the project overlay, does the work, lands it, and writes an evidence block onto the card (worktree, commit link, DOD score) before moving it to Done. For ADT.ai, my APEX deployment tool, the overlay is where the release approvals and the coverage gate live, so the same one-liner ships a tested release. Done columns are purged nightly; git history is the archive.

The git strategy shows up here twice. A card lands on main the moment it is written: create, commit, push, then start the work, because a card's whole job is to be visible while the work is in flight, and a card on a task branch does not exist for Obsidian. The check-off goes the other way and rides the finish-merge, since a done mark before the land is a false claim. And because I drag cards around while a solver run is out, a board write guard refuses to arm on a stale copy of the board, and the pre-commit hook rejects a commit that moves a card whose bytes nobody changed. I learned that one after a merge quietly put two cards back into a column I had just emptied.

The system board is the one I look at first. Nothing reaches me through a lonely log line anymore; jobs and hooks file cards instead:

- [ ] `#1780` **cron-watchdog: weekly review job drifted**
	- detected 2026-09-02 14:00
	- source: cron-watchdog
	- STALE (high): last ran 9.7 days ago, beyond its 7-day cadence


Errors, decisions that need me, weekly insights, and an ideas column that no machine touches. Checking a card dismisses it. That one board replaced logs and a stream of phone notifications.


Was it worth it?

Honestly, it is a lot of process for one person, and none of it existed few months back. Every rule, hook, memory, decision, has a date and a mistake behind it: a session that claimed done with the branch never pushed, a board that lost cards I had dragged by hand, a chime that fired on every single turn.

What I got back is that Claude finishes. I hand it a card, I get a banner, and the work is on main in my vault. The same loop now runs all my tickets: a ticket key goes in, and what comes out is the change in the ticket branch, a deployment patch, a pull request, sometimes even deployment log. Corrections recur less, because a correction now produces a rule or a hook and not a promise. And the whole thing is readable by me, in Obsidian, with no console in sight.

And again, this was the simplified tour. The real thing has scheduled farmers polling my inbox, a kill switch for every autonomous loop, a healer that triages the system board on its own, and about a hundred skills I never mentioned. Some of them may get their own article.

So if you run agents on anything that matters (a production APEX schema counts), try one piece: write down what "done" means for your work, make the agent print it, and have a hook check the print. For me that list starts with "compiled in DEV" and "patch in the release folder". You will be surprised how often "done" wasn't.

If this gave you an idea for your own setup, pass it on to a colleague who is fighting the same intern.


Comments