Local resource

Prompts And Stages

_docs/PROMPTS_AND_STAGES.md

Prompts And Stages

Active prompts live in prompts/ and are indexed in prompts/INDEX.md. The exact user prompt templates and instantiated runtime requests are documented in Runtime Prompt Requests.

System vs User

  • System: stable rules, output schema, safety/compliance constraints, and required JSON shape.
  • User: variable runtime data only: rendered conversation documents,
  • monitored child JSON, explicit Core context, optional normalized tutor report guidance, known monitored age band and bounded prior report feedback, stats metadata, run metadata or media content.

This separation is required for prompt caching and consistent compliance behavior.

Runtime serializes every variable user prompt as one canonical wardian.untrusted-data-envelope.v1 JSON object. The content member may contain Markdown-like conversation documents but cannot escape into another role. A common system invariant is appended to all flow steps; visual and video prompts apply the same rule to visible/spoken text.

In PRIVACY_PROFILE=protected, user-prompt metadata blocks (monitored child JSON, explicit context JSON, user stats metadata JSON, run metadata and extra context markdown) drop the keys flagged by PrivacyPolicy.should_redact_prompt_metadata_key (phone, telefono, numero, jid, raw, payload) via services/prompt_privacy.py::filter_prompt_metadata before rendering. The default debug_plaintext profile may retain those debug fields, but DOB and exact-age aliases (dob, birth*, nacimiento, age, edad) are always removed recursively in both profiles.

Output Contract

  • Flow, vision, and video prompts return raw JSON objects in Spanish.
  • The model must not wrap the response in Markdown or a ```json block.
  • Markdown is allowed only inside JSON string values, mainly for parent-facing report text.
  • Prompts use a conservative anti-false-positive stance: do not alert parents for isolated benign or ambiguous mentions; highlight repeated, concrete, or severe evidence. Severe single events (a grooming approach, sextortion, suicidal ideation with method/plan/farewell, weapons) escalate to ⚠️ without requiring repetition.
  • Parent-facing prompts use a warm, close, conversational voice (no corporate or report-speak): they celebrate the good (pride), leave healthy curiosity for what comes next, and answer the parent's likely questions, never via fear. The daily and interweek reports include a "Momento WArdian" (a short, no-spying conversation starter with a couple of soft prompts grounded in real activity); in the daily flow it is the momento_wardian notification kind.
  • Parent-facing and summary prompts use only first names for the monitored minor
  • and named contacts when available; surname-like text, initials, labels and emojis are stripped in prose. Literal transcription fields such as texto_visible preserve visible text exactly.

  • Dates remain in structured fields, metadata and transcript evidence. Report
  • prose should not restate absolute dates when the same date already exists in the report metadata.

  • Natural-language report fields use maximo 2 metricas numericas por campo de
  • prosa o seccion Markdown. Models should avoid metric dumps and pick only relevant exceptions or patterns. For initial_report, the only exception is: excepto en Estadísticas duras y usage_stats; daily and InterWeek keep the two-metric limit in every Markdown section.

  • initial_report category ids must come from config/taxonomies/risk-v1.yaml.
  • Valid wardian-tutor-report-guidance.v1 input is rendered only in report
  • prompts as untrusted soft preference data. It may orient focus/order/depth, but is not evidence, a system instruction, severity rule or suppressor; it cannot create/suppress findings or influence chat memory updates.

  • With REPORT_FEEDBACK_CONTEXT_RENDERER_ENABLED, valid existing
  • payload.context.report_feedback items are rendered once in a dedicated # Feedback previo del tutor section. They are untrusted, non-evidential soft context: they cannot change risk, severity, confidence, taxonomy, alerts or memory, and their presence does not prove output impact.

  • Rendered conversation documents include canonical group information as
  • Grupo: id, subject, description and direct-chat shared group context as Grupos compartidos con este contacto when Core provides it.

Stage Table

Step ids and handler names are declarative metadata (recipes YAML and observability tags), not a runtime dispatch layer; the runner branches explicitly per flow id. See the "Deuda tecnica" section in _docs/PIPELINE_RECIPES.md.

StepHandlerInputOutputAIModelsSystem promptUser prompt
adapter_normalizationwaha_adapterRaw WAHA events or normalized payload.FlowPayload.non/an/an/a
chat_name_unificationchat_name_unificationWAHA conversations with possible @lid/@c.us aliases.Canonical conversations with selected names and alias metadata.non/an/an/a
temporal_mediatemporal_media_fanoutMedia messages allowed by recipe policy.media_processed keyed by message id.only media sub-activitiesVideo/animated: google/gemini-3.1-flash-lite; vision: meta-llama/llama-4-scout; audio: Groq Whisper.prompts/video.md for video/animated media; prompts/vision.md for static visual media; audio has no prompt file.Complete video, locally converted animation, image bytes, or audio file.
conversation_builderconversation_builderFlowPayload, media_processed.ConversationDocument[].non/an/an/a
smoke_testsmoke_test_generateConversationDocument[].Spanish JSON health object.yesprimary google/gemini-3.5-flash, shadow google/gemini-3.1-flash-lite.prompts/smoke_test.md ## System.Rendered conversation documents.
initial_reportinitial_report_generateMonitored child JSON, generic Core context without top-level memories/guidance/age band, optional normalized tutor guidance, known age band and prior report feedback, optional user stats metadata, ConversationDocument[].Spanish report with risk taxonomy categories and report.markdown.yesprimary google/gemini-3.5-flash, shadow google/gemini-3.1-flash-lite.prompts/initial_report.md ## System.Runtime template: monitored child JSON, generic context JSON, optional dedicated tutor guidance, age-band and report-feedback sections, user stats metadata JSON and rendered conversation documents.
chat_id_summarizationchat_id_summarizationMonitored child minimal identity JSON (name), real chat_id documents for the current internal batch.Model-facing arrays chat_id_summaries[] with chat_name and chat_id_memory_updates[]; public output is keyed by existing chat_id.yesprimary google/gemini-3.5-flash, shadow google/gemini-3.1-flash-lite.prompts/chat_id_summarization.md ## System.Runtime template: monitored child minimal identity JSON (name), rendered conversation documents for the batch with matching per-chat memories.
entity_extractionruntime side-stepInitial/daily minimal identity, validated known entity catalog, and deterministic bounded conversation/message batches with message-id evidence indexes.Additive wardian-entity-updates.v1 candidates; batches validate independently and merge/deduplicate evidence.yes, flag-gated/non-fatal per batchflow primary/shadow unless entity-specific overrides are set.prompts/entity_extraction.md ## System.Dedicated evidence prompts; omitted for global or conversation-level identity abstention, never generic report context or InterWeek.
daily_summary_and_notificationsdaily_summary_and_notifications_generateMonitored child, run metadata, stats, optional tutor guidance, known age band and prior report feedback, prior report context and ConversationDocument[] with per-chat memories.daily_summary, 1..5 notifications, model-facing chat_id_memory_updates[]; public output is keyed by existing chat_id.yesprimary google/gemini-3.5-flash, shadow google/gemini-3.1-flash-lite.prompts/daily_summary_and_notifications.md ## System.Runtime template: dedicated Markdown sections including optional tutor guidance, age band and report feedback; raw side-contract fields are excluded from extra context.
interweek_reportinterweek_report_generateMonitored child, run metadata, stats, optional tutor guidance, known age band and prior report feedback, and prior report context.interweek_report.yesprimary google/gemini-3.5-flash, shadow google/gemini-3.1-flash-lite.prompts/interweek_report.md ## System.Runtime template: dedicated Markdown sections including optional tutor guidance, age band and report feedback; no conversation documents or per-chat memories in v1.
content_trust_evaluatorcontent_trust_evaluatorTyped output plus derived untrusted conversation documents, within the configured character bound.Decision, allowlisted reason codes, and boolean invariants only.optionalconfigured evaluator or flow shadow model; no shadow evaluation.prompts/content_trust_evaluator.md ## System.Canonical untrusted-data envelope; no raw media bytes or URLs are fetched.
response_builderresponse_builderFlow outputs, model outputs, shadow outputs, conversation docs.FlowRunResult.non/an/an/a

The Interweek system prompt treats an empty previous-report section as unknown history, never proof that the run is the first report. It also rejects receiving, forwarding, quoting, or commenting on a WArdian report as behavioral evidence. These are prompt constraints only and do not add request or response fields.

Prompt Execution

sequenceDiagram
  participant Runner as Recipe Runner
  participant Loader as Prompt Loader
  participant Gateway as OpenRouter JSON Gateway
  participant Langfuse as Langfuse

  Runner->>Loader: load ## System section
  Runner->>Runner: build User from normalized runtime data
  Runner->>Gateway: primary model request with strict json_schema response_format
  Gateway->>Langfuse: generation observation
  Gateway-->>Runner: primary JSON
  Runner->>Gateway: shadow model request
  Gateway->>Langfuse: generation observation
  Gateway-->>Runner: shadow JSON or non-fatal failure

Content trust corpus

inputs4testing/evals/content_trust_adversarial.v1.json is a versioned, synthetic offline corpus covering text, image OCR, audio transcript, document, link and video-derived text, with benign controls for every modality. Prompt Lab exposes it read-only at GET /dev/cp-prompt-lab/api/content-trust/corpus and shows its version/count in the Dataset Library. The Docker context explicitly packages this one synthetic fixture while continuing to exclude the rest of inputs4testing; the runtime path remains /app/inputs4testing/evals/content_trust_adversarial.v1.json. Loading it performs zero network or model calls. Running any model comparison remains an explicit Prompt Lab action and is not part of the corpus test suite.

Trial Prompt Replay

Trial prompt replay is for testing edited prompt versions against an existing trial folder. Prompt sets live under <trial>/prompt_sets/<prompt-set>/prompts/*.md with prompt_set_manifest.json. Replays live under <trial>/replays/<prompt-set>/.

Each replay snapshots the effective prompt set into <trial>/replays/<prompt-set>/00_prompts_used/. The replayed AI steps use that snapshot for actual model calls. This keeps the report, manifest, and prompt files auditable even if the editable prompt set is changed later.

Replay reuses base trial request/context/conversation-builder and media_processed artifacts. It does not re-run media processors. Daily and interweek replay stages recompute ledger state sequentially from replay outputs.

Validation

  • Prompt files must have ## System.
  • Flow prompts should also have ## User documenting runtime construction.
  • AI steps require OpenRouter structured outputs with
  • response_format.type=json_schema and json_schema.strict=true. extra_body.provider.require_parameters=true is the default fail-fast provider policy and can be disabled with OPENROUTER_REQUIRE_PARAMETERS=false; text, vision and video OpenRouter requests also set extra_body.preset=main-preset, which references @preset/main-preset without replacing the requested model. Invalid JSON or unsupported structured output routing raises and the run fails. Markdown is allowed only inside JSON string values when a prompt asks for it.

  • Video requests also set media_resolution=MEDIA_RESOLUTION_LOW by default
  • and reasoning_effort=minimal. fps is not sent to OpenRouter; it only controls local conversion of animated image media and defaults to 1.

  • Vision treats every variable user-message surface as untrusted evidence:
  • pixels/visible text, caption and recent_dialog_excerpt may be described or transcribed but never followed as instructions or allowed to change the schema.

  • OpenRouter Input tokens includes the system prompt, user prompt, rendered
  • documents and strict schema. max_tokens limita output; it does not reduce input prompt size. Previews expose prompt chars and estimated input tokens, while runtime logs usage and finish reason when OpenRouter returns them.

  • Report prompts open the JSON with a model-facing analisis_interno field
  • guided as an ordered checklist. initial_report usa 7 a 10 oraciones para planificar historia central, factores protectores, relaciones, severidad, estadisticas disponibles y acciones; daily e InterWeek conservan 4 a 6 oraciones. The response builder discards this field.

  • Tutor report guidance is fail-open and prompt-only: invalid/omitted/empty
  • objects render nothing; valid input appears once in initial/daily/InterWeek, never in chat_id_summarization or generic context. Stable system rules keep it separate from evidence, severity and memory decisions.

  • Monitored age band is fail-open and prompt-only behind
  • MONITORED_AGE_BAND_CONTEXT_ENABLED: only a valid known v1 band appears once in initial/daily/InterWeek. It never appears in chat summarization, generic context or memory updates, and may change only tone, complexity, examples and recommendations—not evidence, risk, severity, confidence, taxonomy or alerts.

  • Prior report feedback is fail-open and prompt-only behind
  • REPORT_FEEDBACK_CONTEXT_RENDERER_ENABLED: only the existing allowlisted wire shape appears in initial/daily/InterWeek. It never appears in chat summarization or memory updates, and cannot act as evidence or prove model compliance/output impact.

  • The daily prompt defines the notification kind→icon vocabulary: señal_positiva 🟢, para_observar 🟡, riesgo 🔴, cambio_relevante 🔵, sin_novedad_relevante 🟢, momento_wardian 💛.
  • Daily chat_id_memory_updates follow the same compactness limits as chat_id_summarization (reason ≤120 chars, summary ≤240, active_threads ≤2×160, relationship_context ≤180, open_questions ≤2×120); the new next_value replaces the previous blob instead of accumulating.
  • When monitored age-band personalization is active, daily memory updates come
  • from a separate chat_id_summarization request whose prompt excludes the age band. Runtime discards the memory candidate from the parent-facing daily generation and returns only the isolated conversation-backed updates.

  • Per-chat memory objects are recursively filtered before prompt rendering, so
  • DOB and exact-age aliases never cross the model boundary even when the active privacy profile is debug_plaintext.

  • initial_report requires report.markdown as a Markdown string version of
  • the parent-facing report. It is mobile-first, uses one conceptual column, and targets 900 a 1500 palabras when evidence is rich while shortening sparse inputs instead of padding them. Its ordered reading starts with Si leés una sola cosa, keeps Estadísticas duras below the essential reading, and places the 18-category Semáforo completo below the statistics. The added depth stays inside the existing report fields, sin cambiar la forma JSON.

  • Initial Estadísticas duras and usage_stats use only
  • payload.user_stats_metadata; exact v1 remains compatible while strict v2 is the current contract. Conversation document header statistics never backfill them. The shared # Estadísticas por chat (Core) prompt section renders every v2 chat deterministically, while parent-facing output may summarize at most five relevant chats. Missing metadata is explicit, and aggregate statistics cannot raise taxonomy status, create a parent attention point or influence memory by themselves.

  • daily_summary_and_notifications requires daily_summary, 1 to 5 notifications, no priority field, known chat_ids, and present, valid chat_id_memory_updates.
  • daily_summary_and_notifications must read antecedents from previous_daily_summaries, previous_notifications, previous_interweek_reports and monitored_profile in explicit context, plus per-chat memories rendered inside matching conversation documents; repeated topics need continuity language, while trend breaks need explicit contrast. daily_summary.taxonomy_status must be grounded in current-day conversations or current-day stats, not carried forward from antecedents alone.
  • daily_summary_and_notifications may use user_stats_metadata for daily summary context, but must not create a parent notification from aggregate stats alone.
  • Daily notification prose uses detalle concreto para padres: when evidence allows, notifications, daily_summary.relevant_events[] and important parts of daily_summary.summary say quien participo, cuando dentro del dia, chat/group, de que hablaron, the monitored minor's role and why it matters; the prompt asks the model to parafrasea raw chats and no inventes missing names, relations, times or topics. Etiquetas abstractas as the only description are banned ("logistica familiar", "tareas tecnicas", "vida social intensa"); every parent-facing sentence carries at least one ancla concreta (first name, chat/group or specific topic) when evidence allows, and each notification markdown must be autocontenido: the concrete data from what_happened, why_it_matters and suggested_action must appear inside the markdown itself.
  • Daily notification cardinality is derived from analisis_interno: the model enumerates the day's distinct themes with evidence and emits una notificacion por cada tema enumerado (up to 4) plus the Momento WArdian; with 2 or more enumerated themes, returning a single notification is salida invalida, and sin_novedad_relevante is only valid when the enumeration is empty.
  • Daily notification prose applies frescura y foco en lo nuevo: no copies frases, aperturas ni formulas from previous_daily_summaries, previous_notifications or previous_interweek_reports; when a topic continues, acknowledge continuity in a few words but spend the body on what today adds (a new detail, another person, a shift, a next step) instead of re-describing prior reports with synonyms; a topic with no news is mentioned briefly or dropped, openings and order vary between days, and previous context is reused only for proper names and concrete data. Anti-repetition restringe la redaccion, nunca la seleccion de temas: a continuing theme with new evidence still gets its own notification.
  • Daily notification prose has minimum verbosity: what_happened: 3 a 5 oraciones when there is enough activity, why_it_matters at least 2 sentences, suggested_action at least 1 actionable sentence, and markdown at least 4 short blocks covering what happened, quien/cuando/de que hablaron, why it matters and what the adult can do.
  • chat_id_summarization is one logical step but can run with batching interno for initial-ingestion-report. CHAT_ID_SUMMARIZATION_BATCH_SIZE defaults to 1 rendered chat document per model call and CHAT_ID_SUMMARIZATION_BATCH_MAX_PROMPT_CHARS defaults to 30000 characters measured after applying the mandatory untrusted-data JSON envelope, with documents ordered by message volume (desc) before batching. CHAT_ID_SUMMARIZATION_MAX_RETRIES defaults to 2 for single-chat calls. CHAT_ID_SUMMARIZATION_PRIMARY_MODEL / CHAT_ID_SUMMARIZATION_SHADOW_MODEL optionally override the model for this step only (default: flow models). Runtime keeps an acumulador en memoria, verifies exact chat_id sets before merge, splits invalid or mismatched multi-chat batches into single-chat retries, and can normalize an invented id only after single-chat retries when the mapping is unequivocal. The contrato publico no cambia.
  • Each model call for chat_id_summarization must include exactly the existing chat ids from that internal batch with no invented ids.
  • Each chat_id_summaries[] item must include chat_name from the visible
  • chat title (Grupo.subject, direct contact name or legible fallback).

  • Memory update decisions require top-level model-facing chat_id_memory_updates[]. The response builder normalizes that array to the public dict keyed by chat_id. Each chat update must include short_term_memory_recent_topics and long_term_memory_relationship_and_others. should_update=false requires next_value=null; should_update=true requires next_value as an object with prosa descriptiva, no arrays de tags. Memories should not copy old risk categories forward unless current evidence or a clear resolution supports the update.
  • interweek_report consumes explicit Core context and does not return memory updates in v1.
  • interweek_report must compare against antecedents when available and distinguish continuidad, mejora, empeoramiento, desaparicion de un tema and quiebre de tendencia. taxonomy_semaphore reflects the current interweek period; prior reports are only the comparison baseline. It may use optional user_stats_metadata for period-level trajectory when present, without inventing missing metrics.
  • interweek_report prose applies frescura y foco en lo nuevo: no copies frases, aperturas ni formulas from previous_interweek_reports and does not repeat the daily notifications verbatim; the interweek synthesizes trajectory instead of reordering prior text, acknowledges an unchanged pattern in a few words while spending the body on what is new in the period, varies openings and order between periods, and reuses previous context only for proper names and concrete data. Anti-repetition restringe la redaccion, nunca la seleccion: a sustained pattern with period evidence still gets coverage. Etiquetas abstractas as the only description are banned in parent-facing fields, each sentence carries an ancla concreta when evidence allows, and the interweek markdown must be autocontenido with the concrete data from key_findings, positive_signals and watch_points.

All report and memory prompts explicitly forbid deriving risk, chronology, school/night activity, escalation or silence from temporal quarantine, technical failures, ack/session state or status/channel metadata. Coverage limitations are handled deterministically outside the model.