Skip to content

Releases

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.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.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.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.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.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.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.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.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.0

Added

  • The librarian’s pull request says how the conventions script ended. The registrar’s gate does not run on a pull request the workflow opens, so refresh runs knowledge-conventions.sh and the pull request reports the outcome beside the validation’s. The pen keeps most conventions by itself. A resource that names no file and a revision that names no commit are the two only this script finds.
  • knowledge-report.sh changes says what a change does to each confirmed concept. Each one it names is followed by still reads as confirmed, reads as edited since that confirmation, removed or its confirmation is gone. A check or a question on a confirmed concept was listed like an edit, so a reviewer could not tell which concepts lost their label.
  • ktl-prose rule 10 says where a text says a thing twice. “own” after a possessive, “itself” after a noun and “at all” after a negative are cut where they draw no contrast. A fourth pass, over the bundles of the three sibling repositories, cut 58 of 84 and 33 of 43. It cleared 305 of the script’s 312 findings, and the text grew by one word in a hundred.
  • prose-check.py counts the words before and after a rewording. The OK line of a comparison gives both counts, so the hand-off quotes them. A day or a month written out that changed, and a text cut by more than a fifth, are notes for the reader.
  • A scheduled librarian run opens no second pull request beside an open one. knowledge-librarian.yaml has a third job, earlier, which lists the pull requests the workflow opened, under a token that reads pull requests and nothing else. While one is open, a scheduled run stops before the agent. A week’s pull request left unreviewed was joined by another each Monday, each from the same default branch. A run a person starts goes ahead, and its pull request names the open one.
  • What a person declined is not proposed again. A pull request of the workflow’s that a person closed without merging left nothing the next run could read, so the same change came back. earlier now hands a scheduled run a record of each: its base commit, the concepts it touched or added, and a hash of each feedback entry it handled. knowledge-report.sh quiet counts none of that as work, and the work list names it under Declined. Each item makes work again once it changes after that pull request. A run a person starts reads no record, so starting one asks the librarian to try again.
  • The preflight warns when git holds no .lokf/uv.lock on a host that carries the registrar workflow, since the gate now installs from that lock.

Changed

  • The pen refuses a patch that handles more than ten reader entries. ktl-librarian stated that limit for a run, and nothing held a run to it.
  • The registrar’s gate and the release workflow install the toolkit with uv sync --locked, as the librarian workflow did. A host whose .lokf/uv.lock is missing from git, or behind .lokf/pyproject.toml, now fails at the install step: run uv lock in .lokf/ and commit the file.

Fixed

  • The pen writes a value back as the file held it. It read each concept’s frontmatter into YAML’s types and wrote the whole block back, so 1.10 became 1.1, 12:30:00 became 45000 and yes became true in any concept it touched. On a person’s event an unquoted at came back in another shape and an all-digit revision as another number. The gate read that event as changed, and the report script lost the confirmation’s date. The pen also refused an all-digit commit hash given as revision.
  • The retrieval score leaves out a question whose concept has left the bundle. No index could lead to such a concept, so the question counted as a miss on every run. It is not asked now, and the score says how many were left out.
  • Conventions rule 10 reports a comment beside id, by, at or revision. A parser drops the comment and a line reader takes it for part of the value. The gate then read an actor nobody is, and the report script could not read the time, so the concept never read as edited since.
  • docs/wikiskill.md said the paper’s wiki keeps every trace. Its raw layer keeps them, and the wiki’s log names the errors that came back.
  • A week is quiet again once the librarian has asked about a source it cannot act on. A confirmed concept whose source is deleted is the curator’s to retire, so the librarian can only add a question, and a question moves no stamp. knowledge-report.sh quiet kept counting that source, and an agent ran every week until the curator acted. A moved source no longer counts once an open question of the librarian’s that names it was committed no earlier than the source’s last change, and the work list names it apart. It counts again when the source changes after the question.

Security

  • A person’s record is the same after the pen writes as before. The pen compares each concept it is about to write with the file it read, and refuses the patch when a human: event or a person’s note would differ. Three patches passed its per-operation checks before: a rewrite whose body brought its own ## Open questions section, a note in a person’s name spelt with an escaped line break, and a delete behind a second heading placed above the real one. knowledge-provenance.sh --unattended refused each in a scheduled run, and nothing did in a live session. Check 19 exercises them.
  • A reader’s question and the hand-off hold no character a reader cannot see, whichever block it sits in. The pen and publish listed ranges, which left out the Arabic letter mark, the soft hyphen and the tag characters, which hide text from a reader and not from a model. The pen cleaned a reader’s question of control characters only. Both now go by Unicode category, as check 21 and prose-check.py do.
  • The hand-off a reviewer reads is the pen’s, after the retrieval call too. That call carries readers’ words in its prompt and runs after the pen has written the hand-off, whose path it could write. The wrapper writes the pen’s lines there again once the call returns.
  • A reply cannot add to what it is scored against. The retrieval scorer read the expected concepts and the reply from one stream. A reply that held lines in the scorer’s own shape raised both counts.
  • The skill a scheduled run installs is pinned to a commit. The librarian workflow named a release tag, and a tag can be moved to other instructions, which an agent would then follow unattended. TRUST_LADDER_SKILLS_SHA now holds the commit that tag named when the pin was set, and the install step installs nothing unless the tag still names it. The release step and scripts/sync-sidecar.sh move the two together, and check 15 holds the template’s commit to its tag.
  • The conventions script and the pen pin PyYAML. Each ran through uv run with a bare pyyaml in its dependency block, so the gate and the scheduled run installed whichever release was newest that day. The block now names one release, and exclude-newer takes no file uploaded after a set date.