Skip to main content

Command Palette

Search for a command to run...

How I Stopped Writing “New Code” and Started Encoding Decisions

A case study in contract-first, self-healing development for a solo, AI-assisted project

Updated
8 min readView as Markdown
How I Stopped Writing “New Code” and Started Encoding Decisions
M

Our extensive experience in Human Capital Management (HCM), combined with a strong background in Finance, ICT employee HR system adoption, and HR consultancy, brings a compelling value proposition. Our expertise in transformations to Entra, Organizational Performance Management, Analytical Skills, Security and Compliance, and End User Adoption is crucial in today’s rapidly evolving business landscape.

A few months ago I started rebuilding Microsoft’s discontinued Social Engagement product as a solo, self-funded side project. The result so far is SocialEngage, a social-listening and insights subsystem split across a Node/TypeScript backend and a Next.js admin UI. The code is real. The Azure resources are real. The PostgreSQL, Key Vault, Service Bus, and Blob Storage are all real, provisioned services — not mocks.

What I didn’t expect was that the hardest part would not be the connectors, the OAuth flows, or the Postgres RLS. The hardest part would be not losing my own context between sessions.

I work on this project in short, sporadic, quota-limited bursts. The agent I pair with (Claude Code / Devin) has no memory of what I did last Thursday. If I leave a story half-implemented because a session runs out, the next session cannot assume that half-finished state is correct. That constraint forced a delivery methodology that has turned out to be the most valuable part of the project: a mechanical, contract-first, self-healing loop that converts every sprint’s hard-won lessons into reusable project knowledge.

Here is how it works.

The two skills that run the project

All implementation is driven by two skills: implement-story and heal-contract-failure. They are not guidelines. They are executable checklists that must be followed in order.

implement-story is how every user story is built. It does not start with code. It starts with a specification pyramid:

  1. The User Story in

    user-stories.

  2. The source ADR that governs it in

    adr.

  3. The Business Requirements (BRD) for permissions and business rules.

  4. The Functional Design (FDD) for request/response schemas and role gating.

The hierarchy is fixed: ADR > BRD/FDD > Story. If the FDD contradicts the ADR, I stop and surface the conflict. No one is allowed to “make it work” against a higher-level decision.

Only after reading those four artifacts do I write an Intent block: the story, the ADR, the exact files that will be touched, the contract I am about to encode, and — crucially — what is explicitly out of scope. Scope creep is caught before any code exists.

Then I write the contract test in Jest, in <repo>/contracts/epic-<N>/story-<X.Y>.<slug>.contract.test.ts. with one test per Acceptance Criterion. The test must fail at this stage. If it does not fail, I have not described the new behavior clearly enough.

Implementation comes after the contract. I write the minimum code to make the contract green. Then I validate against an isolated Postgres database cloned from a template, run the epic-level contract suite or the full suite depending on what files changed, and only then commit, append to the append-only

implementation-log.md, and merge the worktree back intomain.

That is the happy path.

heal-contract-failure is not a debugger

The second skill, heal-contract-failure, is what happens when something goes wrong — or when a prior session left a story uncommitted. This is the single most important discipline in the project, because it is where most solo-AI projects quietly fall apart.

The rule is simple: fix the underlying thing, never the check.

When a contract fails, the instinct is to classify the failure — “this is just a naming issue,” “the test is too strict,” “it’s an environment flake” — and jump to a narrow patch. That instinct is exactly what the skill is designed to kill. Instead, I must re-walk the same five artifacts in the same order, every time:

  1. Re-validate the Intent against the current story and ADR.

  2. Re-validate the Contract against the Acceptance Criteria.

  3. Re-validate the component SKILL.md for stale constraints or missing relationships.

  4. Only then, re-validate and fix the implementation.

  5. Validate, with the full suite where cross-epic files are involved.

If the contract itself looks wrong or stale, I cannot edit it unilaterally. If the only way to get to green is to weaken, skip, delete, or bypass a contract, hook, lint rule, or CI gate, I stop and escalate. I am capped at three full walks of the loop; after that, the failure is a human problem, not an agent one.

This same skill is also how I resume an interrupted story. Uncommitted state from a previous session is treated as unverified, not “almost done.” I re-enter the healing loop and walk it again before committing. That prevents the common trap of an AI agent silently finishing someone else’s half-broken thought.

Three enforcement layers

Intent and discipline are not enough. The project has three mechanical enforcement layers:

  • A PreToolUse hook blocks any write to src/ in either repo unless a *.contract.test.ts file already exists in that repo.

  • A pre-commit hook runs the same check at git time, catching work done outside a Claude Code session.

  • CI runs the full accumulated contract suite and a traceability/naming check on every PR.

None of these can prove a contract is semantically correct, but together they make it mechanically expensive to skip the contract-first step or to let a story leave the repo without its traceability artifacts in place.

From sprints to reusable knowledge

The part I am most proud of is the feedback loop. The project does not just ship features; it ships lessons learned.

Every retrospective or hard-won debugging session feeds one of two artifacts:

  1. docs/project docs/Lessons-Learned-Register.md — an append-only, PMBOK-style narrative register. Each entry describes what happened, the pattern it revealed, and the durable rule it produced, with a cross-reference to the authoritative document. For example, a 2026-08-13 entry about a publishEvent() function that was contract-passing but never called from the real ingestion path led directly to a new “relationship assertion” convention: if a story creates a real call relationship between components, at least one contract must exercise that relationship at the real production call site, not in isolation.

  2. Self-Learning Synthesis outputs, driven by ADR-0122 / FDD-0122. After each epic, a synthesis pass scans git telemetry, contract inventory, healing commits, and ADR implementation-learning status and produces a document like

    Self-Learning-Synthesis-Epic-13.md. It surfaces reusable architectural patterns, environment gotchas to verify, and in-place ADR annotations. The synthesis does not just record history; it tells the next story what to look out for.

This is the “sprint lessons learned” layer in practice. It means a painful discovery in Epic 6 can become a guardrail in Epic 13 without me having to rediscover it.

What has changed as a result

This methodology has produced a few conventions that I now take for granted but would not have arrived at without the loop:

  • Relationship assertions: contracts must touch real call sites.

  • **A **Built:** field in every story, separate from Status, so “Ready” and “already shipped” do not drift.

  • environment-gotchas.md, a consolidated index for the recurring Azure timing, Jest parallel-worker race, and dependency issues that used to live scattered in individual SKILL.md files.

  • Worktree and template-DB isolation: each story runs in its own branch and each Jest run clones an isolated Postgres database, so concurrent agents can work on social-listening-core without collisions.

The project currently carries 209 contract test files across both repos, and the social-listening-core suite alone was at 68/68 suites and 537/537 tests the last time it was fully logged. More importantly, when something breaks, I know where to look first.

Why this matters if you are also a solo, AI-assisted developer

The conventional advice for solo builders is to move fast and ship MVPs. That works until the project is too large to hold in one head — and in a solo-AI setup, the “head” is split between you and a contextless agent anyway.

The methodology I ended up with is basically an external memory system for implementation intent. It makes the following promises explicit:

  • Every feature is traceable to a decision (ADR) and a promise (contract).

  • Every failure is repaired by re-confirming the decision, not by whack-a-mole patching.

  • Every painful lesson is written back into the project so the next agent (or me in three weeks) does not pay the same cost.

It is slower for a single story. It is faster for a multi-year project. And on a project where the only developer is me, the only consistent reviewer is a script, and the only long-term memory is the repo itself, that trade-off has been worth it.

If you are building with AI agents, do not just prompt, review, and merge. Give your future self and your future agents a machine-readable trail: skills, contracts, append-only logs, and a lessons-learned loop. The code is the easy part. The hard part is making sure the next session knows what the last one was trying to do.

11 views