Semantic search over a Markdown knowledge base, served over MCP.
groove validate は文書の frontmatter を groove-schema.toml に照らして検査する。本リリース
までは、schema が名指せる key は title / date / topic / depth / tags の 5 つだけで、
それ以外は名指せなかった。パーサが YAML ブロックをこの 5 つのフィールドを持つ struct へ
デシリアライズし、serde の既定の挙動が、それ以外の key を下流の誰かが見る前にすべて
捨てていたからである。status を名指す schema は読み込みの時点で拒否され、「schema が
名指していない key を指摘する」strict モードは存在し得なかった。バリデータは、そのブロックが
どの key を運んでいたかを一度も知らないからである。
74 文書のプラットフォームエンジニアリングのコーパスを運用しているユーザは、これをこう言い
表した (#252): ドメインのメタデータを持たないコーパスに strict モードは要らない。ドメインの
メタデータを持つコーパスは strict モードを使えない。その frontmatter は 21 種類の key を
運んでおり、そのうち宣言できたのは 5 つだった。報告者が提案したのは loader への 1 行の
変更 — [fields.status] known = true — で、schema が key を検査せずに名指すための
ものである。
ADR-0010 は、受け取ってから捨てていた
--strict フラグを削除し、その復活の値段を付けた: [options].allow_unknown_fields がある
状態で足し直すなら、それは minor リリースである。
本決定が答える問い: frontmatter にどの key が存在するかを決めるのは、パーサか schema か。
Frontmatter が何であるかを変えることになり、それはすべてのパーサが構築し、
索引を読む側すべてが触れる型であるdate は schema が pattern で検査
する文字列であり、depth も文字列である。schema が存在して以来これが規則であり、報告者
もその変更は求めていないsource に条件付けられている。
key を名指すことが必須化を意味する schema にすれば、報告者が持つ全文書が指摘対象になるdocs/stability.md)。この
コーパスが key を必要としている場所はバリデーションだけであるknown = true (報告者の提案)。却下: 単体では成立しない。
パーサが依然として key を捨てるからである。そしてパーサが key を保持するのなら値も保持
してよく、その時点で known は「空のテーブル」を意味するだけのキーワードになるpattern が YAML の構文に対して照合されることになり、mapping が
type = "string" を通ってしまうtags はその型が担保して
いる Vec<String> の保証を失うextra: BTreeMap<String, FieldValue> を
足す。 採用type = "bool" を足す。 却下: enum = ["true", "false"] が既に表現している 1 つの形
の表明のために、「最初から最後まで文字列」の規則を壊す。ドキュメントにそう書くことにし、
読者に発見させるのをやめるFrontmatter.extra へ入る。
YAML の merge key << だけが例外である: 保持すれば展開されないまま表に出てしまい、
そもそもフィールドだったことはない!!binary は
不透明であり、パーサは形の名前 (mapping / nested sequence / binary) だけを
保持して値を読まない。null はそのいずれでもない — status: の後に何も
書かれていないのは値の無い key であり、「無い」として数える。alias はそれが指す値に
解決された状態で届き、標準の tag も形が決まる前に解決されるので、どちらも独立した形ではない[fields.<name>] はどんな名前も受け取る。空の
テーブルは key を宣言し、何も検査しない。required = true は明示のままである。既存の
rule は、type が与えられていなければ保持された値の形に従って、与えられていればその
type に対して適用される。不透明な値は required を満たし、それを読もうとする rule に
対しては自分の形を名指す type_mismatch を 1 件報告する。null は何も満たさないので、
空の title: と同じように、空の status: を required が捕まえる[options].allow_unknown_fields = false、または groove validate --strict は、schema が
名指していない key を 1 件ずつ undeclared_field 違反として key 順に報告する。値を
書かなかった key も、key 自体はあるので報告される。名前付きの
5 フィールドは常に宣言済みである。パーサが拒否したブロックは、これまで通り
frontmatter_unparsed 1 件だけで、それ以外は何も出ない。フラグは締める方向にしか
働かない: --no-strict は無いgroove validate だけである。 schema が宣言
した key を search / graph / MCP ツールでフィルタできるようにすることは、意図された
次の一歩であって、この記録が閉じる扉ではない (#289 は、その意図をどちらにせよ書き残す
ことを求めた)。それが別の決定なのは、索引と、凍結された結果およびフィルタの面に触れ、
それぞれに自分の値段があるからである。この記録が決めるのは、パーサが何を保持し、
バリデーションがそれで何をするかだけであるgroove validate --strict が再び parse される。 1.0.0 で ADR-0010 が外し、1.9.0 で
その ADR が予約していた意味を伴って戻る。[index].fail_on_frontmatter_error は自分の
意味のために自分の名前を保つFrontmatter にフィールドが 1 つ増えた。 Markdown 以外のパーサはこれを空のままに
する。全フィールドを名指して Frontmatter を構築しているコードは、このフィールドも
名指す必要があるunsupported field で拒否していた schema が、1.9.0 では読み込める。
type の無い [fields.tags] pattern を持つ schema は 1.8.0 で報告を始めた。それ以外に、
以前読み込めていたもので報告内容が変わるものは無い。既定の allow_unknown_fields = true
が 1.8.0 の挙動そのものだからである#[serde(flatten)] の方が短く書けるが、やることは逆である — 未知の値をすべて先に
実体化するので、1.8.0 が索引できていた文書を拒否してしまうtopic: [a]) が今も拒否されること、および title に数値を与えたときに従来どおり
文字列 "123" へ強制されること。どちらも以前のテストは覆っていなかったgrooveseek/src/parser/markdown.rs (RawFrontmatter / FieldValueVisitor)、
grooveseek/src/schema.rs (check_extra / require_declared_fields)