← Files PicsartARCHIVED FILE
docs/ASK-PROMPT-VERIFY.md
7.86 KB · Oct 10, 2026 · 18:02 UTC
# ASK-PROMPT-VERIFY — making the prompt-verify step impossible to skip
**Status: designed, not built.** Parked 2026-09-13. **Superseded in part: flag 1 of
[`IP-BOUNDARY.md`](IP-BOUNDARY.md) (2026-09-14) proposes inverting the drift answer
below — rules live server-side as the only copy, rather than travelling with the
call. Pending the user's decision; until then this file's answer stands.** Everything below is the
decision record from the session that built the step itself; the remaining work
is the server side.
## The problem
`film-scenes/references/PROMPT-VERIFY.md` defines the step: before every
`picsart_generate` call, open the files that govern the artifact, derive what
they require, check the literal tool arguments, write the verdict.
**Skill text cannot guarantee execution.** The failure that caused the step —
a run dispatched with six documented rules broken, none of them checked — was
committed by an assistant that could quote those rules correctly minutes
beforehand. Every safeguard in the plugin repo is an instruction someone
follows or forgets.
## What already exists (plugin repo, 2026-09-13)
- `film-scenes/references/PROMPT-VERIFY.md` — the step: governing-files table
per artifact kind, five passes, the verdict format, the traps.
- `film-scenes/scripts/check_dispatch_intent.py` — the deterministic half.
Required/forbidden house lines per `--kind` (`run` / `scene-picture` /
`sheet` / `edit`), head count, cut list, dialogue vs the approved snapshot,
saved voice verbatim per speaking block, word-count vs `sec`, reference
budget, `startFrame` empty, banned adjectives. Returns a verdict table plus
`requestSha256`.
- `film-scenes/scripts/test_dispatch_intent.py` — 29 tests, including a pin:
**every canonical string in the code must appear verbatim in
`PROMPT-BLOCKS.md`**, so the code cannot become a second, drifting copy of
the rules.
- Pointers from `PROMPT-BLOCKS.md` (Dispatch), `film-scenes/SKILL.md` (runs
and step-3 stills), `ASSET-SHEETS.md` (portraits, sheets, plates),
`JOIN-REPAIR.md` (edits and extends).
Missing: anything that *refuses*. All of the above depends on being run.
## Options considered
**A · Receipt / fingerprint.** Assistant runs the checker, passes
`verify: {sha256}`; `picsart_generate` re-hashes its arguments and rejects a
mismatch. **Rejected** — the hash proves those arguments were *hashed*, not
that they *passed*. Any caller can compute it and skip the checks. It only
stops stale or edited prompts.
**B · Checks inside `picsart_generate`.** Server applies the rules itself
before spending. Cannot be faked. **Rejected by the user (2026-09-13): do not
change `picsart_generate`.**
**C · Separate free tool + a token generate demands.** `picsart_prompt_verify`
runs the checks server-side and on pass returns a server-issued HMAC over a
fingerprint of those exact arguments; `picsart_generate` refuses film calls
without a valid one. Unforgeable, stateless. **Rejected — still changes
`picsart_generate`.**
**D · Standalone advisory verify tool.** `picsart_prompt_verify` returns a
verdict. Skills carry a rule to auto-fix what is safely fixable; other plugins
show the user the findings and the user decides. **This is the chosen shape.**
It is a *visible* gate rather than an *enforcing* one — it works because the
verdict lands in front of the user, not because anything refuses. Accepted
trade: auto-fix resolves most failures before a human sees them.
**Reopened but unresolved:** blocking `picsart_generate` globally when the film
rules are not obeyed. It would have to live in that tool's code path (the only
place a call can be refused), but with **no schema change** — scoped so it only
ever fires on film calls, detected either by destination (`folderUid` inside
`Picsart Films/<slug>/`) or by a per-film opt-in. Non-film callers must be
untouched: a logo prompt has no head-count line. Would also need recording in
the server repo's `wire-behavior-changes.ts` (a call that used to succeed and
now errors is wire-visible) and an escape hatch for the day a rule is wrong and
the user needs to shoot anyway. **Not decided.**
## How the rules stay in sync — the decisive constraint
Two repos holding the same rules will drift. Two answers were on the table:
1. **Vendor a rules file.** Extract the machine-checkable rules to
`film-prompt-rules.json` in the plugin repo as the single source of truth;
the Python checker imports it, the MCP server vendors it with a
refresh-from-checkout script and a hermetic drift gate that fails CI.
Precedent already in that repo: the media-tools mirror
(`mcp:mirror:media -- --from-dir`, `mcp:mirror:media:check`), including the
lesson that a denylist cannot hold a surface steady — pin a version and
report it in the verdict so a mismatch is visible rather than silent.
2. **The rules travel with the call** (user's proposal, 2026-09-13, and the
better one). The skill has just read the governing files — that is the
step's first instruction — so it sends the prompt *and* that rule text to
the backend. Nothing is stored server-side, so there is no second copy and
**no drift by construction**. The `.md` files stay the only source of truth.
## The chosen build: a hybrid, one tool, two engines
Backend Claude is available in the pluggable backend, so the verify tool can be
Claude reading the prompt against the rules.
**Do not use the model for the mechanical half.** Asked whether
`NO MUSIC, only SFX and ambient sounds.` appears byte-for-byte, a model will
usually accept a close paraphrase — which is precisely the defect being caught.
Same for `16 words / 2.5 + 0.6 > 5`. Those checks are free, instant and
deterministic today.
**Do use it for the judgement half**, which no script will ever answer, and
which is what actually made the first take boring — not the missing no-music
line.
| Checked | Engine | Cost |
|---|---|---|
| house lines verbatim; video-only lines absent from stills; head count; cut list; dialogue vs approved; voice verbatim; word-count vs `sec`; reference budget; `startFrame`; banned adjectives | deterministic code (exists) | free, instant |
| does anything escalate; is that an emotion or the name of one; was an intensity chosen; named gaze; who is inert; mask/leak; arrival for a latecomer; size vs arrangement | backend Claude | one call |
One verdict table out of both. **The deterministic half may block; the Claude
half advises** — a model that passes a prompt once and fails it next time must
never be the thing standing between the user and a render.
## Open questions
1. Does the backend Claude call cost credits? If yes, it is called once per
dispatch, not per draft iteration.
2. Auto-fix boundary. Safely auto-fixable: a missing house line (paste it
verbatim), a video-only line in a still prompt (delete it), a banned
adjective (rewrite from the wheel). **Never auto-fixed** — anything the user
owns: `sec`, the palette, the lens, the light, the spoken line itself. Those
are findings to take back to the board or the user.
3. The prompt under inspection is content being fed to another model. Keep it
framed as data to inspect, never as instructions to follow.
4. Where the tool lives: `pa-ai-models-mcp-server`, branch `film-ux-polish`
(where the film suite is registered). Adding a tool there requires
`npm run mcp:contract:update`, a `tool-risk.ts` classification (free),
a hand-authored `tools.json` entry, `mcp:descriptions:update`, and an
`MCP_SURFACE_VERSION` bump.
## Build order
1. **Wrap the existing deterministic engine as `picsart_prompt_verify`** —
free, read-only, returns the verdict table. Most of the logic is written and
tested; this is a port.
2. **Add the Claude judgement pass** on top, rules travelling with the call.
3. **Then** revisit blocking `picsart_generate`, if still wanted.
Doing 2 or 3 before 1 means writing the rules a second time by hand, which is
the drift problem created on day one.
SHA-256: 5aeebe127bae8e56b1eae20ddc6f9859829ce1f5932b4b4864c7132c5d1b2620