Codebook authoring
The complete reference for building a pack. A codebook is seven files in a folder, all yours: four data files the engine enforces, and three prompt files the model reads in your words. Start by copying the shipped example (vision/example_codebook) and validate as you go with neuraframe codebook check <dir>.
The pack at a glance
| File | Required | Role |
|---|---|---|
manifest.json | yes | Your domain's tables: context, grading columns, band sets, legality, readings. |
registry.json | yes | Your codes, one entry each: family, record mode, grading. |
rules.md | no | Domain knowledge that rides on EVERY model call: discriminators, confusion pairs, grading discipline. |
detect.md | no | Task framing for wide-net detection calls, verbatim. |
confirm.md | no | Task framing for dossier confirmation calls, verbatim. |
notes.md | no | Standing operator notes (site or standard guidance), appended to confirmation calls. |
excluded.json | no | Registry codes out of scope for this deployment. |
manifest.json, key by key
| Key | What it declares |
|---|---|
name, version, description | Identity. Version your pack like software: the run output records which pack coded it. |
context_fields | The facts a run can be told ([{"key", "values"?, "note"?, "assumed_note"?}]). Anything not supplied falls back to defaults and is listed on every record in assumed_defaults, with your assumed_note explaining the risk. Defaulting silently is the failure this prevents. |
defaults | {field: value}. The safe assumption per field, used only when the run does not supply one. |
grading_field, grading_columns | Which context field selects the grading column, and the legal column names. A registry entry's grading object is keyed by these columns. |
band_sets | Named percentage-band ladders, e.g. {"A": ["<=10","10-20","20-30",">30"]}. A code references one by name; different codes can use different ladders, and the model is only ever offered the bands, never asked for a number. |
legality | {field: {value_substring: [families]}}: a context value under which whole families cannot occur. Unknown context means the gate does not run (confidence reduced, never blocked). |
conditional_legality | [{"codes"|"families", "allow_when": {field: [values]}, "message"}]: codes only legal under certain context. Outside it they are INVALID records and the engine blocks them regardless of the model's confidence. |
presence_gated_families | {family: context_field}: a family legal only when a feature is recorded present (a lining, an attachment). |
remap_families | {prefix: family}: where a loose model code routes when it is not in the book, so the finding is kept with candidates instead of dropped. |
surfaces, surface_rules | Your surface vocabulary and the contradictions to catch: [{"prefix"|"family", "require"|"forbid": [surfaces], "message"}]. |
readings | Numeric scene values some codes carry, read against a datum your standard defines: {field: {"codes"|"families", "ask", "min"?, "max"?, "unit"?}}. The ask text is put to the model in your words and must name the datum. Values outside the range are ignored and flagged, never accepted. Readings drive STATE-mode change detection (state_min_reading_delta). |
regions | Named places around the frame mapped to clock hours: {"<region>": {"clocks": [..]} | {"exclude_clocks": [..]}}. A registry entry's expected_region is checked against the OBSERVED clock (which comes from tracking, not the model): a low-lying code observed overhead is flagged for recoding, never dropped. |
state_at_start | STATE codes that are properties of the whole survey (a level, a size) and exist from the very start. The first record is anchored to the segment start however late the camera first read it, later records are changes only, and a declared code that never appears at all is surfaced for review rather than silently absent. |
hosts | What satisfies each host name a registry entry can require (requires_host). Forms: {"codes"|"families": [..]} (a coded record of these near in time and clock), {"context": "field"} (a survey fact), {"external": true} (only an external record can satisfy it; the engine routes such claims to review), {"visual": true} (the confirmation call itself judges it). A code asserting a host that is not independently present goes to review: a hallucinated feature rarely brings a hallucinated host along with it. |
registry.json, entry by entry
{"code": "WG", "title": "Growth, general", "family": "growth",
"record_mode": "PROMOTABLE", one of SINGULAR | STATE | REPEATED | EXTENSIVE | PROMOTABLE
"graded": true, "axis": "upkeep", axis splits the ratings (e.g. condition vs upkeep)
"band_set": "A", which band ladder this code resolves on
"grading": {"ornamental": {"type": "threshold", "rules": [
{"band": "<=10", "grade": 2}, ...]},
"vegetable": {"type": "fixed", "grade": 4}},
"expected_region": "ground", optional: checked against the observed clock via manifest regions
"evidence_primary": "L", optional: L/luma (structure, default) | C/chroma | T/temporal.
T codes grade as a range until rate metrics resolve them; L codes
with chroma-only support are flagged suspect (unless their
declared evidence_secondary is chroma)
"requires_host": ["stake"], optional: hosts this code asserts, checked against the manifest's
hosts map before a record is written
"min_extent": { optional: an EXTENT FLOOR for codes describing things with
"min_sightings": 6, physical extent (an installed repair covers a section). Both
"min_clock_span_hours": 3}, terms must hold or the finding routes to review - appearance
alone can never carry an extent-gated code. Units the engine
has: sightings and wrap-aware clock span. Policy, tunable.
"anchored": true, optional: SURVEY STRUCTURE, emitted from the job header at the
boundaries (manifest anchored_records says which context field
names the type and what the honest unknown default is). Never
the output of a confirmation call: a tracked claim of an
anchored code routes to review.
"position_constraint": optional: "survey_boundary" - the code may only exist at the
"survey_boundary", start or end of the survey; mid-reach is a contradiction and
routes to review (stands down when the length is unknown).
"clock_applicable": false, optional: the code occupies the FULL view (you pass through
it), so a candidate localised to one clock position is a wall
feature by construction; genuine full-view records have their
meaningless clock stripped.
"residual_code": true, optional: marks a LAST-RESORT bucket (an "Other", a vague
catch-all). Residuals are structurally last: the model must
reject the specific alternatives explicitly to answer one,
downgrades from specific proposals are logged, and broad-support
overrides route to review. Under-call leaves no artifact, so it
is gated, not advised against.
"state_trigger": "survey_start", STATE codes only: survey_start (the record exists from frame one,
anchored at t=0, emitted even if never sighted, value unknown when
illegible) | on_change (exists ONLY at a transition; absence is
the normal output - a change with an unknown value is a claim
that something changed with no idea what). Authoritative over the
manifest's state_at_start list when declared.
"fields": {"cont": "optional"}} cont: "-" forbids a run for this code
Grading types: fixed (one grade for the column) and threshold (a grade per band, in ladder order; the rule count must match the band set, and check verifies it). A graded code with no column supplied resolves to a range across columns; a threshold code with no band resolves to a range across its ladder. The engine never invents the missing information.
The three prompt files
Model calls are assembled in three layers, and you own the first two:
| Layer | Where it lives | Notes |
|---|---|---|
| Task framing | detect.md / confirm.md | Wide-net coverage wording for detection; decisive single-code wording for confirmation. When present, used verbatim in place of the built-in framing. Keep them separate on purpose: mixing "include everything" with "be decisive" degrades both passes. |
| Domain knowledge | rules.md (+ notes.md) | Rides on every call: your decision spine, the confusions your footage produces, your grading discipline. notes.md is standing site guidance appended to confirmations; a per-job notes_file in the run config appends after it. |
| Answer contract | the engine, always | The JSON shape the model must return is appended by NeuraFrame™ and cannot be overridden, so no amount of prompt tuning can break parsing. |
The authoring loop
# 1. copy the example and replace its content with your standard cp -r /opt/neuraframe-studio/current/vision/example_codebook my-pack # 2. validate after every change: every problem is listed, nothing silently ignored neuraframe codebook check my-pack # 3. prove the whole spine free, then live on a SHORT clip neuraframe codebook use my-pack neuraframe codebook run --input clip.mp4 --out coded --stub neuraframe codebook run --input clip.mp4 --out coded --end-ms 30000 # 4. read coded/report.md against the dossiers, then tune
Tuning lives in three places, in order of power: the pack's prompt files (what the model is told), the run configuration, and the registry itself (what exists and how it grades). The configuration has four blocks: run (sampling and tracking; also CLI flags, listed by codebook run --help. sample_fps is the sampling rate and 0 means every frame), coder (endpoint, dossier, location; views_per_dossier sets how many frames of one finding ride in a single confirmation call), records (projection and verification thresholds), and context. The run output records the pack version and every knob in force, so two runs are always comparable.
What the engine will do with your pack, regardless
- Codes are enum-locked to your registry. A loose code remaps or routes to review with candidates; it is never silently accepted and never dropped.
- Every graded attribute is a lookup from your tables. The model supplies a band or a reading at most; an unknown resolves to a range.
- Your legality rules are blocking validators, not suggestions.
- A code that asserts a host structure is only certified when the host is independently present: a nearby coded record, a survey fact, or an external record. No host, no certified code.
- Locations come from tracked geometry, not from the model.
- Every record carries its assumptions, its flags, and its evidence trail (the dossiers are saved).
Prove the mechanisms on your own install any time: python3 vision/nf_codebook_board.py.