fix: comparison and source pages accept the sources: cite add writes; sources may cite sources (#173)
CI / verify (push) Successful in 5m20s
CI / pwsh (push) Successful in 2m2s
Release / release (push) Successful in 36s

Files changed:
- CHANGES.md
- VERSION
- instructions/wiki-ingest/SKILL.md
- kb/CONTRACT.md
- tools/CONTRACT.md
- tools/chemenu/commands/cite_cmd.py
- tools/chemenu/commands/xref.py
- tools/chemenu/tests/test_cite_cmd.py
- tools/chemenu/tests/test_page_ops.py
- tools/chemenu/tests/test_type_resolver.py
- tools/chemenu/tests/test_xref.py
- types/comparison.md
- types/comparison.schema.yaml
- types/source.md
- types/source.schema.yaml

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
This commit is contained in:
torbenandClaude Opus 5.5 committed 2026-10-04 21:35:24 +02:00
1 parent 662a844cf1
commit 862bc04d9c
15 files changed
+310 -19

No files matched your search

+42 -1
View File
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
---
## 8.0.0-beta.39 - 2026-10-04 - wiki-ingest and wiki-manage call xref add with --rel, not the --rel-a/--rel-b removed in 4.0.0
## 8.0.0-beta.40 - 2026-10-04 - Comparison and source pages accept the sources: that cite add writes; sources may cite sources
**Author:** Torben Nehmer
@@ -107,6 +107,7 @@ concern - readable here, never shipped as something to parse.
- Link-Katalog: Beteiligungs- und RACI-Label von der Projektseite aus
- Organisationsseiten: Personen als Abschnitt mit Aufstieg, entity_type organization, member-of, Lint-Befund broken_anchors
- lint: Unfilled Template Sections - a section still holding only its template's TODO placeholders (advisory)
- Comparison and source pages accept the sources: that cite add writes; sources may cite sources
**Low impact**
- version bump no longer points at version release in its output
@@ -152,6 +153,46 @@ concern - readable here, never shipped as something to parse.
- wiki-ingest and wiki-manage call xref add with --rel, not the --rel-a/--rel-b removed in 4.0.0
<!-- /wikitool:bumps -->
### Comparison and source pages accept the sources: that cite add writes; sources may cite sources
`cite add` writes the cited source into the page's `sources:`, but neither the `comparison` nor
the `source` type declared that field, and both schemas set `additionalProperties: false`. So a
comparison page cited the way `kb/comparisons/COLLECTION.md` asks for, or a source page citing one
of its own raw files with `--file`, failed `lint` with `'sources' was unexpected`. Both types now
declare `sources` as an optional array, in the schema and in `page_ref_fields:`. The second part
matters: `rename`/`rm` carry only declared reference fields, and `lint` resolves only declared
fields. A fix to the schema alone would leave dead `sources:` entries behind after a source is
renamed, and nothing would report them.
That also makes a citation between two sources legal: `cite add --page "Source - A" --source
"Source - B"` records `Source - B` on A and leaves B alone. Two changes keep that edge directed:
- `cite add` no longer writes a page's own title into its `sources:`. A source page citing its own
raw file gets the footnote definition and leaves `sources:` untouched, not even as an empty
list. `lint` and `citing_pages()` already ignored the self-edge.
- `xref link-source` refuses a target that is itself a source page. It writes nothing for that
target, names it with the `cite add` command to use instead, links the others and exits 1. It
used to write `B.sources += A`, claiming the citation in the wrong direction, onto a page whose
schema then rejected it.
`kb/CONTRACT.md` § Provenance and citation and `wiki-ingest` step 9 describe both.
**What an existing instance has to do by hand.** The schema and type-spec of `comparison` and
`source` belong to the instance once adopted. `dist upgrade` only renews the `.template` beside
them, so the type half of this change does not arrive on its own. For each of the two types:
1. Copy the `sources` property from `types/<type>.schema.yaml.template` into
`types/<type>.schema.yaml`.
2. Add `sources` to `page_ref_fields:` in `types/<type>.md`. Without it a rename leaves the
entries pointing at the old title.
3. Copy the `sources` row of the frontmatter table into the same file.
An instance that has already loosened only the schema still needs step 2. The `cite add` and
`link-source` changes are under `tools/` and arrive with the normal upgrade. Until the steps
are done, `lint` keeps reporting a cited comparison or source page as a schema error, exactly as
before. Nothing that worked before breaks. Found while writing the first comparison page of an
instance on 7.0.0 (Gitea #173).
### wiki-ingest and wiki-manage call xref add with --rel, not the --rel-a/--rel-b removed in 4.0.0
Since 4.0.0 `xref add` declares one directed edge and takes a single `--rel`, but the
+1 -1
View File
@@ -1 +1 @@
8.0.0-beta.39
8.0.0-beta.40
+4
View File
@@ -351,6 +351,10 @@ validator complains - and the ticked list is the only record that they happened.
call of its own, is `kb/CONTRACT.md` § Linking. The second links the new source to everything
it backs in one pass.
An older source page the new one cites does not go into `--entities`: `link-source` refuses
it. Record it as a citation instead, `tools/wikitool cite add --page "Source - <Title>"
--source "<older source>"` - `kb/CONTRACT.md` § Provenance and citation has the rule.
10. **Check coverage.**
```bash
+11 -4
View File
@@ -300,7 +300,8 @@ Every claim is either traceable to a raw file or explicitly marked as not.
[--file <qualifier>]` mints the id, upserts its `[[Source - X]]` (or
`[[Source - X|storage-model.md]]` for a multi-file source) definition in the page's trailing
Footnotes block (named per [Section headings](#section-headings)), and adds `Source - X` to
`sources:` - it prints the marker to paste at
`sources:` - unless the page *is* `Source - X`, citing one of its own raw files, since a page
never lists its own title there. It prints the marker to paste at
the fact; placing it is still manual. Never hand-type a cite-id (AGENTS.md invariant 1). This
differs from a plain `[[Source - X]]` link, which only means "related to".
- **Notation inside code is notation, not a reference.** A `[^cite-id]` or a `[[wikilink]]`
@@ -309,13 +310,19 @@ Every claim is either traceable to a raw file or explicitly marked as not.
means a marker appended to a line *inside* a fence cites nothing - put it on a source line
under the block (`<source-word>: [^cite-id]`, in the KB language), where it renders as a
footnote instead of travelling with the command when someone copies it.
- A source cited inline must also appear in the page's frontmatter `sources:` list;
`wikitool lint` checks this in both directions, and hard-errors on a leftover pre-migration
- **A source page may cite another source page**, the same way any page does: `cite add --page
"Source - A" --source "Source - B"` writes `Source - B` into A's `sources:` and leaves B
untouched. A citation between two sources has a direction, and `cite add` is the only command
that records it - `xref link-source` refuses a target that is itself a source page.
- A source cited inline must also appear in the page's frontmatter `sources:` list - except a
source page's citation of itself, which never does; `wikitool lint` checks this in both
directions, and hard-errors on a leftover pre-migration
`^[[...]]` marker, an undefined `[^cite-id]` reference, or an orphaned Footnotes definition.
`tools/wikitool cite sync` reconciles a page's block after a prose edit changes which ids are
actually referenced.
- `tools/wikitool xref link-source --source "Source - X" --entities A,B,C` adds a new source
to every page it backs in one pass.
to every page it backs in one pass. Its targets are the pages the source *mentions*, never
another source page.
- Every raw file is expected to be claimed by some source page;
`tools/wikitool sources coverage` lists the ones that are not.
+5 -1
View File
@@ -755,12 +755,14 @@ Batch-link a source page to every entity/concept it mentions.
- 0 success
- 1 Source page not found
- 1 A page in `--entities` does not exist, or a target could not be written; the others were linked
- 1 A page in `--entities` is itself a source page; the others were linked
- 1 The source page itself could not be written after its targets were
**ON FAILURE**
- Source page not found -> Fix the title and retry once
- A page in `--entities` does not exist, or a target could not be written; the others were linked -> Fix the named pages and re-run - safe, since every write is idempotent. `sources trace --page "<Title>"` shows who is already linked
- A page in `--entities` is itself a source page; the others were linked -> Drop it from `--entities` and record the citation with `cite add --page "<citing source>" --source "<cited source>"` instead
- The source page itself could not be written after its targets were -> Fix the write failure and re-run (idempotent)
**NOTES**
@@ -769,6 +771,7 @@ Batch-link a source page to every entity/concept it mentions.
- Which field a target lands in follows its collection (`kb/entities/` -> `entities:`), so a new collection needs no code change.
- A target whose collection matches no reference field the source type declares is linked one way and named in the output.
- A target that does not exist is skipped and named; the others are still linked, and the run then exits 1.
- A target that is itself a source page is refused, named with the `cite add` command to use instead, and handled like a missing page: nothing is written for it, the others are still linked, and the run exits 1. `link-source` means "A mentions X" and writes `X.sources += A`, which for X = another source would claim the citation in the wrong direction; a citation between two sources has a direction only `cite add` records.
- Idempotent in both directions. `--dry-run` reports what would be linked.
**SEE ALSO**
@@ -897,7 +900,8 @@ Upsert a `[^cite-id]: [[Source - X]]` definition in a page's footnotes region.
- Upserts a `[^cite-id]: [[Source - X]]` definition in the page's generated footnotes region, creating the region between `<!-- wikitool:footnotes -->` markers if absent.
- Reuses the id when the page already cites this exact source/file pair, so a repeat changes nothing.
- Adds `Source - X` to the page's frontmatter `sources:`.
- Adds `Source - X` to the page's frontmatter `sources:` - except on `Source - X` itself: a source page citing one of its own raw files (`--file`) gets the definition and leaves `sources:` untouched, since a page never lists its own title there.
- A source page may cite another source page this way - `cite add` is the command that records a citation between two sources, in the direction it was made.
- Prints the `[^cite-id]` marker; pasting it into the prose is a manual, editorial step.
- `--dry-run` reports without writing.
+18 -7
View File
@@ -88,7 +88,12 @@ def upsert_citation(page: Page, source_title: str, qualifier: Optional[str]) ->
"""Ensure `page` has a Footnotes definition for (source_title, qualifier)
and that source_title is in its frontmatter `sources:`. Returns
(cite_id_to_use, new_body, changed) - reuses an existing definition for
the same pair instead of minting a duplicate id."""
the same pair instead of minting a duplicate id.
A source page citing itself (one of its own raw files, via `--file`) gets
the definition but no `sources:` entry - not even an empty list. An edge
to itself carries nothing, and `lint` and `provenance.citing_pages()`
already ignore it."""
head, definitions = split_cite_block(page.body)
existing_id = next(
@@ -103,10 +108,12 @@ def upsert_citation(page: Page, source_title: str, qualifier: Optional[str]) ->
definitions[marker_id] = (source_title, qualifier)
block_changed = True
sources = page.frontmatter.setdefault("sources", [])
sources_changed = source_title not in sources
if sources_changed:
sources.append(source_title)
sources_changed = False
if source_title != page.title:
sources = page.frontmatter.setdefault("sources", [])
sources_changed = source_title not in sources
if sources_changed:
sources.append(source_title)
new_body = render_page_body(head, definitions)
changed = block_changed or sources_changed or new_body != page.body
@@ -131,7 +138,11 @@ def upsert_citation(page: Page, source_title: str, qualifier: Optional[str]) ->
"region, creating the region between `<!-- wikitool:footnotes -->` markers if absent.",
"Reuses the id when the page already cites this exact source/file pair, so a repeat "
"changes nothing.",
"Adds `Source - X` to the page's frontmatter `sources:`.",
"Adds `Source - X` to the page's frontmatter `sources:` - except on `Source - X` "
"itself: a source page citing one of its own raw files (`--file`) gets the definition "
"and leaves `sources:` untouched, since a page never lists its own title there.",
"A source page may cite another source page this way - `cite add` is the command that "
"records a citation between two sources, in the direction it was made.",
"Prints the `[^cite-id]` marker; pasting it into the prose is a manual, editorial "
"step.",
"`--dry-run` reports without writing.",
@@ -168,7 +179,7 @@ def cite_add(
):
"""Upsert a Footnotes definition for `--source` (reusing it if the page
already cites the same source/file pair) and ensure `--source` is in the
page's frontmatter `sources:`. Prints the `[^cite-id]` marker to paste
page's frontmatter `sources:` (unless the page is `--source` itself). Prints the `[^cite-id]` marker to paste
into the prose - placing it is still the caller's job.
"""
pages = load_kb_pages(config.KB_DIR)
+27 -1
View File
@@ -389,6 +389,11 @@ def xref_remove(
"linked one way and named in the output.",
"A target that does not exist is skipped and named; the others are still linked, and "
"the run then exits 1.",
"A target that is itself a source page is refused, named with the `cite add` command to "
"use instead, and handled like a missing page: nothing is written for it, the others "
"are still linked, and the run exits 1. `link-source` means \"A mentions X\" and writes "
"`X.sources += A`, which for X = another source would claim the citation in the wrong "
"direction; a citation between two sources has a direction only `cite add` records.",
"Idempotent in both directions. `--dry-run` reports what would be linked.",
),
failures=(
@@ -402,6 +407,11 @@ def xref_remove(
reaction="Fix the named pages and re-run - safe, since every write is idempotent. "
"`sources trace --page \"<Title>\"` shows who is already linked",
),
cli_contract.Failure(
cause="A page in `--entities` is itself a source page; the others were linked",
reaction="Drop it from `--entities` and record the citation with "
"`cite add --page \"<citing source>\" --source \"<cited source>\"` instead",
),
cli_contract.Failure(
cause="The source page itself could not be written after its targets were",
reaction="Fix the write failure and re-run (idempotent)",
@@ -431,6 +441,7 @@ def xref_link_source(
linked: list[str] = []
skipped: list[str] = []
refused: list[str] = []
failed: list[str] = []
unrouted: list[str] = []
source_changed = False
@@ -439,6 +450,13 @@ def xref_link_source(
if page is None:
skipped.append(name)
continue
if page.kind == "source":
# Refused rather than linked: this would write `name.sources +=
# source` ("name cites source" - the wrong direction), and the
# back-reference would add `source.sources += name` as well. Which
# of two sources cites the other is only said by `cite add`.
refused.append(name)
continue
sources = page.frontmatter.setdefault("sources", [])
if source not in sources:
sources.append(source)
@@ -489,6 +507,12 @@ def xref_link_source(
typer.echo(f"{verb} source '{source}' to: {', '.join(linked)}")
if skipped:
typer.echo(f"Skipped (page not found): {', '.join(skipped)}")
for name in refused:
typer.echo(
f"Refused (a source page - record a citation between sources with "
f"`wikitool cite add --page \"{source}\" --source \"{name}\"` if '{source}' cites it, "
f"or the other way round): {name}"
)
if failed:
typer.echo(f"Failed to write (fix and re-run for just these names): {', '.join(failed)}")
@@ -496,10 +520,12 @@ def xref_link_source(
typer.echo("No files written (--dry-run).")
return
if skipped or failed:
if skipped or refused or failed:
parts = []
if skipped:
parts.append(f"page(s) not found: {', '.join(skipped)}")
if refused:
parts.append(f"source page(s) refused as targets: {', '.join(refused)}")
if failed:
parts.append(f"page(s) failed to write: {', '.join(failed)}")
fail(f"Linked {len(linked)}/{len(names)} page(s); " + "; ".join(parts))
+118
View File
@@ -203,3 +203,121 @@ def test_cite_sync_command_over_kb(kb_dir, raw_dir, monkeypatch):
result = runner.invoke(app, ["cite", "sync", "--all", "--dry-run"])
assert result.exit_code == 0, result.output
assert "No pages needed a Footnotes block change." in result.output
# --- citing on comparison and source pages ----------------------------------
#
# Both types once declared no `sources:` field, while `cite add` wrote one onto
# every page it touched - so a correctly cited comparison or source page failed
# `lint` with `'sources' was unexpected`, under `additionalProperties: false`.
def _write_source(kb_dir, title, raw_file):
"""A source page the shipped schema accepts as it stands - the fixture's own
`Source - Aurora` carries a stray `source:` key and would mask the finding."""
from chemenu.frontmatter_io import write_page
path = kb_dir / "sources/notes" / f"{title}.md"
write_page(
path,
{
"type": "types/source.md", "source_type": "notes", "author": "Fixture Author",
"raw_files": [raw_file], "date": "2026-08-02", "summary": f"{title} summary",
},
f"\n# Source: {title}\n\n## Summary\n\nNotes.\n",
)
return path
def _schema_errors_for(kb_dir, title):
from chemenu.lint_core import run_lint
return [e for e in run_lint(kb_dir)["schema_validation_errors"] if e["page"] == title]
def test_cite_add_on_a_comparison_page_keeps_it_schema_valid(kb_dir, raw_dir):
from chemenu.cli import app
from chemenu.frontmatter_io import write_page
path = kb_dir / "comparisons/aurora vs Borealis.md"
write_page(
path,
{
"type": "types/comparison.md", "created": "2026-08-02",
"entities": ["aurora", "Borealis"], "summary": "Server against workstation",
},
"\n# Comparison: aurora vs Borealis\n\n## Überblick\n\nTwo machines.\n",
)
assert _schema_errors_for(kb_dir, "aurora vs Borealis") == []
result = runner.invoke(
app, ["cite", "add", "--page", "aurora vs Borealis", "--source", "Source - Aurora"]
)
assert result.exit_code == 0, result.output
fm, _body = read_page(path)
assert fm["sources"] == ["Source - Aurora"]
assert _schema_errors_for(kb_dir, "aurora vs Borealis") == []
def test_a_source_page_may_cite_another_source_page(kb_dir, raw_dir):
"""The citation lands on the citing side only, in the direction it was made."""
from chemenu.cli import app
from chemenu.provenance import citing_pages
path_a = _write_source(kb_dir, "Source - A", "raw/notes/Aurora.md")
path_b = _write_source(kb_dir, "Source - B", "raw/notes/Uningested.md")
before_b = path_b.read_bytes()
result = runner.invoke(app, ["cite", "add", "--page", "Source - A", "--source", "Source - B"])
assert result.exit_code == 0, result.output
fm_a, _body = read_page(path_a)
assert fm_a["sources"] == ["Source - B"]
assert path_b.read_bytes() == before_b
assert _schema_errors_for(kb_dir, "Source - A") == []
assert "Source - A" in citing_pages(load_kb_pages(kb_dir), "Source - B")
def test_a_source_citing_its_own_file_writes_no_sources_entry(kb_dir, raw_dir):
"""A self-citation names one of the page's own raw files. The definition is
written; `sources:` is left exactly as it was - not even an empty list."""
path = _write_source(kb_dir, "Source - A", "raw/notes/Aurora.md")
page = _reload(kb_dir, "Source - A")
assert "sources" not in page.frontmatter
marker_id, body, changed = upsert_citation(page, "Source - A", "Aurora.md")
assert changed is True
assert "sources" not in page.frontmatter
assert f"[^{marker_id}]: [[Source - A|Aurora.md]]" in body
page.body = body
_id, _body, changed_again = upsert_citation(page, "Source - A", "Aurora.md")
assert changed_again is False
assert path.exists()
def test_a_self_citation_leaves_an_existing_sources_list_unchanged(kb_dir, raw_dir):
_write_source(kb_dir, "Source - A", "raw/notes/Aurora.md")
page = _reload(kb_dir, "Source - A")
page.frontmatter["sources"] = ["Source - Aurora"]
upsert_citation(page, "Source - A", "Aurora.md")
assert page.frontmatter["sources"] == ["Source - Aurora"]
def test_cite_add_self_citation_on_disk(kb_dir, raw_dir):
from chemenu.cli import app
path = _write_source(kb_dir, "Source - A", "raw/notes/Aurora.md")
args = ["cite", "add", "--page", "Source - A", "--source", "Source - A", "--file", "Aurora.md"]
result = runner.invoke(app, args)
assert result.exit_code == 0, result.output
fm, body = read_page(path)
assert "sources" not in fm
assert f"[^{cite_id('Source - A', 'Aurora.md')}]: [[Source - A|Aurora.md]]" in body
assert _schema_errors_for(kb_dir, "Source - A") == []
again = runner.invoke(app, args)
assert again.exit_code == 0, again.output
assert "Already up to date" in again.output
+30
View File
@@ -193,6 +193,36 @@ def test_rename_fixes_a_dangling_source_reference(patched_wiki):
assert frontmatter["sources"] == ["Source - Aurora Notes"]
def test_rename_carries_sources_on_comparison_and_source_pages(patched_wiki):
"""Both types declare `sources` in `page_ref_fields:`. A schema-only fix
would have let them validate, but a rename would then have left a dead
`sources:` entry behind on each - unreported, since `lint` resolves only
the declared fields too."""
from chemenu.lint_core import run_lint
write_page(
patched_wiki / "comparisons/aurora vs Borealis.md",
{"type": "types/comparison.md", "created": "2026-08-02",
"entities": ["aurora", "Borealis"], "sources": ["Source - Aurora"],
"summary": "Server against workstation"},
"\n# Comparison: aurora vs Borealis\n",
)
write_page(
patched_wiki / "sources/notes/Source - B.md",
{"type": "types/source.md", "source_type": "notes", "author": "Fixture Author",
"raw_files": ["raw/notes/Uningested.md"], "date": "2026-08-02",
"sources": ["Source - Aurora"], "summary": "B"},
"\n# Source: B\n",
)
page_ops.rename_command(old="Source - Aurora", new="Source - Aurora Notes", dry_run=False)
for path in ("comparisons/aurora vs Borealis.md", "sources/notes/Source - B.md"):
frontmatter, _body = read_page(patched_wiki / path)
assert frontmatter["sources"] == ["Source - Aurora Notes"]
dangling = {e["page"] for e in run_lint(patched_wiki)["dangling_frontmatter_refs"]}
assert not dangling & {"aurora vs Borealis", "Source - B"}
def test_rename_stops_before_renaming_the_file_if_a_reference_write_fails(patched_wiki, monkeypatch):
"""A write failing partway through must not still rename the target file -
that would leave some references pointing at the new title and others
+2 -2
View File
@@ -45,8 +45,8 @@ def test_get_page_ref_fields_reads_the_type_spec():
so `lint`/`rename`/`rm` need no hardcoded list to update for a new type."""
assert resolver.get_page_ref_fields("types/entity.md") == ["related", "sources"]
assert resolver.get_page_ref_fields("types/concept.md") == ["related", "sources"]
assert resolver.get_page_ref_fields("types/source.md") == ["entities", "concepts"]
assert resolver.get_page_ref_fields("types/comparison.md") == ["entities", "related"]
assert resolver.get_page_ref_fields("types/source.md") == ["entities", "concepts", "sources"]
assert resolver.get_page_ref_fields("types/comparison.md") == ["entities", "related", "sources"]
def test_page_ref_fields_exist_in_the_type_schema():
+32
View File
@@ -482,6 +482,38 @@ def test_xref_link_source_dry_run_leaves_the_source_page_alone(kb_dir):
assert path.read_text(encoding="utf-8") == before
def test_xref_link_source_refuses_a_target_that_is_itself_a_source(kb_dir):
"""`link-source` means "A mentions X" and writes `X.sources += A`; for X =
another source that claims "B cites A", and the back-reference would add
`A.sources += B` too. A citation between sources has a direction, and only
`cite add` says which one - so the target is refused, the rest still linked."""
from chemenu.frontmatter_io import write_page
runner, app = _runner_env(kb_dir)
path_b = kb_dir / "sources/notes/Source - B.md"
write_page(
path_b,
{"type": "types/source.md", "source_type": "notes", "author": "Fixture Author",
"raw_files": ["raw/notes/Uningested.md"], "date": "2026-08-02", "summary": "B"},
"\n# Source: B\n",
)
result = runner.invoke(
app,
["xref", "link-source", "--source", "Source - Aurora", "--entities", "gdeploy,Source - B"],
)
assert result.exit_code == 1
assert "Source - B" in result.output
assert 'cite add --page "Source - Aurora" --source "Source - B"' in result.output
pages = load_kb_pages(kb_dir)
assert "Source - Aurora" in pages["gdeploy"].frontmatter["sources"]
source_a = pages["Source - Aurora"].frontmatter
source_b = pages["Source - B"].frontmatter
for field in ("sources", "entities", "concepts"):
assert "Source - B" not in (source_a.get(field) or [])
assert "Source - Aurora" not in (source_b.get(field) or [])
# --- the inbound view ------------------------------------------------------
+2 -1
View File
@@ -4,7 +4,7 @@ name: comparison
description: Structured type for comparison pages that set several entities or approaches against one another
schema: types/comparison.schema.yaml
base_dir: comparisons
page_ref_fields: [entities, related]
page_ref_fields: [entities, related, sources]
guidance: types/comparison.guidance.md
---
@@ -24,6 +24,7 @@ and how to write a conforming page is [types/comparison.guidance.md](comparison.
| `created` | Yes | Creation date (YYYY-MM-DD) |
| `entities` | Yes | Titles of the entities compared |
| `related` | No | Declared outbound edges - one `compares-with` edge per subject, written by `wikitool xref add` |
| `sources` | No | Titles of the source pages cited on this page, written by `wikitool cite add` |
| `summary` | Yes | One-liner for `kb/index.md` |
## Template
+7
View File
@@ -38,6 +38,13 @@ properties:
provenance-style list, so without this one the edge the page exists to
make would live only in hand-written prose - which is the thing
instructions/link-taxonomy.md was built to end.
sources:
type: array
items:
type: string
description: >-
Source page titles cited on this page, written by `wikitool cite add`.
Optional, so a comparison that cites nothing keeps validating without it.
summary:
type: string
description: 1-line summary for index.md
+2 -1
View File
@@ -6,7 +6,7 @@ schema: types/source.schema.yaml
subtype_field: source_type
base_dir: sources
title_prefix: "Source - "
page_ref_fields: [entities, concepts]
page_ref_fields: [entities, concepts, sources]
capture_fields: [fidelity, authority]
guidance: types/source.guidance.md
layout:
@@ -42,6 +42,7 @@ how to write a conforming page is [types/source.guidance.md](source.guidance.md)
| `tags` | No | Navigation tags for categorization |
| `entities` | No | Titles of the entities mentioned in this source |
| `concepts` | No | Titles of the concepts mentioned in this source |
| `sources` | No | Titles of *other* source pages this source cites, written by `wikitool cite add` - never its own title |
| `summary` | Yes | One-liner for `kb/index.md` |
## Template
+9
View File
@@ -72,6 +72,15 @@ properties:
items:
type: string
description: Concept titles mentioned in this source
sources:
type: array
items:
type: string
description: >-
Other source pages this source cites, written by `wikitool cite add`.
Never this page's own title - a citation of one of its own raw files
leaves the field untouched. Optional, so a source that cites no other
source keeps validating without it.
summary:
type: string
description: 1-line summary for index.md