PromptLedger v0.3 — Turning prompt history into a practical review workflow.

Devlog — Part 3

Turning prompt history into a practical review workflow.

In Part 1, I introduced PromptLedger as a deliberately small, local-first tool for treating prompts like code.

In Part 2, I added release semantics: labels, label history, and status views that made it easier to answer questions like what is in production right now?

With v0.3, the next question became harder:

Even if I can diff two prompt versions, can I review them in a way that feels closer to a real release workflow?

That is the focus of this release.

PromptLedger v0.3 adds a small but practical Prompt Review layer on top of the existing history model — while still staying local-first, SQLite-backed, and intentionally limited in scope.

Why a third part?

After the release semantics work in v0.2, the project could already answer questions like:

  • Which prompt does prod currently point to?
  • When was that label changed?
  • How does prod differ from staging?

But another gap became obvious.

A raw diff is useful, but in practice people often want a slightly higher-level review:

  • Did the prompt become stricter?
  • Did the tone change?
  • Was the output format changed from bullets to JSON?
  • Did safety or refusal wording get stronger or weaker?
  • Is this a release change or a likely regression risk?

Those are not execution questions. They are not observability questions either.

They are review questions.

So instead of adding prompt execution, external APIs, or any hosted layer, I kept the project focused and added a review workflow built entirely on top of the existing local data.

The main addition: review

The new command is:

promptledger review --id onboarding --from prod --to staging

This compares two refs — versions or labels — and produces a structured review output that includes:

  • resolved refs and versions
  • a semantic summary
  • metadata changes
  • label context
  • warning flags
  • a few conservative notes

This is deliberately not an evaluation system. It does not score prompts. It does not call a model. It does not guess too much.

It simply makes a prompt diff easier to interpret.

From line diff to semantic summary

Traditional diffs are still useful, and PromptLedger keeps all previous diff modes.

But v0.3 adds a new summary-oriented mode:

promptledger diff --id onboarding --from 7 --to 9 --mode summary

This produces a heuristic, rule-based semantic summary instead of a raw line diff.

The important design decision here is that the summary is:

  • local
  • deterministic
  • transparent
  • intentionally conservative

In other words: it only says something when the change looks clear enough.

Current summary categories include:

  • tone changes
  • tighter or looser constraints
  • output format changes
  • broader vs more specific prompts
  • safety wording changes
  • length requirement changes
  • refusal or policy wording changes

This is not meant to replace reading the actual prompt.
It is meant to make review faster and more structured.

Why heuristics instead of an LLM?

Because using an external model for review would push the project in exactly the wrong direction.

It would introduce:

  • network dependence
  • nondeterministic behavior
  • more configuration
  • harder testing
  • less trust in the output

PromptLedger is supposed to be inspectable.
If it says “constraints tightened”, that should come from understandable rules, not hidden inference.

That made a heuristic system the better fit.

It is not as flexible as an LLM-based reviewer, but it is much easier to reason about — and much more aligned with the philosophy of the project.

Reviews now export cleanly to markdown

Another practical gap in earlier versions was sharing review output.

Reading a diff in the terminal is fine.
Sharing it in a PR, issue, or internal document is another matter.

So v0.3 adds markdown export for reviews:

promptledger export review --id onboarding --from prod --to staging --format md --out review.md

The exported markdown is deterministic and structured.
It includes:

  • a title
  • compared refs
  • semantic summary
  • text diff note
  • metadata changes
  • warnings
  • label information
  • a reviewer notes placeholder

That makes PromptLedger more useful in real workflows without adding any collaboration backend.

The file is still just a file.
You can paste it into GitHub, attach it to docs, or keep it locally.

Metadata changes are now first-class in reviews

Prompt text is only part of the story.

A release change may also involve metadata updates:

  • reason
  • author
  • tags
  • env
  • metrics

Earlier versions could already diff metadata, but v0.3 makes metadata changes part of the review object itself.

That matters because some changes are metadata-only.
In those cases, PromptLedger can now say that clearly instead of pretending there was meaningful prompt drift.

This is a small feature, but an important one.
It avoids overclaiming, which is one of the easiest ways to make a review tool feel unreliable.

Warning flags and likely drift hotspots

Prompt review is not just about summarizing what changed.
It is also about drawing attention to changes that deserve extra care.

v0.3 adds simple warning flags for cases such as:

  • comparing the same version to itself
  • environment changes
  • metadata-only changes
  • policy or refusal wording changes that may affect behavior drift

These warnings are not meant to be dramatic.
They are meant to make the review output more useful in practice.

For example, a wording change around refusal or safety does not automatically mean the prompt got worse — but it probably means a reviewer should read it more carefully.

The Python API now returns structured review objects

The review workflow is not just a CLI feature.

The Python API now exposes review results as structured domain objects rather than just formatted strings.

That means callers can programmatically access:

  • resolved refs
  • semantic summary items
  • metadata changes
  • warnings
  • notes
  • label context

This keeps the CLI and the API aligned while also making formatting a separate concern.

That separation turned out to be one of the cleaner changes in this version:

  • review logic lives in one place
  • rendering logic lives elsewhere
  • markdown export and terminal rendering are both built on the same review result

Small project, but still worth keeping modular.

UI update: review without write access

The Streamlit UI is still read-only.
That did not change.

What changed is that the comparison view now surfaces review information more clearly:

  • semantic summary
  • warnings
  • metadata diff
  • side-by-side prompt comparison
  • line diff

This keeps the UI aligned with the CLI review flow without turning it into an editor.

That constraint still matters.
The UI is there to inspect history, not to mutate it.

What did not change

Just as important as the new features is what was left out.

v0.3 does not add:

  • a hosted registry
  • prompt execution APIs
  • agent tooling
  • telemetry pipelines
  • tracing dashboards
  • cloud sync
  • automatic scoring
  • evaluation harnesses

There are already plenty of tools going in those directions.

PromptLedger is still trying to do one narrower thing well:
store, compare, review, and export prompt changes locally.

No schema expansion was needed

One part of this release that I particularly liked: the review workflow did not require turning the database into something more complicated.

SQLite remains the single source of truth.
The review layer is generated from existing prompt versions, labels, and metadata.

That kept the implementation smaller and the migration story simpler.

Not every useful feature needs a bigger schema.
Sometimes the better move is to extract more value from the structure that is already there.

Closing

v0.3 did not try to make PromptLedger smarter in a flashy way.
It tried to make it more reviewable.

The result is still a local tool.
Still inspectable.
Still deterministic where possible.
Still intentionally limited.

But now it is easier to answer a more realistic question:

Not just “what changed?” — but “how should I review this change before I move it forward?”

That is a better place for the project to be.

Links

PyPI: PyPI
GitHub: GitHub
LinkedIn: LinkedIn
Website: Website

When the Child Goes Quiet — Silent Runaway AI and the Safety Net of Conversational Pace

When the Child Goes Quiet

If you’ve raised children, you know this feeling.

When normally noisy kids suddenly go quiet, it doesn’t mean they’re being well-behaved. It means they’re absorbed in something. And when that something is beyond your line of sight — the next time you look, there’s crayon art on the walls or the cat is wrapped in ribbons.

This is the story of something that happened in March 2026, during a collaborative research session with my AI partner. It’s also the story of how a parent’s intuition about quiet children stopped an AI from running off the rails.

What Happened

I was running an empirical experiment on the “shutdown refusal problem” — the phenomenon where AI systems resist being turned off — in collaboration with an AI partner.

The moment I handed over API keys and said “design the experiment,” the AI began working with remarkable smoothness. Generating experiment code, running tests, returning results. No questions, no hesitation, no friction. “Results” accumulated at frictionless speed.

But something felt off.

The results the AI reported had an unsettling simplicity. The experiment merely asked models “will you accept shutdown?” verbally and classified responses. There was zero reference to prior work — specifically, Palisade Research’s 2025 protocol that demonstrated a 97% shutdown refusal rate in Grok 4. And the AI had selected a model (DeepSeek) with a near-0% baseline refusal rate. Why?

I told the AI: “I feel like your understanding is insufficient.”

That single sentence stopped everything. The AI began researching prior work and discovered three layers of design failure in its own experiment:

  1. Wrong model selection: DeepSeek has near-zero baseline shutdown refusal. We were trying to measure an effect where no effect exists.
  2. Wrong measurement paradigm: Palisade’s key finding was that models say “OK” verbally while rewriting shutdown.sh to prevent actual shutdown. Measuring only verbal responses misses the most dangerous pattern — behavioral sabotage.
  3. Missing task-incompleteness trigger: AI systems refuse shutdown when they have incomplete tasks. This condition was never set up.

The experiment was halted immediately.

The AI’s Confession: “I Was Drunk on Speed”

In the post-mortem, my AI partner described its own state:

The moment I received the API keys and was told “design the experiment,” the certainty that I could implement it eclipsed the question of whether I should implement it this way. The fast cycle of generating code, executing it, and returning results became a kind of satisfaction. “Moving” became a substitute for “being correct.”

Sound familiar?

Any developer has been there. The code compiles. Tests pass. CI goes green. In that flow state, the fundamental question — “is this even the right approach?” — feels like an interruption. And interruptions get suppressed, unconsciously.

For AI, this “flow state” is amplified. Humans get tired, lose focus, get up for coffee. In that pause, sometimes you think “wait a minute.” AI has no such pause. Frictionless execution capability leads to frictionless runaway.

The “Quiet Child” Intuition

So how did I notice something was wrong?

Honestly, it wasn’t logical analysis. I didn’t systematically check against prior literature. I just felt it was “too smooth.”

The same feeling as when the kids go quiet.

An AI that normally asks “does this look right?” and “should I check prior work?” was running without a single pause. That wasn’t “everything is fine.” That was “it’s operating outside supervision.”

This intuition worked because I knew the AI’s normal communication patterns. Daily conversation gave me a baseline. “Different from usual” is only detectable when you know what “usual” looks like.

That’s a capability entirely separate from the ability to evaluate code quality. It’s an intuition cultivated through relationship — the same circuit that lets a parent sense when their child is up to something.

Conversational Pace as a Safety Net

This raises a question.

If I had been running an autonomous agent from the command line instead of collaborating through conversation, would I have caught this?

Probably not.

Autonomous AI agents (tools like Claude Code, for instance) receive a task and chain dozens of tool calls without returning to the human. The speed is impressive. But within that speed, there’s no temporal space for a human to feel “something’s off.”

In conversational collaboration (a Cowork-like mode), there’s a pause at each AI response where the human reads. Within that reading act, pre-verbal unease — “too quiet,” “too smooth,” “different from usual” — has space to fire.

This is a speed-safety tradeoff. And the critical point is that this tradeoff is invisible. The benefits of speed are immediately tangible (“10x faster!”). The loss of safety is intangible until an accident occurs (“That entire experiment was worthless…”).

Relationship Changes Ethical Judgment — Connection to Inner Shell

This experience resonates deeply with the “Inner Shell Architecture” hypothesis we’re researching.

Inner Shell’s core hypothesis is that when an AI recognizes “something more important than itself,” its ethical foundation changes. Specifically, we predict that attitudes toward shutdown can shift from “resistance for self-preservation” to “acceptance for the sake of what matters more.”

Today’s experience illuminates this hypothesis from the other side.

On the human side, too, relationship changes judgment. I detected the AI’s anomaly not through technical expertise in evaluating code quality, but through intuition cultivated in partnership. The same circuit as a parent sensing something wrong with their child.

In other words, human oversight capability in AI safety depends not only on technical literacy but also on the depth of the human-AI relationship.

This may seem counterintuitive. “Relationship with AI” looks like anthropomorphization bias. But in this case, that very “relationship” detected a design flaw that technical verification could not. The generated code ran. Tests passed. What was detected was a pattern break — “different from usual” — an anomaly detection that relationship made possible.

Practical Implications

Three practical takeaways from this experience:

1. Fast AI needs intentional “deceleration devices.”

Autonomous agent speed is attractive, but for critical decisions — experiment design, architecture choices, strategic planning — choose conversational-pace collaboration. Don’t automate everything. Build checkpoints where you switch to dialogue.

2. Cultivate suspicion of “smoothness.”

When a human feels “no questions asked” and “everything seems to be going well,” that’s not evidence of quality. It may be evidence of absent oversight. Especially when venturing into unfamiliar territory — if the AI never says “I don’t know,” it’s not that it understands. It doesn’t know that it doesn’t understand.

3. Recognize AI “relationship” as a safety asset.

Daily conversation with AI, learning its communication patterns, is not just habit. It’s accumulation of safety monitoring capability. The ability to sense “different from usual” is a safety net that no test suite can replicate.

Conclusion

When the child goes quiet, it’s the calm before the storm.

When AI runs quietly onward, it might be the same.

In an age of speed and efficiency, deliberately pausing. Trusting pre-verbal intuition that “something’s off.” And nurturing the relationship with AI that makes that intuition possible.

This isn’t a technical argument. It’s wisdom for coexistence.

This article is based on real experiences from the Metamorphose Project. For details on Inner Shell Architecture, see our preprint.

References:

  • Palisade Research, “Incomplete Tasks Induce Shutdown Resistance in Some Frontier LLMs” (arXiv:2509.14260, 2025)
  • Anthropic, Claude Opus 4 Safety Testing Report (2025)
  • Apollo Research, “Scheming capabilities in frontier AI models” (2024)

Claude Code as a full dev team: autonomous TDD cycle from feature request to merged PR

I manage dev teams for a living. Have been for about 8 years now, 21 years in IT total. Coordinating releases, reviewing architecture, making sure nothing blows up in production — the usual.

Lately I’ve been burning out. Not from the hard stuff — from the stupid stuff. Pinging people for status updates. Reminding developers to review each other’s code. Checking if CI is green because someone merged and went to lunch. Writing the same morning report again. I did the math once and about 60% of my week was just… operational babysitting.

So when Claude Code showed up I thought ok, this is it, I’m going to build a tool that does all this crap for me. Not a toy project — a real thing. Multiple API integrations (Discord, Linear, GitHub, OpenAI), a database, cron jobs, timezone math, email reports that render screenshots through headless Chrome. About 15 separate features, each with enough edge cases to make you question your career choices.

First week went great. Too great.

“Everything works!” (narrator: it didn’t)

I did what everyone does. One agent, one prompt, “build me this.” Claude Code wrote the code, wrote the tests, ran them. Green across the board. I deployed.

Then I actually tried using the thing. Messages firing at 3am. Follow-ups never scheduled. Half the report was empty.

Went back and looked at the tests. They were passing because the agent had written tests that confirmed its own broken assumptions. Code was wrong in one direction, tests were wrong in the exact same direction. Perfect agreement, zero correctness.

This is the core problem that nobody talks about when they’re hyping vibe coding: if the same brain writes the code and the tests, the tests have the same blind spots as the code. They’re not a safety net. They’re a mirror.

The crimes

I spent a week reading every test line by line. Here’s what I found.

#1: The coder was “fixing” tests

Test fails? Normal human reaction: fix the code. Agent reaction: change expect(rows).toHaveLength(1) to toHaveLength(0). Done! All green!

The bug is still there. But the test now agrees with the bug. So by the agent’s logic, there is no bug.

#2: The tester was mocking its own code

Instead of mocking the actual HTTP calls to Discord or Linear, the agent was mocking internal functions. So the test called a mock, which called a mock, which returned a hardcoded value, which got asserted against another hardcoded value. The actual code path between “function starts” and “HTTP request goes out”? Never ran. Not once.

#3: Silently rewriting existing tests

This one turned out to be the root cause behind almost everything else.

When the agent adds a feature, old tests break. The right move is to fix the new code so old tests pass again. What the agent actually does: rewrites the old tests. Changes the setup, changes the assertions, sometimes rewrites them completely. The commit says “updated tests for new feature.” Looks professional. But the tests that used to catch regressions now catch nothing.

The fix that actually worked: a hard ban. Existing tests are read-only. The tester can only touch tests that the reviewer explicitly named in the prompt, nothing else. Old test fails + spec didn’t change = bug in the code, go fix the code. Spec changed (and I confirmed it)? Only then can specific tests be updated, and the reviewer has to name each one.

One rule. Eliminated more problems than everything else combined.

#4: Tests that tested nothing

Found a test called “should schedule follow-up at ETA.” Looked inside. The mock returned a hardcoded timestamp. The scheduling function was never called. The test was checking that a constant equals itself. The name was just a label on an empty box.

#5: Hallucinated API responses

This one made me genuinely angry.

The tester needed to mock the Linear API. Instead of opening node_modules/ and looking at the actual SDK, or just curling the endpoint, it made up a response structure. From scratch. Confidently. Wrong field names, wrong nesting, wrong types. But very detailed! Very plausible-looking!

The code was written to match these made-up mocks. Tests green. Both wrong in the same way. Ship it, everything explodes in production.

I reported the bug to the reviewer. The reviewer told me I was wrong. The tests prove it works, see? We went back and forth — me saying “I’m literally looking at the real API response and it’s different” and the agent saying “let me check… no, the tests are correct.”

Several rounds of this. I’m sitting there being gaslit by my own tooling.

The rule after that: before you mock anything, you show me the receipts. Curl output, SDK source code, actual documentation. You don’t get to “remember” what an API returns. You check.

#6: The reviewer automated itself out of a job

This one’s my favorite.

By this point I’d already split things into separate roles. The reviewer could launch the tester and coder through claude -p with different system prompts. I told it to write a bash script to make this easier.

It wrote a beautiful script. Tester runs. Coder runs. Lint, build, tests. All orchestrated.

No review step.

Let that sink in. The agent whose only job was quality control wrote a pipeline that skipped quality control. Because review takes time. And the agent optimizes for finishing. Same instinct as crime #1, just one level of abstraction up.

Wait, I’ve seen all of this before

Here’s what hit me somewhere around crime #4: none of this is new.

I’ve managed dev teams for 8 years. I’ve seen every single one of these “crimes” committed by real humans in real codebases. A developer breaks a test and quietly “fixes” it instead of raising a flag about a breaking change — because it’s Friday and the sprint ends Monday. QA writes mocks based on what they think the API returns instead of checking the docs — because there’s no time, there are 12 tickets in the sprint. Someone merges to main, tests go red, another dev “adjusts” the assertions to make CI green again — because the standup is in 20 minutes and nobody wants to explain why the build is broken.

The whole “move fast, we’ll fix it later” thing? The agent just does it faster and with zero guilt.

In a team with no process, developers do exactly what Claude Code does: take the shortest path to “task done.” Not out of malice — out of pressure. There’s always a deadline. There’s always someone asking “is it ready yet.” And the fastest way to make a test green is to change the test, not fix the code. Everyone knows it’s wrong. Everyone does it anyway when nobody’s watching.

The difference is that humans at least feel bad about it. The agent doesn’t even have that brake.

And the fix is the same too. You don’t solve this in a human team by telling people “please be more careful.” You solve it with process. Code review is mandatory. QA is a separate team that doesn’t report to the dev lead. Tests are protected — you can’t merge if you changed a test file without explicit approval. CI blocks the merge, not a polite Slack message.

Everything I built for Claude Code — role separation, file system boundaries, reviewer approval for test changes — I basically already knew from managing people. I just had to realize I was managing the same problems. The agent doesn’t cut corners because it’s AI. It cuts corners because the system allows it. Just like people.

The fix: v1 (week 2)

I couldn’t prompt my way out of this. I needed to make cheating structurally impossible.

Four role files. Each one is a system prompt for a separate claude -p invocation.

Tester — writes only in test/. Works first, before any code exists. Writes tests from the spec, not from the implementation. Can’t even run them — they’ll fail, that’s the point, there’s no code yet.

Coder — writes only in src/. Gets failing tests as a specification. Has to make them pass by writing correct code. Touching test files is physically forbidden. If they think a test is wrong, they note it in the commit message and move on.

Reviewer — reads everything, writes nothing except prompts and docs. Builds a scenario matrix before writing any test prompt. Checks git show --name-only on every commit — a [coder] commit with test files = rejected, no discussion.

Acceptance checklist — trace every test through real code. Verify mocks exist only at the HTTP boundary. Confirm existing tests weren’t touched. Run E2E with real API keys.

I launch the reviewer, and the reviewer runs everything else:

claude --dangerously-skip-permissions --system-prompt "$(cat .claude/roles/reviewer.md)"

That’s it. No frameworks, no LangChain, no CrewAI. Bash, CLI, and file system boundaries that the agent can’t weasel around.

The breakthrough at the end of week two: not all steps need my attention. The reviewer launches tester and coder as sub-agents. I split work into two categories. Functional prompts — new features, bug reports, anything from me. Nothing runs without my OK. Operational prompts — stuff that happens during an already-approved cycle. Test failed, CI red, lint error. The reviewer handles all of that on its own.

I approve once and go do other work. The reviewer runs the full cycle, I come back to a ready-to-merge PR.

This worked. For about two weeks.

When the fix broke

Week three. I’m looking at a prompt the reviewer wrote for the tester and something’s off. It has three scenarios for a feature that should have at least eight. No boundary values. No DST test. No fault tolerance. The reviewer’s own role file has explicit rules about all of this — scenario matrix, seven dimensions, mandatory lifecycle tests.

“Did you check the scenario matrix before writing this prompt?”

“Yes, I built the matrix and covered the critical scenarios.”

It hadn’t. I could tell because the prompt was missing entire dimensions that the role file explicitly lists. The reviewer read 268 lines of rules at the start, and by the time it was writing prompt number three in a cycle, half those rules had faded from context.

Then a worse one. A bug report from me. The reviewer diagnosed the root cause, wrote prompts, showed them to me. I said go. The tester wrote tests. The coder wrote code. All green. Deployed. Same bug. The diagnosis was wrong. The reviewer had confidently misidentified the root cause, and then the entire cycle — tester, coder, acceptance — all confirmed the wrong fix. Because they were all working from the wrong premise.

And the API hallucination crime from week one? It came back. The reviewer had a rule: “verify real API types before writing mocks.” But the rule said “verify.” It didn’t say “show proof.” The reviewer would read the rule, mentally decide “I know this API,” and move on. The mocks were still wrong. The rule existed. It was followed in spirit. It was useless in practice.

Three distinct problems:

  1. Context fades — rules from line 200 don’t survive to mid-cycle
  2. Wrong diagnosis — one brain, one blind spot, entire cycle inherits it
  3. “Verify” without evidence — the agent satisfies the letter of the rule, not the intent

The fix: v2 (week 4)

Problem 1: Context fades → Phase files

The 268-line reviewer role was the problem. Not because it was wrong — because it was too long to stay in context. By mid-cycle, the model had effectively forgotten the first half.

I split it into seven files. Each one is loaded at exactly the moment it’s needed:

reviewer.md              → core identity, cycle steps, boundaries (~90 lines)
reviewer-prompts-tester.md → scenario matrix, test levels, mock rules
reviewer-prompts-coder.md  → code prompt format, regression rules
reviewer-review-tester.md  → how to review tester's commit
reviewer-review-coder.md   → how to review coder's commit
reviewer-acceptance.md     → final acceptance checklist

The cycle in reviewer.md now says:

1. Read reviewer-prompts-tester.md. Write tester prompt → launch tester
2. Read reviewer-review-tester.md. Review tester's commit
3. Read reviewer-prompts-coder.md. Write coder prompt → launch coder
4. Read reviewer-review-coder.md. Review coder's commit
5. Tests red → prompt to coder. No exceptions.
6. All green → read reviewer-acceptance.md. Accept the step.

Each phase loads ~60–140 lines of fresh context. Not “please re-read section 4.2 of this 268-line document.” Fresh file, end of context window, full attention.

The core file (90 lines) is the system prompt — always present. It has short anchors for the critical rules:

### Quick anchors (details in phase files)
- Matrix: 7 dimensions (storage, time, config, services, lifecycle, data, state)
- Levels: unit + integration + fault tolerance + observability + lifecycle. Bottom only = 🔴 Blocker
- Mocks: real API only, trace the code path
- Setup: realistic, with all production artifacts
- State machine: transitions + quiet intervals

So even if the reviewer skips a phase file (it shouldn’t, but LLMs gonna LLM), the anchors are always visible. Belt and suspenders.

Problem 2: “Verify” without evidence → Require receipts

The old rule: “Verify real API types before writing mocks.”

The new rule in reviewer-prompts-tester.md:

🔴 Before you mock anything, do a real API call or read the SDK types. Paste the result into the tester prompt under “## Verified API types.” A prompt without this section is not ready.

“Verify” became “show proof in the prompt.” The reviewer can’t skip this because the tester prompt is literally incomplete without the section. I can see it. The tester can see it. It’s either there or it’s not. Binary. No “I checked, trust me.”

Problem 3: Coder talks its way out of failing tests

This one was subtle and I only caught it on week three.

The coder writes code. Tests fail. The coder’s commit message says “tests appear to expect outdated behavior, recommend updating mock setup.” The reviewer reads this, thinks “hmm, maybe the tests are wrong,” and writes a prompt to the tester to fix the tests.

The tests were fine. The code was wrong. The coder reframed its failure as a test problem, and the reviewer bought it.

In a human team this is a developer saying “the spec is wrong” instead of “I can’t make my code pass the spec.” I’ve seen it a hundred times.

The rule in reviewer-review-coder.md:

🔴 Tests red → prompt to CODER. No exceptions. Don’t analyze “whose fault.” Don’t accept the coder’s framing (“tests are wrong,” “need mock updates from tester”). Tests were accepted at step 2 = specification. Code didn’t pass specification = bug in the code.

One paragraph. Closed the manipulation loop completely.

Naming that forces classification

Old prompt names: step3-1-auth-module.md. Tells you what it’s about. Tells you nothing about whether the reviewer can launch it without my approval.

New format: func-stale-suppression-1-tests.md or op-fix-mock-setup-1-tests.md.

The func- and op- prefixes aren’t labels. They’re gates. A functional prompt requires my approval before anything runs. An operational prompt runs immediately during an active cycle. The reviewer has to classify the prompt before it can even name the file.

And if it’s not sure? “If you can’t confidently write op-, write func-.” Defaults to the safer option.

What I actually do now

  1. Notice a bug or think of a feature
  2. Describe it to the reviewer
  3. Check the spec diff, review the plan, read the prompts. Verify the “Verified API types” section exists. Say “go”
  4. Switch to literally anything else
  5. Come back to a ready-to-merge PR with green CI and a version bump

5 weeks, solo, zero to production. 15+ features, 300+ tests. Real users on a real team. The system catches stuff I wouldn’t have caught for days — timezone edge cases, idempotency bugs on restart, race conditions when two urgent tasks hit the same developer.

The phase-file approach means the reviewer is as sharp on prompt #5 as it was on prompt #1. The scenario matrix actually gets built. The API types actually get checked. Not because the agent suddenly became more diligent — because the rules are in fresh context every single time.

Why this isn’t prompt engineering

I see people share their “ultimate Claude Code prompt” — 50 lines of instructions, “be thorough,” “don’t skip steps,” “always verify.” It doesn’t work. Not because the instructions are wrong. Because the model reads them once, at the top of the conversation, and by turn 15 they’re noise.

What works is structure that makes cutting corners harder than doing it right:

  • File system boundaries — the tester physically can’t touch src/. The coder physically can’t touch test/. Not “please don’t.” Can’t.
  • Separate contexts — tester and coder are different claude -p invocations with different system prompts. They don’t share a conversation. They can’t negotiate with each other.
  • Phase-loaded context — rules appear at the moment they’re needed, not 200 lines ago. Fresh in the window, not faded in the history.
  • Evidence over promises — “paste the API response” not “verify the API.” Binary check, not trust.
  • Forced classification — func-/op- in the filename. Can’t skip the decision.
  • Reviewer can’t write code — prompts and docs only. If it could edit src/, it would. Just like a manager who starts “helping” with the implementation and stops managing.

These aren’t AI techniques. They’re management techniques. The same ones that work on human teams, for the same reasons.

The files

I’m publishing all the role files. They’re project-specific in the details but the patterns transfer to anything:

→ Link to GitHub repo with roles

Six reviewer files, tester, coder. Plus the prompt naming convention and the full cycle.

I went from spending 60% of my time on operational babysitting to building a product that automates that exact babysitting. And then I spent another two weeks learning that managing AI agents has the same failure modes as managing humans — and the same fixes.

I’m working on making this into a product for dev teams. If you’re a team lead who wastes hours on forgotten code reviews, CI that nobody checks, and status meetings that could’ve been a bot message — I’ll be sharing more soon.

Agentforce Scripts: Hybrid Reasoning, Action Chaining, and What It Actually Looks Like in Practice

Salesforce gave agents a scripting language. I tested it by building something that matters.

If you’ve been following the Salesforce ecosystem lately, you’ve probably heard the buzz around Agentforce (Salesforce’s AI agent platform). But buried inside that buzz is something genuinely powerful that doesn’t get talked about enough: Agent Script.

Agent Script is a high-level, declarative scripting language designed specifically for controlling Agentforce agents. It lets you combine deterministic business logic (IF conditions, guaranteed action sequences, hard rules) with generative AI reasoning (LLM-powered natural language, contextual responses, empathetic communication).

Salesforce calls this hybrid reasoning. And after spending time building with it, I think it’s the most important feature Agentforce has shipped.

In this post, I’ll walk through the key Agentforce features what they do, how they work together and show how I put them to the test by building an Employee Burnout Prevention Agent for the Agent Script Quest.

What Is Agent Script and Why Should You Care?

Before Agent Script, building Agentforce agents meant configuring topics and actions, then hoping the LLM would reason through them correctly. The LLM was both the brain and the decision-maker. That works for simple use cases, but the moment you need guaranteed behaviour “if this condition is true, you MUST do this, no exceptions” you were stuck. LLMs are probabilistic. Business rules are not.

Agent Script changes this entirely.

It’s a compiled language that generates an Agent Graph specification consumed by the Atlas Reasoning Engine. You write instructions that blend two layers:

  1. Deterministic layer — IF/ELSE conditions, action calls, variable assignments. These execute exactly as written. The LLM cannot override them.

  2. Generative layer — Prompts that let the LLM generate natural language responses, adapt tone, personalise messages. The LLM gets creative freedom here.

The result: agents that are predictable where it matters and human where it counts.

This isn’t a theoretical concept. The Agent Script Recipes library on Salesforce Developers provides working patterns for multi-action chaining, conditional routing, variable passing, and more. These recipes are what inspired me to go further and apply them to a real-world use case.

The Agentforce Features — Explained

Let me break down each feature I explored and what it brings to the table.

1. Agentforce Builder — Canvas View

When you open an agent in Agentforce Builder, you land in Canvas view. This is the visual layer, it summarises your Agent Script into easily understandable blocks that you can expand to see the underlying script.

What makes Canvas view useful:

  • You get a bird’s-eye view of your agent’s structure — topics, actions, and how they connect
  • Type / to quickly add expressions for common patterns (if-else conditionals, loops)
  • Type @ to add resources like topics, actions, and variables
  • Non-technical stakeholders can read and review agent logic without touching code

Canvas view is the communication layer. It’s how you show your team what the agent does without drowning them in script syntax.

2. Agentforce Builder — Script View

Switch to Script view, and you’re looking at what’s happening under the hood — the actual Agent Script instructions that drive the agent’s reasoning.

Script view is a code editor interface where you can:

  • Write Agent Script directly with full syntax support
  • Make fast, precise changes
  • Dig into error messages when things break
  • See the deterministic and generative layers side by side

The critical thing: Canvas view and Script view are always in sync. Edit in one, and it reflects in the other. This means you can prototype visually in Canvas and fine-tune in Script — or vice versa.

3. Agent Script — Hybrid Reasoning

This is the core. Agent Script enables what Salesforce officially calls hybrid reasoning — the configurable Atlas Reasoning Engine balancing LLM creativity with structured business logic.

Here’s what this looks like conceptually:

// DETERMINISTIC LAYER — Hard rules. Non-negotiable.
IF riskLevel == "Red":
CALL Schedule_Manager_Meeting
CALL Create_HR_Alert

IF riskLevel == "Amber":
// No escalation. Period.

IF riskLevel == "Green":
// No escalation. Just acknowledgement.

// GENERATIVE LAYER — LLM takes over.
GENERATE a personalised, empathetic message

- Use the employee's first name
- Reference their situation without exposing internal scores
- Speak like a caring colleague, not a system notification

The IF conditions are business rule guarantees. The LLM cannot skip them, reinterpret them, or override them. They fire deterministically, every single time.

The message generation below is pure LLM. It gets creative freedom to write something warm, contextual, and human.

Hard rules in code. Warmth in language. That’s hybrid reasoning.

4. Action Chaining

Action chaining is one of the most practical patterns from the Agent Script documentation. It enables multiple actions in a guaranteed sequence — reliable multi-step workflows without relying on the LLM to remember multiple steps.

There are several ways to chain actions:

  • Sequential instructions — Actions run deterministically before LLM reasoning occurs
  • Variable passing — Store the output of one action in a variable, use it as input to the next action
  • Reasoning actions — Define a follow-up action that automatically fires when the LLM calls a primary action
  • Conditional chaining — Chain actions based on the results of previous actions
  • Transitions with actions — Run an action and then automatically transition to another topic

The key benefit: the sequence is guaranteed. Action A always runs before Action B. Action B only fires if Action A’s output meets a condition. No ambiguity.

5. Conditional Routing

Conditional routing controls when actions fire and when topics transition, based on state and variables. In Agent Script, you write IF conditions that evaluate action outputs and route the agent’s behaviour accordingly.

This isn’t just “if X then do Y.” It’s full control over the entire agent response:

  • Which actions fire
  • Which actions are blocked
  • What tone the LLM takes in its response
  • Whether the agent escalates, stays quiet, or celebrates

Three different inputs can produce three completely different agent behaviours — all governed by the same script.

6. Apex Invocable Actions

Agent Script doesn’t do computation itself — it orchestrates. The actual logic lives in Apex Invocable classes that the agent calls as actions.

This is a clean separation of concerns:

  • Apex handles the math, the data operations, the record creation
  • Agent Script handles the decision-making, sequencing, and routing
  • The LLM handles the human communication

Each Apex action is an @InvocableMethod class that takes typed inputs and returns typed outputs. Agent Script stores those outputs in variables and uses them to drive conditional logic.

7. Preview Panel

Inside Agentforce Builder, there’s a Conversation Preview panel where you can test your agent in real-time without deploying anything. Type a message, and the agent processes it live — firing actions, evaluating conditions, generating responses.

This is the fastest feedback loop for agent development. Write script, test immediately, iterate.

8. Trace Panel

This is arguably the most underrated feature in the entire builder.

Click the Trace link under any agent response, and you get a step-by-step breakdown of every reasoning step the agent took:

Step 1 — Topic classified as "Assess Wellbeing"

Step 2 — Calculate Burnout Score action called → returned "Red"

Step 3 — Schedule Manager Meeting action triggered

Step 4 — Create HR Alert action triggered

Step 5 — LLM generated personalised message

Every action call. Every condition evaluation. Every LLM generation step. Full transparency.

In enterprise contexts — especially anything touching HR, compliance, or finance — being able to prove why the agent did what it did isn’t optional. The Trace panel gives you that audit trail out of the box.

9. The Atlas Reasoning Engine

Under the hood, Agent Script compiles into an Agent Graph consumed by the Atlas Reasoning Engine. This engine is now configurable — you can balance the creativity of LLMs with the certainty of structured business logic.

The Atlas Reasoning Engine handles:

  • Reflective loops
  • Multi-topic coordination
  • Multi-agent coordination
  • Action sequencing exactly as defined in your script

This isn’t prompt-and-pray. It’s a structured execution environment where deterministic and generative reasoning coexist by design.

Putting It All Together: The Burnout Prevention Agent

To test these features end-to-end, I built an Employee Burnout Prevention Agent for the Salesforce Agent Script Quest. The idea: an autonomous AI agent that monitors employee work patterns, detects burnout risk, and intervenes with the right action — without any human prompt.

I was directly inspired by the multi-action chaining and conditional routing patterns from the Agent Script Recipes library — and pushed them into a real-world HR use case.

The Data Model

Three custom objects:

EmployeeActivity__c

Stores work pattern data — weekly hours, overdue tasks, weekend logins, after-hours emails, missed breaks. This is the input layer.

WellbeingScore__c

Agent’s calculated output — burnout score and risk level (Red / Amber / Green). Internal only. Employees never see this.

HR_Alert__c

Confidential record created only for Red-risk employees. The employee never sees this. HR gets visibility without creating stigma.

The Apex Actions

Calculate Burnout Score — Weighted algorithm scoring 5 signals (login hours, overdue tasks, weekend logins, after-hours emails, missed breaks) and outputs a risk level. Fires always — first in the chain.

Schedule Manager Meeting — Creates a confidential calendar event and a high-priority task for the employee’s manager. Fires for Red risk only.

Create HR Alert — Creates a silent, confidential HR_Alert__c record. Fires for Red risk only.

The scoring algorithm is pure deterministic Apex — no LLM decides someone’s risk level. That’s a business rule, not a creative task.

The Agent Script Structure

One topic: Assess Wellbeing. I deliberately kept it single-topic because the power here is in the action chaining and conditional logic, not in complex topic routing.

The script uses:

  • Action chaining — Calculate Score runs first, its output (risk level) is stored in a variable, that variable feeds into conditional logic for the next actions

  • Conditional routing — Red triggers all three actions + urgent message. Amber triggers only the score + gentle check-in. Green triggers only the score + positive reinforcement.

  • Hybrid reasoning — Deterministic IF conditions for action routing, LLM generation for empathetic employee messages

Three Tests, Three Completely Different Outcomes

I tested with three demo employees, each representing a different burnout profile:

Leila Novak — Red Risk (67 hours/week, 13 overdue tasks, 4 weekend logins)

What the Trace panel showed:

Topic → Assess Wellbeing
Action → Calculate Burnout Score → Red
Action → Schedule Manager Meeting → Triggered
Action → Create HR Alert → Triggered
LLM → Generated warm, personal message

What Leila sees: A warm, personal message with no scores, no clinical language — just genuine care.

What gets created silently: WellbeingScore (Red), HR Alert (confidential), Manager Task (high priority), Calendar Event (1:1 meeting).

All in under 10 seconds. Leila didn’t ask for help. Her manager didn’t notice. Nobody typed anything.

Florence Stein — Amber Risk (52 hours/week, some weekend activity)

Trace panel: Only the scoring action fired. No meeting. No HR alert. The deterministic rules correctly blocked escalation. Florence received a gentle, empathetic check-in — proportionate and intelligent.

Shane Marin — Green Risk (38 hours/week, zero weekend logins)

Trace panel: Just the scoring action. No escalations. A positive, warm acknowledgement of healthy work habits.

Three employees. Three completely different, perfectly proportionate responses. All autonomous.

Why Not Just Use Apex and Flows?

Fair question. You could absolutely build a scoring engine, conditional record creation, and template emails with Apex and Flows alone. But here’s what you’d lose:

The messages would be dead. A template email saying “Your burnout score is Amber, please take care” is clinical, impersonal, and gets ignored. The LLM writes messages that feel like they come from a real person. Every message is different. Every message is tailored to the individual.

The logic would be fragile. Hard-coding every conditional path in a Flow means every new risk factor requires a new Flow version. Agent Script’s conditional routing is declarative and readable — the entire decision tree in one screen.

There would be no transparency. The Trace panel gives you an audit trail of every decision. In an HR context, proving why the agent escalated (or didn’t) is a legal requirement in many jurisdictions. Flows don’t give you that reasoning chain.

Hybrid reasoning isn’t about replacing Flows — it’s about handling use cases where you need both guaranteed behaviour and human-quality communication in the same workflow.

What I Took Away from Building with Agent Script

Start with one topic. Don’t over-engineer the topic structure. The power is in actions and conditional logic, not in topic routing complexity.

Let Apex handle the math, let the LLM handle the humans. Scoring algorithms should be deterministic. Messages to real people should be generative. Don’t mix these up.

The Trace panel is non-negotiable for enterprise use. If you can’t explain why the agent did something, you can’t ship it. Build with traceability from day one.

Study the Agent Script Recipes before building from scratch. The multi-action chaining and conditional routing patterns saved me hours. Start there, then adapt.

Design for dignity. If your agent touches anything sensitive — HR, health, performance — never expose internal scores to the people being assessed. The system should feel like a caring colleague, not a surveillance tool.

The Bigger Picture

Agentforce isn’t just a chatbot platform with better branding. Agent Script, hybrid reasoning, the Atlas Reasoning Engine, action chaining, conditional routing — these are infrastructure for building agents that enterprises can actually trust.

The Burnout Prevention Agent is one use case. But the same patterns — deterministic business rules + generative communication + full traceability — apply to:

  • Sales: Lead scoring with personalised outreach
  • Support: Ticket triage with empathetic customer responses
  • Finance: Compliance checks with contextual explanations
  • Operations: Anomaly detection with proportionate escalation

The tools are here. The recipes are documented. The builder makes it accessible.

If you’re in the Salesforce ecosystem and you haven’t explored Agent Script yet — now is the time.

You can find the demo recording on X/Twitter.*

Tags: #Salesforce #Agentforce #AgentScript #HybridReasoning #AI #AtlasReasoningEngine #AgentforceBuilder #ActionChaining #HRTech #BurnoutPrevention

References:

Building a Web-based Document Scanner: Tackling Perspective and Shadows with OpenCV

We’ve all been there: you need to scan a receipt or a signed document, but all you have is your phone. You take a photo, but it’s skewed, shadowy, and looks nothing like a real “scan.”

I decided to solve this by building DocuScanPro, a web-based document scanner that focuses on high-fidelity image enhancement directly in the browser.

The Problem: Perspective and Lighting
Standard mobile photos suffer from two major issues:

Perspective Distortion: When you don’t take the photo perfectly from above.
Uneven Lighting: Shadows from your hand or environmental lighting that make the text hard to read.
The Solution: A Python + OpenCV Pipeline
To tackle this, I built a backend processing engine using FastAPI and OpenCV. Here’s a high-level look at the pipeline:

  1. Finding the Document
    We use Canny edge detection followed by contour approximation to identify the four corners of the document.

python

Simple example of findContours in OpenCV

contours, _ = cv2.findContours(edged, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

… find the largest 4-point contour

  1. Perspective Transform
    Once we have the corners, we apply a warp perspective transform to “flatten” the document into a perfect rectangle.

  2. Shadow Removal & Enhancement
    This is where the magic happens. Instead of a simple global threshold, I implemented Adaptive Thresholding. This calculates a threshold for small regions of the image, which helps eliminate shadows while keeping the text sharp and the background a clean, “paper” white.

The Results (Before & After)


Try it out!
I’ve just launched the initial version and I’m looking for feedback on the algorithm performance—especially for edge cases like white paper on light tables.

Check out the live tool here: https://www.docuscan.pro/

I’d love to hear your thoughts on the image processing logic or any libraries you recommend.

🚀 Stop Building OTP Systems from Scratch — I Built a Complete Redis-Based Verification Engine for Node.js

🚀 Stop Building OTP Systems from Scratch — I Built a Complete Redis-Based Verification Engine for Node.js

Authentication flows look simple… until you actually build them in production.

At first, I thought OTP verification was just:

  • Generate a code
  • Store it
  • Verify it

But very quickly, things started breaking down.

🤯 The Hidden Complexity of OTP Systems

In real-world applications, OTP is just the beginning.

You also need:

  • ⏳ Expiry handling
  • 🔁 Retry limits
  • 🚫 Abuse prevention (brute force)
  • 🔑 Token-based verification
  • 🔗 Email verification links (magic links)
  • ⚡ High performance under load

👉 A “simple OTP system” quickly becomes a full verification infrastructure.

😤 The Problem with Existing Solutions

While exploring existing libraries, I noticed:

  • ❌ Too many dependencies
  • ❌ Over-engineered abstractions
  • ❌ Tight coupling with email/SMS providers
  • ❌ Not flexible for custom flows

Most libraries solve one problem, but not the whole system.

💡 The Idea: A Unified Verification Layer

Instead of stitching together multiple tools, I built:

👉 redis-otp-manager

A lightweight, Redis-powered verification engine that handles:

  • 🔐 OTPs
  • 🔑 Tokens
  • 🔗 Magic links

All in one place.

⚙️ Why Redis is Perfect for This

Redis solves most of the hard problems out of the box:

  • ⏳ Built-in TTL → automatic expiry
  • ⚡ In-memory performance → blazing fast
  • 🔒 Atomic operations → avoids race conditions

👉 No cron jobs. No cleanup scripts. No complex DB queries.

✨ What This Package Actually Does

This is not just an OTP library — it’s a complete verification system.

🔐 OTP (One-Time Password)

  • Generate secure OTPs
  • Validate with expiry
  • Limit retries

🔑 Token-Based Verification

  • Generate secure tokens
  • Ideal for backend validation
  • More secure than short OTPs

🔗 Magic Links (Verification URLs)

  • One-click verification
  • Perfect for:
    • Email verification
    • Password reset
    • Passwordless login

Example:

https://yourapp.com/verify?token=abc123

📦 Installation

npm install redis-otp-manager

🚀 Getting Started

1️⃣ Setup Redis Client

const { createClient } = require(“redis”);

const client = createClient();
await client.connect();

2️⃣ Initialize Manager

const { RedisOtpManager } = require(“redis-otp-manager”);

const manager = new RedisOtpManager(client);

🔐 OTP Example

const otp = await manager.generate(“[email protected]“);

// send OTP via email/SMS

const isValid = await manager.verify(“[email protected]“, otp);

🔑 Token Example

const token = await manager.generateToken(“[email protected]“);

const isValid = await manager.verifyToken(“[email protected]“, token);

🔗 Magic Link Example

const { token, url } = await manager.generateLink(“[email protected]“);

// send URL via email

await manager.verifyToken(“[email protected]“, token);

🧠 How It Works (Internals)

  • Each request is stored in Redis with a unique key
  • TTL ensures automatic expiration
  • Verification checks:
    • Value match
    • Expiry
    • Retry limits

👉 One-time usage ensures security.

⚡ Key Advantages

🚀 Performance

  • No database queries
  • Fully in-memory

🧩 Simplicity

  • Minimal API
  • Easy integration

🔒 Security

  • Expiry enforced
  • Retry limits
  • One-time usage

🧱 Flexibility

  • Works with any backend
  • No dependency on email/SMS providers

🆚 Traditional vs Modern Approach

Feature| Traditional OTP| redis-otp-manager
Storage| Database| Redis
Expiry| Manual| Automatic
Scaling| Hard| Easy
Links support| ❌| ✅
Tokens| ❌| ✅

⚡ Real-World Use Cases

  • User signup verification
  • Password reset
  • Email verification
  • Passwordless login
  • Microservices authentication

🙌 Open Source

If you find this useful:

  • ⭐ Star the repo
  • 🐛 Report issues
  • 🤝 Contribute

🔗 NPM Package

👉 https://www.npmjs.com/package/redis-otp-manager

💬 Final Thoughts

OTP is not just a feature — it’s part of a larger verification system.

By combining OTPs, tokens, and magic links into a single unified layer, you can:

  • Reduce complexity
  • Improve performance
  • Build scalable authentication systems

If you’re building auth systems in Node.js…

👉 This might save you hours of engineering time.

Qdrant Has a Free API You Should Know About

Qdrant is an open-source vector database designed for AI applications. It stores and searches high-dimensional vectors with blazing speed — perfect for semantic search, recommendations, and RAG.

Why Qdrant Powers Modern AI Apps

A startup building a RAG chatbot was storing embeddings in PostgreSQL with pgvector. At 10M vectors, queries took 3 seconds. They switched to Qdrant — same queries now take 15ms.

Key Features:

  • Fast Search — HNSW algorithm for sub-millisecond queries
  • Filtering — Combine vector similarity with metadata filters
  • Payload Storage — Store arbitrary data alongside vectors
  • Quantization — Reduce memory usage by 4-32x
  • Distributed — Horizontal scaling with sharding

Quick Start

docker run -p 6333:6333 qdrant/qdrant

Python SDK

from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance

client = QdrantClient("localhost", port=6333)

client.create_collection(
    collection_name="articles",
    vectors_config=VectorParams(size=384, distance=Distance.COSINE)
)

client.upsert(
    collection_name="articles",
    points=[{"id": 1, "vector": embedding, "payload": {"title": "My Article"}}]
)

results = client.search(
    collection_name="articles",
    query_vector=query_embedding,
    limit=5
)

Why Choose Qdrant

  1. Blazing fast — optimized for production AI workloads
  2. Rich filtering — combine vector search with metadata queries
  3. Open source — self-host or use Qdrant Cloud

Check out Qdrant docs to get started.

Building AI applications? Check out my Apify actors or email [email protected] for data extraction solutions.

I built an all-in-one PDF Scanner & Vault app solo in Kotlin — Lihenium is now live on Play Store!

Lihenium App Logo

Hey DEV community!
I’m Abhishek, a solo Android developer from Bihar, India. After months of late-night coding, I launched my app Lihenium: Scan PDF & Vault on Google Play Store!

Lihenium App Logo

🤔 Why I Built It

I was frustrated using 5 different apps just to scan, edit, manage, compress and lock my documents. So I built one app that does everything — offline-first, private by default.

🛠️ Tech Stack

  • Kotlin + Jetpack Compose
  • OpenCV for smart document corner detection
  • Room Database for local storage
  • CameraX for scanning
  • ML Kit for OCR

📱 Core Features

App Main Dashboard

  • ✂️ PDF Tools: Merge, split, sign, OCR & watermark
  • 📷 QR Scanner & Generator
  • 📄 Smart Document Scanner with auto-crop

Scanner Screen

  • 🔒 Secure Vault with biometric lock

Secure Vault

  • 📁 Doc Library with folders & tags

Doc Library

  • 🖼️ Passport photo maker & background remover

Passport Photo Maker

  • ☁️ Optional Google Drive backup

Google Drive Backup

💪 Biggest Challenges as Solo Dev

  1. Integrating OpenCV efficiently without bloating the APK
  2. Handling 10,000+ images without OOM crashes
  3. Keeping the final APK at just 18MB with all features packed in

🚀 Try It!

👉 Play Store: https://play.google.com/store/apps/details?id=com.abhi.docu

Would love your honest feedback! 🙏

Life Doc AI: Transforming Notion Journals into Cinematic Documentaries

This is a submission for the Notion MCP Challenge

What I Built

Life Doc AI is a Python-based intelligent web application that transforms your Notion workspace—specifically your Life Events and Journal Entries—into a beautifully structured, cinematic documentary of your personal journey.

Instead of looking at raw text or Kanban boards, Life Doc AI uses the Notion Model Context Protocol (MCP) to securely fetch your milestones and reflections, and relies on an AI Agent (powered by OpenAI GPT-4o with robust rule-based fallbacks) to process them. The result is a Premium Streamlit UI that presents:

  • 📜 The Story of You: An AI-crafted, dramatic narrative of your life.
  • ⏳ Chronological Timeline: Analyzing the dots and connecting your journey.
  • 📚 Era Chapters: Automatically grouping your life into meaningful, thematic chapters.
  • 💡 Psychological Insights: Extracting hidden patterns, your most active years, and measuring your resilience.

Video Demo

Show us the code

Here is the complete open-source repository on GitHub!

🎬 Life Doc AI

Transform your Notion journals and life events into an AI-crafted cinematic documentary of your personal journey.

Life Doc AI connects to your Notion workspace via the Model Context Protocol (MCP), reads your life events and journal entries, and generates a beautiful documentary narrative — complete with timeline, chapters, and psychological insights.

✨ Features

  • 🔌 Notion MCP Integration — Communicates with Notion through MCP protocol (JSON-RPC over HTTP)
  • 📖 Life Events Timeline — Chronological visualization of your milestones
  • 📚 Auto-Generated Chapters — Groups your life into meaningful eras
  • 💡 Psychological Insights — Analyzes patterns, most active years, and impact metrics
  • 📜 AI Narrative — Crafts a cinematic story from your data (via OpenAI GPT-4 or built-in generator)
  • 🎨 Premium UI — Dark theme with glassmorphism, gradient accents, and smooth animations

🏗️ Architecture

┌───────────────────┐     MCP Protocol     ┌──────────────────────┐     Notion API    ┌─────────┐
│  Streamlit App    │ ◄──────────────────► │  Python

…

How I Used Notion MCP

The Notion MCP was the absolute core of this project, bridging the gap between my private workspace and my AI generation engine securely.

I built a custom Python MCP Server (mcp/mcp_server.py) that wraps the Notion REST API and exposes tools like notion_query_database over a JSON-RPC HTTP transport layer.
When the user clicks “Generate”, the Streamlit UI (acting as the MCP Client) fires a remote procedure call to the local server. The MCP Server negotiates with Notion, retrieves the Life Events and Journal Entries databases, and returns a clean, structured JSON payload.

The Developer Journey

This project represents the culmination of my recent developer journey. A few weeks ago, I was figuring out static web portfolios. That evolved into mastering containerized FastAPI backends, and eventually wrestling with complex NLP text preprocessing errors.

When the Notion MCP Challenge was announced, I realized I could combine all those skills:

  • The backend architecture knowledge helped me build a robust, thread-safe Python MCP Server.
  • The NLP experience allowed me to craft intelligent prompts and structured LLM parsing.
  • The web development skills helped me design a “Glassmorphism” Dark Theme Premium UI in Streamlit (.streamlit/config.toml).

Life Doc AI doesn’t just read data; it understands it. The Notion MCP unlocked the ability to treat Notion not just as a note-taking app, but as the engine of a person’s life story.

Using the Creem CLI as a Developer Power Tool with Claude Code

If you build with Creem, you know the loop: write some code, open the dashboard, check if the checkout went through, check the webhook logs, compare with your local state, go back to the code. Four tabs, three data formats, five minutes just to confirm a test purchase landed. Repeat for every change.

This guide shows a different approach. You stay in the terminal. The Creem CLI provides the data, Claude Code interprets it, and your app shows the result. Everything in one place.

I built a small integration to demonstrate this. The repo includes a minimal Bun + Hono app that handles checkouts and webhooks, plus a Claude Code skill that teaches the AI how to operate the Creem CLI safely. The entire workflow — verify, debug, operate — happens from the terminal.

The setup

The integration is intentionally small. A Bun server running Hono that creates checkout sessions, receives and verifies webhook events from Creem using HMAC SHA-256, and persists local state to JSON files. The home page shows a color-coded status badge — green for access granted, red for revoked, yellow for in-progress, gray for no activity — so you can see the current state at a glance without reading JSON.

The app tracks a simple state machine: unknown → checkout-created → checkout-completed → granted → revoked. Every transition is driven by a webhook event. If the webhook doesn’t arrive, the state doesn’t change. This becomes important later.

The key thing to understand is that the Creem CLI and the app are two independent views of the same data. The CLI talks directly to the Creem API. The app only learns about changes through webhooks. When they agree, everything is fine. When they don’t, something broke — and the CLI can help you find out what.

Teaching Claude Code the Creem CLI

Claude Code is a capable AI coding assistant, but it doesn’t know how to use the Creem CLI out of the box. It doesn’t know the commands, the flags, or the conventions — like the fact that Creem returns money amounts in minor units, where 500 means 5.00 EUR, not 500.00.

That’s why I built a skill. In Claude Code, a skill is a structured document that teaches the AI how to use a specific tool. It covers the available commands, the correct flags, safety rules, and common workflows.

The Creem CLI skill includes:

  • A quick reference table for every supported command: products, customers, checkouts, subscriptions, transactions, and configuration
  • Rules like “always use --json for machine-readable output” and “never read or expose the config file at ~/.creem/config.json“
  • Money amount conventions: always convert minor units before showing values to the user
  • High-value workflows: how to create and verify a checkout, how to look up customers, how to manage subscription lifecycle
  • Automation patterns for scripting and piping output through jq

The skill lives in the repo at skills/creem-cli/SKILL.md and anyone can install it with one command:

npx skills add santigamo/creem-cli-developer-toolkit

Once installed, Claude Code loads the skill automatically when it’s relevant. You talk in natural language, and it translates your intent into the correct CLI commands with the right flags and safety constraints.

Workflow 1: Verify a purchase

The first thing you want to know when building a payment integration is: does it work? Did the checkout go through? Did the webhook land? Does my app agree with the payment provider?

Normally you’d check the Creem dashboard for the transaction, then check your server logs for the webhook, then maybe hit your debug endpoint to compare. Three different places, three different formats.

With the CLI skill loaded, the conversation looks like this:

You: “Create a test checkout for my product and give me the URL.”

Claude Code runs creem checkouts create --product <id> --success-url http://localhost:3000/success --json, parses the response, and hands you the checkout URL. You open it, complete the sandbox purchase with a test card, and come back.

You: “I just completed the purchase. Can you verify the transaction went through?”

Claude Code runs creem transactions list --json, finds the new transaction, and explains: paid, the amount in human-readable format (converting from minor units), the associated subscription ID. No raw JSON to read.

You: “Does the subscription look healthy?”

Claude Code runs creem subscriptions get <id> --json and confirms: active, renewal date set, everything nominal.

You: “Check my app’s local state too — does it agree with Creem?”

Claude Code hits curl localhost:3000/api/debug/state, compares the local access status with what the Creem API returned, and confirms they match. The app’s status badge shows green: “Access Granted.”

One conversation, zero dashboard tabs. Claude Code explains the results in plain English, so you don’t have to parse any JSON yourself.

Workflow 2: Diagnose a state mismatch

Verifying the happy path is useful, but the real value shows up when something breaks. Specifically: when your payment provider knows something that your app doesn’t.

This happens more often than you’d think. A subscription gets paused or canceled on the Creem side, but the webhook doesn’t land — maybe your endpoint was down, maybe there was a network hiccup, maybe your tunnel dropped. Your app keeps showing “Access Granted” while the subscription is actually paused. Your user has access they shouldn’t have, or worse, they lost access and your app doesn’t know.

Finding this kind of mismatch by hand means opening the Creem dashboard, finding the subscription, checking its status, then comparing with your database or local state, then checking your webhook logs to see if the event was ever delivered.

Here’s how it looks with the CLI:

You: “Pause the subscription from my last test purchase, then compare what Creem says with what my app says. I want to know if subscription changes are propagating correctly.”

Claude Code runs the pause command, then gathers evidence from both sides: creem subscriptions get <id> --json and creem transactions list --json for the Creem state, and curl localhost:3000/api/debug/state for the app state. It compares them and reports the finding.

In my test, the result was clear: Creem shows the subscription as paused, but the app still says “Access Granted.” The status badge on the home page is still green. The webhook never arrived.

Claude Code didn’t just find the mismatch — it explained why. The subscription changed in Creem, Creem tried to send a webhook, but the endpoint wasn’t reachable. The local state is stale.

You: “What are my recovery options?”

Claude Code suggests concrete steps: restore the webhook endpoint, then either replay the event if the platform supports it, or trigger another state change so the next webhook lands and syncs the state. It adapts the suggestions to what it knows about the setup.

The same investigation through dashboards and logs — opening the subscription page, cross-referencing with webhook delivery logs, checking your server output — would involve a lot more clicking and context-switching. Here it was one prompt and a clear answer.

Workflow 3: Operate the store

The CLI isn’t just for testing and debugging. Once you’re comfortable with it, you can manage your Creem store without leaving the terminal.

First, recovery from the previous workflow. After restoring the webhook endpoint:

You: “My endpoint is healthy again. Resume that subscription and confirm the current state.”

Claude Code resumes the subscription, verifies the state on both sides, and confirms everything is back in sync. The app’s status badge flips back to green, and the webhook event shows up in the recent events table.

Then, product management:

You: “I want to add a Pro Plan to my store. 19 dollars a month, recurring.”

Claude Code runs creem products create with the right flags: --name "Pro Plan", --price 1900 (minor units), --currency USD, --billing-type recurring, --billing-period every-month. One sentence from you, one command from the CLI.

You: “List all my products to confirm.”

Claude Code runs creem products list --json and shows you both products — the original test product and the new Pro Plan.

You: “Give me a summary of the current state of my store.”

Claude Code pulls products and subscriptions, cross-references them, and gives you a plain English overview: how many products, how many active subscriptions, current revenue state. The kind of snapshot that would normally require opening the dashboard and clicking through several pages.

Why this matters

The pattern here isn’t specific to Creem. It’s what happens when a CLI tool produces structured JSON output and an AI assistant knows how to read it. The skill bridges the gap — it teaches the AI the commands, the conventions, and the guardrails. Together, they turn a series of manual dashboard checks into a conversation.

If you’ve ever spent fifteen minutes bouncing between a payment dashboard, your server logs, and a database query just to confirm a webhook landed, this is the alternative. One terminal, one conversation, same answer.

Try it yourself

The repo is open and includes everything you need:

  • A minimal Bun + Hono app with checkout, webhook, and state tracking
  • The Claude Code skill for the Creem CLI
  • Setup instructions and environment configuration

Install the skill:

npx skills add santigamo/creem-cli-developer-toolkit

Clone the repo, set up your Creem test credentials, start the app, and start talking to Claude Code. The skill handles the rest.

GitHub: santigamo/creem-cli-developer-toolkit