Four things nobody has planned yet, drawn so you can rule them
Everything in the lane's build queue is now shipped and live. These four are the remainder:
known wants with no plan on disk. Each one is drawn here at the size it would actually ship,
with the fact that makes it necessary and the fork only you can settle.
Nothing here is built. Reading this is the decision: say yes, no, or pick a fork, and it
becomes a work order. A fifth item, the AI never replying to a comment-reply, was on
this list and came off it, because it already works
(comment-pipeline.ts:604-626: the reply pass yields on a parent id
while moderation still runs above that line, replies are ingested with their parent, and Posts
already threads them).
A
The screen is the only thing stopping a forbidden act
a real hole
The registry says which acts a row allows. Both screens obey it. The server only
obeys one line of it: a deleted comment is refused. Everything else in
commonOverrides() is enforced by the buttons we chose to draw,
which is not enforcement at all.
Today
A crafted request, or a stale tab, can post this:
POST /api/moderation/actions
{ igCommentId: "…", action: "hide", expectedStatus: "posted" }
The row is a flow-run. The registry declares no hide path for it.
The screen never drew the button, so nobody ever tried, and the server never checked.
Proposed
One guard, in the one place every act already passes through:
Same shape as the deleted-comment guard added on 2026-08-04, same file, and
actBulk delegates to act, so one guard
still covers both.
Why it is not already done: the server holds a comment's status, not the
composed outcome word the registry keys on. It would have to run
computeOutcome at act time, which it now can: that composition
moved server-side for Posts on 2026-08-05 and is one import away.
The fork
Refuse, and say why. The act returns the registry's own reason, so a stale tab tells the truth instead of failing blankly. Recommended.
Refuse silently. Cheaper, and indistinguishable from a bug when it fires on a real operator.
Log only, for one week. Learn whether it ever fires before making it fatal. Safer, but leaves the hole open while you watch it.
B
An Instagram DM has no link back to Instagram
honest, and useless
Every kind on Activity offers two Sources: our record, and the real thing. Instagram DMs
offer one, because the thread id Instagram addresses a conversation by is
never stored. The registry says so in as many words.
Today, on an IG DM row
Open the conversation in Inbox
No second button. The panel says the absolute source is unavailable, which is honest
and leaves you retyping a handle into Instagram to see the real thread.
Open the conversation in InboxOpen the conversation on Instagram ↗
One nullable column on the conversation, written from the webhook envelope that already
carries it, and the link composes itself. Additive migration; nothing backfills, so old
threads keep saying they cannot link, which stays true for them.
The fork
Store it from now on. New conversations link; old ones keep the honest fallback. Recommended: no backfill, no invention.
Store it and backfill. Needs a Graph read per old conversation, and the thread id may no longer resolve. Spend for a tail.
Leave it. The Inbox link is genuinely the useful one; the absolute source matters most for comments, which already have it.
C
Corrections are kept, counted, and never read
a link with nothing behind it
The Activity sheet's head carries a line you approved: "N corrections kept since DATE.
Nothing is added to the agent's instructions automatically." It is not built, because
there is nothing to count and nowhere for the link to go. Every time you mark a decision
wrong, or reject a draft with a reason, that correction is stored on the row and never
gathered.
wrongshould have answered, the figure is published
Open the row
1 Aug
"Influencer email if possible?"
replied
wrongthis is someone pitching us, it belongs hidden
Open the row
29 Jul
"Which location of Hyatt Place?"
answered with the venue
right
Open the row
Copy the wrong ones into BehaviourExport as markdownNothing here edits the agent by itself.
The rule this screen exists to keep: corrections are
collected, never auto-injected, your ruling of 2026-08-01. So the primary button
copies them where you can read and edit them, and the screen says so out loud rather than
leaving you to wonder whether marking something wrong quietly changed the bot.
The fork
Since a date you pick. The head counts from a stored "reviewed up to" mark you move when you have read them. Recommended: the count means "owed", not "ever".
Since forever. One number that only grows, and stops meaning anything by October.
Rolling 30 days. No state to keep, but a correction can age out unread.
D
Half the agent's instructions are invisible in Behaviour
your order, 2026-08-05
You said the prompt should all be organised and shown in Behaviour, not hidden. Today
Behaviour shows your five headings per situation, and the comment agent is also
given a seeded policy that lives in code, which you cannot see or edit. It is not a small
part: it carries the two-tier reply logic, the moderation bar and the reply-style examples.
What the comment agent is actually sent
Role
visible in Behaviour
Goal
visible in Behaviour
Tone
visible in Behaviour
Guardrails
visible in Behaviour
Knowledge
visible in Behaviour
Seed policy
in code, invisible, ~2.4k chars
Output contract
in code, invisible
Safety rail
in code, invisible
The seed is what flipped its own CTA when you took comment-to-DM live today. You could
not have read that, let alone changed it.
Proposed, Behaviour shows everything, and says what is yours
Goalyours · edited 4 Aug
Reply to one public comment. We have already opened the DM…
Comment policy (seed)from us · editable
Two-tier reply logic, the moderation bar, the reply-style examples. Editing it makes it yours; a Reset returns this text.
Safety railread only
Skip anything about a person's appearance. Never output an @handle. Shown so you know it is there; not editable, on purpose.
The one thing that must not become editable: the output contract (the
reply-or-skip JSON the engine parses) and the hard safety rail. Everything else is yours.
Showing them read-only is the difference between "hidden" and "not yours to change".
The fork
Show all three, edit two. Seed becomes an ordinary editable field with a Reset; the rail and the contract render read-only. Recommended.
Show all three, edit none of the new ones. You can read the seed but not change it. Solves "hidden", not "operator-extensible".
Fold the seed into your five headings. One surface, no extra card, but it rewrites text you have already recategorised by hand, and the merge is not reversible.