GoogleCloudPlatform / GoogleCloudPlatform/open-knowledge-format

Proposal: producing a bundle in parts — reserved filenames collide on merge, and a regenerated bundle-root `index.md` may drop `okf_version`

Open
#26 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
HTML
Stars
467
Forks
35
Avg merge
9m
Merged PRs (30d)
1

Description

## Summary

For disclosure: we are not blocked. We produce a bundle of roughly 10,000 concepts in about 100 parts, one zip per part, under v0.2 as written. This issue records where the spec is silent about that workflow and proposes two independent things, either acceptable alone: **one sentence in §8** and **one informative subsection §3.2** in the style of §10.5. No new key, reserved filename, or §11 item. It touches the same file as #11 and the same key as #24 (see *Related*); a fix for the reference-implementation side of gap 1 is proposed in PR #25.

## What already works

- Parts can omit `index.md`: §3 marks it Optional, §8 says it MAY appear, §11 constrains it only "when present" and forbids rejecting a bundle for "Missing `index.md` files". §8 already says "Producers MAY generate `index.md` automatically", and the reference agent regenerates them at the end of every `enrich` (`src/reference_agent/runner.py:297-298`).
- A link into a part not yet merged is broken until then; §6.1 says such a link "is not malformed" and consumers MUST tolerate it.

## Gaps

1. **A regenerated bundle-root `index.md` may lose `okf_version`.** §8 permits `okf_version` only in the bundle-root `index.md` (§12 cross-references this) and lets producers generate `index.md`; nothing says what a producer that rewrites an existing bundle-root `index.md` does with the key. The reference implementation drops it: `regenerate_indexes()` never reads the existing file (`src/reference_agent/bundle/index.py:85-86`) and writes headings and bullets only (`index.py:35-48`, `:103-104`). Reproduced on a copy of `bundles/acme_retail` at `ad30107` with `okf_version: "0.2"` added: gone after one run. Nothing in the repository writes or reads the key today, so the loss is invisible until a bundle declares one.
2. **Reserved filenames collide on merge.** Two parts that both carry an `index.md` or `log.md` at the same path cannot both survive extraction into one root; the last one written wins silently. Nothing forbids regenerating `index.md` (§8), but §8 ties `okf_version` to that file and does not say whether listing content that no frontmatter supplies (the subdirectory descriptions in `bundles/acme_retail/index.md`, a form §8's own template sanctions) is authoritative. §9 fixes the shape of `log.md` but not how two logs for one scope combine.
3. **Same path, different content.** A concept's ID is "the path of the concept's file within the bundle, with the `.md` suffix removed" (§2); nothing says what a merge does when two parts carry the same concept path. That state exists only inside the merge step, never in a bundle, which is why the text below is informative.

## Proposal 1: one sentence in §8

After "Producers MAY generate `index.md` automatically; consumers MAY synthesize one on the fly when none is present." (the end of §8) add, in the idiom of §4.1's "Consumers SHOULD preserve unknown keys when round-tripping":

> A producer that regenerates a bundle-root `index.md` SHOULD preserve an existing `okf_version` key unless it declares a different version.

It qualifies the generation permission it follows and does not declare `index.md` derived: `bundles/acme_retail/index.md` carries hand-written subdirectory descriptions that exist in no frontmatter, and `regenerate_indexes()` replaces them (open question 1).

## Proposal 2: informative §3.2

**Update, 2026-09-14.** Proposal 2's third bullet revised and split in two after the review below; the clause it replaced read: "the merging producer may write those `index.md` and `log.md` files afterwards (§8, §9)".

> ### 3.2 Producing a bundle in parts (informative)
>
> This subsection is informative, not normative.
>
> A producer may write a bundle as several *parts* that are merged later, for example one part per batch of an export pipeline. A part is a directory tree rooted at the bundle root, holding a subset of the bundle's files at their bundle paths; it is a fragment of one bundle rather than a bundle of its own (§2), and a concept's path and concept ID (§2) are the same in the part and in the merged bundle. The conditions in §11 apply file by file, so a part can be checked before it is merged; links into parts not yet merged are broken until then (§6.1).
>
> Merging is a union: a part asserts the concepts it contains and says nothing about those it omits, so it cannot express that a concept was removed or renamed. Recording a removal is not addressed here.
>
> - Derive each concept's path from a stable identifier of what it describes, unique across parts, so that re-running a part writes the same paths. Do not rely on letter case to keep paths distinct: a case-folding filesystem drops one file silently; comparing case-folded paths across parts before extraction catches this.
> - The merging producer takes one version of each part (which parts exist and which paths each writes is producer bookkeeping that OKF does not specify); a later run replaces that part's earlier output at those paths, including any `verified` events (§5.2) or `status` (§5.4) added to the merged bundle after the earlier merge. The same concept path in two parts with different content is a conflict: keeping one copy silently loses knowledge, so fail or report it. Identical copies are not a conflict.
> - A part contributes to every directory at or above the files it writes, including the bundle root. Keep reserved filenames (§3.1) out of directories that more than one part contributes to; any `index.md` or `log.md` there is written by the merging producer, after merging (§8, §9). All parts of one bundle target the same OKF version; a merged bundle's `okf_version`, if declared, is written by the merging producer (§12).
> - Such a file can hold more than the parts supply. What the parts do supply, such as an `index.md` listing built from frontmatter, a later merge can regenerate (§8). What no part can restate, the merging producer carries forward, from the existing file or from its own bookkeeping: the `okf_version` key of a bundle-root `index.md` (§12), and any `log.md` entry the current merge did not itself write, whether an earlier merge or a later editor put it there, since a `log.md` records the history of changes to its scope (§9) and an entry there can state a decision about the bundle as a whole. A file rebuilt from the parts alone drops such content silently, since §11 checks only a reserved file's structure.
>
> How a part ships is a producer choice; §3 specifies distribution forms only for a whole bundle, and OKF does not specify manifests or checksums (§1 leaves such conventions to the producer).

## Backwards compatibility

Additive. No new reserved filename, frontmatter key, or §11 item. §3.2 contains no MUST, SHOULD or MAY, like §10.5. The one keyword, the SHOULD in §8, binds only a producer that regenerates a bundle-root `index.md`; every bundle and consumer conformant today stays conformant. Whether this goes into 0.2 in place or waits for a 0.3 line is your call; §12 counts additions as a minor bump, and #24 raises the same question for PR #6.

## Alternatives considered

- **Each batch as its own subtree** (`batches/0017/...`): concept IDs would encode batch numbers, which carry no meaning for the knowledge itself.
- **Standardizing a part manifest.** OKF defines no manifest. One producer ships a machine index inside the bundle root (comment on #11, `bundle/.manifest.json`); a part manifest describes one archive and is read by the merging producer, not by a consumer of the merged bundle. We propose nothing.

## Related

- #24 asks what the value `okf_version: "0.2"` identifies after PR #6; this issue asks whether the key survives regeneration of `index.md`. Orthogonal to each of #24's resolutions.
- #11 proposes that `index.md` (or another discovery surface) carry entries for removed concepts, which are not derivable from present concepts. Hence §3.2's union with removal left to #11, and a §8 sentence that preserves a key rather than declaring `index.md` derived.
- #14: a non-reference producer reports that a predecessor format lost 104 notes silently to filename collisions on a case-folding filesystem, the reason for the letter-case sentence.
- PR #25: proposed reference fix for gap 1.

## Open questions

1. How should a producer that regenerates `index.md` treat a hand-authored one such as `bundles/acme_retail/index.md`, whose subdirectory descriptions exist nowhere else? §8 permits only `okf_version` in `index.md` frontmatter, so a marker key would change §8, which §11 item 3 makes a conformance condition, and is not proposed; #10's topic-lead concept could give directory descriptions a frontmatter home, and a leading generator comment (``) may fit §8 as written.
2. Should `okf_version` be declarable outside the bundle-root `index.md`, so it is not tied to a file producers MAY generate? (Orthogonal to #24, which concerns the value, not the location.)

Happy to send a PR with the wording if this is the direction you prefer.

Contributor guide

Open the contributing guide

Research direction

Read the proposed changes in §8 and §3.2 alongside §3, §9, §11, and §12, then review related issues #11 and #24 and PR #25. Resolve the two open questions about regenerated index files and version placement with maintainers. Done means the specification wording and compatibility implications are agreed and submitted as a focused documentation change.

Written by the indexing model from the issue text.

Assessment

Domain
documentation
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.