Skip to content
🚧 Early alpha — building the foundation. See the roadmap →

A missing field should not fail an import, and a filter you typed should be used

Created Updated

Two things went wrong on the ordinary import wizard — the classic column-table path, not the beta live mapping workbench.

What was wrongWhat it does now
A property mapped from a field that some records simply do not have made those records fail to renderThe property is left off that note. The note is written
The row filter you typed was carried into generation only in workbench mode, and silently dropped otherwiseThe filter is carried on both paths, as a per-run instruction

Both are the same failure shape this project keeps ruling out: acting confidently on an incomplete picture instead of saying what it actually knows. A dropped filter produced more notes than you asked for and said nothing. A missing field produced an error naming a property, not a cause you could act on.

Neither change loosens anything strict. File names, identity, folder structure, body content, links, and hand-written canonical recipes all behave exactly as before, including still failing loudly when a required value is missing.


Say you import a JSON export of technique records. Most records have a revoked field; some do not. You type revoked=false in Keep only matching records on the record-selection screen.

Here is a single record that has no revoked field at all, and you mapped revoked as a note property:

BeforeNow
Your typed filterdropped on the ordinary path; every record was importedapplied during generation; only matching records become notes
A record missing revokedthe row failed to render, naming the propertythe note is written without a revoked property
A record with revoked: false—keeps the value. False and zero are values, not absence
A record missing the field you mapped as the file nameerrorstill an error. A selected row is never quietly skipped to make an import succeed
A hand-written canonical recipe with a required propertystrict errorstill a strict error. Only wizard-generated property templates changed

Why false and zero matter. Treating “absent” and “false” as the same thing is how a compliance note quietly loses the fact that a control was explicitly marked not applicable. Omission is for absence — a key that is not there, a null, an empty value — and nothing else. The existing parser’s value conversions are unchanged, so this promises omission behavior, not raw type fidelity.

One honest gap you will see in the interface. The Step 3 preview still samples your source unfiltered, so a preview row may not survive your filter. Rather than rebuild preview, the screen now says so plainly when a filter is set: Preview uses unfiltered source samples. Your filter is applied during generation. The authoritative filtering happens in generation, where an unknown field name or a filter that matches nothing reports an actionable error instead of quietly writing the wrong set of notes.


An import that produces too many notes is worse than one that fails. A failed import is obvious and you fix it. A silently unfiltered import lands hundreds of notes you did not want into a vault where import-set ownership now has to account for every one of them, and you find out weeks later.

The missing-field failure has a different cost: it makes heterogeneous real sources unimportable. Published exports are not rectangular. STIX, OSCAL and vendor JSON routinely carry fields on some objects and not others, and demanding every mapped field on every record means the tool works on tidy spreadsheets and refuses the actual published artifacts it exists to ingest.

The limit worth saying in the same breath: an absent property means the source did not supply one, not that the answer is no. That is the same reading discipline as an absent source fingerprint, and the same one the closure-cache fix established: an incomplete answer must not be dressed as a complete one.

4. What was accepted, rejected and deferred

Section titled “4. What was accepted, rejected and deferred”
Decision
AcceptedProperty templates synthesized by the legacy shim are optional by default. Absent, null and empty values are omitted; the renderer’s existing optional and omission semantics do the work, unweakened globally
AcceptedAn explicit omitIfEmpty: false on a mapping keeps that property strict. The escape hatch stays available per property
AcceptedFalse and zero are preserved under the existing parser and renderer conversions. Only absence omits
AcceptedOwnership of managed properties stays declaration-based. An omitted declared property still removes a stale managed value on a permitted refresh, and user_preserve still wins. Merge authority and write decisions are untouched
AcceptedThe wizard’s filter becomes an already-translated per-run predicate passed through the generation boundary, applied immutably to a copy of the resolved recipe before hashing and source-stage preparation, so joins, transforms and fields the engine does not interpret survive
AcceptedA nonblank filter replaces an existing declared predicate, matching workbench behavior. A blank field leaves a canonical predicate intact. No implicit combining of the two
RejectedTurning ordinary imports into recipe-override imports to carry the filter. That branch decides provenance recipe identity; changing it would move recipe ids, minted import-set identity, ontology pinning, refresh selection and orphan ownership for every classic import. The run predicate is applied after that choice, so none of it moves
RejectedAny global relaxation of the renderer, and any wiring of the currently inactive global empty-handling setting or unrelated legacy formatting flags
RejectedLoosening file names, identity derivation, folder or hierarchy inputs, body content, links, or canonical recipe templates. A selected row missing a required value is still an error
RejectedRebuilding preview to filter its samples, adding a predicted-count promise, or claiming preview and output parity. Honest copy instead
DeferredEverything the failed runs would still need to prove: the JSON negative control, and any statement about the CSV or XLSX filter controls, which have no evidence of their own

5. The one consequence worth stating plainly

Section titled “5. The one consequence worth stating plainly”

Ordinary imports will produce a different effective recipe hash than they did before, because the property templates the shim generates are literally different text.

This is correct and expected. It is what that hash is for: it answers did output-affecting behavior change?, and it did. What did not happen is any reinterpretation — no hash algorithm changed, no canonical recipe digest changed meaning, no source-byte digest is involved, and no stored identity was repurposed. Nothing is rewritten or backfilled to chase the new value; existing skip, no-op and preservation policies stay authoritative, exactly as they did for the recipe-document digest work.

  • It does not close the JSON acceptance case that blocked the XLSX slice. That remains open until a real run reaches it.
  • It does not establish that the CSV or XLSX filter controls behave correctly. The demonstrated path is the ordinary wizard’s record filter; do not generalize it without its own evidence.
  • It does not change the canonical source_ref contract, any schema, spec/ in any form, or the v0.1 schema spec.
  • It does not filter preview, promise a row count before generation, or add any control to the preview screen.
  • It does not add a setting, a storage surface, a parser, a dependency, or any replay behavior.
  • It does not close v0.1.7, clear a quarantine, or make any performance, bundle-size, scale or mobile claim.
  • It does not erase the two earlier failed runs. They happened, their evidence is archived, and this repair is a response to them rather than a replacement for them.