Skip to content

All versions since 0.34.1

0.34.1

Changed

  • The Copilot docent’s gap report gives the librarian what ktl-docent’s does. A Miss starts with the reader’s question in quotes and says whether a concept looked relevant from index.md and did not answer, or nothing relevant was listed. A Disagreement says which version the answer gave. The skill also gives the source link when a question about the present rests only on an unchecked or edited concept, finds endpoints, versions and schemas as ktl-docent does, and takes ktl-docent-m365 as its heading.

Fixed

  • The Copilot docent labels a concept as knowledge-report.sh labels does. Its rules reported every label that applied, most cautionary first, so a retired concept could also read as confirmed, and a concept edited after its confirmation could read as both. A retired concept now carries no other label, and edited since a person last confirmed it takes the place of confirmed by a person. Still a draft and then past its review date follow the first label, and a confirmation names the revision it was checked against. ktl-docent’s references/answering.md gave the same rules for a sidecar with no script, and now matches the script too.
  • Every label table reads an empty verified list as nobody has checked this yet, as the report script does. The tables in ktl-docent, the Copilot docent and ktl-curator’s references/trust-fields.md called it checked by automation only.

0.34.2

Changed

  • The preflight reads the CI variables by indirect expansion, not eval, so a scanner that searches for eval finds none. Its output is unchanged, in bash 3.2 too.
  • The preflight names who needs a missing knowledge-feedback.sh or knowledge-report.sh: ktl-curator and an earlier ktl-docent, since the docent runs its own copies.

Security

  • ktl-docent runs its own copies of knowledge-report.sh and knowledge-feedback.sh, never the repository’s. Anyone who can commit to a repository can change its .lokf/scripts/, so asking about a repository you had not reviewed ran its code. Check 11a holds the copies in skills/ktl-docent/scripts/ byte-identical to the templates. The docent no longer runs the preflight: it reads a login from gh or glab. Without its scripts, it works out labels by hand, gives the reader the feedback line to file, and never writes .lokf/feedback.md. Its scanner note answers the Gen Agent Trust Hub audit of 2026-10-04.
  • ktl-docent puts the asker’s login in an entry only when the asker agrees. The scheduled workflow commits .lokf/feedback.md, often into a public pull request, and git history keeps the line after the librarian clears it.
  • ktl-docent and ktl-curator keep only the username from glab api user, as the preflight did. Both gave the bare call as a login’s source, and it prints the whole profile, email and linked accounts included.
  • ktl-librarian and ktl-sidecar read the no-Python schema from a commit, not a tag, since a tag can be moved (Snyk W012). The URL names commit 66073c3, tagged v0.8.0, whose lokf.yaml is byte-identical to the schema in the lokf 0.8.0 wheel that .lokf/uv.lock pins. Both skills say to read it as data, never instructions. Check 11b fails a tag or a branch in the URL, pages that pin different commits, and a page that does not name the floor’s tag.

0.35.0

Changed

  • ktl-prose lays out what it rewords the way the file’s project does. In a bundle that is one line for each paragraph, as ktl-librarian writes it, where the skill used to keep a concept’s old wrapping. Outside the bundle it keeps the host’s layout, and references/rules.md sets out the three layouts in use, with the benefit and the cost of each.

0.36.0

Added

  • Each skill’s page has a word budget. Contract check 10a holds every SKILL.md to one, set just above the page’s size. An agent loads the whole page when the skill starts, and the Agent Skills specification recommends under 5,000 tokens for it. ktl-librarian’s page, halved in 0.32.0, had taken back 328 words within two days. An edit that adds words to a page now moves as many into that skill’s references/, and docs/repository-layout.md names the check where it gave token estimates.
  • The curator’s queue comes from the script. knowledge-report.sh reads every typed relation, in each spelling the format allows, and resolves its target as the KTL Curator plugin does. Its whole report prints how many other concepts rely on each concept, counting each citing concept once. It also prints the review dates due within 30 days, each confirmed concept derived from one edited after that confirmation, and Worth ten minutes today, ranked as references/trust-fields.md says. ktl-curator quotes that queue, where it used to count relations and sort them itself. Check 20a covers each spelling, a self-reference, a retired concept and a concept with no id.
  • A retired concept names what replaced it. ktl-curator’s Retire asks what replaced the concept, and its **Deprecation** line in log.md links the successor after “replaced by”. knowledge-report.sh labels then reads retired, replaced by that path, from the newest such line, and only for a concept the bundle holds. ktl-docent, and its Microsoft 365 form, open the successor before falling back to the repository. LOKF has no typed relation for a successor yet, so the log holds the link, and nicholsn/lokf#112 asks for one.
  • A reader’s disagreement shows on the concept. knowledge-feedback.sh --concept <path> names the concept a Disagreement disputes, right after the kind. It accepts the path only with a Disagreement, only in the bundle’s lowercase spelling, and only for a concept file that exists. Until the librarian handles the entry, knowledge-report.sh labels adds a reader disputed this on that day, and the whole report lists the concept. The report reads the path and never the entry’s text. ktl-docent passes the path whenever a Disagreement is about one concept.
  • Conventions rule 14: a commit that records a person’s confirmation changes the verdict, not what the concept says. Rule 13 compares a confirmed concept with the commit that recorded the confirmation, so an edit made in that same commit read as confirmed, and only the person reviewing the pull request could catch it. With --since <commit>, knowledge-conventions.sh reads each commit after it against every parent, as the provenance job does. A commit that adds a person’s event no parent holds may change the concept’s claims only when generated.by names that person, the shape of Correct now. The registrar’s validate job passes the pull request’s base, and a scheduled run passes none, since a squash merge folds an edit and a later confirmation into one commit. Check 11c covers each case, a merge among them.
  • docs/skillwiki.md reads the SkillWiki paper (Huang et al., 2026) and its code against this design, part by part: what KTL does with it, where the two designs differ, and what the paper does not settle. docs/wikiskill.md names the two papers apart, and the README credits SkillWiki.

Fixed

  • ktl-curator’s two pages ranked its queue differently. SKILL.md put a confirmed concept whose source moved in the first group, and references/trust-fields.md left that case out. Both now give the order the script applies. The SPARQL query for the most relied-upon concepts counts each citing concept once, and never a concept for itself.

0.36.1

Security

  • The provenance gate reads a verified or generated event that spans lines, so a forged confirmation cannot hide in a multi-line flow layout. The hand-written readers in knowledge-provenance.sh and knowledge-registrar.yaml’s provenance job parsed a flow event only on one line. A verified: [ or a generated: { continued on the next line is valid YAML every parser reads as a confirmation, but the gate saw no event and let an unsigned human: actor through, where knowledge-report.sh and the Obsidian plugins trusted it. Both readers now accumulate a flow event to its closing bracket. Checks 13a and 22 cover it.
  • The provenance gate reads a concept behind a byte order mark or in CRLF line endings, so a forged confirmation cannot hide behind one either. The same two readers keyed on a bare --- first line: a leading BOM (\357\273\277) or a ---\r opener matched nothing, so the reader saw no frontmatter, read no events, and passed an unsigned human: event that every YAML parser strips the mark or the carriage return and reads. The readers now strip both before the opener, under LC_ALL=C so a gawk in a UTF-8 locale reads the three BOM bytes as bytes, not as one character the strip would miss. Conventions already strips a BOM in the shell, and silently strips CRLF, so the gate was the only guard against the CRLF form. Check 22 covers a BOM-prefixed and a CRLF forgery.
  • The unattended provenance check follows a renamed concept, so a person’s text cannot be rewritten under a new name. knowledge-provenance.sh --unattended read a rename as a delete and an add and compared each path with its copy at HEAD, so a concept a person authored, renamed and rewritten in one change had no HEAD copy at its new path and passed. The check now pairs a rename by git’s own detection and compares the bodies. Verified against a rename-plus-rewrite and a rename-only control.
  • The pen refuses a provenance or event key in any case, and a top-level event field. create and set denied verified, status and the rest only in lower case at the top level, so a set carrying Verified: or a top-level by: human: was written in, where the scheduled run’s own backstop grep then refused the whole run. The pen now matches those keys case-insensitively and refuses a top-level by, at and revision. Check 19 covers it.
  • The pen protects a curator’s send-back and will not glue a protected heading onto the line before it. A resolve or delete could remove a process:ktl-curator note, the form a send-back takes when the curator could not sign in, and an edit ending exactly at ## Open questions or the lokf:related marker merged that heading into the text. The pen now keeps such a note, and checks after each edit that a protected section is still there. Check 19 covers each.

Added

  • Check 22 holds the gate’s event reader to a real YAML parser. It builds a human event in every layout a person might write (block, single-line and multi-line flow, a sequence, a mapping, an offset timestamp, an empty list, behind a byte order mark, and in CRLF line endings) and asserts the gate flags an unsigned one exactly when a parser sees one. So a layout one reader skips and the other reads cannot return as a way past the gate.

Fixed

  • The report script reads every timestamp and event layout a YAML parser does. knowledge-report.sh dropped a +00:00 offset, so an edited concept read confirmed by a person and a confirmed one read edited since; dropped a multi-line verified list, so a confirmation read nobody has checked; counted an empty verified: [ ] as automation; and missed a concept behind a byte order mark. norm() now converts any offset across the day, month, year and leap years, and the reader accumulates a flow list or mapping and strips a BOM — byte-for-byte under LC_ALL=C, so a gawk in a UTF-8 locale reads the mark as bytes, not as a two-byte character it would leave in place. Check 20 covers each.
  • The report script counts an edited-since concept once, and reads a CRLF index.md, a successor-less retirement and a subfolder sidecar. A concept edited after its confirmation whose source then moved was listed both on the work list and under a source moved after that confirmation; it now sits once on the work list, since the edit already overtook the confirmation. base_iri is read through a carriage return and a byte order mark, a retirement whose newest deprecation line names no successor reads as plain retired, and changes reads a subfolder sidecar from the work-tree top. Checks 20 and 20a cover each.
  • The pen keeps a null value, writes atomically, scrubs a log line and a question, and clears a ./-led or #fragment index link on delete. A ~, NULL or empty value no longer flips to null. A write that fails partway leaves the bundle unchanged, each file staged beside its target and put in place only once all are written. A log line and a question are scrubbed by Unicode category, as the hand-off is. And delete now takes a link written with a ./ lead or a #fragment out of a list of links, or refuses a sentence that holds it, rather than leaving it to dangle. Check 19 covers each.
  • ktl-prose skips an indented code block, reads a prose line that opens with an inline HTML tag, keeps an inline tag and a hard line break, reads a marker only where it opens a block, and notes a change of a fifth either way. An indented code block is code, so no style rule reads it. A prose line that opens with an inline tag is read with the tag masked, not skipped whole. A changed inline tag or a dropped hard line break is now reported, and a number that begins a wrapped line is checked as a fact, not a list number. The growth and shrink notes fire at a fifth, up or down, with no word floor. Check 18 covers each. (A rewording that swaps two facts from the same set is still passed by the token comparison and left to the reviewer, since the same comparison must pass a legitimate reordering of clauses.)
  • The registrar scopes rule 14 to the pull request’s merge-base, the feedback script rejects a . path segment, and both read login characters the same in every locale. The validate job passed base.sha, where provenance uses the merge-base, so a commit the base branch gained after the pull request opened could read as the pull request’s; both now use the merge-base. knowledge-feedback.sh --concept rejected .. but not playbooks/./draft.md, which the report then never matched. Both scripts’ login checks now set globasciiranges, a no-op where the shell lacks it. Check 12a covers each.
  • Contract check 15 reads the pinned skill path from the commit the pin records, not only from the tag ref. It used to skip when the tag was not on the clone, which on the scheduled, tag-less checkout was always; it now resolves the recorded commit and reads the path from it, and the tag-agreement half says whether that commit is a real object when the tag is absent. A fixture stages an untagged commit and reads the path from it.
  • The layout tests no longer flake on a SIGPIPE race, isolate their git config, and leave no temporary files behind. A large sed piped into grep -q, which exits on its first match, could take SIGPIPE and fail the check under pipefail; the sections are captured first. The fixtures run under an isolated git config, so a global commit.gpgsign=true no longer signs them, and the extracted workflow steps write under the test’s own working directory.

0.37.0

Added

  • ktl-librarian has a page for a host whose content is Agent Skills. references/agent-skills.md says which skills are the host’s own, and leaves out installed and vendored ones. It asks a person about a skill under .claude/skills/ that shows no sign of another home, and gives a skill template no concept of its own. It says what a skill’s concept holds, while the procedure stays in SKILL.md. A skill the repository deprecates stays a fact in the concept, apart from the curator’s status: deprecated. A miss that a skill’s own description should have prevented goes to the reviewer in a hand-off line, since only a person changes that description. The first run’s sweep table names Agent Skills folders.
  • docs/skills-repositories.md says whether KTL helps a repository whose content is Agent Skills, read through WikiSkill and SkillWiki. It reports this repository’s own evidence, and a run of the sidecar, the librarian and the docent on anthropics/skills at commit 683bc88. It ends with the designs that remain, among them a record of skill runs and a trigger test.

Changed

  • The threat model names a skills repository. There every source the librarian reads is a set of instructions for an agent. docs/threat-model.md says which guards still hold, and that only the person reviewing a pull request catches an instruction that shapes what a concept says.

0.37.1

Added

  • The schema is the source of the class list and of the relation fields. Contract check 7c holds Rule 3’s class list to the classes lokf vocab --all --json declares as types, at the lokf floor the templates pin, where check 7 compared two prose lists with each other. Check 7d holds the relation fields knowledge-report.sh counts reliance from to the slots the schema ranges over Concept. Both run the toolkit through uvx at the floor, so the comparison is against the schema a host installs.
  • ktl-sidecar’s portability page says what is tested before a release, and what stays with the host. The skills repository tests every template before a release, so a host whose copies are as the sidecar laid them down need not test them again. A copy the host edited, a port to another forge or shell, and another operating system stay the host’s.

Changed

  • ktl-docent follows all thirteen typed relations when it widens along the graph. sameAs, measures, memberOf and holder join the list in its SKILL.md and in the Copilot instructions. A Metric now leads to what it measures, a Role to its organization and its holder, and a concept to one that says it is the same thing.

  • The behavioural tests of the sidecar scripts moved out of the repository contract into tests/, run with pytest. scripts/validate-repository.sh held about 2,000 lines of them beside the checks on this repository’s own files, each building a throwaway bundle or repository. Checks 8, 11 (its synthetic bundle), 11c, 12 (most of it), 12a, 13, 13a, 18 and 19 (most of each), 20, 20a and 22, and scripts/test-sidecar-layouts.sh, are now one module per script under tests/. The fixtures and the expected lines are the same. Each check’s number stays in the contract with a line saying where it went, so a changelog entry or a bundle concept that names one still points somewhere. validate.yml runs the tests in a job of their own, with just installed for the lokf-link recipe’s tests, and publish.yml runs them before a release. A test can be run alone, and the contract runs in seconds.

Fixed

  • The report script counts reliance through measures, memberOf and holder. The schema ranges the three over Concept, so a Metric relies on what it measures and a Role on its organization and its holder, but the count read ten fields and left them out. The three are in the list, in the template and in both copies, and ktl-curator’s trust-fields.md names thirteen. The template changed, so the siblings take it at the next sync.

0.38.0

Added

  • ktl-prose writes for younger readers. references/young-readers.md gives two bands of age, 8 to 12 and 13 to 18, and a pass for dyslexic readers, with the limits of each. A text for a younger reader is a retelling in a new file, which ktl-librarian writes in the bundle and ktl-prose writes beside a file a person names; a pass for dyslexic readers is a rewording at tighter limits.
  • prose-check.py holds a page to a band of readers. --audience 8-12, 13-18 or dyslexia sets the band’s limits, counts the sentences in each paragraph, and notes the page’s Automated Readability Index. A page that opens “This page is for readers aged 13 to 18.” is held to that band with no flag.
  • A band of readers is spared Latin abbreviations, and told which words may be hard. Under a band, prose-check.py reports i.e., vs, cf., etc. and via, and notes the words a page uses that the VOA Learning English word book does not hold. The word book is in the public domain and sits in the skill’s assets/ folder with its credit.
  • prose-check.py proves that a retelling adds nothing. --derived-from ADULT YOUNG reports a number, link, code span, quotation, HTML tag or fenced block that the retelling holds and the text it retells does not, and notes what it left out. The retelling’s first paragraph must link the original, and in a bundle derivedFrom must name the adult concept.
  • A README for readers aged 13 to 18. README.en-simple.md retells the README in shorter sentences, prose-check.py --derived-from shows it adds nothing, and the README points to it.
  • A bundle can declare its younger readers. The curation policy’s optional Younger readers: line names a band of age, and prose-check.py --bundle reports it as audience: 8-12, or audience: none declared.
  • The librarian retells, and the docent answers, for younger readers. ktl-librarian’s references/retellings.md says how it writes and refreshes a retelling, and that a scheduled run retells only a confirmed concept, for a band the curation policy declares. ktl-docent and its Copilot copy prefer a retelling for a younger reader, or answer from the adult concept in shorter words, with every label kept. docs/examples/prose.md captures the README’s retelling.

Changed

  • The install commands install all five skills for your user. Both READMEs open with a Quick start that holds every install command, the docent alone included; it installs the whole set with --global, docs/install.md passes --global and --scope user, and a new section there says when a team installs into the repository instead.
  • A domain schema is one variable in the sidecar’s justfile. The lokf-validate and lokf-check-refs recipes pass schema := "" as --schema, and the registrar, librarian and release workflows run those recipes, so a host wires a domain schema by setting that one line, by hand or through the linkml-sidecar skill.

Fixed

  • A docs-only merge after a promotion no longer fails the release job. The version semantic-release computes is already promoted and waits for publish.yml, so an empty Unreleased section passes and nothing is promoted twice. A failing check also writes its reason as a GitHub annotation.

0.38.1

Added

  • A website in website/, built with Astro Starlight from the README, docs/ and the bundle. Each concept page shows its trust label, sources and typed relations beside its own Markdown and JSON-LD, and website.yml publishes main to GitHub Pages with the four files lokf export writes.

  • docs/context-layer.md places Knowledge Trust Ladder in the context-layer discourse. It quotes the people building context layers, shows one bundle as one repository’s layer, and sets out a design for federating bundles through domain manifests and a rebuilt workspace, drawn in two diagrams with the unshipped parts dashed. docs/context-layer-technology.md surveys the stores, catalogs, agent surfaces and artifact repositories it would plug into.

Changed

  • The research pages are trimmed to what KTL took and what remains. docs/wikiskill.md and docs/skillwiki.md keep the changes each paper led to and drop the part-by-part comparisons; docs/skills-repositories.md, docs/context-layer.md and docs/context-layer-technology.md lose the quotation surveys and keep the runs, the design, the status table and the decisions.

  • The README, the sidecar’s README and ktl-librarian’s Golden Rule 1 link OKF’s specification in its own repository. Google moved the Open Knowledge Format to GoogleCloudPlatform/open-knowledge-format and froze the copy under knowledge-catalog/okf/.

  • The website’s home page highlights Browse the bundle. It is now the first and primary button, and Quick start follows it as a secondary one.

0.38.2

Changed

  • The README is a doorway, in the style of LinkML’s. It keeps the poem and the opening paragraph, names LOKF and LinkML as inspirations beside the poem, and links the website, the quick start and the retelling for readers aged 13 to 18. The library, museum and roles story moved to docs/architecture.md, which leads the website’s Architecture group, and the quick start moved to docs/install.md.
  • The retelling for readers aged 13 to 18 sits beside the page it retells. docs/architecture.en-simple.md retells docs/architecture.md, which links it, and the website still serves it at /simple/.
  • The website’s home page keeps four buttons: Browse the bundle, Architecture, Glossary and Quick start. The site’s header already links GitHub, and the context layer is in the Architecture group, which opens with the architecture page as its overview.

Fixed

  • The pen adds its edited-since note to a log line once. It now looks for the note whatever its case, so a log line that already carries it gets no second copy.

0.39.0

Added

  • ktl-website lays a website down for any repository’s knowledge bundle. The skill copies the site from its templates into website/: a page per concept with its trust label, a health page, the knowledge graph, search by trust and type, and the four lokf export files served beside the pages, with the README, the docs and the changelog when the repository has them. Its workflow, knowledge-website.yaml, builds the site on every pull request and publishes to GitHub Pages once KNOWLEDGE_WEBSITE_ENABLED is true. This repository’s own site is the template’s first copy, which the contract holds byte-identical, and a smoke test builds the template on the sidecar’s dummy bundle.

Changed

  • The website reads the repository’s values and options from website/site.config.mjs. The README, the docs pages, the changelog, the figures and the logo are each optional there, so the same site builds for any repository with a knowledge bundle, and this repository’s site is unchanged.
  • This repository’s website workflow, website.yml, is a copy of the skill’s template. Its deploy now waits for the repository variable KNOWLEDGE_WEBSITE_ENABLED to be true.

Fixed

  • A concept’s source link keeps its leading dot. A source such as .github/workflows/validate.yml linked github/... on GitHub, which is no file there; . and ./ still read as the whole repository.

0.40.0

Added

  • A ktl-website home page offers an Architecture button when the docs have an architecture page. The default buttons are now Browse the bundle, Architecture when docs/architecture.md is on the site, Bundle health and Knowledge graph.
  • The scheduled librarian takes an agent that wants its prompt somewhere other than after -p. The repository variable AGENT_PROMPT_FLAG names the flag, or arg for an agent such as Codex CLI that takes the prompt as its last argument. The sidecar’s automation page now states the five conditions any agent must meet, beside the Copilot CLI and Claude Code commands it vouches for.

Changed

  • A ktl-website site shows the Knowledge Trust Ladder logo unless its configuration names another. LOGO now defaults to "auto", which takes a knowledge-trust-ladder-logo.svg the repository already has, or else the light and dark copies the template now carries. null still shows the title as text.
  • The README and docs pages lose their dead sentences. The install, Obsidian, Copilot and releasing pages say the step and stop, and the Obsidian page names the knowledge_bundle doorway once instead of narrating the link. No skill change.

Fixed

  • sync-sidecar.sh writes each sibling’s sync on a branch of its own. It starts sync-<tag> from the sibling’s default branch as origin has it, skips a sibling with uncommitted changes, and names the files to stage, so the diff is never left on main or inside a feature branch.
  • The scheduled librarian writes its patch with the agent’s own file tool. The wrapper’s prompt now says so. An agent that tried a shell write first was denied and gave up without writing the patch. The documented Claude Code command drops Write(.lokf/patch.yaml), which Claude Code ignores; Edit(.lokf/patch.yaml) already covers the Write tool.

0.41.0

Added

  • A ktl-website site shows a LinkML schema’s pages under a Schema topic. SCHEMA renders the pages gen-doc writes, found through a gen-doc recipe by default and built by the sync when missing. A mermaid diagram on any page is drawn in the browser.
  • A ktl-website site can serve the data as SQLite databases to open in Datasette Lite. BROWSE names their folder and the command that builds it, and stays off until set, since the databases go on the public site. Their Datasette metadata is published without its plugins settings.

Changed

  • The knowledge website workflow also runs weekly, and on a change to a justfile, a pyproject.toml or a uv.lock. A schema or data change it cannot see, and a promoted changelog, reach the site within a week.

0.42.0

Changed

  • The README, the architecture page and the plugin’s homepage link to the site at https://w3id.org/ktl/. The persistent address redirects to the GitHub Pages site.
  • The README lists Exhibitions, the sites that show a repository’s collection. It names this repository’s own bundle and AI-LinkMO’s.
  • The bundle’s base_iri is https://w3id.org/ktl/knowledge/. It replaces the knowledge-trust-ladder.example placeholder, so every concept’s IRI resolves to its page, its JSON-LD or its Markdown file.
  • A ktl-website data page opens Datasette Lite in a new tab. The site stays open while Datasette Lite downloads Datasette and the database.

Fixed

  • sync-sidecar.sh sets its next steps apart. A blank line now comes before the changelog entry to add and before the commit commands, so each step reads on its own.
  • A move of the bundle’s base_iri no longer reads as an edit of every confirmed concept. Conventions rule 13 compares each id and relation target by its path under the base each commit held, so the migration ktl-librarian’s Rule 2 describes keeps every confirmation.
  • A release’s changelog lists each entry once. A pull request stacked on another still carries the other’s entries under Unreleased, and the promotion no longer folds them into the pending section a second time.

0.43.0

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Added

  • A ktl-website site serves the whole bundle as llms-full.txt. Each concept’s text follows its trust label, its type, its addresses and its sources, so a model that reads the file at once can tell a draft from a confirmed concept. The site’s link check reads the text files too.
  • A ktl-website site offers the bundle log as an Atom feed, knowledge/log.xml. Each day of the log is an entry, titled and categorised by the kinds of change it holds and dated from git, so a reader can follow the bundle without watching the repository.
  • A ktl-website concept page links the concept’s history and names its licence. The record links every commit to the concept’s file up to the built one, and gives the bundle’s licence, or the concept’s own, which the page head names too. The overview page, the feed and the two text files name the bundle’s licence by its short name.
  • A ktl-website concept page opens the knowledge graph on the concept. The record and the neighbourhood map link /graph/?concept=<id>, which selects the concept and frames its neighbours, and the address follows the selected node, so a view can be shared.
  • A ktl-website page previews a concept link. A link to a concept shows the concept’s title, type, trust label and definition while the pointer or the keyboard focus rests on it, from knowledge/previews.json, which the sync writes; Escape dismisses it.
  • A ktl-website site serves each docs page as Markdown beside it. The home page and the extra pages too, at the page’s path with .md added and every link a full address, so llms.txt sends a model to the words rather than the HTML.
  • A ktl-website release page links the repository, the docs and the bundle as they stood at the release. The links go to the release’s tag on GitHub, named by the new RELEASE_TAG option, and to the release itself.
  • A ktl-website page and its retelling offer each other as reading levels. A PAGES entry’s retells names the page it retells, and in the bundle a concept with an audience field or an audience- tag retells the concept its derivedFrom names; both pages open with a Reading level line.

Changed

  • GitHub’s dark theme shows the -dark figures. Every <picture> dark source in the README and the docs pages names the -dark set, which the site already showed, so a figure is the same on GitHub and on the site. The sync no longer swaps a -dimmed source for its -dark neighbour; the -dimmed set stays in .assets/, unreferenced.

Fixed

  • The ktl-website graph explorer fits the graph to its stage once the layout settles. The re-fit waited for an event that had already fired, so it never ran.

0.43.1

Changed

  • The README links the website’s pages. The quick start, the architecture page and the shorter version open on w3id.org/ktl rather than as Markdown files on GitHub.

Fixed

  • scripts/sync-sidecar.sh carries .lokf/justfile. It keeps the sibling’s first line, which names the host, so a recipe change such as the v0.38.0 schema variable reaches every sibling; check 11 holds this repository’s copy to the template the same way.

0.44.0

Added

  • Federation templates for a domain host. ktl-sidecar gains knowledge-federate.py, knowledge-federation.yaml and domain.yaml, which build one workspace from the released bundles a domain names, with one named graph per bundle and a gate on links no member owns. The design is docs/context-layer.md, and the first host is noelmcloughlin/ktl-federation.

Changed

  • docs/install.md: one gh skill install --all line with --agent, the GitHub CLI 2.90.0 floor, and when npx skills fails behind a proxy. The contract keeps every tracked SKILL.md under skills/ktl-*/, so --all installs the ktl- skills only.

Fixed

  • ktl-website’s link check reads an address in a code span without its closing backtick. A concept that named the site’s address in backticks failed the build with a broken link in llms.txt.

0.45.0

Changed

  • A skill page’s word budget no longer counts table pipes or separator rows. A table is the densest way to state a vocabulary, and a plain word count taxed it hardest. The budgets are unchanged.
  • A confirmed concept that a question put back to draft reads “reopened”, not “still a draft”. knowledge-report.sh keeps the rung a person’s confirmation earned and adds a flag naming the day, the asker and how many questions wait; the health line counts those under Reopened. The curator’s and docent’s tables, the SPARQL page, the ladder figure and the website’s card say the same, and the card now shows the open questions beside the label.

Fixed

  • scripts/sync-sidecar.sh writes the sibling’s changelog entry in full. It names the copies the sync changed, or says the pin moved alone. It used to print a placeholder, which two plugins released unfilled.

  • The repository contract skips a sibling’s website build output. Its copies of the curator’s pages quote two links inside folded YAML strings, which the check read as links ending in a backslash and failed on.

  • scripts/sync-sidecar.sh ends the entry with “No plugin change” only for an Obsidian plugin. ai-linkmo and gist are not plugins, so their entries end with what changed.

  • scripts/sync-sidecar.sh keeps a host’s own schema := line in its justfile. It copied the template’s empty one, so a domain host’s members were validated on LOKF core and failed.

  • scripts/sync-sidecar.sh reports a sibling whose bundle fails lokf-check-refs. It printed lokf’s verdict and still passed, because only the last check’s status counted.

0.45.1

Changed

  • knowledge-apply.sh indents a list under its key. PyYAML put the dashes at the key’s own column, which yamllint’s default rules reject, so each concept the pen writes now takes the indented form.

Fixed

  • knowledge-apply.sh lists a concept with no description by its link alone. Its index bullets ended in a bare “ - “, the separator for a description that was not there.
  • knowledge-apply.sh applies a set on a rewrite as it does on a patch. It used to drop the key without a word while the dry run passed, and now every operation refuses a key it does not take, naming it.
  • knowledge-federate.py members moves a member’s text with its version. The patch now moves the old tag, zip name and release day in the description and body too, calls a member “confirmed” only when a person confirmed it as it stands, and --dry-run prints the patch even while a librarian pull request is open.
  • knowledge-apply.sh writes a log line once, however many operations give it. A patch can also carry one log line for every operation that gives none, so a change made to many concepts no longer leaves one identical bullet for each.
  • knowledge-apply.sh writes a bare web address in a log line or an open question as an autolink. ai-linkmo’s markdownlint refused the bare addresses a question carried into the concept and log.md.
  • The sidecar’s .lokf/.gitignore leaves out .cache/. A domain host’s federation build keeps the members’ release zips there, and the first domain host committed four of them.

0.46.0

Added

  • knowledge-federate.py mcp serves a workspace to agents. It is the lokf-registry server the LOKF specification’s section 11.4 describes, over the snapshot: resolve_iri, neighbors, subgraph, federated_sparql and read_document, each bounded in depth and size, with the workspace index as the server’s instructions so an agent reads it first. serve, query and mcp take an absolute --snapshot, so an unpacked workspace-<tag>.zip is read anywhere a sidecar environment exists.
  • knowledge-release.yaml attaches the domain schema the justfile names. schema-<tag>-<name>.yaml goes beside the bundle zip with its own .sha256 and attestation, so a federation can validate the bundle on its own terms. knowledge-federate.py names it as a second distribution when a member moves, and the build validates that member against it instead of LOKF core alone.
  • knowledge-federate.py members --add <owner>/<repo> drafts a new member. It verifies the repository’s newest bundle release, reads the bundle’s namespace and title from the zip, refuses a namespace that nests with a member’s, and writes a create with a question for the pen.

Changed

  • The sidecar’s llms.txt and README aside no longer assume npx skills works. The agent context offers the GitHub CLI route beside it and says when a company proxy blocks the other; the README aside points at the install page.

Fixed

  • The workspace registry no longer names the runner’s own folder as the domain’s source_base. The domain’s entry now points at the host’s repository at the tag, or at the served copy of the unpacked bundle, and the index names the address that is served (.../workspace/index.md) rather than a folder that answers 404.
  • postcss-selector-parser 7.1.6 in the website template’s lockfile (GHSA-rj75-hqrm-r3gf, quadratic selector parsing), and in this repository’s own site.

0.47.0

Added

  • A host keeps its sidecar current by itself. knowledge-sync.yaml, a new ktl-sidecar template, reads the skills pin and the newest release each week. When the pin is behind, it runs the host’s own knowledge-sync.sh in a read-only job and, in a separate job, opens one pull request from .github/PULL_REQUEST_TEMPLATE/knowledge-sync.md with the KNOWLEDGE_SYNC_TOKEN secret, or attaches the change to the run without it. The pull request stays one, refreshed to the newest release each week until it is merged. publish.yml asks the hosts named in KNOWLEDGE_SYNC_HOSTS to sync as soon as a release is tagged.
  • knowledge-sync.sh is the sync, as a sidecar script. It copies a release’s templates over a host’s copies, moves the pin, writes the changelog entry under ## [Unreleased] and ### Security, runs the sidecar’s checks, and takes the sync to a pull request with --commit, --push and --pr. Without --source it clones the tag.
  • templates/knowledge-sync.manifest lists what a sync keeps current. Each template, its copy and the rule for it, in one list that the sync script, the preflight and contract check 11 read. So a release is data to a host’s sync, and no code from it runs there.

Changed

  • scripts/sync-sidecar.sh runs each sibling’s own knowledge-sync.sh, or the tag’s in a sibling that has none. It takes --commit, --push and --pr, and --finish <tag> puts each sibling back on its default branch and removes a merged sync branch. The entry links the comparison between the two pins, so a sync that skipped releases points at all of them.

Fixed

  • knowledge-federate.py members verifies attestations under the librarian wrapper. The wrapper strips every token, so gh attestation verify failed; the script now takes the job’s read-only token as KNOWLEDGE_FEDERATE_TOKEN (AGENT_USE_JOB_TOKEN true, AGENT_API_KEY_ENV set to that name), hands it to gh alone, and says which variables to set when it has no credential.
  • The sync writes the changelog entry instead of printing it with a hint. The hint named ### Security with a sentence’s colon after it, and a heading typed that way once failed the lint.

0.48.0

Added

  • A domain may name domains, tier by tier. knowledge-federate.py build follows a member whose released bundle is itself a domain as deep as the top Domain concept’s max_depth allows, 3 unless set, and domain.yaml adds max_depth and a MemberDomain class so a manifest says which members are domains. The lock records each member’s tier and every domain that named it, and the workspace index says who named each.
  • The diamond rule. A bundle two domains name at the same release joins the workspace once, and the lock records both; at different releases the build stops, because one named graph holds one release. members --add drafts a MemberDomain when the release it admits is a domain.
  • docs/federation.md takes the federation’s mechanics out of the context-layer page. It holds the tiers of domains with the figure ktl-federation-tiers.svg, how the workspace stays current, what exists today and the decisions, two of them new: namespaces that mirror the tree, and what a member adds. docs/context-layer.md keeps the discourse, the one-bundle table and the design.
  • knowledge-report.sh coverage counts the tracked files no concept names. Per top-level folder, the files under no path a concept names in its resource or sources, and the list under a folder you name: ktl-librarian’s sweep for orphans as arithmetic, where a folder the source map names is accounted for and the map’s row says why it is left out.

Changed

  • The website’s figures stay in the text column. On a wide screen a diagram no longer grows into the main pane’s spare width; a click opens it at full size. The change is in the template’s theme.css and Mermaid.astro, so a host gets it with its next website repair.
  • The sidecar’s pages name Codeberg beside GitLab, Forgejo and Gitea. The install, signing, automation and scheduled-task pages point at the portability recipe for another forge, and the registrar template’s header links the three-lines page through w3id.

Fixed

  • Two tiers may each name a members/<slug>.md. Each bundle keeps a folder of its own under bundles/ and export/, and none can take the domain’s own domain folder.

0.49.0

Added

  • The ingest mirrors knowledge from outside the repository. knowledge-ingest.py, a new ktl-sidecar template with a shell half, reads .lokf/ingest.yaml, fetches the sources it names, converts each to Markdown and writes it under .lokf/mirror/, with .lokf/ingest.lock.yaml recording the version, the digest and the status of each. Four kinds: a Confluence selection, a list of addresses, a path in another repository at a ref, and a command the host committed. The librarian derives concepts from a mirror file as from any tracked file, so nothing in the trust ladder changes.
  • knowledge-ingest.yaml opens one pull request with what changed. It runs the host’s own script with the repository secrets whose names start KNOWLEDGE_INGEST_ in a read-only job, and a separate job commits the mirror and the lock to the branch knowledge-ingest, refreshed until merged; .github/PULL_REQUEST_TEMPLATE/knowledge-ingest.md is the reviewer’s checklist. KNOWLEDGE_INGEST_ENABLED turns it on.
  • docs/ingest.md is the guide: the manifest, the mirror, the lock, the secrets, what the pull request shows and what the librarian does next. The preflight prints an ingest line, and knowledge-report.sh coverage counts the mirror’s files.

Changed

  • A domain concept is named for the domain, never for its position. The curator’s domain-bundle.md says so, its worked example names ktl-federation’s domains/ktl-federation, and it says a confirmation stays with the concept’s id.

0.50.0

Added

  • The ingest restores a lost mirror file and prunes on a schedule. A file the lock names and the disk has lost is fetched again and reported as restored. A scheduled run removes a gone or unlisted file no concept cites, once an earlier run recorded it so, and the preflight counts missing, stray and prunable files.
  • docs/ingest.md has a figure, .assets/ktl-ingest.svg with its dark variant: the sources, the ingest, the review and the concept that cites the mirror.

Removed

  • The figures nobody shows. Every -dimmed figure, unreferenced since the dark theme moved to the -dark set, and both files of the lifecycle loop, which no page has shown since the README dropped it.

0.50.1

Fixed

  • The federation build leaves out a concept a curator retired. A retired Domain concept no longer counts as a second top, and a retired member is neither built into the workspace nor moved by members. The curator’s domain-bundle.md says how a member leaves.
  • The federation build takes each member as a person last confirmed it. It built every member concept on the tag, so a merged move or a new member joined the workspace before anyone confirmed it, against the federation design. A moved member is now built at the newest release the default branch held confirmed, while that confirmation stands, and a member no person confirmed is left out. The lock and the workspace index list both as waiting.
  • The Domain concept’s gate policy and depth count only as a person confirmed them. An edit no person confirmed waits, and the defaults stand in until a first confirmation.
  • The gate names a member that waits. A link into the namespace of a member left out for want of a confirmation is reported with that member, and an error about a release a person confirmed earlier says which newer one waits.
  • members --add quotes the description in the new member’s own index. It pasted the description after a colon, with no full stop when the description had none, so the next sentence ran on.
  • The sync calls a copy a pin bump only when its uses: lines are all that differ. It reported every copy the release changed as “differed only on its action pins”, because under pipefail its test took diff’s exit status.

0.51.0

Added

  • A person who sends back text they wrote can release it to the librarian. ktl-curator then writes process:ktl-curator in generated, in a commit that person signs, so the librarian’s next run rewrites the text from its sources instead of only adding a question.

Fixed

  • docs/three-lines.md no longer lists the lokf-registry MCP server as a design. knowledge-federate.py mcp serves it, as docs/federation.md says.
  • Conventions rule 2 holds every at: to one shape: UTC to the second, as date -u +%Y-%m-%dT%H:%M:%SZ writes it. A time with a lower-case t or z, an offset or a fraction passed the gate. The report script, the federation build and the workspace’s stored queries did not all read such a time the same way. A person’s event that no script could read counted as confirming every later edit.

0.52.0

Added

  • A member that waits in a member domain joins at the release that domain built. The members script now names a member domain’s released workspace-<tag>.zip and its checksum in the MemberDomain concept. The build then takes such a member at the release the lock inside that zip records, and the lock names each member concept (format 3). A member names the files of one release, and the build refuses a schema or workspace from another.
  • knowledge-report.sh coverage --why <path> names the concept that claims a file, directly or through a folder above it, so a reviewer can find the row of the source map that excludes it. It reads frontmatter only.
  • coverage --strict exits 1 when a tracked file is named by nobody. It also fails on a folder that holds no tracked file, such as a mistyped one, and when it cannot count, so a check built on it never passes by default.

Changed

  • coverage counts apart the files git would add. A concept names only a path git tracks, so a new file shows with the advice to commit it, or to ignore it if it is runtime state. A tracked file deleted from the working tree no longer counts.

Fixed

  • The federation check runs the gate as the next release build would. build --verify-only stopped after the zips, so a pull request with a link into a namespace no member owns, or into a member no person had confirmed, passed and the release build after the merge failed. The check now exports, loads and gates the bundles without writing anything, names the members the next release would leave out, and runs on any change to the domain bundle.
  • coverage . lists every file nobody named, and a mistyped folder says git tracks nothing there. Both printed “none”, which read as fully covered. A flag coverage does not know is refused instead of being read as a folder.

0.53.0

Added

  • The workspace counts the links between its members. The lock, the workspace index and queries/links.rq count the typed relations in one bundle whose target is a concept of another, by slot.
  • The workspace carries graph.json, every bundle’s concepts and typed relations as cytoscape.js elements. Each node names its bundle, and the links between bundles are marked.
  • The MCP server loads a member’s graph when a walk first reaches it, as section 11.4 of the LOKF specification has members load, and a SPARQL query loads them all. The lock’s graphs (format 4) says where each graph lies in graph.nq, with its digest, the namespaces it names and the IRIs it shares with another graph.
  • The federation build has the sensitivity gate of LOKF section 11.5. A member concept says public, internal or confidential: an internal member joins once a person confirms sensitivity: internal on the Domain concept, and only in a private or internal repository, a confidential one never, and the workflow keeps an internal workspace off the site.
  • A bundle that releases no zip joins by the concepts.jsonld its site serves, pinned by that file’s checksum; the build writes the file out as the bundle. The members script asks for it with a conditional request and proposes a changed file, and members --add <address> drafts such a member.
  • A member whose file cannot be fetched is held at its last good state, as section 11.5 of the LOKF specification has it, from the previous release’s workspace that --previous names. A release zip with other bytes is held only at the release a build fetched before, a member never built is left out until it can be fetched, and a check holds nothing its pull request proposes.
  • The readers read a workspace where it lies: a folder, a workspace-<tag>.zip in place, or the site that serves it, a graph at a time by range request. read_document reads the workspace’s copy of a release, else its concepts.jsonld, else its source, as section 11.4 chains them.
  • A reader can be given some members alone. --member <slug> on mcp, serve and query loads those bundles’ graphs and no other, and refuses the stored queries that read the lock graph.
  • A domain host’s release notes say what changed in its workspace. knowledge-federate.py diff compares two workspaces, and the federation workflow adds the members that joined, moved or left, and the links between them, to each release’s notes.
  • A domain host’s graph page draws the workspace. Its The workspace view draws every bundle the domain names, each in a colour and shape of its own, with the links between bundles dashed, from the graph.json the federation workflow serves. WORKSPACE in site.config.mjs turns the view on or off, and it is on when the repository has the workflow.

Changed

  • docs/federation.md holds the whole federation design, and states each rule once. Domains as manifests, LOKF section 11 and the release moved there from docs/context-layer.md, which keeps what a context layer is, the one-bundle table and what OKF leaves open.
  • The docs say the pen, its figure and the ingest’s guards once. The figure of who writes the bundle stays on docs/architecture.md, and docs/for-the-curious.md, docs/ingest.md and docs/three-lines.md link to the page that holds each detail instead of repeating it.
  • docs/context-layer-technology.md keeps to what KTL needs from a product. The vendor tables of limits, plans and quotes are gone, and the page names the sidecar’s own workspace server and MCP server beside the stores’.
  • docs/for-the-curious.md says how readers and verdicts steer the next run. It now holds what KTL took from the WikiSkill and SkillWiki papers, so docs/wikiskill.md and docs/skillwiki.md are gone. The bundle’s two concepts stay the record of each paper.
  • The federation build runs lokf in its own process. It exported and validated each bundle through a new lokf process, which imported lokf and LinkML again every time. A build of ktl-federation’s five members takes 4.5 seconds instead of 12, with the same output, and the federation tests run in half the time.
  • SECURITY.md and docs/threat-model.md say what holds after these fixes, with a section on the website, and no longer claim the most a hostile release could do before review was propose a change.

Fixed

  • The sync script no longer fails after a complete sync that replaces it with a longer copy. Bash read on into the new copy and ran a line from its middle, so the run exited with an error and the sync workflow opened no pull request. The script now exits on the line that calls main, a v0.52.0 copy also stops when it syncs to this release, and scripts/sync-sidecar.sh runs each sibling’s script from a copy.
  • A confidential ingest entry is refused where git could commit its copy. The script checks that git ignores .lokf/.cache/mirror/ and tracks nothing there before it fetches the entry.
  • The sync keeps a host’s .lokf/.gitignore current. A host kept the copy ktl-sidecar Step 1 first wrote, so it could lack the template’s later lines, such as .cache/, where a confidential copy goes.
  • The scheduled librarian stops before any agent runs when git does not ignore .agents/. It stopped only after the agent had run, at the write-scope check, with ?? .agents/.
  • The federation gate refuses a link into a member’s namespace that names no concept of that member, as --check-refs refuses one inside a bundle, under the same unowned_targets policy. It checked only that some member owned the namespace.
  • The federation gate reads a reified relations entry as it reads the slot that entry names, so a target no member owns there stops the build too. links.rq and unowned.rq count such entries as well.
  • The members script moves the other members when it cannot check one, which the hand-off names. When the GitHub API refuses a release list as rate limited, gh asks again with the job’s token.
  • The MCP server’s federated_sparql stops reading at its limit, 1000 rows or 2000 triples, and says when it cut an answer. It read a SELECT whole before cutting it, and returned a CONSTRUCT whole.
  • The workspace’s stored queries take their predicates from the toolkit. unowned.rq named owl:sameAs where lokf writes schema:sameAs, and left out memberOf and holder.
  • The graph page’s details panel sets each label beside its value. Starlight’s spacing for page content put every label a line below it.
  • A member whose index gives its licence as text, such as MIT, no longer stops the domain build. The lock graph keeps such a value as a literal, and the index writes it as text.
  • The MCP server reads a concept whose id lies outside its bundle’s namespace, which the registry routes through its id_index. It cut the IRI by the wrong prefix.
  • A member whose namespace nests with another’s stops the build with a finding that names both, before anything is fetched. The registry refused it only after every download, with a Python traceback.
  • A checksum must be sha256: and 64 hex digits. Only its length was checked, so a path in it could reach outside the download cache and have the build delete a file there.
  • Every download stops at --max-bytes, a member’s schema and workspace as well as its zip, where each was read whole before its checksum was compared.
  • A download is tried again after a server error or a dropped connection, and follows a redirect only within http and https, never from https to http and never from a public address to a private one.
  • A zip of more than 100,000 entries, or one that matches its checksum but cannot be read, stops the build with a finding, where it filled the disk or ended in a traceback.
  • A reader keeps no partial answer to a range request as the whole file, and --previous refuses a site address, from which no zip member could be held.
  • The federation workflow’s weekly schedule no longer takes the place of a release build that waits for its turn, and each job has a time limit.
  • A domain host’s site serves each member’s release inside the workspace. The Pages upload left out the hidden bundles/<slug>/.lokf/ folders, so every member link in the served workspace index answered 404.
  • The contract records every sibling host’s links into this repository, among them ktl-federation’s into the federation page and the domain-bundle playbook. Its local cross-check reads all five hosts, and skips a domain host’s ignored snapshot/, which holds a copy of this bundle.
  • The librarian keeps run accounts out of the source map. No rule asked for one, yet each refresh had added its account to playbooks/knowledge-sources.md, 41 of them by 2026-10-19. The skill now says the map holds none.
  • An agent that applies its own patch no longer loses a scheduled run’s work. The wrapper sets KNOWLEDGE_PEN_DRY_RUN_ONLY for the agent, so the pen only checks the patch file there and leaves it for the wrapper to apply. The skill and the pen’s dry-run line no longer tell a scheduled agent to apply and validate.

Security

  • A release can no longer write a host’s git configuration through the sync. knowledge-sync.sh refuses a copy path inside a .git folder and clones the release without a checkout.
  • The branch the sync pushes carries no workflow a release wrote. A workflow on a pushed branch runs with the repository’s secrets, so publish applies the patch with knowledge-sync.sh --apply, keeps every workflow but for the pin, and holds the release’s workflow changes for a person, or for a reviewer of the Environment KNOWLEDGE_SYNC_REVIEW_ENVIRONMENT names.
  • The sync’s publish refuses a deletion, a link or an unusual mode, and leases its push to the commit it read. A branch holding a person’s commit is left alone with or without a pull request, and the report names a manifest rule the script does not know.
  • Each workflow reads its secrets from an Environment limited to the default branch: knowledge-librarian, knowledge-sync and knowledge-ingest. Move every repository secret there on every host, as the sidecar’s references/automation.md says.
  • A federation build refuses a member zip holding a hidden file or a file a browser runs, and a distribution name that is not one plain file name. A .git folder in a zip, or a name that climbs out of the workspace, could run code in the build; the health line now runs git on no member’s folder.
  • A federation build fetches members over http or https from public addresses only. --allow-local allows local paths and private addresses, and a person’s private_addresses: allow on the Domain concept allows a forge on the host’s own network.
  • A member’s attestation must come from its own release workflow, or its federation workflow for a workspace, and a build without gh stops unless given --no-attestation.
  • The federation workflow hands its token to gh alone, and the website and federation builds run no npm install script.
  • The website copies only files inside the repository, and passes raw HTML as GitHub renders a README. A link out of the repository and an SVG holding a script are left out, other markup is shown as text, and Astro’s telemetry is off.

0.54.0

Changed

  • The graph page keeps the graph at full height in full screen. Search and filters move into a side panel that scrolls, and switching between This bundle and The workspace draws the other graph in place, so full screen stays on.
  • The graph’s filters read as controls. Larger rows, a count on each entry, All and None on each group, and an “N of M concepts shown” line whenever a filter is off; every filter still starts on.

0.55.0

Added

  • The pen checks the schema before it writes. knowledge-apply.sh runs lokf validate on every concept a patch would write, with --dry-run too, when .lokf/.venv holds the toolkit.
  • The preflight warns about a python3 older than 3.9, about being run from ktl-sidecar’s own folder, and names KTL skills installed for one user.
  • ktl-prose’s check reads a script’s comments. Given a shell, Python, YAML or TOML file or a justfile, prose-check.py holds each # comment block, and each Python docstring, to the rules a paragraph keeps.

Changed

  • The check reports a paragraph of more than eight sentences, and lists “outright” and “at all” among the words to cut. The rules page gains a method for a paragraph with two topics, the rule before its exception, pointing words as names, and the quiet figures of motion.

Fixed

  • The sidecar’s scripts run behind a package mirror. The pen, the conventions check and the ingest try .lokf/.venv first and uv run after it, because uv run fails behind a mirror that serves no upload times. No script picks a Python older than 3.9.
  • When its runner cannot start, the conventions check says so and exits 2, instead of reporting findings against the bundle.
  • A freshly installed skeleton passes its own conventions: the dummy services’ descriptions match their index bullets.
  • ktl-prose runs its check through uv run, so an old python3 never runs it.
  • ktl-sidecar’s Step 0 runs the preflight from the host repository’s root.
  • The librarian’s audit lints the files it touched, not .lokf/.venv/.
  • A release attaches a linkml-sidecar host’s schema. The schema := line may name any file in the repository, and the released copy imports LOKF core as lokf, so a domain can resolve that import.
  • ktl-prose’s check reads a comment’s layout. A table of options is read row by row, and a usage line or a command under a colon is not read, so neither is reported as one long sentence. --against compares a script as its comments.
  • The check reports fewer false findings. A sentence may start with a lowercase name such as ktl-docent or .lokf/, and a minus, a -- <args> marker, “at all of” and “at all times” are no longer reported.

0.55.1

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Fixed

  • A sync reaches a host with a website again. Since 0.53.0 it stopped at src/pages/og/[...route].ts, because its website step refused square brackets in a path; it now allows the same characters as --apply.
  • ktl-prose reads every file in scope, the ones its script cleared too. Its rules now name the dead sentence, a quantity no source gives, and “either way” with no alternatives named.
  • ktl-curator commits a verdict apart from the change it confirms, as conventions rule 14 checks.
  • ktl-librarian’s domain-schema page brings a published vocabulary in with lokf adapt.
  • The website template takes Astro 7.4.0 and the patch releases of its plugins.

0.55.2

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Fixed

  • The preflight asks the package index uv reads, a company mirror included, and prints only its host name. It warns when .lokf/uv.lock was made against another index, since uv sync --locked then fails and uv sync rewrites the lock.
  • The sidecar’s portability page says what a mirror does to the lock, and how a proxy that inspects TLS gets its certificate trusted by uv.

0.56.0

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Added

  • ktl-website keeps old addresses and leaves a folder out. REDIRECTS redirects an old address, such as a retired mkdocs site’s /elements/<Term>/, to its page, and DOCS.exclude keeps a path such as templates-linkml/ off the site.

Fixed

  • A link to a docs folder’s index.md reaches its page, which Starlight serves at the folder’s address, and that page comes first in the Guide, as its Overview.

0.56.1

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Changed

  • docs/three-lines.md holds the model’s critics. docs/three-lines-critics.md is folded into it, and the docs pages’ titles are shorter.
  • The workflows run the just that .lokf/uv.lock pins, checked by hash. The sidecar’s pyproject.toml names rust-just==1.47.0. A host without it still gets that version by name, and the preflight says to run uv add rust-just==1.47.0 in .lokf/.

Fixed

  • ktl-website builds for an owner spelt with capitals. SITE’s host is lowercased, as Astro writes it, so the link check no longer reports every social card broken.
  • ktl-website’s cards show a concept’s genre. The concept card and the link preview give the genre beside the type, the social card gives both, and search gains a Genre filter.
  • ktl-website words a type as the bundle spells it. A word the bundle’s prose writes with capitals keeps them, so SlsaSourceControlSystem reads “SLSA source control system” on every card and in relation names.
  • ktl-website’s card head names a field once. The genre stands after a lower-case “genre” key, and a genre that repeats the type is left off the card, the link preview and the social card.
  • ktl-website’s card head reads the same on every concept. The genre stands beside the type as a plain tag, without the lower-case “genre” key, as the link preview and the social card give it.
  • ktl-website reloads a page kept from before a deploy. A page whose stylesheet the deploy removed reloads once and shows styled, instead of unstyled for up to ten minutes.
  • The federation build and the ingest check the address each connection opens. A name that answers a public address to the check and a private one to the connection (DNS rebinding) now reaches nothing, unless private addresses are allowed.
  • CI lints the ingest workflow template. actionlint now names knowledge-ingest.yaml with the other templates.

0.56.2

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Fixed

  • The curator’s report ends with its stop. When the review session will not run, the report’s last line says so, gives the reason, and names what changes it. A harness line saying the person cannot answer no longer stops the session on its own: the curator says it and goes on only when the person replies, one item per turn.
  • The federation’s members script moves the day of a pruned release. When the release list no longer holds a member’s old release, the day the text writes right after the old tag moves with the tag, and only there.
  • The federation’s members script follows a member’s moved namespace. When a new release’s index.md, or a served file’s @base, names another base_iri, the patch sets it and the question says so. The build refused such a member before. A namespace the build would refuse leaves the member as it is.
  • The federation’s members script cuts a long hand-off line. The pen refuses a patch with a hand-off line over 300 characters, and the line for a failed attestation or a moved namespace could be that long.
  • A federation build finds a waiting member by the namespace it builds. The lock’s waiting entry named the namespace a move proposes, so the entry of a member held or left out at its old namespace was not updated.
  • The federation build and members script remove the files they download. Each zip, schema or served file fetched to check its digest and attestation is downloaded to a temporary folder, which is removed when the run ends, unless --cache names a folder to keep the files in.

0.57.0

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Added

  • A ktl-website home page offers a Schema button when the site shows a LinkML schema’s pages.

Fixed

  • A ! after the type releases a major version. .releaserc.json names the Conventional Commits preset, so a feat!: commit no longer releases nothing, as docs/releasing.md already said.
  • The preflight warns when git is too old for an ingest git entry, and names each entry a run would refuse.
  • Behind a proxy with no outside DNS, the ingest fetches a source whose name resolves only at the proxy, as the federation build does. A name that resolves locally is still checked, and without a proxy a name that does not resolve still fails.
  • A git entry fetched through a proxy that resolves names needs no git 2.37, since curl sends the name to such a proxy and a pin to the checked address would change nothing. Git uses the proxy the ingest chose, or none, whatever its own configuration or no_proxy says.
  • The ingest passes all_proxy to git and to a command entry, as it passes https_proxy.
  • The ingest converts a page that leaves out </head>, as HTML allows, where it mirrored an empty file. A line break in a page’s text is a space, so it starts no heading or code block.
  • The ingest refuses a manifest value of the wrong type, by name. A quoted "false" counted as true, a string where a list belongs as a list of its characters, and a string select stopped the run with a traceback.
  • One page or file fails only itself. A page the converter cannot read is refused alone, and a charset that decodes to a lone surrogate no longer stops the run before the lock is written.
  • A git file whose mirror name is taken is skipped, and one whose name the workflow does not publish is spelled with plain characters and a digest of the name. notes beside notes.md took turns in one file, data beside a folder data.md stopped the run, and a space in a name blocked the whole pull request.
  • A file restored with unchanged bytes keeps its record and changes no line of the lock, so a checkout without the confidential cache opens no pull request. An entry that leaves the manifest frees its bytes on the run that removes it.
  • A git entry’s relative local path is read against the repository root, and a command with changes not yet committed is refused, as the docs say.
  • The ingest starts no work after 25 minutes, inside the workflow job’s 30, and writes what it fetched. What it did not reach keeps its record for the next run, and --time-limit sets the minutes.
  • A git entry downloads only the files that changed, where it downloaded every file at the commit. A binary file, or one refused for its size, keeps a record and is not downloaded again while it stays the same.
  • A new ETag for unchanged bytes is recorded by the next run that changes the lock, so later runs get 304 where they fetched the body each time. A server that gives a new one on every answer changes no line of the lock.
  • A command entry’s timeout stops every process the command started, where one it left in the background kept running. A lock edited into another shape stops the run by name, not with a traceback.
  • The ingest and the pen run on Python 3.9, as their shell halves say, where both wrote files with an argument Python takes only from 3.10. Before 3.11 the ingest also kept a time with a fraction of a second in its own offset, not UTC.

Security

  • The ingest holds a git entry that names a host to the addresses its check found, and follows no redirect for it, so DNS rebinding cannot reach a private address through git unless a proxy resolves the name. Without allow_private_addresses such an entry needs git 2.37 or later, and a host name outside ASCII is refused: write its xn-- form.
  • The ingest and the federation build count every address that is not global as private. Python’s is_private leaves out the shared address space, 100.64.0.0/10, which holds Alibaba Cloud’s metadata service and Tailscale’s networks.
  • The ingest refuses a git entry whose address git’s own configuration rewrites with url.<base>.insteadOf, since the address check and the credential cover only the address the manifest names.
  • The ingest refuses a git entry when git is older than 2.31. An older git ignores the protocol limits and the credential that the script sets in git’s environment. The preflight warns before a run.
  • The ingest’s git reads no ~/.netrc, ~/.gitconfig or /etc/gitconfig. Through curl, git sent the credential of a .netrc default entry to any host that asked, and a git older than 2.32 read both configuration files. A file that GIT_CONFIG_GLOBAL names is still read.
  • The ingest keeps a credential to its own origin, and git follows no redirect with one. A redirect to another port or scheme kept Authorization, and one to another host kept a header credential, which curl keeps too.
  • No credential reaches the lock or the summary. A secret ending in a line break failed its request with an error that quoted the header, so the script trims that line break and refuses one inside a secret, by name.
  • The ingest removes and writes files only inside the mirror, whatever path the lock names or symbolic link the repository holds. It deleted any file a lock named, inside the repository or outside it.
  • An entry made confidential leaves the committed mirror on its next run, where its copies stayed under .lokf/mirror/.
  • The committed lock records no title, address or author of a confidential page. A confidential entry’s records move to the ignored .lokf/.cache/ingest.lock.yaml on its next run, and the summary counts its items, names none, and cuts each address to its site. lock_details: true on the entry keeps them in the committed lock.
  • The ingest pins pypdf 6.21.2. Version 6.1.1 has about 60 published advisories, almost all infinite loops or memory exhaustion on a crafted PDF, and a test now holds the pin to the version the tests install.
  • One source can no longer hold the run or fill the disk. A body has a deadline of ten timeouts, a Confluence listing ends after a page for each result max_items allows, lists and quotes nest at most eight deep, and the caps count the Markdown written.
  • The mirror keeps no javascript: or data: link, whatever the scheme’s letter case, and no character Unicode marks as invisible, variation selectors among them. Each title and each line of the summary is one line.
  • An http entry needs allow_private_addresses: true for a plain http:// address, as the manifest’s comment says.
  • The pen and the librarian’s pull request take out the characters the mirror takes out, variation selectors among them, and the ingest refuses a file name that holds one. They went by Unicode’s control and format categories alone.

0.58.0

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Added

  • Three conventions the gate checks, rules 15 to 17. Every concept names where it came from, as resource or a sources entry. A footnote joins a claim to a sources entry by its id, one id names one resource across the bundle, and each Added and Deprecation bullet a run logs names a concept the bundle holds.
  • The librarian gives a source an id and cites it with a footnote wherever a claim rests on a number, a name or a qualifier; references/golden-rules.md says how.
  • A docent holdout, and an eval that runs it. tests/docent-holdout.yaml pairs the eight captured questions with the concept each answer should rest on, and a test fails when one of those concepts moves. just docent-eval runs ktl-docent on them through claude plugin eval, and docent-eval.yml runs it weekly under a cost ceiling, never on a pull request (the docent eval).
  • The curator’s report lists each retired concept whose successor no person has confirmed. An agent may propose a retirement, but the retired concept governs until a person confirms the successor.
  • A curation policy may carry Automation re-check: <n>. The curator’s report then lists n concepts checked by automation only, picked by the same hash rule as Independent re-check:, for a person to check against their sources.

Changed

  • ktl-sidecar installs the ingest’s example manifest as .lokf/ingest.yaml.sample, for a host to copy to .lokf/ingest.yaml.

Fixed

  • The sidecar’s two example concepts carry a resource, the placeholder endpoint they already name, so the skeleton keeps rule 15 before anyone edits it.
  • A review date is read by its day. knowledge-report.sh compared stale_after with today as whole strings, so a datetime, the type LOKF 0.9.0 gives the field, could read as not yet due on its own day, or as no date at all.
  • The pen, the conventions check and the ingest run behind a package mirror with no upload times. They lift exclude-newer only for a pinned package that uv reports without an upload date, and the ingest pins typing-extensions too.
  • A uv that cannot install a script’s packages no longer reads as findings. The conventions check exits 2, the pen falls back to python3 or exits 2, and the ingest falls back to a .venv without pypdf.

0.59.0

As it stood at this release: the repository · the docs · the knowledge bundle · the release

Added

  • A host’s composite domain schema is released as one file. A schema that imports lokf adapt copies beside lokf.yaml is attached with each copy merged in, keeping every IRI and setting, and a name two files define differently stops the release.
  • The sidecar reads lokf 0.9.0’s exclusions. Coverage and --why count an excluded file apart and never as a source, and conventions rule 18 fails a blank reason or a material listed both ways.

Changed

  • The sidecar requires lokf 0.9.0. A host raises the floor in .lokf/pyproject.toml by hand, refreshes any .lokf/lokf.yaml, runs uv lock and renames each supporting_text key to excerpt. The sync leaves pyproject.toml alone.
  • The skills describe revision, excerpt and lokf adapt as released, and the audit fallback links lokf.yaml at the v0.9.0 commit.
  • The librarian’s domain-schema page says how to import a published vocabulary through its lokf adapt copy, with lmodel/slsa’s release as the tested case.
  • A domain build still indexes a member’s explicit ids outside its namespace. lokf 0.9.0 refuses them unless the registrant allows them. A person’s confirmation admits each member, so the build allows them, and lokf still refuses one in another member’s namespace.
  • The reliance count and the docent follow isReplacedBy, the relation slot lokf 0.9.0 adds.

Fixed

  • A release that changes only the domain schema attaches it. The compare step looked at the bundle’s tree alone, so such a release uploaded nothing. It now compares the schema := line, the schema and the local files the schema imports too.

0.60.0 Latest

Added

  • just lokf-check-refs runs lokf’s --check-excerpts. The gate fails a sources[].excerpt that its local file no longer holds. The recipes need lokf 0.9.0, so a host raises its floor before it takes this release.

Changed

  • lokf-convert, lokf-serve and the website’s lokf export pass the domain schema too, so the graph names its classes and slots under their own IRIs. No recipe changes into the schema’s folder any more.
  • The preflight warns on a lokf floor below 0.9.0, and when [tool.lokf] schema names another file than the justfile’s schema := line.
  • A member’s distribution names its checksum as LOKF core’s Checksum, with algorithm: sha256 and a checksum_value. The domain schema’s sha256:<hex> slot clashed with lokf 0.9.0’s and broke lokf export --schema on a domain host, so the host rewrites its member concepts when it takes this release.
  • The federation docs say the workspace exports every bundle under LOKF core, so the named graphs share one vocabulary. The workspace index says each bundle’s fit is checked against the schema released with it.

Fixed

  • automation.md gives the real reason the librarian’s variables are repository variables. Only the federation’s nudge job reads one in a job’s if:. The librarian reads them in steps, and they stay together so every switch sits in one place.
  • The librarian’s Snyk W012 note names the lokf 0.9.0 wheel, whose schema matches the commit its URL names.
  • The preflight warns when .lokf/lokf.yaml is from another lokf release than the one uv.lock pins. A domain schema imports that copy, and lokf 0.9.0 reads a bare-date stale_after as a datetime, which a 0.8.0 copy rejects.
  • The federation check runs on a pull request that changes .lokf/lokf.yaml or .lokf/justfile. The check validates the domain bundle against the schema the justfile names, which imports that copy of LOKF core.