CRUMB a card from devarno-cloud

kick Variable Resolution Pipeline

eva intermediate 5 min read

ELI5

kick builds the prompt like a stack of transparencies: first the case file slides on, then your --var flags overlay it, then guard.sh slaps a final correction sticker on top. The last sticker wins, then awk swaps every {{key}} for its value.

Technical Deep Dive

Precedence (lowest → highest, bin/kick:150-182)

  1. examples/<case>/inputs.yml (when --case is passed)
  2. --var key=value and --var-file path (parsed left-to-right)
  3. guard.sh stdout KEY=VALUE lines

Each layer appends to the same VARS array; later additions for the same key shadow earlier ones because the awk renderer at bin/kick:32-43 rewrites the document once per pair, in order.

Sequence

sequenceDiagram
participant U as user shell
participant K as bin/kick
participant Y as inputs.yml
participant G as guard.sh
participant A as awk renderer
participant C as claude CLI
U->>K: kick id --case happy --var x=1 --send
K->>Y: parse_yaml_flat (case)
Y-->>K: case_vars
K->>K: VARS = case_vars + cli_vars
K->>G: exec with EVA_VAR_* exported
G-->>K: stdout KEY=VALUE
K->>K: VARS += guard_lines
K->>A: render(prompt.xml, VARS)
A-->>K: rendered text
K->>C: pipe rendered → claude
C-->>K: stdout (tee'd to TMP_OUT)

Substitution Mechanics

The renderer (bin/kick:32-43) iterates the VARS array, runs awk per pair, and replaces literal {{key}} occurrences with the value verbatim — no escaping, no nested templates, no recursion. A pair whose key is file_contents is what --var-file <path> produces (bin/kick:139), so prompt.xml should reference it as {{file_contents}}.

Env Surface for Hooks

flowchart LR
V["VARS array"] -->|export| E["EVA_VAR_<key>"]
V --> P["EVA_PROMPT_ID"]
V --> CN["EVA_CASE"]
E --> G["guard.sh"]
E --> Vs["verify.sh"]

Key Terms

  • VARS — bash array of KEY=VALUE strings, populated by case → CLI → guard, consumed by render.
  • --var-file — shorthand that injects the file’s contents as the variable named file_contents.
  • EVA_VAR_* — every var is exported under this prefix so guard/verify scripts can read it without re-parsing argv.

Q&A

Q: What is the precedence order between —case, —var, and guard.sh output? A: Case lowest, --var middle, guard.sh stdout highest. Implemented by append-order in the VARS array (bin/kick:150-182); the awk renderer applies pairs in order so later same-key pairs overwrite earlier substitutions on the document.

Q: Which env-var prefix exposes vars to guard.sh and verify.sh? A: EVA_VAR_<key>, plus EVA_PROMPT_ID and EVA_CASE (bin/kick:162-167).

Q: Which placeholder syntax does kick substitute? A: Literal {{key}} — exact double-brace, single token, no whitespace, no filters. The renderer is a literal awk index() swap.

Examples

kick refactor --var-file src/foo.py --var 'constraints=no new deps'
# VARS = [file_contents=<file>, constraints=no new deps]
# prompt.xml references {{file_contents}} and {{constraints}}

neighbors on the map