Skip to content

v0.28.0

Latest

Choose a tag to compare

@github-actions github-actions released this 17 Sep 14:31
68e944b

Added

  • spans-more-than-one-service warns where one service delivers the whole of an offering. An offering naming
    one service in implemented-by restates the service record beside it. The rule is guarded on the field, so a
    corpus that declined services is not warned about one it cannot fill.

  • A rule expression can count a field's entries. entries('implemented-by') answers how many, where
    present() answers whether. A scalar counts as one, and an absent field as none, so a rule that must not fire on
    an absent field guards with present() first. min-items: states the same floor as an error. Use the fact where
    the shortfall is worth a warning and not a failure. docs/design/expressions.md lists it.

  • An integration travels to a consumer. .schema/integrations.yaml declares an export: block, so kac export
    writes a file per integration record. What it does and Failure modes travel at full. Contract, Commercials
    and Contacts stay behind, because each describes owning the account. docs/design/export.md now lists data as
    the one type declaring no block.

  • An explanation travels to a consumer. .schema/explanations.yaml declares an export: block, so kac export
    writes a file per explanation record. Where the detail lives is the one section it declares, so it is the only one
    that travels, at full. The rest of the body is free-form and no schema can name it. docs/design/export.md no
    longer lists explanations among the types declaring no block.

  • A postmortem travels to a consumer. .schema/postmortems.yaml declares an export: block, so kac export
    writes a file per postmortem record. Summary, Root cause, Contributing factors and What went well travel at
    full, and Impact as its opening paragraph. Timeline and Actions stay behind, because a timeline names one
    estate's clocks and each action links a work item the reader cannot open. docs/design/export.md no longer lists
    postmortems among the types declaring no block.

  • An offering travels to a consumer. .schema/offerings.yaml declares an export: block, so kac export
    writes a file per offering record. Every section travels at full, because Where the detail lives is the only
    place an offering states its work items and the rest of the record is short by design. docs/design/export.md no
    longer lists offerings among the types declaring no block.

  • feature-file-repo warns where a feature file names a repository no implementing service does. A path in
    feature-files starts with its repository, spelled as that repository's service spells repo:. The check compares
    that first segment against the services in implemented-by and names the ones it found. It warns rather than fails,
    because a regression pack can live in a repository no service claims. Nothing reads the rest of the path until
    feature-file-orphans runs.

  • A service names the NFRs that bind it. .schema/services.yaml declares nfrs:, and it travels in the services
    export. nfrs.applies-to and both nfrs: fields declare reciprocal:, so an NFR and the record it binds each name
    the other, and validate reports either end that does not.

Changed

  • An offering says who it is for. Who it is for is a required section, and it travels in the export. ITIL 4
    defines a service offering by the consumer group it serves, and that group is what decides where one offering ends
    and the next begins. The Why it exists guidance in each _template.md no longer asks for the audience, because
    the new section holds it. An existing offering gains one heading.

  • An offering that is live states an NFR. nfrs is required once the status reaches live: a customer
    already has the offering, and nothing else on the record says how well it has to work. A corpus that declined
    nfrs is not asked, and adopting the type starts the obligation with no edit to .schema/offerings.yaml.

  • offerings names its prior art. lineage said "None that fits" and left alignment and divergence empty.
    It now names ITIL 4 Foundation 2.3.2, the service offering, and states what the type takes from ITIL, the GOV.UK
    Service Manual and Backstage, and where it parts from each. Both values render into every adopting corpus's
    knowledge-as-code/lineage.md. docs/framework/lineage.md records that ITIL is paywalled, beside the rows that
    already were.

  • The capability type is now offering. A record lands in offerings/ as ofr-borrowing, the page beside
    it is offerings.md, and the identity line reads Offering:. ITIL 4 calls this document a service offering and
    defines it by the consumer group it serves, which is what this type always meant. Capability meant something else
    to two of its likely readers: ArchiMate uses it for an ability an organisation possesses, and SAFe for functionality
    below an epic. A corpus that adopted capabilities renames the folder and the page. It changes each record's
    type:, id: and identity line, and rewrites every cap- reference in the records of other types. It then writes
    offerings over capabilities in types: and deletes .schema/capabilities.yaml by hand. kac update --drop-type capabilities
    refuses that last step, because the template no longer declares the name. The export is offerings@1, so a consumer
    sees capabilities stop and offerings start. The template version moves to 18, and kac new stamps
    template-version: 18.

  • kac validate no longer asks for a field no record in the corpus can fill. A field whose ref: names only
    types nothing there supplies is dropped from the required pass, and a required-when: on such a field never fires.
    A type an import publishes counts as supplied, so a standard citing a producer's policy clause is still asked for in
    a corpus adopting no policies of its own. Adopting one of those types starts the obligation with no edit to
    .schema/. A field with allow-literal: is fillable without them, so it is still asked for. ref-resolves is
    unchanged: a value a record does write is held to the same standard as before.

  • A postmortem records what ended the incident, and all three of the lessons. Resolution and two further
    sections, What went wrong and Where we got lucky, join What went well, and all five travel in the export.
    Google SRE groups the three lessons under one Lessons Learned heading; each is declared on its own here, so
    required-section asks for it and empty-section refuses a bare one. An existing postmortem gains three headings.

  • A postmortem may name more than one root cause. The template said "Resist listing several". Google SRE writes
    Root causes in the plural and PagerDuty records contributing factors and no root cause at all, so the guidance now
    says to name more than one where more than one stands out. Nothing about the section changes.

  • A postmortem states when service came back, and duration is checked against it. restored-at is a third
    timestamp, required once a postmortem is published, and duration is now an ISO 8601 duration such as PT4H20M.
    duration-matches-the-moments fails a value the two moments refuse, and its message carries the span they give, so
    the fix is a paste. restored-not-before-occurred fails service coming back before it went. Nothing requires
    restored-at to follow detected-at: an incident can recover before anybody notices it.

  • A rule expression can ask for the time between two moments. span('occurred-at', 'restored-at') answers with an
    ISO 8601 duration in hours, minutes and seconds, and with nothing where either field is absent, is not a moment, or
    where the second is the earlier. docs/design/expressions.md carries it in the table of what an expression may call.

  • A postmortem states when an incident began and when it was noticed, to the second. occurred-on and
    detected-on are now occurred-at and detected-at, and each takes a UTC timestamp as 2026-09-07T20:18:00Z. The
    gap between the pair is what the pair is for, and it is usually measured in minutes. -on names a date everywhere
    else in the schema, so the names moved with the type. A corpus holding postmortem records renames both keys and
    writes a time into each: validate reports the old spelling as unknown-key.

  • Where the detail lives and the frontmatter say the same thing, and validate checks it. implemented-by and
    nfrs declare mirrors-section: Where the detail lives, so related-matches-section reports either end naming an
    id the other does not. That section is a bulleted list rather than a headerless table, which is the form services
    already uses for the same shape and the one a screen reader can read. It lost its Tested by line, which restated
    feature-files and had already drifted from it, and its Decided in line, which no field ever backed.

  • kac new seeds an offering template and type page that name no tracker. Both said functional detail lives in
    Azure DevOps epics. They now describe a work item and leave the tracker to the corpus. Inside the corpus the
    template links only to services, so one that adopted neither adrs nor nfrs no longer receives a definition
    into a folder it does not have. The type page also states the floor the type has: an offering whose
    implemented-by names one service is a synonym for that service. Both files seed, so an existing corpus keeps the
    wording it was created with.

  • A report's generated.at dates the content's last meaningful change. .schema/reports.yaml described it as
    "the moment the content last changed", dropping the qualifier [OKF v0.2] states, while the framework moves the stamp
    for neither a hand-raised sources version nor the Imported: bullet an imported entry's raise rewrites. The
    description and the field's notes: are read by whoever maintains a corpus's schema, and kac update takes both
    down. No generated block changes: the schema-reports table prints the description of generated itself and none
    of its entries.

  • kac new seeds report guidance the schema accepts. reports.md told you to verify a report you had just
    written, which no-self-verification rejects. It now says to write yourself into generated.by, leave verified
    empty, and ask somebody else to read it. Both it and reports/_template.md also say which sources entry a
    hand-raise touches: the corpus the report answers for comes first and its raise edits frontmatter alone, and raising
    an imported entry also means editing the Imported: bullet under ## Limits and adding a verified entry. The
    files seed, so an existing corpus keeps the wording it was created with.

  • kac new seeds the example policy as policies/devi-deviations.md. The file was
    devi-deviations-are-recorded.md, which restated the record's title. A policy filename names what the policy is, and
    the H1 states the intent. .schema/policies.yaml and the policies.md type page both state the rule. The file
    seeds, so an existing corpus keeps the name it was created with. The template version moves to 17, and kac new
    stamps template-version: 17.

Removed

  • ado-epics is gone from offerings, and with it the int value type. A work item id assumed one tracker,
    and a corpus planning on GitHub issues had nowhere to put the equivalent. Work items are now links in the
    Where the detail lives list, labelled the way the corpus's own tracker labels them. ado-epics was the only field
    in the taxonomy declared of: int, so int-format guarded nothing and type: int and of: int are no longer
    values a schema may declare. kac checks prints one check fewer.

Fixed

  • An index column heading spells an initialism in capitals. kac generate headed the their-sla column Their sla,
    because id was the only field name it read as an initialism. That column now heads Their SLA. Every other field
    heads a column in sentence case, as it did.