You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The record-type schema declares required and optional field sets per record type, but validation never enforces the optional set as an allowlist. The frontmatter rule (packages/kb/src/rules/frontmatter-rule.ts) checks recordType-in-vocab, required-present, date formats, and tags-shape, but never "is this field declared?" So a note may carry any undeclared frontmatter field and pass validation: the declared field lists are advisory documentation, not an enforced contract.
This surfaced while removing the unused immutable flag (#748). Allowlist enforcement is the generic mechanism that would make a write-once record type's invariant real without a special-purpose flag: an operation that stamps a field a record type doesn't declare (e.g. updated/last-verified on an event) would be rejected for every record type, driven entirely by the existing field lists.
Context
The check belongs in (or beside) frontmatter-rule.ts. Record types declare required + optional in RecordTypeSchema (packages/kb/src/types.ts), and recordType is the implicit discriminant that is always allowed.
Existing notes carry undeclared fields today (e.g. type: howto appears on assertion fixtures). Turning this on as an error is therefore a breaking change to the current corpus. The severity (error vs warning) and whether to grandfather or migrate offending fields is the core decision this ticket must settle.
kb-add, capture-event, and kb-edit already validate frontmatter against the schema, so they inherit the check automatically once it lands.
Add an undeclared-field check that flags any frontmatter key absent from the record type's required ∪ optional ∪ {recordType}. Settle severity and rollout as part of the work: a warning first (surfaces drift without breaking existing writes), tightening to error once the corpus is clean, is the safer sequence; an error with a one-time migration that either declares or strips offending fields is the alternative. The finding names both the offending field and the record type.
Acceptance criteria
Must have
Validation produces a finding for a frontmatter field not declared in its record type's required/optional sets, with recordType always permitted.
The finding names both the offending field and the record type.
The severity-and-rollout decision (error vs warning; grandfathering of existing undeclared fields) is settled and reflected in the implementation.
New behavior is covered by tests: a record carrying an undeclared field, and a clean record that stays finding-free.
Should have
packages/kb/README.md documents that a record type's optional set is an enforced allowlist, not merely advisory.
Problem
The record-type schema declares
requiredandoptionalfield sets per record type, but validation never enforces the optional set as an allowlist. Thefrontmatterrule (packages/kb/src/rules/frontmatter-rule.ts) checks recordType-in-vocab, required-present, date formats, and tags-shape, but never "is this field declared?" So a note may carry any undeclared frontmatter field and pass validation: the declared field lists are advisory documentation, not an enforced contract.This surfaced while removing the unused
immutableflag (#748). Allowlist enforcement is the generic mechanism that would make a write-once record type's invariant real without a special-purpose flag: an operation that stamps a field a record type doesn't declare (e.g.updated/last-verifiedon anevent) would be rejected for every record type, driven entirely by the existing field lists.Context
frontmatter-rule.ts. Record types declarerequired+optionalinRecordTypeSchema(packages/kb/src/types.ts), andrecordTypeis the implicit discriminant that is always allowed.type: howtoappears on assertion fixtures). Turning this on as an error is therefore a breaking change to the current corpus. The severity (error vs warning) and whether to grandfather or migrate offending fields is the core decision this ticket must settle.immutableflag in favor of this generic mechanism) and Validate a store schema against the generic kb-skill contract #746.Proposed solution
Add an undeclared-field check that flags any frontmatter key absent from the record type's
required ∪ optional ∪ {recordType}. Settle severity and rollout as part of the work: awarningfirst (surfaces drift without breaking existing writes), tightening toerroronce the corpus is clean, is the safer sequence; anerrorwith a one-time migration that either declares or strips offending fields is the alternative. The finding names both the offending field and the record type.Acceptance criteria
Must have
recordTypealways permitted.Should have
packages/kb/README.mddocuments that a record type's optional set is an enforced allowlist, not merely advisory.Dependencies