Door B listed `inbox.iterdir()` and kept only top-level files. A file in a subdirectory was neither ingested nor refused: it appeared in none of the result's buckets, so a nested drop produced a bundle that was silently short of what was dropped and no count said so. That broke the K1b identity for any inbox with folders in it. Operator decision 2026-09-06. - `walk_inbox` is the ONE walk rule, shared with `tools/okf_corpus_run.py`: the denominator N is now counted over exactly the set of files the door ingests, rather than over a second listing that happened to agree. - Sorted on the whole relative path, not the basename, so the order is a function of the tree; that is what keeps rebuild-from-scratch byte-equal to an incremental update. - A concept's `source_file` is the path relative to the inbox root, `/`-separated. The concept NAME still comes from the basename, so two folders holding one basename hit the existing §3 collision refusal instead of one silently claiming the other's concept. - Dot-directories and a bundle directory inside the inbox are skipped with a CODE, in a new `InboxResult.skipped`. Recursion makes the door's own output reachable as its own input; a silent skip would be the same absence-without-a-denominator defect one level down. - `--path-prefix` reduces per component and rejoins with `/`, so the caller driving a nested corpus can carry the relative directory. Reducing the whole string folded the separator into a `-` and flattened `sub/sub2`. `tests/test_inbox_flow.py::test_subdirectories_are_not_walked` asserted the opposite and is superseded in place, with the reason written down. Measured on the K2 corpus (flat, N=43): 39/43 merged, 4 coded, K1b holds. The bundle digest is `1472e98aec8643c5beee540f4c42b5e437bd26e7c61d69a91bcff799f06a6d13` over 1108 files -- byte-identical to a run of the same corpus at190086fWITHOUT this change (`diff -r` exit 0), so recursion costs a flat inbox nothing. It differs from the stored 2026-09-03 artifact by one line in `index.md` (`- [Corpus run history](log.md)`), which95eb271added 15 hours after that bundle was built. Suite 1113 passed, `ruff` clean, `mypy --strict src/ tools/` clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
511 lines
19 KiB
Python
511 lines
19 KiB
Python
"""The corpus harness: it reports numbers with denominators, or it fails.
|
|
|
|
Three properties, all of them reactions to measured defects rather than good
|
|
intentions:
|
|
|
|
- **K1b is a COMMAND, not a sentence in a report.** The conservation identity
|
|
`merged + Sigma(coded rejections) == N` is checked by the harness, which
|
|
EXITS NON-ZERO when it does not hold. A prose assertion is something a reader
|
|
has to trust; an exit status is something a pipeline cannot ignore.
|
|
- **`N` is computed, never typed.** A literal `43` keeps passing after the
|
|
corpus changes, and the number it then reports is a fact about a directory
|
|
that no longer exists.
|
|
- **Three counts, never one.** The guard sits between extraction and persist,
|
|
so a healthy persisted count can hide a pile of quarantines. Extracted,
|
|
gated and persisted are reported separately for that reason.
|
|
|
|
The negative control is the point of this module. A harness that can never
|
|
fail proves nothing, so `test_a_file_neither_merged_nor_coded_fails_the_run`
|
|
hands the conservation check an inventory it must reject -- if that test ever
|
|
passes silently, every green run above it becomes meaningless.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "tools"))
|
|
|
|
import okf_corpus_run # noqa: E402
|
|
|
|
INGESTED_AT = "2026-07-25T12:00:00Z"
|
|
|
|
SUBSTANTIVE = "Krav til seksjonering av bygget.\n\nEn andre setning som baerer innhold.\n"
|
|
DEGENERATE = " \n\t\n \n"
|
|
|
|
|
|
def corpus(tmp_path: Path, files: dict[str, str]) -> Path:
|
|
root = tmp_path / "corpus"
|
|
root.mkdir(parents=True, exist_ok=True)
|
|
for name, text in files.items():
|
|
(root / name).write_text(text, encoding="utf-8", newline="")
|
|
return root
|
|
|
|
|
|
def test_the_denominator_is_the_directory_not_a_literal(tmp_path: Path) -> None:
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE, "b.md": SUBSTANTIVE, "c.md": SUBSTANTIVE})
|
|
report = okf_corpus_run.measure(root, tmp_path / "bundle", ingested_at=INGESTED_AT)
|
|
assert report.n == 3
|
|
assert report.n == len(list(root.iterdir()))
|
|
|
|
|
|
def test_the_denominator_counts_files_in_subdirectories_too(tmp_path: Path) -> None:
|
|
"""N is the corpus, and the corpus is the whole tree.
|
|
|
|
The measurement and the door must walk by the SAME rule -- `walk_inbox` is
|
|
the one implementation -- or the report would state a denominator over a
|
|
different set of files than the one that was ingested, and the
|
|
conservation identity would hold over the wrong N.
|
|
"""
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE})
|
|
(root / "sub").mkdir()
|
|
(root / "sub" / "b.md").write_text(SUBSTANTIVE, encoding="utf-8", newline="")
|
|
|
|
report = okf_corpus_run.measure(root, tmp_path / "bundle", ingested_at=INGESTED_AT)
|
|
|
|
assert report.n == 2
|
|
assert report.persisted == 2
|
|
# `unaccounted` is `dropped - merged - coded`: it can only be empty if the
|
|
# measurement's names and the door's names are the SAME strings, which is
|
|
# what pins the two walks to one rule rather than to two that agree today.
|
|
assert report.unaccounted == ()
|
|
# Re-extracted through `corpus / source_file`, so the relative name has to
|
|
# resolve back to the file it came from.
|
|
assert report.substantive == 2
|
|
|
|
|
|
def test_the_denominator_skips_a_bundle_written_inside_the_corpus(tmp_path: Path) -> None:
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE})
|
|
bundle = root / "bundle"
|
|
|
|
okf_corpus_run.measure(root, bundle, ingested_at=INGESTED_AT)
|
|
second = okf_corpus_run.measure(root, bundle, ingested_at=INGESTED_AT)
|
|
|
|
assert second.n == 1
|
|
assert second.persisted == 1
|
|
assert second.unaccounted == ()
|
|
|
|
|
|
def test_the_conservation_identity_holds_and_the_run_exits_zero(tmp_path: Path) -> None:
|
|
root = corpus(
|
|
tmp_path,
|
|
{"a.md": SUBSTANTIVE, "b.md": DEGENERATE, "c.md": SUBSTANTIVE, "d.bin": "x"},
|
|
)
|
|
code = okf_corpus_run.main(
|
|
["--corpus", str(root), "--report", str(tmp_path / "r.md"), "--ingested-at", INGESTED_AT]
|
|
)
|
|
assert code == 0
|
|
report = okf_corpus_run.measure(root, tmp_path / "bundle2", ingested_at=INGESTED_AT)
|
|
assert report.merged + report.rejected == report.n
|
|
assert not report.unaccounted
|
|
|
|
|
|
def test_a_file_neither_merged_nor_coded_fails_the_run(tmp_path: Path) -> None:
|
|
"""The negative control. Without it a green run proves nothing.
|
|
|
|
The check is handed an inventory where one name is in neither column --
|
|
exactly what a silently dropped file looks like from the outside -- and it
|
|
must both refuse and NAME the file.
|
|
"""
|
|
unaccounted = okf_corpus_run.unaccounted_names(
|
|
dropped=("a.md", "b.md", "vanished.md"),
|
|
merged=("a.md",),
|
|
coded=("b.md",),
|
|
)
|
|
assert unaccounted == ("vanished.md",)
|
|
assert okf_corpus_run.unaccounted_names(dropped=("a.md",), merged=("a.md",), coded=()) == ()
|
|
|
|
|
|
def test_the_harness_exits_non_zero_and_names_the_unaccounted_file(
|
|
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE, "b.md": SUBSTANTIVE})
|
|
real = okf_corpus_run.measure
|
|
|
|
def lose_one(*args: object, **kwargs: object):
|
|
report = real(*args, **kwargs) # type: ignore[arg-type]
|
|
return okf_corpus_run.replace(report, unaccounted=("b.md",))
|
|
|
|
monkeypatch.setattr(okf_corpus_run, "measure", lose_one)
|
|
code = okf_corpus_run.main(
|
|
["--corpus", str(root), "--report", str(tmp_path / "r.md"), "--ingested-at", INGESTED_AT]
|
|
)
|
|
assert code != 0
|
|
assert "b.md" in capsys.readouterr().err
|
|
|
|
|
|
def test_the_degenerate_rule_is_reproducible_from_its_statement(tmp_path: Path) -> None:
|
|
"""Zero characters after stripping whitespace. A definition, not a threshold."""
|
|
assert okf_corpus_run.is_degenerate("")
|
|
assert okf_corpus_run.is_degenerate(" \n\t ")
|
|
assert not okf_corpus_run.is_degenerate("x")
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE, "b.md": DEGENERATE})
|
|
report = okf_corpus_run.measure(root, tmp_path / "bundle", ingested_at=INGESTED_AT)
|
|
assert report.substantive == 1
|
|
assert report.degenerate == 1
|
|
assert report.substantive + report.degenerate == report.merged
|
|
|
|
|
|
def test_the_three_counts_are_reported_separately(tmp_path: Path) -> None:
|
|
"""A healthy persisted count can hide a pile of quarantines."""
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE, "b.bin": "x"})
|
|
report = okf_corpus_run.measure(root, tmp_path / "bundle", ingested_at=INGESTED_AT)
|
|
assert (report.extracted, report.gated, report.persisted) == (1, 1, 1)
|
|
assert report.n == 2
|
|
text = report.render()
|
|
for label in ("extracted", "gated", "persisted", "denominator"):
|
|
assert label in text
|
|
|
|
|
|
def test_the_report_names_the_resolved_converter_and_its_version(tmp_path: Path) -> None:
|
|
"""The vendored binary is bypassed silently otherwise -- measured three times."""
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE})
|
|
report = okf_corpus_run.measure(root, tmp_path / "bundle", ingested_at=INGESTED_AT)
|
|
text = report.render()
|
|
assert "converter" in text.lower()
|
|
assert okf_corpus_run.converter_identity()[1] in text
|
|
|
|
|
|
def test_wall_time_per_file_is_reported(tmp_path: Path) -> None:
|
|
"""The only evidence the scale requirement will ever have."""
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE, "b.md": SUBSTANTIVE})
|
|
report = okf_corpus_run.measure(root, tmp_path / "bundle", ingested_at=INGESTED_AT)
|
|
assert report.seconds_total >= 0.0
|
|
assert "per file" in report.render()
|
|
|
|
|
|
def test_the_bundle_carries_a_log_md_that_makes_k1b_checkable_from_the_bundle(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""K1b was verifiable only from the harness's stdout, which no bundle carries.
|
|
|
|
Measured by a consumer 2026-09-03: given the bundle alone, `N` could not be
|
|
recovered, so `merged + Sigma(coded rejections) == N` was not checkable from
|
|
the artifact -- only `merged` was. SPEC section 9 already reserves `log.md`
|
|
for exactly this, so the run path writes the denominator and every rejection
|
|
code into it. Existence is not the property under test: an empty stub would
|
|
pass that and recover nothing.
|
|
"""
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE, "b.md": DEGENERATE, "c.bin": "x"})
|
|
bundle = tmp_path / "bundle"
|
|
code = okf_corpus_run.main(
|
|
[
|
|
"--corpus",
|
|
str(root),
|
|
"--report",
|
|
str(tmp_path / "r.md"),
|
|
"--bundle",
|
|
str(bundle),
|
|
"--ingested-at",
|
|
INGESTED_AT,
|
|
]
|
|
)
|
|
assert code == 0
|
|
|
|
log = (bundle / "log.md").read_text(encoding="utf-8")
|
|
# Section 9 form: reserved frontmatter type, a heading, ISO date headings.
|
|
assert log.startswith("---\ntype: Log\n")
|
|
assert f"\n## {INGESTED_AT[:10]}\n" in log
|
|
# The numbers, not merely the file. N is the denominator that was missing.
|
|
assert "N = 3" in log
|
|
assert "merged = 2" in log
|
|
assert "`extractor_unknown`: 1" in log
|
|
# The identity itself, so a reader does not have to re-derive it.
|
|
assert "2 + 1 = 3" in log
|
|
|
|
|
|
def test_the_log_is_dated_from_ingested_at_and_never_the_wall_clock(tmp_path: Path) -> None:
|
|
"""Determinism is bit-exact here as everywhere: two runs of the same corpus
|
|
at the same `ingested_at` produce the same `log.md` bytes."""
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE})
|
|
first = tmp_path / "b1"
|
|
second = tmp_path / "b2"
|
|
for bundle in (first, second):
|
|
okf_corpus_run.main(
|
|
[
|
|
"--corpus",
|
|
str(root),
|
|
"--report",
|
|
str(tmp_path / "r.md"),
|
|
"--bundle",
|
|
str(bundle),
|
|
"--ingested-at",
|
|
INGESTED_AT,
|
|
]
|
|
)
|
|
assert (first / "log.md").read_bytes() == (second / "log.md").read_bytes()
|
|
|
|
|
|
SEGMENTABLE = (
|
|
"# 1 Innledning\n\nBakgrunnen for anskaffelsen er beskrevet her, med nok\n"
|
|
"tekst til at seksjonen baerer innhold.\n\n"
|
|
"# 2 Kravspesifikasjon\n\nKravene til leveransen er listet i dette\n"
|
|
"avsnittet, ogsaa med en andre setning.\n"
|
|
)
|
|
|
|
|
|
def _propose(source: Path, out: Path) -> None:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "tools"))
|
|
import okf_propose_segments
|
|
|
|
assert okf_propose_segments.run(source, out, okf_type="reference", proposed_at=INGESTED_AT) == 0
|
|
|
|
|
|
def test_a_plan_directory_makes_the_run_segment_and_mark_every_concept_proposed(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""The defect this closes: the harness ran under `STRUCTURED_V1` and passed
|
|
no plans at all, so a corpus arrived as one flat concept per file with no
|
|
`adjudication` key anywhere -- measured by a consumer as 0 of 39.
|
|
|
|
Segmentation is not a flag the harness may set on its own: the plans are
|
|
produced per document by the proposer first, and the harness replays them.
|
|
So the wiring under test is `--plans-dir`, and the profile follows from it.
|
|
"""
|
|
root = corpus(tmp_path, {"doc.md": SEGMENTABLE})
|
|
plans = tmp_path / "plans"
|
|
plans.mkdir()
|
|
_propose(root / "doc.md", plans / "doc.json")
|
|
|
|
bundle = tmp_path / "bundle"
|
|
code = okf_corpus_run.main(
|
|
[
|
|
"--corpus",
|
|
str(root),
|
|
"--report",
|
|
str(tmp_path / "r.md"),
|
|
"--bundle",
|
|
str(bundle),
|
|
"--ingested-at",
|
|
INGESTED_AT,
|
|
"--plans-dir",
|
|
str(plans),
|
|
"--bundle-id",
|
|
"k2-trinn1",
|
|
"--okf-version",
|
|
"0.2",
|
|
]
|
|
)
|
|
assert code == 0
|
|
|
|
concepts = sorted(
|
|
path for path in bundle.rglob("*.md") if path.name not in ("index.md", "log.md")
|
|
)
|
|
# 1-to-N: one dropped file, more than one concept.
|
|
assert len(concepts) > 1
|
|
bodies = [path.read_text(encoding="utf-8") for path in concepts]
|
|
# Every concept carries the marker, and it says PROPOSED -- the proposer
|
|
# never adjudicates, so an `adjudicated` here would be a machine's guess
|
|
# standing where a human's judgement belongs.
|
|
assert all("\nadjudication: proposed\n" in body for body in bodies)
|
|
assert not any("adjudication: adjudicated" in body for body in bodies)
|
|
|
|
|
|
def test_a_plan_directory_without_the_caller_owned_root_values_is_refused(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""`okf_version`'s value belongs to the catalog (decision E1) and `bundle_id`
|
|
to whoever delimits the bundle. A literal for either in this harness would
|
|
claim a decision this repository does not own, so both are arguments and
|
|
their absence is a refusal rather than a default."""
|
|
root = corpus(tmp_path, {"doc.md": SEGMENTABLE})
|
|
plans = tmp_path / "plans"
|
|
plans.mkdir()
|
|
_propose(root / "doc.md", plans / "doc.json")
|
|
|
|
code = okf_corpus_run.main(
|
|
[
|
|
"--corpus",
|
|
str(root),
|
|
"--report",
|
|
str(tmp_path / "r.md"),
|
|
"--bundle",
|
|
str(tmp_path / "bundle"),
|
|
"--ingested-at",
|
|
INGESTED_AT,
|
|
"--plans-dir",
|
|
str(plans),
|
|
]
|
|
)
|
|
assert code == 2
|
|
assert not (tmp_path / "bundle").exists()
|
|
|
|
|
|
def test_without_a_plan_directory_the_run_is_unchanged(tmp_path: Path) -> None:
|
|
"""The wiring is additive. A run with no plans stays exactly the flat,
|
|
`STRUCTURED_V1` run it was, so the K1/K2 numbers already published remain
|
|
reproducible from the same command."""
|
|
root = corpus(tmp_path, {"doc.md": SEGMENTABLE})
|
|
bundle = tmp_path / "bundle"
|
|
assert (
|
|
okf_corpus_run.main(
|
|
[
|
|
"--corpus",
|
|
str(root),
|
|
"--report",
|
|
str(tmp_path / "r.md"),
|
|
"--bundle",
|
|
str(bundle),
|
|
"--ingested-at",
|
|
INGESTED_AT,
|
|
]
|
|
)
|
|
== 0
|
|
)
|
|
concepts = [path for path in bundle.rglob("*.md") if path.name not in ("index.md", "log.md")]
|
|
assert len(concepts) == 1
|
|
assert "adjudication:" not in concepts[0].read_text(encoding="utf-8")
|
|
|
|
|
|
def test_a_second_run_into_the_same_bundle_reproduces_it_byte_for_byte(tmp_path: Path) -> None:
|
|
"""`log.md` is a file the harness writes INTO a directory Door B enumerates
|
|
on the next round: it matches the concept glob and is excluded only by
|
|
`index.md`'s name, so a rebuild could see it as pre-existing curated content
|
|
or prune it. Rebuild-equals-incremental is the property the segmented bundle
|
|
is built on, and a log that broke it would be worse than no log.
|
|
"""
|
|
root = corpus(tmp_path, {"doc.md": SEGMENTABLE, "flat.md": SUBSTANTIVE})
|
|
plans = tmp_path / "plans"
|
|
plans.mkdir()
|
|
_propose(root / "doc.md", plans / "doc.json")
|
|
bundle = tmp_path / "bundle"
|
|
argv = [
|
|
"--corpus",
|
|
str(root),
|
|
"--report",
|
|
str(tmp_path / "r.md"),
|
|
"--bundle",
|
|
str(bundle),
|
|
"--ingested-at",
|
|
INGESTED_AT,
|
|
"--plans-dir",
|
|
str(plans),
|
|
"--bundle-id",
|
|
"k2",
|
|
"--okf-version",
|
|
"0.2",
|
|
]
|
|
|
|
assert okf_corpus_run.main(argv) == 0
|
|
first = {
|
|
path.relative_to(bundle).as_posix(): path.read_bytes()
|
|
for path in sorted(bundle.rglob("*"))
|
|
if path.is_file()
|
|
}
|
|
assert "log.md" in first
|
|
|
|
assert okf_corpus_run.main(argv) == 0
|
|
second = {
|
|
path.relative_to(bundle).as_posix(): path.read_bytes()
|
|
for path in sorted(bundle.rglob("*"))
|
|
if path.is_file()
|
|
}
|
|
assert second == first
|
|
|
|
|
|
def test_the_root_index_links_the_bundles_own_log(tmp_path: Path) -> None:
|
|
"""A log nothing links is a file on disk, not a member of the bundle.
|
|
|
|
Measured on the artifact: the K2 bundle carried a conformant root `log.md`
|
|
that no index named, so a consumer walking the bundle from `index.md` --
|
|
which is the walk section 8 exists to support -- never reached the one file
|
|
carrying `N`.
|
|
|
|
This is a LOCAL choice, not a conformance requirement, and the distinction
|
|
is worth keeping straight. Section 9 lets `log.md` sit at any level and
|
|
section 8 has an index enumerate its directory's contents, but upstream's
|
|
own reference bundles do not link it: measured at `9a15b13`, 0 of the 24
|
|
shipped `index.md` files name the single `log.md` in the bundle set. So
|
|
upstream proves the link is not required, not that it is disallowed.
|
|
|
|
It is made HERE, in the harness, because the library cannot make it. The
|
|
log's content is the run's outcome, so it cannot be written before the
|
|
indexes are projected -- and an index that enumerated `log.md` off the
|
|
directory would gain the link only on the SECOND run, breaking the
|
|
rebuild-equals-incremental property the segmented bundle is built on. The
|
|
harness instead writes the link after the log, and only when it is not
|
|
already there -- a test, not an append, because the two reprojections
|
|
disagree about this line: the per-directory one drops it as a managed
|
|
entry, the flat one keeps it because its target is not an owned concept.
|
|
"""
|
|
root = corpus(tmp_path, {"doc.md": SEGMENTABLE, "flat.md": SUBSTANTIVE})
|
|
plans = tmp_path / "plans"
|
|
plans.mkdir()
|
|
_propose(root / "doc.md", plans / "doc.json")
|
|
bundle = tmp_path / "bundle"
|
|
argv = [
|
|
"--corpus",
|
|
str(root),
|
|
"--report",
|
|
str(tmp_path / "r.md"),
|
|
"--bundle",
|
|
str(bundle),
|
|
"--ingested-at",
|
|
INGESTED_AT,
|
|
"--plans-dir",
|
|
str(plans),
|
|
"--bundle-id",
|
|
"k2",
|
|
"--okf-version",
|
|
"0.2",
|
|
]
|
|
|
|
assert okf_corpus_run.main(argv) == 0
|
|
index = (bundle / "index.md").read_text(encoding="utf-8")
|
|
assert "](log.md)" in index
|
|
assert index.count("](log.md)") == 1
|
|
|
|
# The link is to the log in THIS directory, so a nested index must not
|
|
# carry one: there is no `log.md` beside it to reach.
|
|
for nested in bundle.rglob("*/index.md"):
|
|
assert "](log.md)" not in nested.read_text(encoding="utf-8")
|
|
|
|
# Rebuild equals incremental, still. This is what discriminates the two
|
|
# ways the append could be wrong: a link the reprojection keeps would be
|
|
# doubled here, and one the harness forgot to re-write would vanish.
|
|
first = {
|
|
path.relative_to(bundle).as_posix(): path.read_bytes()
|
|
for path in sorted(bundle.rglob("*"))
|
|
if path.is_file()
|
|
}
|
|
assert okf_corpus_run.main(argv) == 0
|
|
second = {
|
|
path.relative_to(bundle).as_posix(): path.read_bytes()
|
|
for path in sorted(bundle.rglob("*"))
|
|
if path.is_file()
|
|
}
|
|
assert second == first
|
|
|
|
|
|
def test_the_log_link_holds_on_the_unsegmented_path_too(tmp_path: Path) -> None:
|
|
"""The two run modes reproject through different code, so both are pinned.
|
|
|
|
A run without `--plans-dir` uses `STRUCTURED_V1`, whose index is not
|
|
per-directory and is rewritten by the singular reprojection rather than the
|
|
per-directory one. The harness writes `log.md` in both modes, so a link
|
|
that only held on the segmented path would leave the plainer bundle with
|
|
exactly the orphan this closes -- and if that path kept the line instead of
|
|
dropping it, the second run would carry two.
|
|
"""
|
|
root = corpus(tmp_path, {"a.md": SUBSTANTIVE, "b.md": SUBSTANTIVE})
|
|
bundle = tmp_path / "bundle"
|
|
argv = [
|
|
"--corpus",
|
|
str(root),
|
|
"--report",
|
|
str(tmp_path / "r.md"),
|
|
"--bundle",
|
|
str(bundle),
|
|
"--ingested-at",
|
|
INGESTED_AT,
|
|
]
|
|
|
|
assert okf_corpus_run.main(argv) == 0
|
|
index = (bundle / "index.md").read_text(encoding="utf-8")
|
|
assert "](log.md)" in index
|
|
|
|
assert okf_corpus_run.main(argv) == 0
|
|
assert (bundle / "index.md").read_text(encoding="utf-8") == index
|