01 Problem
The problem
Posting for more than one brand meant a loop spread across seven separate tools - learning what worked, deciding what to make, planning it, writing it, publishing it - with a spreadsheet holding the parts none of them held.
Even inside my first version, planning and writing were two screens that each held part of a piece of content, and neither held the whole thing. Scheduling was a pop-up prompt with no history behind it.
02 Evidence
How I knew it was real
- The rewrite that made it one product names the problem in its first line: seven separate tools, one loop.
- Using it on real brands exposed leaks. One brand's logo and palette appeared in every brand's preview, insights ignored which brand was active, and the library mixed every brand together.
- The generated writing read generic - and once put a stranger's revenue into my own first person. That is the moment a content tool becomes a liability.
03 Decision
What I set out to change
One object per piece of content, one lifecycle it moves through, and a writing tool that supplies material while I supply the voice.
What I deliberately left alone
- Posting. It prepares, I post, and I mark it published. There is no publishing integration.
- Anything personal. A personal example can never be accepted into a field - the model is not allowed to invent my life.
- Spending money without asking. A paid scrape needs an approval claimed atomically, and automatic spend is held at zero.
The choices worth arguing with
Chose The model proposes and never writes. Every accepted suggestion stores what it replaced, so it can be undone.
Over Letting the model write straight into the fields.
Because The system supplies material; the creator supplies the voice.
Chose Writing starts from the thought, and the per-platform package, the score and the shot list are gone.
Over Platform and goal in, scored package out - which is what I built first.
Because It reliably produced generic content. A marketing abstraction is a bad thing to hand a model.
Chose The library is the record of truth; a scrape is only evidence.
Over Auto-merging whatever the provider returned above a match threshold.
Because Providers return subsets, and a wrong merge corrupts two records at once.
Chose Old statuses are aliased when they load, not migrated.
Over A migration rewriting every live row.
Because Live rows were written under older models, and one missed mapping would silently turn real work back into ideas.
04 Build
What I built
A scraper collects real reels in a niche with their view, like and comment counts, keeps the ones above a view floor, and pulls a hook and a topic out of each. Ideas are generated only from that evidence - with none, it refuses rather than falling back to a generic answer.
Every idea becomes one content item with one lifecycle: twelve statuses and their allowed transitions, declared in a single file. The writing workspace offers proposals, twenty-seven kinds of them, and I accept, edit or reject each one.
Scheduling is dragging a card onto a calendar slot. After posting, the account's own posts come back in, are reconciled against the library, and are labelled against the account's own median - five labels, never a score.
- SourceNiche scrapeReal reels with views, likes and comments. Paid runs need approval.
- KeptWhat performedDeduplicated, and kept above a view floor.
- ModelHooksPulled from what worked, highest views first.
- ModelAn ideaOnly from evidence. With none, it refuses.
- KeptOne content itemEvery idea becomes one row with one lifecycle.
- ModelProposalsTwenty-seven kinds. Suggested, never written in.
- A personMy voiceAccept, edit or reject. Every accept can be undone.
- A personScheduledI drag it onto the calendar.
- OutPosted, then measuredMarked by me. Labelled against the account's own median.Back to One content item · back as an idea
05 Validation
How I tried to break it
One brand's logo in another brand's preview
The brand on screen now scopes the previews, the insights and the library.
Broke, then fixed · Aug 2026
A stranger's revenue written as mine
A personal example can never be accepted into a field.
Broke, then fixed · Aug 2026
A scrape running past the 60-second limit
Started, then polled. Counters that said zero while rows were being written now resume instead.
Broke, then fixed · July 2026
A provider returning only part of an account
Treated as evidence, never as the complete record. A provider's silence is not evidence of anything.
Broke, then fixed · 2 Sep 2026
A paid scrape started twice
One door: it runs only after an approval moves atomically from approved to executed.
Held
Rows written under an older lifecycle
Aliased as they load, so nothing silently becomes an idea again.
Held
What did not work
- The first writing tool - one idea fanned out into a scored asset per platform, improved in one click - reliably produced generic content. I removed it on 31 August.
- 'Ground it in real examples' was a disabled option from the first commit. The scraper's evidence feeds the ideas; it never reached the writer the way I first described it.
- Hooks inferred from captions read generic, because a caption is a weak stand-in for a reel.
- Keeping brands apart was retrofitted in four commits after leaks were found, not designed in from the start.
06 Outcome
What changed
- 410 / 410 checks passing across 16 test files
- Run on 2026-09-11. There are no browser tests yet.
- 0 of 10 hooks opening on manufactured curiosity after the rewrite
- Ten hooks checked in the commit that removed the content factory, 2026-08-31.
- 25.7 s longest live research run, under a 60-second limit
- Live validation notes. One synchronous scrape had taken 74.1 s before it was made asynchronous.
- ~$1.69 per thousand reels scraped
- Measured on the scraper and written down beside its code.
It is one place now: one object per piece of content, one lifecycle, brands kept apart, and a writing tool that argues with me instead of writing for me. The product moved from grading generated copy to protecting the creator's own voice.
It is still a private tool with one user, and the numbers that would prove it saves time - ideas that become posts, days from idea to post - are not computed yet.
What I measure next
- Idea to postedThe share of content items that reach posted, by brand and by where the idea came from.
- Days from capture to postEach item's created date against its published date.
- How often I accept a proposalProposals accepted against proposals offered - both are already recorded.
- What the model costs in moneyNot recorded today: model spend is logged as zero, and only tokens are kept.
07 Learning
What I learned
A marketing abstraction is a bad thing to hand a model. Asking for a package for a platform and a goal produced polished copy that sounded like nobody; starting from what I am trying to say produced something worth editing.
Prompt rules do not hold under load. The rule that mattered most was lost from the prompts three times. The rules that hold are the ones enforced outside the prompt.
The object outlived every screen around it. One content item, and old statuses aliased rather than migrated, made each rewrite of the surface cheap.
What I would change next
- Compute idea-to-posted for each brand, so 'it saves time' becomes a number.
- Record model spend in money, not only in tokens.
- Remove the three places that still write a retired status.