Skip to content

feat(open-knowledge): validate frontmatter against JSON Schema (#2919)#817

Merged
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync
Jul 25, 2026
Merged

feat(open-knowledge): validate frontmatter against JSON Schema (#2919)#817
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync

Conversation

@inkeep-oss-sync

Copy link
Copy Markdown
Contributor

Copybara-translated 1 Inkeep OSS change. Rebase-merge this PR so the prepared commit lands directly on public main.

* docs(open-knowledge): frontmatter data contract spec, evidence, and story catalog

Carried over from theming-as-plugin so the frontmatter validation work
can be kicked off from main later. Brings the durable data contract
spec with its worldmodel evidence, the doc-patterns / property-schemas /
linting story catalog it serves, and the lint-plugins ecosystem-standard
seam spec it is grounded in. Implementation, themes, markdownlint, and
the plugin research reports stay behind on the old branch on purpose;
four report links inside the seam spec dangle as a result.

* docs(open-knowledge): carry the Vale/remark-lint engine survey report

The frontmatter data contract's worldmodel evidence cites this report
twice as the internal engine survey behind the ajv / JSON Schema
decision (buy the engine, keep the thin data layer; parse is not
validate). It was the only branch-only report the contract leans on;
the other cited reports already live on main. Its link to
zed-vscode-rule-management-architecture dangles on the source branch
as well, so it is carried unchanged.

* docs(open-knowledge): flatten frontmatter config key to contentRules.frontmatter

The plugins nesting level between contentRules and frontmatter carried
no data or behavior of its own and is being removed in the linter work;
the contract example now shows the flattened path and notes that
branch-era evidence still shows the old nesting.

* docs(open-knowledge): worldmodel groundings — schema-rules file location + agent read-time schema discovery

Two fresh /worldmodel runs on the frontmatter data contract, per review
feedback. First: whether schema rules belong inside .ok/config.yml or
in separate files, re-derived from prior art without anchoring on the
spec's pinned shape (web survey of four persistence shapes, current-tree
and branch code, internal reports, LANDSCAPE seed). Second: how agents
interact with glob-scoped schemas and what conventions exist for making
a doc's schema known at read time before a write (harness-resolved
globs, fetch-schema-before-write, validate-repair loops, OK's current
MCP read/write surfaces). Observational only; divergences flagged,
including spec-glob vs branch-regex appliesTo and the not-yet-implemented
write-advisory ride-along.

* docs(open-knowledge): reframe branch-code divergences as provenance notes

The theming-as-plugin implementation is reference-only and will not
merge; the rebuild starts from origin/main. The glob appliesTo and
flattened config nesting decisions in the contract are normative, so
the evidence docs now record the old code's regex matching and plugins
nesting as provenance, not open questions.

* docs(open-knowledge): ground the two-file-split question (rules.yml vs contentRules block)

The file-location worldmodel answered where schema content lives but
not the sharper question the feedback asked: is there prior art for a
dedicated rules file next to the main config within one tool. Adds a
re-probe: both currents are mainstream (markdownlint-cli2, golangci,
Claude/Cursor rules dirs split; ESLint flat, Biome, Deno, Cargo lints
consolidate), with the documented predictor being portability to other
consumers, cascade scope, and mechanism class rather than fashion.

* docs(open-knowledge): ratify D7 (rules stay in config.yml) + D8 (templates-pattern agent surfaces)

Folds the two 2026-07-14 grounding outcomes into the contract: a
decision entry confirming the contentRules block belongs in config.yml
rather than a dedicated rules.yml (the prior-art predictors for a
separate file do not hold), and a new serving-surfaces section pinning
how agents learn applicable schemas — OK resolves appliesTo as the
harness, read enrichment advertises schemas the way templates_available
does today, write advisories cover the new-file gap, lint stays the
on-demand audit.

* docs(open-knowledge): cold-reader audit findings for the frontmatter contract doc set

17 findings (4 high, 7 medium, 6 low) from a fresh-context audit
running coherence lenses and factual verification over the SPEC, its
evidence files, the story catalog, and the seam spec. Headliners: the
example schemas' https draft-07 $schema URI is unknown to ajv and
would schemaError under the contract's own D2 rule; the branch DOES
already ship the frontmatter write-advisory (computeLintViolations,
cap 10) contradicting the SPEC's greenfield claim; the schema file
path base is stated as both project-root-relative and content-dir
bounded; and the primary evidence snapshot still presents regex
appliesTo as decided. Findings only, nothing fixed.

* docs(open-knowledge): greenfield the contract; fix $schema URIs; stamp stale evidence

Audit findings 1, 2, and 4. Examples now carry the canonical
http://json-schema.org/draft-07/schema# URI (ajv rejects the https
form) and D2 pins the accepted alias set. The spec no longer grounds
itself in the discarded theming-as-plugin prototype: the branch-relative
deltas section is gone (all five items were already ratified as D1, D2,
D4, D6), the sign-off ratifies a greenfield implementation off main,
and the prototype survives only as provenance in the evidence files.
The agent-discovery evidence gets an erratum (the prototype DID ride
frontmatter violations on write advisories via computeLintViolations,
capped at 10), and the 2026-07-13 snapshot gets a provenance stamp
marking regex-appliesTo and the plugins nesting as superseded.

* docs(open-knowledge): fix path base to project root; real glob dialect name; seam-spec supersession notes

Audit findings 3 (option i: file resolves from and is bounded by the
project root, so .ok/schemas/ works under any content.dir), 5 (the
dialect is globby/picomatch set semantics, not gitignore re-inclusion),
and 11 (the seam spec now marks its regex appliesTo and forbid keys as
superseded by data-contract D1/D6).

* docs(open-knowledge): pin dedup key, config-channel errors (D9), defaults + implicit-** rule

Audit findings 7, 8, 9, per review. Dedup identity is the resolved
schema-file path (byte-identical distinct files deliberately validate
twice). New D9: schemaError and invalid-glob problems surface on the
config channel (settings panel, top-level warnings on lint responses,
CLI stderr), never as per-doc diagnostics; per-doc anchoring gains the
line-1 fallback for root-level errors and fence-less docs. The slice
pins enabled: true as default with an absent block meaning inert
enabled-with-zero-schemas, and degenerate appliesTo sets get one rule:
no positive glob means an implicit **, so negation-only lists carve
out rather than match nothing.

* docs(open-knowledge): settle the NG1 [NEVER] reading (D10) + pin the write-advisory cap

Audit finding 10, both halves. New D10 settles the nested-frontmatter
spec's NG1 tag by intent: it forbids blocking enforcement, and this
contract is advisory-only on every surface — violations warn, never
gate a write; making them gate is a posture-breaking change. NG1 itself
gains a cross-pointer recording the settled reading. The agent
write-advisory pins the cap: at most 10 violations per write response,
full set on lint. assessment.md's discharged claim is now true.

* docs(open-knowledge): sweep the five low-severity audit findings

Verdict ownership moved to the spec (the groundings stay observational),
D8's harness-resolves-globs headline carries its qualification, the
schemas comment no longer implies entry-order precedence, the
no-doc-path jargon is glossed, the worked example shows the actual
offending value, the 'is exactly JSON Schema' quote is reattributed to
the seam spec, and the repair-loop percentages now cite their study.

* docs(open-knowledge): present the spec as the proposed contract, not its changelog

Strips the internal dialog: no feedback-round narration, no
earlier-draft blockquote, no discarded-prototype asides, no
revisited/reaffirmed parentheticals. Grounding collapses to one
statement pointing at the evidence worldmodels, and the revision
history lives in git and the evidence files.

* docs(open-knowledge): cite agents manage-ui JsonSchemaEditor as GUI prior art

* docs(open-knowledge): ground the frontmatter contract against the shipped plugin PR

PR #2433 landed the substrate on main, so re-ground the doc set against
shipped code rather than the worktree prototype:

- SPEC header: the registry, LSP diagnostics, async lint(), advisory
  channel, MCP lint + ok lint, and Plugins settings shipped; only the
  frontmatter artifacts remain greenfield.
- Advisory asymmetry claim corrected (P6, D5, the derived-behavior note):
  the shipped channel carries every enabled lint source, markdownlint
  included, capped at 10; the data-vs-style distinction now lives in
  per-plugin enablement.
- D9 aligned to the shipped warnings[] surfaces (audit response, MCP
  structured payload, ok lint report lines).
- Flagged the enabled-default tension for approval: shipped registry
  convention is default-OFF opt-in; the contract proposes default-on.
- assessment.md + agent-schema-discovery evidence: post-ship corrections
  (advisory widened not frontmatter-scoped; rules.yml/errorRuleIds
  retired; cli2 cascade shipped).

* docs(open-knowledge): frontmatter plugin defaults off, matching the registry convention

Resolves the flagged enabled-default question: no default-on carve-out.
The frontmatter plugin is enabled the same way every other lint plugin
is (Settings -> Plugins, contentRules.frontmatter.enabled, default
false); an absent block means off.

* [US-001] core appliesTo glob matcher (picomatch, set semantics, invalid-pattern reporting)

* [US-002] core frontmatter validation engine (ajv draft-07, key-line anchoring)

* [US-003] register the frontmatter lint plugin (registry, slices, config leaf)

* [US-004] server frontmatter schema-file resolution and injection

* [US-005] frontmatter results + config warnings across the lint surfaces

* [US-006] ok lint validates frontmatter headless

* [US-007] MCP read-time schema advertisement (exec enrichment)

* [US-008] settings panel: frontmatter schema mappings editor

* [US-009] non-destructive schema-file write path (applyFieldConstraint + endpoint)

* [US-010] settings GUI: per-field schema editor

* [US-011] property panel renders schema-driven enum selects

* [US-012] changeset, knip and lint sweep for the frontmatter plugin

* feat(open-knowledge): frontmatter schema file picker + agent-surface tests

Add a pick-or-create schema-file affordance to the frontmatter mapping
editor: a folder button that browses existing .ok/schemas/*.json and a
file-plus button that scaffolds a new draft-07 schema by name. Backed by a
new GET /api/lint/frontmatter-schemas listing endpoint and a create-empty
branch on the existing schema-write endpoint (a field-less request scaffolds
the skeleton, idempotent, escape-guarded).

core: FrontmatterSchemasListSuccessSchema; the write-request schema now
accepts a field-less create-empty request (field/constraint refined as a
both-or-neither pair).

server: listProjectSchemaFiles + handleFrontmatterSchemasList (GET, read
only); createEmptyFrontmatterSchemaFile reusing the existing escape-guard
and atomic-write path; the write handler branches edit vs create-empty.

app: FrontmatterSchemaFilePicker (Command-in-Popover, two peer buttons)
wired into the mapping draft and existing rows; list/create client helpers
and a live schema-files hook on the lint-config client.

tests: HTTP integration for the write (per-field and create-empty) and list
endpoints against a real server; an exec composition test proving
schemas_applicable read-time enrichment flows config to the agent response;
unit coverage for create-empty and listing; picker DOM tests. Registers the
new GET handler as read-only in the attribution + conflict-gate meta-tests.

* feat(open-knowledge): offer schema-file deletion when removing a tool-managed mapping

Removing a frontmatter mapping whose schema file lives in .ok/schemas
(created in the tool) now asks whether to also delete the file. Files
mapped from elsewhere in the project only ever lose the mapping.

Server: delete branch on the schema write endpoint, scoped to flat
.ok/schemas paths, idempotent, unlinks the directory entry so a planted
symlink cannot reach outside. New CC1 lint-config derived-view channel
signals when the persisted project config or a schema file changes on
disk, closing the stale-problems race with the config persistence
debounce. Schema create/delete also signals the files channel so
show-OK trees update without a reload.

App: confirm dialog on the mapping row, delete client helper, and the
problems banner now hides entries for files no live mapping references
so the stale compose never flashes.

* feat(open-knowledge): schema field delete, rename, description, enum type

Close the parity gaps with the agents-manage-ui schema builder while
keeping the non-destructive merge posture: every operation is a surgical
edit and unmodeled keywords survive verbatim.

Core: removeSchemaField and renameSchemaField (rename carries the full
property object, required membership, and key position; refuses missing
source or existing target), description joins the modeled constraint
keywords, and the write request schema gains removeField and renameTo
shapes with mutual-exclusion refinements.

Server: remove and rename wrappers over a shared guarded transform spine
(existing file required, escape-guarded, atomic write); handler branches
on the new shapes.

App: field rows get an editable name (commit-on-blur rename), a remove
button, and a description input; the type select adds an enum
pseudo-type for discoverability that maps to the standard enum
constraint on disk.

* feat(open-knowledge): Source/Fields toggle for opened schema files

Opening a tool-managed frontmatter schema (.ok/schemas/*.json) in the
editor now dispatches to a dedicated SchemaConfigEditor with the same
segmented Source/Fields toggle the markdownlint config editor ships:
Source is the raw read-only file, Fields mounts the per-field schema
editor from Settings directly over the file. Fields is gated to files a
live frontmatter mapping references (the field editor reads the resolved
schema from the effective config); unmapped schemas keep Source with an
explanatory disabled segment. Shares the lint-config view-mode
preference so one choice governs both config surfaces.

* feat(open-knowledge): nested schema field editing via parentPath

Address fields inside object-typed properties end to end: the core edit
operations (constraint merge, remove, rename) take a parentPath of
object-property segments (an array on the wire, not a dotted string,
since frontmatter keys may contain dots), navigating with
create-on-write for adds and typed refusals for missing or non-object
segments. Required membership toggles on the addressed parent, matching
JSON Schema's per-object required semantics.

The field editor recurses into object rows (bounded depth) with an
add-field affordance per level, and every row operation carries its
path. HTTP request schema caps parentPath at 8 segments and requires it
to accompany a field operation.

* feat(open-knowledge): typed array elements + type icons in the schema editor

Array fields get an element-type select (string, number, boolean, enum,
object) writing items.type through the same non-destructive merge, and
array-of-object elements render as editable nested rows: parentPath
gains an {items: true} segment that descends into an array field's items
sub-schema, so element fields support the full operation set (add,
constraint edits, rename, remove, per-element required). No
array-of-array UI on purpose; unmodeled items keywords keep the
preserved flag.

Both type selects now carry per-type colored icons, matching the
agents-manage-ui builder affordance.

* feat(open-knowledge): denser schema field rows

Flatten each field card's header to one scan line, matching the
agents-manage-ui builder's density without adopting its persistence:
leading colored type icon, mono name input, inline description, required
switch, and remove button share a row; type and pattern keep the second
line. Commit-on-blur write semantics unchanged.

* feat(open-knowledge): schema editor surface for *.schema.json anywhere

Widen the Source/Fields dispatch from tool-managed .ok/schemas paths to
any file following the ecosystem *.schema.json naming convention, plus
the existing .ok/schemas/*.json scope. Mappings already accept schema
files anywhere in the project and the per-field write endpoint already
edits any in-project path, so the editor surface was the one place still
bound to the delete-flow's safety scope. Fields stays gated on a live
mapping; a bare dotfile named .schema.json and unconventionally named
json stay on the asset preview.

* feat(open-knowledge): surface root-level advanced schema rules in Fields view

Per-field preserved flags cannot cover keywords that belong to no field:
a schema's if/then, dependencies, or additionalProperties were invisible
in the Fields view even though they validate and gate edits. The editor
now leads with a note naming the root-level keywords it does not model,
pointing at the schema file for the full picture.

* feat(open-knowledge): schema browser settings panel

Rework the frontmatter plugin settings from mapping rows + file picker
to a browser over every schema file in the project, mirroring the
markdownlint rule browser UX: each discovered schema (project-wide
*.schema.json plus .ok/schemas/*.json plus anything config.yml maps) is
a toggleable row with a Modified badge, reset-to-default, search, and a
New schema button top right. Toggling on writes an enabled: true mapping
whose appliesTo pills edit inline below the row; off keeps the mapping
with enabled: false; reset wipes it. Clicking a schema opens the file
itself in the editor via hash nav — field editing lives on the file
surface now, so the settings-embedded field editor, the mapping file
picker, and the remove-mapping dialog are gone.

Core: mappings gain an optional enabled flag (absent = on); the lint
plugin and the agent-surface enrichment skip disabled entries while
resolution still inlines their content for the editor, and disabled
mappings stay silent on the problems channel. Server: the schemas list
endpoint unions the .ok/schemas scan with a filtered content walk for
the *.schema.json convention.

* feat(open-knowledge): schema browser polish — delete, layout, only-modified

Each schema row gains a trash affordance behind a confirmation dialog
that deletes the file and wipes its mapping in one gesture; the server
delete scope widens from .ok/schemas to the browser's own predicate
(*.schema.json anywhere plus .ok/schemas/*.json) so the visible and
deletable scopes agree, with the entry-unlink and realpath guards
unchanged. The New schema button moves out of the header to sit right of
the search bar, alongside a markdownlint-style Only modified filter.

* feat(open-knowledge): plain-language appliesTo summaries

Each enabled schema row now reads its globs back in words under the
pills: a core classifier maps authored patterns onto nameable shapes
(every doc, everything under a folder, docs directly in a folder, a
single doc, any doc named X, with one plain brace set expanded) split
into includes and excludes, and the panel renders the localized
sentence. Anything the classifier can't confidently describe falls back
to naming the raw glob, so the summary never claims more than the
matcher does. Classification mirrors compileAppliesTo's real semantics:
picomatch over content-relative, extension-less doc paths.

* fix(open-knowledge): honest appliesTo summaries + suspicious-glob warnings

Fix the two summary defects: trailing/leading-slash patterns and globs
that fail to compile now classify as non-matching (the sentence says
nothing can match instead of claiming a doc), and interior-** subtree
patterns get nameable shapes (blogs/**/pizza/** reads as everything
under pizza/ folders inside blogs/; **/pizza/** as everything under any
pizza/ folder).

Validation: compileAppliesTo now reports suspicious patterns alongside
invalid ones — syntactically fine globs that can never match a doc path
(trailing or leading slash) or that target a doc extension (paths are
extension-less). The schema resolver surfaces them as advisory config
problems, silenced for disabled mappings, and the settings panel admits
them to the problems banner.

* refactor(open-knowledge): dedupe frontmatter schema selection and write guards

Extract selectApplicableFrontmatterSchemas into core so the lint plugin and
the property panel's enum constraints share one selector. This fixes a real
divergence: a toggled-off schema mapping still drove enum selects in the
property panel while contributing no diagnostics. Pinned by a new test.

Server-side, a shared src/lint/fs-safety.ts now owns the lexical containment
check and the traced atomic tmp+rename write that were copy-pasted across the
frontmatter loader, the frontmatter schema writer, and the markdownlint
writer. The schema write surface's triplicated escape-guard flow collapses
into one resolveSchemaWriteTarget resolver, and basename() replaces the
platform-dependent split('/').pop().

App-side, the five near-identical frontmatter-schema POST wrappers collapse
onto one helper; delete/create now validate the 200 response like their
siblings. Net -121 lines, behavior-identical except the panel fix.

* docs(open-knowledge): frontmatter schemas page + two-linter language pass

Add advanced/content-rules/frontmatter.mdx mirroring the markdownlint page:
schema mappings and appliesTo semantics, draft-07 schema files and the config
problems channel, diagnostic anchoring, the settings editor, the Source/Fields
schema editor, property-panel enum selects, and the schemas advertisement
agents see on reads.

Update the surrounding language now that content rules has two linters: the
overview no longer says markdownlint is the only linter, both linter pages
cross-link, the MCP reference's lint row covers frontmatter validation and the
config-problems warnings channel, and the configuration reference gains the
contentRules.frontmatter rows. Also fix the markdownlint row's key path, which
documented a contentRules.plugins.* nesting that never existed.

* docs(open-knowledge): show real lint and fix output on every API surface

Captured from actual runs against a scratch project (built CLI, real server,
real MCP stdio session), not hand-written: the ok lint text report, the --json
shape (0-based LSP ranges, fixes on auto-fixable findings, config-problem
warnings, counts), the --fix report, the MCP lint tool's single-doc text and
fix: true summaries, the agent write response's lint-violation advisories, and
the exec listing's schemas: advertisement.

Overview carries the shared shapes (CLI + MCP + write advisories); each linter
page shows its own finding: markdownlint with the fixes payload the Fix action
applies, frontmatter with anchoring, the got-value enum message, and the
config-channel line for a broken schema. The MCP reference lint row links to
the examples.

* feat(open-knowledge): schema settings and problems-panel discovery polish

Review feedback on the frontmatter data-contract work:

- The appliesTo pill input names its grammar ("Add file or folder pattern,
  e.g. guides/**/*") instead of the generic "Add tag"; the field editor's
  allowed-values inputs say "Add value".
- Enabled mappings whose globs match zero docs now warn on the config
  channel (settings panel + project audit), next to the invalid/suspicious
  glob warnings. New core helper findZeroMatchAppliesToPatterns; the audit
  walk is extracted as collectDocFiles so the lint-config surface and the
  audit agree on what counts as a doc.
- Each schema row carries an explicit Edit button beside the name click.
- Opening a schema from Settings lands on the WYSIWYG Fields view via a
  one-shot intent, without flipping the persisted Source/Fields preference.
- The Problems panel names the active lint plugins and, with none enabled,
  replaces the misleading empty state with a pointer to Settings -> Plugins.

* feat(open-knowledge): schema row and problems-panel indicator follow-ups

- The schema name is plain text now; the Edit button is the row's only way
  into the file.
- The problems panel's plugin indicator collapses to a compact
  "Checked by N plugins" line whose tooltip names the plugins, instead of
  an always-visible badge row.

* feat(open-knowledge): move Checked-by hint into the Problems header row

* feat(open-knowledge): Checked-by hint as a PanelCount pill (matches Graph header)

* feat(open-knowledge): Beta tag on the frontmatter schemas plugin settings

* feat(open-knowledge): Beta tag on the schema file editor surface

* fix(open-knowledge): schema editor Beta tag to the top left

* docs(open-knowledge): align content rules docs with shipped contracts

Audited the three content-rules pages against the lint and audit CLI
commands, the MCP lint tool, and the core lint types. Fixes the places
where the docs disagreed with the code, and fills in the contracts that
were never written down.

Corrections:
- appliesTo: a list with no positive glob gains an implicit **, so an
  exclusion-only list matches everything except the exclusions
- write advisories nest under `document`, not at the response root
- MCP lint structured content is close to `ok lint --json` but not
  identical: contentDir vs cwd, fixedCount counts files vs problems,
  plus the cap fields
- the Source/Rules toggle is JSON and JSONC only; YAML and
  .markdownlintrc open in the read-only preview
- YAML configs are re-serialized on edit, which drops their comments
- the Problems panel has a Fix all action

Newly documented:
- `ok audit --json` response shape, with linkTarget on link findings
- validation.links, its three values, and where to set it
- brokenLinks on write responses, with all three reason values
- omittedFileCount and omittedDiagnosticCount
- the per-file `fixed` field on `ok lint --json`
- advisories for appliesTo globs that compile but cannot match

Also corrects the settings path in the markdownlint blog post to
Settings > This project > Plugins.

* fix: restore 15 skill symlinks deleted by accident

The symlinks under .agents/skills/ point into
public/agents/plugins/agents/skills/ and are resolved by bare name across
agent harnesses, so removing them silently breaks skill resolution repo-wide.

They were deleted in afe849caa, a docs commit about presenting a spec as the
proposed contract, which has no functional relationship to skill symlinks.
That is the signature of an unintended `git add -A`, not an intended
retirement.

Restored from origin/main.

* fix(open-knowledge): silent-correctness fixes from local review

Three verified defects in the new frontmatter data-contract paths, plus the
docs and changeset corrections that came with them.

deleteFrontmatterSchemaFile reported success on ANY parent-dir stat failure.
Only ENOENT and ENOTDIR prove the entry is gone; EACCES/EPERM/ELOOP mean we
could not prove it, and returning `deleted` there made the GUI drop the
mapping and silently stop validating those docs. Those now return a typed
refusal. Reachable on macOS via TCC, and via a chmod that removes the search
bit on .ok/schemas.

resolveFrontmatterSchemas suppressed the broken-schema config warning when a
DISABLED mapping loaded the file first: the outcome cached, and a later
enabled mapping for the same file skipped the cache-miss branch and never
reported. Docs governed by that schema went unvalidated with no operator
signal, and the suppression was order-dependent. The report now sits outside
the cache-miss guard with a per-file dedup set so two enabled mappings still
report once.

applyFieldConstraint replaced tuple and boolean `items` with a fresh object
holding only the new constraint, destroying the user's positional definition
on any array-field edit. Worse than data loss: draft-07 ignores
additionalItems once items is a single schema, so a surviving
`additionalItems: false` became dead config. The same shape is already
refused by navigateToParent, so editing now refuses too, which removes an
internal contradiction rather than adding a new posture.

Also: correct the ok lint --json example (warningCount said 3 with only 2
diagnostics), note that --errors-only never gates on frontmatter findings,
document the exec tool's schemas output in the MCP reference, bump the
changeset to minor per the pre-1.0 convention for additive features, and fix
an isToolManagedSchemaPath docstring that named the wrong predicate and
understated the delete scope.

* fix(open-knowledge): review follow-ups on lint diagnostics and a11y

Invalid appliesTo globs now carry the engine's own reason. picomatch names
the actual defect ("Missing closing: ]"), which was being discarded, leaving
the user with a message that said a glob was invalid but not why.
invalidPatterns becomes {pattern, detail} to carry it, mirroring the
suspiciousPatterns shape already in that interface.

listProjectSchemaFiles logged nothing when the directory could not be read,
so an EACCES under macOS TCC produced an empty schema picker indistinguishable
from a first run. ENOENT and ENOTDIR stay quiet; anything else warns.

The schema-description input had no accessible name of its own. A placeholder
is only a last-resort fallback in the name computation, and every row computed
the same one, so the name neither identified the field nor stayed correct once
a description existed. Now labelled per field through the t macro, matching the
three sibling interpolated labels in the same component. Catalogs re-extracted.

Collapse the third copy of isInside onto the fs-safety primitive this branch
introduced, and correct two docstrings that understated the delete surface:
it admits any *.schema.json in the project, not only tool-managed
.ok/schemas/ paths. Documents why write scope is deliberately broader than
delete scope rather than narrowing it, since a mapping may point at any
project-relative JSON the linter can already read.

* test(open-knowledge): pin the order-dependent suppression and enum multi-ness

The frontmatter-schemas fix landed without a test for the scenario it fixes.
The existing suite covered a single disabled mapping staying silent, but not
the duplicate-file ordering that was the actual bug, so the fix was one
refactor away from silently regressing.

Adds the disabled-then-enabled case, the two-enabled no-double-report case,
and the disabled-only silence case. Mutation-checked: reintroducing the
report inside the cache-miss branch fails the first test and only that one.

Also covers the existing multi-ness mismatch fallback in the property panel,
where a scalar enum and an array items.enum govern one field. Vocabularies in
that test are identical so only the multi-ness guard can cause the drop, and
both orderings are asserted since neither schema may decide the shape.

* fix(ok-marketing): declare core's new ajv, ajv-formats and picomatch deps

The vendored @inkeep/open-knowledge-core dist is derived from
packages/core at build time, but the vendored package.json is committed
and is what tells pnpm which runtime deps to install. The frontmatter
schema feature added ajv, ajv-formats and picomatch to core, so the
derived dist imports them while the manifest never declared them, and the
Vercel build failed on the first one it could not resolve.

Only ajv-formats surfaced in the log because the build stops at the first
unresolvable import; the other two were latent behind it.

* chore: lock the vendored core's new ajv, ajv-formats and picomatch deps

* fix(open-knowledge): drop four unnecessary exports flagged by knip

All four symbols are used, but only inside their own module, so exporting
them added public surface with no consumer. knip fails the lint job on
unused exports; this branch never ran it until the PR existed, which is
why four accumulated at once.

listFrontmatterSchemas is called twice in its own file; the three
applies-to types are referenced only by the exported CompiledAppliesTo
interface, which does not require them to be exported to remain usable.

* perf(open-knowledge): lazy-load the two config-file editors

EditorArea imported LintConfigEditor and SchemaConfigEditor statically, so
both shipped in the main chunk for every session even though they render
only when a .markdownlint.* or *.schema.json file is opened. That put the
main app bundle 5.49 kB over its size-limit budget.

Both now load through React.lazy behind a Suspense fallback, matching the
pattern already used in this file for LazyActivityModeContent and in
DocPanel for LazyGraphPanel. Main bundle drops 490.49 kB to 480.13 kB,
back under the 485 kB budget.

The two dispatch tests that assert routing to these editors now await the
dynamic import. Only the first assertion against each lazy component needs
it — later tests in the file find the module already resolved — so the
await is on those two and the comment says why.

* chore(open-knowledge): raise the all-JS size budget for frontmatter schemas

The feature ships a JSON Schema validator plus two editor surfaces, so
total JS grows. Splitting cannot offset a total-size budget, and this one
was already over before the config editors moved behind React.lazy.

3.55 -> 3.6 MB (actual 3.57). The main-chunk budget stays at 485 kB, which
the lazy-load brought back under, so initial load is unchanged for sessions
that never open a config file.

Matches how every recent feature has tracked this budget: the markdownlint
Source/Rules toggle, Mermaid WYSIWYG, the Cmd+K palette, ACP agent threads
and the in-app feedback form each raised it alongside their change.

* chore(open-knowledge): refresh the ng-anchors catalog after the main merge

The catalog is a code-as-truth provenance record whose counts track the real
source tree. Merging origin/main added files (production 101 to 104, test 215
to 216) without regenerating it, so the freshness test that deep-equals the
committed catalog against a fresh build failed.

Regenerated via enumerate-ng-anchors; only the counts and generatedAt move.

* fix(open-knowledge): make the enum free-text drop terminal

enumConstraintsForDoc dropped a field to free text when two schemas
disagreed, but the drop was not sticky: a third schema hit the "no existing
constraint" branch and re-added its own vocabulary. With A offering [x,y],
B mismatching on multi-ness, and C offering [z], the property panel ended
up offering [z] — a value A forbids — and which schema won depended on
entry order.

That contradicts the module's contract that an offered value satisfies
every governing schema. Reachable because schema selection dedups by file
key, so distinct files with overlapping appliesTo all govern one doc.

Blast radius was the panel only; validateFrontmatterSource always warned
independently against every schema. But the panel could steer a user toward
a value it had implied was safe.

Conflicted fields are now tracked and skipped on later schemas, so the
result is order-independent across the whole schema set rather than just
pairwise. Both terminal paths (multi-ness mismatch and empty intersection)
are covered by tests that fail against the previous behaviour.

* fix(open-knowledge): stop the schema listing from re-admitting .ok

The frontmatter schema-file listing walked the content tree with the .ok
re-admission flag set, which lifts ContentFilter's always-skip floor. That
made the listing a second surface able to enumerate OK's internal state,
and the showok-caller-coverage gate exists to catch exactly that: the flag
is pinned to the tree-listing reveal, and a new consumer is meant to be a
deliberate decision rather than a new call site.

The re-admission was for consistency with the Show All Files tree, not
necessity. The handler's first discovery source already scans
.ok/schemas/ directly, so the walk only needs to find the ecosystem
*.schema.json convention in ordinary project directories. Dropping it
narrows the capability back to the one sanctioned surface; the only
discovery lost is a *.schema.json living under .ok/ but outside
.ok/schemas/, which the tool never creates.

Introduced by f51ef5992 and unnoticed because CI had not run on this
branch until the PR existed. 48 integration and meta tests pass, including
the frontmatter listing over HTTP.

* fix(open-knowledge): converge other windows on a markdownlint rule write

handleWriteFrontmatterSchema signals the lint-config CC1 channel after a
successful write; its markdownlint sibling never did, so toggling a rule in
one window left every other window linting against a stale config until it
happened to refetch. This PR is what establishes lint-config as the channel
for config convergence, so the sibling write surface belongs with it.

Also closes two coverage gaps in code this PR added: a disabled mapping must
not reach agents through schemas_applicable, and three schemas must narrow to
the running intersection rather than folding against the original values.

* docs(open-knowledge): cross-reference the two appliesTo selection sites

schemasApplicableToDoc applies the same skip-disabled / match / dedup triad
as selectApplicableFrontmatterSchemas, but over persisted mappings rather
than resolved entries, because enrichment advertises which files govern a
doc without loading them. Nothing links the two, so a change to what
governs could land in one and silently miss the other — and only the core
one is covered by the validator's tests.

GitOrigin-RevId: 86e58ac35b8767d82165ef210136d56d63e165a3
@inkeep-oss-sync
inkeep-oss-sync Bot merged commit 0c5c0bb into main Jul 25, 2026
@inkeep-oss-sync
inkeep-oss-sync Bot deleted the copybara/sync branch July 25, 2026 00:24
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants