Imported from tacticaldoll/kengen (
AGENTS.md). Install upstream withnpx skills add tacticaldoll/kengen. Copyright stays with the author.
AGENTS.md
Meta-guideline for AI coding agents and contributors working in this repository. Read this first,
then let openspec/specs/ and active change specs be the source of durable architecture truth.
Kengen In One Sentence
Kengen is a thin, sans-I/O access-policy adjudicator: given the verdicts a set of policy rules reached about a request, it returns the single decision — Allow, Ask (an authority must confirm), or Deny — by folding them over the verdict lattice, while owning no policy content and enforcing nothing.
This repository is intentionally narrow. Kengen is not a policy-evaluation engine, a policy store, an enforcement point, or a session or token service. Which rules exist, what each decides, the default stance, and what happens after a verdict are the consumer's, never the core's identity.
Architectural Axioms
Before proposing or writing code, protect these axioms:
- Sans-I/O: the core performs no I/O — no filesystem, network, clock, or global state. A verdict is a pure function of its inputs.
- Mechanism, not content: Kengen combines already-decided per-rule verdicts; it never decides what a rule means. Which rules exist, what each decides, and the default stance are the caller's — forever.
- Adjudicate, do not enforce: Kengen returns a
Verdict; it never acts on one. Gating, sessions, tokens, and enforcement live outside. - The lattice is total and stable:
Allow < Ask < Deny; combination keeps the most restrictive and is commutative, associative, and idempotent, and never silently downgrades aDenyor anAsk. - Vocabulary is governance: the adjudication register in
docs/domain-language.mdfences what Kengen may become;scripts/naming-guard.shrefuses enforcement, engine, auth-state, and I/O words as type or module names.
Lineage
tianheng + 〔sans-I/O · OpenSpec · vocabulary-as-governance · least-commitment〕
│ inherited discipline — provenance, not coupling
▼
● kengen
siblings: ▢ ▢ ▢ intentionally blank — this repo is sibling-blind. Which
products compose together is a consumer app's knowledge, never
a component's; naming a sibling here would leak that knowledge
and rot when the roster changes.
note: skeleton from tacticaldoll/rust-family-template.
Kengen shares a discipline with its lineage, not code: its own crates, specs, constitution, and
release cadence. It does not import, track, or depend on any sibling product, and its governed
prose (PROJECT.md, AGENTS.md, BACKLOG.md, specs, and code comments) names none.
Document Authority
openspec/specs/is shipped architecture truth.openspec/changes/contains active proposed truth until it is synced.PROJECT.mdstates product vision, positioning, and non-goals.docs/domain-language.mdis the canonical vocabulary.BACKLOG.mdrecords settled and deferred decisions, open design questions, and candidate patterns, not mandatory phases.AGENTS.mdis operating protocol for agents and contributors.AGENTS.kengen-law.mdis the generated, freshness-gated projection of the accepted Rust constitution incrates/kengen-governance. The constitution is authoritative; read the projection after this file, regenerate it with its documented command, and never edit it by hand.- Other files under
docs/elaborate one topic each and yield to the documents above.
Decision provenance lives in git — the commit body and pull request that made a change record its
rationale. Forward-looking or reversed decisions are noted in BACKLOG.md. There is no separate
architecture-decision-record file class; the living documents above are the single source of
truth for current state, and git is the source of truth for why it changed.
If these documents conflict, fix the conflict through an OpenSpec change before implementing feature code.
Adversarial Review Stance
Every change passes an adversarial review at BOTH the propose and apply phases before it is committed. Actively challenge the design:
- Does it introduce I/O into the core (clock, filesystem, network, global state)?
- Does it start owning policy content or rule evaluation instead of only combining supplied verdicts?
- Does it act on a verdict (enforce) rather than return it?
- Does any combination silently downgrade a
Denyor anAsk?
If a change grows I/O, policy-content ownership, rule evaluation, or enforcement, stop — that is the monolith returning.
Disposition Discipline
An open design question is dispositioned from Kengen's identity as a design pattern, never by deferring to a consumer that does not yet exist. Adjudicate each open item into exactly one of:
- a noun — part of Kengen's owned surface (its vocabulary or its mechanism), articulated now from the pattern's own needs; or
- a verb — judgment, execution, or driving that belongs to the consumer, declared downstream and permanently not Kengen's.
"Wait for the first real consumer to force the shape" is not a valid disposition: it hands Kengen's design authority to a coupling partner and drifts the pattern toward the consumer-driven monolith it is defined against. Declaring an item a permanent consumer verb commits Kengen to less, not more, than freezing a shape once a consumer appears, so this discipline strengthens least-commitment rather than weakening it. Review rejects any proposal that reintroduces consumer-driven deferral. This is a judgment-level, review-governed rule — not a Tianheng tooth.
A third disposition sits alongside noun and verb: manifestation — making an already-decided-true property of Kengen explicit or machine-guarded (for example, enforcing that the facade re-exports exactly its intended surface). Manifestation adds no obligation the consumer must satisfy and changes no contract, so it needs no consumer to justify it and does not over-commit. The rider: manifestation is only free for what is already decided true — manifesting an open question would smuggle a decision, which is over-commitment in disguise.
Governance and Conformance
Kengen separates the judgment from the check on its projection.
- Governance is judgment, and lives in prose —
openspec/specs/, this file,PROJECT.md, andBACKLOG.md. Intent and meaning are decided here and stay review-governed. - Code is the projection of a judgment onto the structural plane — a
pub useset, an absentasync fn, a dependency edge, a missing trait bound. - Conformance verifies the projection still matches the judgment. It is a family: Tianheng
(structure, dependencies, source scans),
rustc(type facts), and tests (behavior). They bite the projection, never the judgment itself.
Tianheng's accepted constitution projects into AGENTS.kengen-law.md; a freshness test byte-checks
that generated context against the live declaration, so accepted law is visible without a second
hand-maintained authority. A green gate means "no visible violation", not proof: a judgment that
casts no structural shadow stays prose, and a source scan cannot see what a macro expands to.
Before turning a judgment into a Tianheng tooth, it must pass four gates — casting a shadow is necessary, not sufficient:
- Shadow — does the judgment project into a syntactically decidable structural fact? (No → it stays prose and review.)
- Faithful — is that fact a faithful proxy, not a gameable one? (Lines of code are not thinness; a proxy invites Goodhart.)
- Stable — is the judgment stable? A tooth on a moving projection is a recurring maintenance tax and a second copy of the truth; prefer a test.
- Sync — is the extra
prose ⟷ toothcoupling worth it? The tooth is itself a second projection of the judgment, and nothing mechanically checks it matches the prose — only review does. The regress terminates in a human.
Fail any gate and the honest home is prose, review, or a test — never a faked tooth. A tooth complements review; it never replaces it. Where an accepted boundary does hold a claim, its reason is the single statement of that rule, and prose that merely restated it may be retired.
Repair code toward a violated reason; never weaken a law, baseline new drift, or change severity merely to make a check green. A deliberate law change requires explicit authority, focused violating and clean reaction proofs, projection regeneration, and adversarial review.
OpenSpec Workflow
openspec/ is the version-controlled, agent-neutral source of truth: openspec/specs/ is the
living specification of what the system is, and openspec/changes/ holds active change proposals
as delta specs. Per-agent command files (.claude/, .codex/, editor shims) are generated per
clone and never committed; generate your own with openspec init --tools <tool>.
The lifecycle is:
explore -> propose -> apply -> sync
- Explore: investigate and shape intent. Read the relevant
openspec/specs/first. Do not write feature code outside a change. - Propose:
openspec new change "<change>", then writeproposal.md,design.md,tasks.md, and delta specs with success, failure, and edge scenarios. Commit asdocs(<change>): propose <summary>. - Apply: implement against the active delta specs, one task at a time, and check a task off
only after the Definition of Done passes. Keep changes minimal and scoped; never bundle
unrelated work. Commit coherent compiling milestones as
feat(...)orfix(...). - Sync: merge verified delta specs into
openspec/specs/(agent-driven — the CLI has no sync command), thengit rm -r openspec/changes/<change>/. There is no archive: the change's content now lives inopenspec/specs/and git history. Never runopenspec archive. Commit asdocs(specs): sync <change>.
Requirement changes reach openspec/specs/ through sync, never through silent code edits.
Without agent slash commands, use the CLI:
openspec list [--json] [--specs]
openspec new change "<change>"
openspec status --change "<change>" --json
openspec instructions <artifact> --change "<change>"
Language
- Write OpenSpec artifacts,
BACKLOG.mdentries, code comments, and commit messages in English. - Converse with users in the language they use.
- Wrap Markdown prose near 100 columns; tables and code blocks are exempt.
Commit And Integration Governance
Branch Commits
- Use Conventional Commits:
type(scope): summary. - Write the subject in English, lowercase imperative mood, at no more than 72 characters.
- Use the body to record motivation, important decisions, constraints, and verification when that context exists. Do not merely enumerate changed files.
- Do not append pull request or issue numbers to the subject or body.
- Development branches may contain multiple coherent commits because the pull request is squash-merged.
Pull Requests
- Branch from
mainand open every change directly againstmain. - Make the pull request title the intended squash commit subject.
- Give every pull request a non-empty body that explains why the change is needed, what changed, consequential decisions or tradeoffs, and verification.
- Rebase the branch onto the current
mainbefore final verification. - Do not introduce a release integration branch between a change and
main.
Squash Merges
- Squash-merge every verified pull request into
main. - Make the squash commit subject exactly the approved pull request title. Hosting tools append the pull request number by default; remove it.
- Give every squash commit a non-empty, self-describing body distilled from the approved pull request body: preserve durable rationale, decisions, constraints, and verification; omit transient checklists and generated commit lists.
- Do not append a pull request number, issue number, or URL to the squash subject or body.
- Every content-changing commit on
main, including release preparation, must come from a squash-merged pull request. - Keep
mainreleasable after every merge.
Attribution
- Do not include AI, agent, model, tool, automation, or generation attribution in commits, pull requests, tags, changelogs, or release notes.
- Prohibited forms include AI
Co-authored-bytrailers,generated by,written with, model or agent names used as signatures, and tool signatures. - A
Co-authored-bytrailer is allowed only for a real human contributor.
Changelog
CHANGELOG.mdfollows Keep a Changelog and Semantic Versioning, and is a strict release ledger: it has no[Unreleased]section. Unreleased work is recorded in OpenSpec changes, pull requests, andBACKLOG.md.- Write each version's entry in its own release-preparation pull request, cross-checked against the commit history since the previous release.
- Every
## [X.Y.Z] - YYYY-MM-DDheading has a matching[X.Y.Z]: <url>footer link to.../releases/tag/vX.Y.Z.scripts/changelog-guard.shchecks this and runs in the Definition of Done.
Release Finalization
- Prepare release content in a pull request whose squash subject is exactly
chore(release): prepare X.Y.Z. - Sweep crate-level README files and other non-governed prose for stale version markers or
disposition language that
BACKLOG.mdhas since resolved, superseded, or placed downstream. - Give the release preparation squash commit a non-empty body describing scope, compatibility, metadata changes, and verification.
- Run the complete Definition of Done after that commit reaches
main. - Publish crates in dependency order, waiting for each to appear in the crates.io index before publishing its dependents. If an upload's result is uncertain, query crates.io for the exact version before retrying — a published version cannot be overwritten.
- Finalize with annotated tag
vX.Y.Zon that commit, with message exactlyrelease: X.Y.Z. - Push the tag without another commit. Release branches and empty release commits are not part of the flow.
Definition Of Done
Run these from the workspace root before checking off implementation tasks or syncing specs. This
is the single source for the gate list — README.md and docs/development-flow.md point here
rather than restating it. If a command cannot run in the current environment, report that
explicitly.
cargo build --workspace
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all --check
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps
cargo deny check
cargo run -p kengen-governance -- check --manifest-path Cargo.toml
./scripts/changelog-guard.sh
cargo +1.88 build --workspace
./scripts/naming-guard.sh
./scripts/naming-guard.sh is the naming-worldview guard — see docs/domain-language.md.
CI (.github/workflows/ci.yml) runs the same gates on push and pull request. Rust style lives in
these checks: rustfmt formats, clippy denies warnings, rustdoc denies documentation warnings,
cargo-deny owns resolved supply-chain policy, and kengen-governance owns Tianheng architecture
boundaries.