v0.26.0
Added
-
kac validatechecks that a quoted clause still says what it is quoted as saying. A control quotes the clause it
verifies, and nothing until now compared the two.clause-quoted-faithfullytakes every double-quoted span on a line
that also cites a clause, and reports it as an error where the cited clause no longer contains those words.
Whitespace is collapsed on both sides, so a quotation wrapped across two lines is read whole. A quoted span on a line
citing nothing is left alone, and so is a citation into a corpus you consume: an export sends a record's ids and
fields, not its wording..schema/controls.yamldeclares the rule, so take it with
kac update --from <template>. -
A fix travels in an export.
.schema/fixes.yamldeclares anexport:block at shape 1, sokac exportwrites
one JSON per fix. The record carriessymptom-keywords, which is what a lookup searches on. It carries Symptom,
Cause, Resolution and Why it happens whole, because a resolution read without its cause is half an answer. It carries
verified, and the record'strustis derived from that list. Three things stay behind:How we found it, which
names commands a consumer cannot run,promoted-from, which names a discovery that travels nowhere, andowner.
Take the schema withkac update --from <template>, and adopt the type withkac update --add-type fixes. -
kac report --out <path>writes the report to a file. Without it the report still goes to standard output, so
a caller piping one loses nothing. With itkacwrites the file after it has read the corpus, which is what keeps
a run off the console encoding and out of the way of a shell holding the same path open. A path a file already
occupies is refused and nothing is written, because a finished report holds verdicts somebody wrote. -
Every record can say where its content came from.
.schema/_universal.yamldeclaressources, an optional list
whose entries carry a requiredresource. Aresourcenames something a reader can follow, such as a ticket URL, or
the population the content was drawn from.sourcesis what the Open Knowledge
Format calls the same list, and
resourceis its one required key. No type carries the universal field into an export, so a record a consumer holds
stands up without the ticket behind it.reportskeeps its ownsources, which requires the field and adds the
versioneach corpus was at. Take the field withkac update --from <template>.
Changed
-
The prose in
.schema/is rewritten to the writing rules. Everydescription:,notes:,message:and comment
in the schema was rewritten against them. A record author reads a shorter field description in the## Metadata
table, and a plainer sentence fromkac checksand from a rule that fails. No check id, severity,expr:, pattern,
threshold or export shape changed. Reasoning that had grown into anotes:now sits on the design site, under
Checks,
Shaping a type and
Reports, and thenotes:cites it. Run
kac generateafter taking the schema withkac update --from <template>. -
The
## Metadatatable says what a field is, where it used to describe the schema. A field declaring only
notes:fell back to them for its table cell, so maintainer commentary was published to whoever writes a record.
Every field now declares adescription:. An ADR'ssuperseded-byreads "The ADR that replaces this one." where it
read "CI reconciles both directions, so a one-sided supersession fails the build." Runkac generateafter taking
the schema withkac update --from <template>. -
Every check message opens lower case. Twenty-three rule messages in
.schema/opened with a capital, where
kacprints a message mid-line after the check id. Several also ran to four or five sentences. Each now opens lower
case and states what is wrong, then what to write instead. -
.schema/states the reasoning local to a field and cites the site for the rest. Anotes:had grown into a
multi-paragraph design argument in forty-six places, and much of it repeated a documentation page. The prose in
.schema/is a third shorter. The per-type export choices and the framework-register rules now sit at
https://paul80nd.github.io/knowledge-as-code/design/shaping-a-type/, which each type file cites. -
kac report frameworkssays more beside each framework's table. Every reference row carries aCitationscount,
so a reference one clause cites reads as1without counting the cell next to it. Each framework's section opens on
the standing the register files it under, linked to the register entry that placed it, which is the line a reader
would otherwise scroll back to the totals table for. -
Each report writes its own
## Limits. Both printed one wording, written for a reading of clause coverage.
frameworksnow says that anAlignmentcell stays in the corpus that wrote it, so it counts the citations written
here, and that a citation records the naming rather than a clause meeting what it cites. -
The five lookup skills drop their search procedure.
kac new,kac updateandkac bundlesend skills that
name no search tool and no search flags. A trial ran three variants ofpolicy-lookupover five questions: the
skill as it shipped, one without the tool name, and one without the search section at all. Every variant found and
cited every governing clause. Each run of the shipped skill spent tool calls hunting a Grep tool the session did
not hold. What stays is what an agent cannot work out for itself: the file map, the field table, and the warning
that a field name likestatusmatches every line of the file. -
glossary-lookup,policy-lookupandstandards-lookupanswer the near miss. Each carries a section for a
subject the corpus has not written down that sits beside one it has. "Password rotation" meets a policy about
rotating secrets, and the skill now says to name the nearest clause as the nearest one and leave the reading to
its owner. -
confirmedis nowverified, it takes any actor, and the export carries the trust tier derived from it. The
field is renamed onfixesandreports, which is what the Open Knowledge
Format calls the same list. It no
longer refuses an agent: a session that reproduced a symptom and ran the resolution has checked something real, and
verified-by-a-known-actoradmits it, named with its version the way the tool names itself. That rule still refuses
arole:, because a post cannot read an answer. Who is in the list decides the record's tier, which each record file
now ships astrust: an empty list isunverified, agents alone aremachine-confirmed, and onehuman:actor is
human-reviewed. A type whose export does not nameverifiedcarriestrustasnull. One actor is still
refused, andno-self-verificationis the new rule and reports asself-verification: a report may not be verified
by the producer itsgenerated.bynames. A fix declaresraiser-does-not-verifyand nothing runs it, because
nothing on a fix names who raised it.kac reportwritesverified: []where it wroteconfirmed: []. A corpus
that adopted either type renames the key in every record and in its_template.md, and takes the new schema with
kac update --from <template>. Thereportstype'sshapeVersionmoves to 2, so a consumer reading records of
that type reads the new key. -
The
faqtype is nowfix, and itsFixsection is nowResolution. A record lands infixes/as
fix-0001, the page beside it isfixes.md, andkac validateholds the record to Symptom, Cause and
Resolution. The type and its third section no longer share a word. A corpus that adoptedfaqsrenames the
folder and the page, changes each record'stype:,id:, identity line andFixheading, then writesfixes
overfaqsintypes:and deletes.schema/faqs.yamlby hand.kac update --drop-type faqsrefuses that
step, because the template no longer declares the name.kac update --from <template>then takes the new
schema file. The type's page and its_template.mdare seeds, so a corpus keeps the copies it has, and
validatenames every line of them still saying FAQ.
Fixed
-
kacprints UTF-8 on Windows. Standard output took whatever code page the machine was installed with, so a
clause citing§9reached the reader as a replacement character while the same text in the record was intact.
Every command writes through the same stream.kac reportis where it showed, because a report quotes citation text
back. -
kac reportwrites frontmatter a parser accepts.owner: human:is not valid YAML, so a generated report met
frontmatter-parsesover the whole document rather than a message naming what to fill in.id,ownerand
verifiednow arrive empty, andkac validatereportsrequired-fieldagainst each one. -
kac reportcalls a fresh report a draft. The schema requiresstatusand no run wrote it, so every generated
report failedkac validateuntil somebody added the field by hand. The frontmatter now carriesstatus: draft. -
id-matches-filenamenames the two things that disagree. It printed the whole id where it meant the id's own
slug, so a record filed asreports/rpt-clause-coverage.mdwas told thatrpt-clause-coveragedoes not match
rpt-clause-coverage. It now readsid 'rpt-clause-coverage' carries slug 'clause-coverage', and the filename carries 'rpt-clause-coverage'.The number and the mnemonic branches take the same wording. -
An unhandled fault goes to stderr. Spectre's own handler wrote one to stdout, so
kac report coverage > out.md
put the message inside the report and left the console silent. Every verb's own refusal already went to stderr, and
this joins them. -
The
reportpage taught a filename that does not validate. Its example wrotereports/rpt-clause-coverage.md,
and a record whose filename repeats the id prefix failsid-matches-filename. The filename carries the question the
report answers, and the prefix belongs to theid. -
sectionsis described as the object it is.policy-lookupandstandards-lookuptold a reader to read two
or three things fromsectionswithout saying it is keyed by heading, and a reader parsing it as a list gets
nothing back.