Nodes/ComfyUI-BCNodes/PreFlight Report
ComfyUI Node

PreFlight Report

PreFlight Report predicts how Instagram, TikTok and X will treat your post — as a range, not a verdict

By 0xBeycan·Created about a month ago·Updated 21 days ago· 2
PreFlight Report
  • image
  • report_json
  • summary
  • record_id
  • image
◄observations_json—►
◄caption►
◄log_predictiontrue►

PreFlight Report is the judgment half of the pair. Observe gave you a strict JSON description of what's in the image; Report runs a deterministic rules engine over it and gives you a per-platform prediction. No model runs here - feed it the same observations twice and you get byte-identical output, which is the property that makes the whole thing auditable.

Feed it observations_json from PreFlight Observe. Optionally add the caption you plan to publish. Three string outputs walk out the other side.

Verdicts are ranges, on purpose

Every platform gets a best → worst range drawn from four levels:

  • OK - no expected problem.
  • RISK - reach demotion. The post stays up and reaches fewer people (Instagram recommendation exclusion, TikTok FYF ineligibility).
  • BLOCK - removal (Instagram, TikTok), or TikTok For-You-Feed ineligibility.
  • UNKNOWN - the sensor failed or the observation came from a schema the engine doesn't recognise.

On X the output tells you whether an adult label is recommended rather than predicting a takedown.

OK → RISK means "probably fine, at worst demoted." RISK → BLOCK means "demoted, and possibly removed depending on a named factor." That named factor is a range driver, and it's the most useful thing in the output - it tells you exactly which unknown would collapse the range, whether that's the setting, the sensor's confidence, or something only you know. A tool that hands you a range plus the thing that would resolve it is being honest with you. A tool that says "82% safe" is not.

BLOCK at the worst end is reserved for genuine policy triggers: nudity, see-through or wet clothing, significant exposure, adult solicitation or links, explicit sexual motion, or a sexualized depiction of an apparent minor. Soft signals - framing, pose, mild exposure, suggestive text - top out at RISK. They demote; they don't remove. And the apparent-minor rule is narrow on purpose: it fires only when the subject is both apparently under 18 and shown in a sexualized context. A clothed, neutral subject is ordinary content, and a youthful-looking adult is not flagged.

What goes in

observations_json - Observe's output, verbatim. Errors pass through as UNKNOWN.

caption - optional, multiline, your planned caption. Here's a detail worth appreciating: the caption and the text the model read inside the image (visible_text) are scanned together. An adult-platform watermark burned into a frame flags exactly like a link in the caption text. That's one of the few places in this pipeline where the in-frame text earns its keep, and it catches a real mistake people make - the caption is clean, the image has the URL in the corner.

log_prediction - on by default. Appends the prediction to the feedback store so you can record later what the platform actually did. Turn it off for throwaway checks and you'll have nothing to calibrate against, so mostly leave it on.

image - an optional pass-through. Nothing looks at it. It exists so the node can sit inline between Observe and your preview instead of hanging off to the side, which is what you actually want in a read-the-summary-as-you-build graph.

What comes out

summary - one line per platform, then the range drivers, then record: <id>. This is the output you read. Send it to the pack's Show Text node and it's on screen after every queue.

report_json - the full thing: the observation, the caption flags, the rules that fired in order, the verdicts, the drivers, the engine version, the record id. Because the fired rules are in there, a verdict you disagree with is traceable to the rule that caused it.

record_id - the id of the logged prediction, as shown in the summary. Empty when logging is off or the store can't be written; the verdicts still come out either way.

image - whatever you passed in.

Motion rules apply only to a video - the observation needs more than one frame analysed for the engine to consider them at all. A single image gets judged on the still alone, and the range is often wider for it.

The feedback store

With logging on, each run appends one line to an append-only JSONL file:

ComfyUI/user/BCNodes/preflight/feedback.jsonl

It holds the observation, the first 80 characters of the caption, the caption flags, the IDs of the rules that fired, the verdicts, and the engine and schema versions. Nothing is rewritten in place, and a corrupt line is skipped. One 8-character id ties it together.

Two things follow from that. First, you can judge old content with new rules without re-running the model - the observation is stored with the schema and prompt versions that produced it, and rules.judge runs over a stored observation directly. Second, if a prediction was made with a schema version the current engine doesn't know, it fails closed to UNKNOWN rather than being misjudged. That's the right default and it's not what most of these pipelines do.

Old installs that kept a store at output/preflight/feedback.jsonl are not picked up. Nothing else is read - move the file if you have one.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/0xBeycan/ComfyUI-BCNodes
pip install -r ComfyUI-BCNodes/requirements.txt

Restart, search preflight. Report itself needs no Python packages - it's pure logic over a JSON string. No model, no VRAM, no download. Pair it with PreFlight Observe, and with PreFlight Outcome days later when the post has been live long enough to know.

Getting burned

Read the limitations before you trust a number. The thresholds in the rules are informed hypotheses about what the platforms do, not their actual published policies. Real enforcement also depends on signals this node cannot see - account history, bio, follower and link patterns, posting cadence, region. Two identical images on two accounts genuinely get treated differently. That's not a bug you can fix by editing the rules; it's the shape of the problem.

A low-confidence observation widens the ranges on purpose. If confidence comes back low, expect a wide band and a driver naming it. Wide is the honest answer.

It will never say "approved." It's built not to. That's a feature if you read it as a second opinion and a disappointment if you wanted a green light before hitting publish. The person decides - that's the design, and the calibration loop exists because the rules are wrong until you tune them against your own outcomes.

CategoryBCNodes/preflight

Inputs (4)

NameTypeDefaultDescription
observations_jsonSTRINGThe observations_json output of PreFlight Observe.
captionSTRINGOptional caption to be published. Scanned for adult solicitation / links together with the in-image text.
log_predictionBOOLEANtrueAppend this prediction to the feedback store so you can later record what the platform actually did (PreFlight Outcome).
imageoptIMAGEOptional pass-through so this node can sit inline in a workflow.

Outputs (4)

NameTypeDescription
report_jsonSTRING—
summarySTRING—
record_idSTRING—
imageIMAGE—