types/: Seiten-Type-Spec-Anleitungsprosa in stackeigene guidance-Datei ausgelagert (schliesst #104)
CI / verify (push) Failing after 43s
Release / release (push) Successful in 34s

Files changed:
- AGENTS.md
- CHANGES.md
- VERSION
- docs/ownership-and-templates.md
- instructions/migrations/6.0.0-type-guidance-split.md
- instructions/setup-instance.md
- tools/CONTRACT.md
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/commands/types_cmd.py
- tools/chemenu/tests/test_dist_cmd.py
- tools/chemenu/tests/test_dist_upgrade.py
- tools/chemenu/tests/test_type_resolver.py
- tools/chemenu/tests/test_types_cmd.py
- tools/chemenu/type_resolver.py
- tools/chemenu/types_core.py
- types/comparison.guidance.md
- types/comparison.md
- types/concept.guidance.md
- types/concept.md
- types/entity.guidance.md
- types/entity.md
- types/source.guidance.md
- types/source.md
- types/type-guidance.md
- types/type-guidance.schema.yaml
- types/type-spec.md
This commit is contained in:
torben committed 2026-09-15 18:23:38 +02:00
1 parent 90ce41964f
commit d49513bda6
26 files changed
+789 -238

No files matched your search

+35 -4
View File
@@ -338,6 +338,32 @@ def instance_owned_type_stems() -> set[str]:
return stems
# The suffix a type-spec's own two files carry - `<stem>.md` and
# `<stem>.schema.yaml` - as opposed to a sibling file that merely starts with
# the same stem, such as `<stem>.guidance.md` (Gitea #104). Checked as an
# exact suffix rather than by splitting on the first `.`, which is what let
# `entity.guidance.md` be mistaken for the `entity` type-spec's own file
# before this existed - a stack-owned file re-keyed as though it were the
# instance's `.template` to adopt, and flagged as a leak by the other call
# site for not being one.
_TYPE_SCHEMA_SUFFIX = ".schema.yaml"
def _owned_type_stem(relative: str) -> Optional[str]:
"""The type stem `relative` (a path under `types/`, no `.template`
suffix) names, if it is exactly that type-spec's own `<stem>.md` or
`<stem>.schema.yaml` - `None` for anything else under `types/`,
including a `<stem>.guidance.md` file. `_plan_types()` and `find_leaks()`
both ask this instead of computing their own stem, so the two answer the
same question about the same path (AGENTS.md invariant 8)."""
name = relative.rsplit("/", 1)[-1]
if name.endswith(_TYPE_SCHEMA_SUFFIX):
return name[: -len(_TYPE_SCHEMA_SUFFIX)]
if name.endswith(".md") and not name.endswith(".guidance.md"):
return name[: -len(".md")]
return None
def _plan_types() -> dict[str, PlannedFile]:
"""`types/`, with the page type-specs re-keyed as templates.
@@ -347,6 +373,11 @@ def _plan_types() -> dict[str, PlannedFile]:
to be adopted before it counts. A type-spec's `.schema.yaml` travels with
it, because the two are one type (see types/type-spec.md § Anatomy) and
adopting half of it would leave a spec validated by a file it does not own.
A type-spec's optional `<name>.guidance.md` (Gitea #104) is the opposite:
stack-owned even where the type-spec itself is instance-owned, and ships
verbatim beside the `.template` - `_owned_type_stem` is what keeps it out
of this re-keying despite sharing the type-spec's own stem.
"""
plan = _copy_tree(config.TYPES_DIR, "types", frozenset())
stems = instance_owned_type_stems()
@@ -355,9 +386,8 @@ def _plan_types() -> dict[str, PlannedFile]:
rekeyed: dict[str, PlannedFile] = {}
for relative, planned in plan.items():
name = relative.rsplit("/", 1)[-1]
stem = name.split(".", 1)[0]
if stem in stems:
stem = _owned_type_stem(relative)
if stem is not None and stem in stems:
rekeyed[f"{relative}.template"] = planned
else:
rekeyed[relative] = planned
@@ -512,7 +542,8 @@ def find_leaks(plan: dict[str, PlannedFile]) -> list[str]:
elif (
relative.startswith("types/")
and not relative.endswith(".template")
and name.split(".", 1)[0] in owned_types
and (owned_stem := _owned_type_stem(relative)) is not None
and owned_stem in owned_types
):
leaks.append(f"{relative} (this instance's page type-spec; ship the .template)")
elif relative.startswith("instructions/dev/"):
+13 -6
View File
@@ -55,7 +55,10 @@ def describe_type_command(
"""Print one type's full contract: frontmatter fields (required/optional,
with enums where declared), its subtype field if any, and its authoring
body - the same information an LLM would otherwise gather by reading the
raw type-spec and `.schema.yaml` files directly."""
raw type-spec and `.schema.yaml` files directly. Where the type-spec
declares `guidance:`, that stack-owned file's prose is composed in ahead
of the type-spec's own body, so a `root: kb` type's contract still reads
as one answer even though it lives in two files (Gitea #104)."""
try:
described = describe_type(name)
except UnknownType as exc:
@@ -89,9 +92,13 @@ def describe_type_command(
typer.echo("")
typer.echo("## Authoring guidance")
# A type-spec over 100 lines carries a generated table-of-contents region
# (`chemenu/toc.py`), which serves whoever opens the file. Here it would be
# noise: this command already hands over the whole body, so there is
# nothing left for a navigation aid to navigate - only markers and a list
# of headings the reader is about to see anyway.
# A type-spec (and its guidance file) over 100 lines carries a generated
# table-of-contents region (`chemenu/toc.py`), which serves whoever opens
# the file directly. Here it would be noise: this command already hands
# over the whole body, so there is nothing left for a navigation aid to
# navigate - only markers and a list of headings the reader is about to
# see anyway.
if described["guidance"]:
typer.echo(toc.strip_region(described["guidance"]))
typer.echo("")
typer.echo(toc.strip_region(described["body"]))