Goldie Blocks

Deterministic Write-Safety For Hosted App Flow Conditions

Why the hosted app gates writeModule with evaluateFlowCondition while local no-runtime skills remain agent-interpreted

Decision

The hosted app uses a narrow deterministic helper, evaluateFlowCondition, to gate writeModule writes for modules with module.flow.condition.

This is not a full interview-flow resolver. The agent still decides turn order, explanation, prerequisite-asking, tone, and whether the user's message is asking to fill data or only to clarify the selected document, module, or block. The deterministic helper answers one question only: given the current saved document data envelope, is the target module applicable for a writeModule operation?

Plan: Agent Interview Progress.

Context

The schema already defines flow.condition on modules, fields, and blocks. Conditions are authored as DSL strings and may reference data across modules, including shared facts on data_pool:

data_pool.data.has_minor_children = TRUE()

The hosted app is multi-turn and persistent. If the user selects a conditional module or block and asks the agent to fill it, an LLM-only interpretation of that condition is not safe enough for the write path. For example, a guardianship module should not be saved until saved data establishes that the will maker has minor children.

The target local model remains no-runtime execution: the end user can run a document skill with Claude Code, Codex, or another local agent without Node, Python, Convex, or an executable resolver. In that venue, the agent reads authored module files, dataSchema, flow.condition, module guidance, and final-review guidance directly.

Helper Contract

evaluateFlowCondition is pure and unit-testable:

type DocumentDataEnvelope = Record<
  string,
  {
    data: Record<string, unknown>;
    interviewStatus: "not_started" | "in_progress" | "complete" | "skipped";
  }
>;

evaluateFlowCondition(
  condition: string,
  envelope: DocumentDataEnvelope,
): { applicable: boolean | "unknown"; missingPrereqs: string[] };

The envelope is keyed by module handle so cross-module paths such as data_pool.data.has_minor_children resolve without guessing. Unknown tokens, malformed expressions, and unresolved paths return applicable: "unknown" with missingPrereqs. They never return true.

Initial evaluator scope covers the authored grammar currently needed by app write-safety: equality, boolean literals, dotted paths, and TRUE() / FALSE().

Runtime Use

The app evaluates write-safety in two places:

  1. getInterviewContext({ selection }) resolves activeModule, selected document/module/block focus, nextActionableModule, and ordered module summaries. For a selected module or selected block's parent module, it injects both raw flow.condition and { applicable, missingPrereqs } into the agent turn.
  2. The writeModule tool handler rebuilds the fresh document data envelope and re-runs evaluateFlowCondition at write time for the explicit targetModuleHandle.

The second gate is authoritative. Prompt instructions tell the agent not to write when applicable !== true, but prompt compliance is not the safety boundary. The writeModule handler rejects unsafe writes with a typed write-blocked result the agent can explain to the user.

What Stays Agent-Led

The helper does not decide:

  • which module the agent should ask about next;
  • whether the user's message is a question, a fill request, a change request, or a skip request;
  • how to explain the raw condition in plain language;
  • what prerequisite question to ask;
  • whether a clearly non-applicable target module should be skipped through writeModule.

The agent reasons over scalar document order, raw flow.condition, saved data, module status, selected focus, and nextActionableModule. The evaluator only removes non-determinism from hosted persistence.

What This Does Not Change

  • No new schema field is introduced for flow resolution.
  • No agentCursor, flowMode, completedBlocks, or document-level interview cursor is introduced.
  • Field-level and block-level condition evaluation remain separate concerns unless a future plan expands deterministic evaluation beyond selected-module write-safety.
  • Local no-runtime skill behavior remains agent-interpreted. The authored DSL and module guidance are the source of truth locally; the hosted app adds a persistence-floor guard where Convex is available.

Revisit When

  • App writes need to evaluate the broader expression grammar beyond the initial equality/boolean/path subset.
  • Field-level conditions need deterministic server-side gating before asking or saving individual fields.
  • The authoring compiler and runtime evaluator need to share one parser package so author-time validation and runtime evaluation cannot drift.

On this page