Skip to content

feat(approve): per-doc approval level, AI sign-off where a doc allows it - #13

Merged
pasrom merged 1 commit into
mainfrom
feat/ai-approval
Sep 30, 2026
Merged

pasrom merged 1 commit into
mainfrom
feat/ai-approval

Conversation

@pasrom

@pasrom pasrom commented Sep 30, 2026

Copy link
Copy Markdown
Owner

What

A doc can now say who may sign it off.

approval Who may sign off
unset or human a person (initials in approved_by)
ai an AI review alone is enough, written approved_by: "ai:<model>"
any value, on audience: customer or type: verbatim-block always a person; approval: ai there is an error
  • km approve <doc>... --by WHO [--at DATE] [--review-by DATE]:
    • moves a review doc to the served status and writes approved_by / approved_at in place, then gates it with km validate;
    • refuses, before writing, what the level forbids, a non-review doc, and a review_by that has passed or can't be parsed, since the next km demote would undo the approval;
    • restores a refused doc byte for byte.
  • km validate:
    • fails a signed-off doc whose ai: approver its level does not allow, keyed on the configured served status;
    • the rule lives once in km.common.approval_problem, shared with approve.
  • reviewed_by (list): records pre-reviews, such as a PR bot's model, date and PR. It is not an approval. promote and --replace drop it with the approval. A cross-repo promote also drops approval, so the target decides its own level.
  • status_rules:
    • keys are read generically as <status>_<requires|recommends|forbids>, so accepted_requires needs no code of its own;
    • a malformed key, or a brain key naming an unknown status, fails fast;
    • km's own keys stay valid in a brain that renames its statuses.
  • Team template: requires owner and review_by for accepted docs, which its CONVENTIONS already stated.

It is metadata, not authentication, like the rest of the gate.

Verification

  • All four smoke suites pass: gate 80, index 30, team 96, encoding 4.
  • Every rule was mutation-checked in both directions, and each mutation fails at least one test.
  • Positive control on a real team brain:
    • ai: without approval: ai is refused before writing;
    • with it, km approve signs the doc off;
    • removing approval: ai afterwards fails km validate.
  • /simplify ran, then three review passes. The findings are fixed and each has a test:
    • served status keyed on the literal word;
    • a stale review_by accepted, including through a backdated --at or a past --review-by;
    • a misspelled status in status_rules silently ignored;
    • a BOM lost on restore;
    • CRLF docs rejected;
    • an unparseable review_by passing.

Not changed

  • A successful approve writes UTF-8 with LF, like every km write.
  • km demote keeps reviewed_by, since the text it describes has not changed.

A doc may now say who can sign it off: `approval: human` (the default when
unset) needs a person, `approval: ai` lets an AI review alone approve it.
Customer-facing docs and verbatim-blocks always need a person, and
`approval: ai` on them is an error.

- km approve <doc>... --by WHO [--at DATE] moves a review doc to accepted
  and writes approved_by / approved_at in place (no YAML round-trip), then
  gates it with km validate and restores it if that fails. An AI approver
  is written ai:<model> and is refused before any write where the level
  does not allow it; only a review doc can be approved. A doc whose
  review_by has passed (as after a demotion) is refused unless
  --review-by sets a new date, since the next demote would undo it; both
  compare with today, like demote, so a backdated --at gets no way round
  them. A refused doc is restored byte for byte, and CRLF docs are read
  like every other km command reads them.
- km validate fails an accepted doc whose ai: approver its level does not
  allow. It is metadata, not authentication, like the rest of the gate.
- reviewed_by (list) records pre-reviews, such as a PR bot's model, date
  and PR. It is not an approval: promote and --replace drop it with the
  approval, and a cross-repo promote also drops approval, so the target
  brain decides its own level.
- status_rules keys are now read generically as
  <status>_<requires|recommends|forbids>, so accepted_requires (and any
  renamed served status) needs no code of its own, and a malformed key
  fails fast instead of being ignored; a key the brain adds must also name
  a status the schema knows, while km's own keys stay valid in a brain
  that renames its statuses. The team template now requires
  owner and review_by for accepted, which its CONVENTIONS already stated.

The rule lives in one place, km.common.approval_problem, shared by approve
and validate, and keys on the configured served status, not the literal
word "accepted".
@pasrom
pasrom merged commit 7efa119 into main Sep 30, 2026
4 checks passed
@pasrom
pasrom deleted the feat/ai-approval branch September 30, 2026 05:27
@pasrom pasrom mentioned this pull request Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant