feat: 1.0-readiness findings, shipped as 0.10.0 - #198
Merged
Conversation
The `focus_order` fix in #195 carried a `BREAKING CHANGE:` footer, and release-please's default for a 0.x project is to treat that as the signal to cut 1.0.0 -- so PR #196 opened as "chore(main): release 1.0.0". Nothing about that branch was a decision to declare the API stable. `bump-minor-pre-major` makes a breaking change bump the minor while the project is on 0.x, which is what SemVer allows pre-1.0 and what VERSIONING.md now documents. `bump-patch-for-minor-pre-major` stays off: a feature should still move the minor, not hide in a patch. 1.0.0 becomes a deliberate act -- a commit that raises the version explicitly, after the readiness audit -- rather than a side effect of a commit footer. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
A pack that declared modules but no `focus_order:` fired nothing at all. CLIPS drains only the agenda of the module holding the focus, so with an empty focus list every rule scoped to a declared module sat unfired and the caller got the default decision back — a wrong answer, not an error, from a pack whose rules all matched. `load_modules` now focuses the declared modules in declaration order when nothing else has set a focus, and only then: a `focus_order:` in any file or an earlier `set_focus` still wins outright. This invalidated the benchmark added in #195, which generated exactly such a pack: its two evaluation cases were timing an empty agenda and reporting healthy microseconds for doing nothing. The generated pack now declares its focus, and the script refuses to report timings for a pack whose rule_trace comes back empty, so the same mistake cannot pass silently again. The real numbers are inside their published targets (single 71us, 100-rule 156us). Docs: this behaviour is now stated in the module reference, the runtime concept page, and the modules tutorial. The rest of the pages here are the source-drift backlog from #195, which the docs job only warns about: - writing-rules.md taught YAML that no longer compiles — bare `expression: active` (must be `equals(active)`) and `alias: req` (must be `$req`), plus an emission claim the compiler never made. - template.md said no validator was applied to SlotDefinition.name, allowed_values, or default; all three are validated now. - audit-attestation.md was missing the input-fact snapshot step and had a stale `sign()` signature. - yaml-compilation.md said module `priority` drives focus ordering. - function.md's validator table was missing the hierarchy validators and the unknown-key rejection; cli.md did not mention that `compile` now emits literals by declared slot type; hot-reload.md told readers to use a reload listener without naming `Engine.subscribe_reload`. - fact.md and five-primitives.md carried line citations that had moved. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
The doc-source freshness gate was advisory in every job that ran it, and the result is the previous commit: eight reference pages describing validators, YAML forms, and a call signature that had not existed since #195, one of them teaching rule YAML that no longer compiles. A warning nobody reads is not a gate. It was advisory for a real reason, though: editing a source ages every page that cites it, so a whole-repo gate fails whoever opens the next pull request rather than the author who changed the source. That is the treadmill that got it switched off, and turning it back on unchanged would just rebuild it. `--changed-vs REF` narrows the check to the sources the branch itself touched. Pages citing files this branch did not edit are someone else's drift and stay in the advisory sweep; pages citing files it did edit must be re-verified here. An unreadable ref falls back to checking everything rather than passing, since a gate that no-ops on a bad ref is worse than a noisy one. The required `docs` job now runs the scoped check on pull requests, and CONTRIBUTING documents what a contributor has to do when it fires. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
Fathom shipped eight releases without saying what it promised. `__all__` existed on the top-level package and nowhere else, so every other module's contents were public by accident of import: a caller doing `from fathom.chained_log import _scan` had no way to learn that was internal, and this project had no way to change it without maybe breaking someone. What is covered is now stated in two places that cannot disagree. VERSIONING.md names the surfaces — the Python symbols, the YAML authoring keys, the REST/gRPC/MCP contracts, the CLI, the environment variables — and tests/test_public_surface.py fails when its symbol list and `fathom.__all__` drift apart, when a module VERSIONING names declares no `__all__`, or when a top-level export is not public in the module that defines it. `__all__` added to engine, models, errors, audit, fleet, attestation, and chained_log. The top-level package re-exports the audit sinks, the fleet types, the full exception hierarchy (`FathomError` was not exported at all, so `except FathomError` needed a submodule import), and the attestation and chained-log entry points. Those last four resolve through a module `__getattr__` rather than at import: they need `jwt` and `cryptography` from the optional `attestation` extra, and importing them eagerly would make `import fathom` fail on every install that did not ask for signing. Accessing one without the extra raises ImportError naming the extra. The generated Python SDK reference follows `__all__`, so this adds fourteen pages; the docs jobs now install the attestation extra so mkdocstrings can resolve the lazy four. VERSIONING.md also states what the version numbers mean, which nothing did: breaking changes go in a `0.x` minor and never in a patch, deprecations run two minors before removal, and the supported version is the latest minor. The PyPI classifier moves from Alpha to Beta. Not to Production/Stable: that is a claim about a 1.0 readiness audit that has not happened. Also adds docs/reference/configuration.md, which VERSIONING has to be able to point at for "documented FATHOM_* variables". All thirteen are listed with their defaults, and the gRPC TLS story is written down for the first time — the server refuses to start without a key pair unless FATHOM_GRPC_ALLOW_INSECURE=1, which was true in the code and stated nowhere. Both surfaces' token scopes are documented alongside it. The hot-reload and release-signing how-tos were reachable only by direct link; they are in the nav and the how-to index now. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
The old file named 0.7.x as the supported version (0.8.0 had shipped), offered no disclosure policy, no embargo, no CVE process, and no statement of what counts as a vulnerability in a project whose documented features include two deliberate escape hatches for running arbitrary CLIPS. Now: the support window points at VERSIONING.md instead of a version table that goes stale on every release; the timeline covers acknowledgement, assessment, and fix; disclosure is coordinated through GitHub's CNA with a 90-day backstop so a stalled report is not a silent one; and scope is explicit in both directions. The out-of-scope list matters most. `test:` conditional elements and `type: raw` functions emit author-written CLIPS verbatim by design, and `FATHOM_GRPC_ALLOW_INSECURE` and the unsigned-ruleset dev escape each require an explicit opt-in and log what they turned off. Without saying so, every one of those reads as a finding to anyone auditing the code. Also corrects VERSIONING's description of the Studio: it mounts the REST app at `/api`, not `/studio/api`. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
`/v1/evaluate` dropped the firing rule's `then.metadata` on the floor, and never populated `attestation_token` at all: the REST app held an attestation service on `app.state` but built its engines without it, so the field existed in the schema and was always null. The gRPC response carried neither field, and had no way to distinguish "no rule decided" from a decision of "" — proto3 sends an unset string as "" to every client. - `EvaluateResponse` (REST) gains `metadata`; `rest.evaluate` now passes the `app.state` attestation service into both the stateless engine and the session engines, so a configured service actually signs what it returns. - `SessionStore.get_or_create` takes the service and applies it at creation. - `fathom.proto` gains `metadata` and `attestation_token`, and marks `decision`/`reason` `optional` so unset is distinguishable from empty. - The Go and TypeScript clients carry `metadata`; the Go gRPC client also reads back `attestation_token`, which it previously discarded. Regenerates the Python and Go stubs, the OpenAPI spec, the Postman collection, and the gRPC/Go/TS reference docs. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
`docs/concepts/audit-attestation.md` claimed an exported audit line could not be modified without breaking `verify_token`, while the same page said two sections earlier that the JWT was deliberately kept off `AuditRecord`. The second statement was the true one, so the threat-model claim was empty: a line lifted out of the log carried no signature at all. - `AuditRecord` gains `attestation_token`, copied from the result the same evaluation produced, so a line is verifiable on its own. - `ChainedAttestationLog` gains `write()`, which makes it satisfy `AuditSink`: `Engine(audit_sink=ChainedAttestationLog(path, service))` now writes evaluations into a hash-chained log, where a deleted or reordered entry breaks linkage. A per-line signature cannot detect that. - The threat-model section now separates what the token covers (decision, rule_trace, session, iat, input_hash) from what it does not (reason, metadata, duration, the fact lists) and points at `verify-chain` for continuity. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
The README described integrations the repo had outgrown in both directions. It listed one framework adapter where four are shipped, four rule packs where five are, and pointed at `python -m fathom.studio.app`, an entry point that stopped existing when the Studio moved into its own package. It also listed `fathom-editor` — a stub with no tests and no backend wiring — beside the Go and TypeScript clients as an SDK. - New how-to: Running Policy Studio. It is the only shipped surface with no page of its own: how to run it from a checkout (it is not on PyPI), how its token gate works, what its five views drive, and the four things it is not — a sandbox, a stable API, a durable audit log, or safe to expose. - README: adapters, packs, Studio, and SDK entries now match the tree; the test count is gone rather than left to rot. - planned-integrations: the TypeScript suite is required in CI now, the Go SDK does implement the gRPC surface, and the Studio gets an entry saying what is missing from it (no release, no stability promise, in-memory audit). - docs/index.md: links the pages added since it was last touched. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
se-jo-ma
force-pushed
the
feat/1.0-readiness-as-0.10
branch
from
August 20, 2026 13:30
67e0159 to
17bdae4
Compare
|
|
||
| import pytest | ||
|
|
||
| import fathom |
`verify-grpc` diffs the committed stubs against a fresh generation, and the generators write the protoc version into each file's header — so stubs built with a locally-installed protoc 5.27.3 fail the drift check against CI's pinned 25.3 even though the descriptors are identical. Regenerated with 25.3, and the Makefile now names the three pinned versions instead of leaving them to be discovered from a red build. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
The DCO app checks every commit on a pull request, including the docs regeneration commit release-please-docs.yml pushes onto the release branch. That commit had no Signed-off-by, so every release PR came up red and needed a hand-written third-party remediation commit. Committing with -s as release-bot signs it off as its own author. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
The release PR's version-bump commit is authored by the RELEASE_PLEASE_TOKEN owner and carried no Signed-off-by, so the DCO app flagged it on every release PR. release-please's "signoff" config option adds the line; it names the token owner so it matches the commit author the app checks against. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
The bench gate ran at --slack 1.5 on the claim that a shared runner is 'roughly that much slower'. Five consecutive runs of the job on this branch put the single-rule median at 105, 118, 130, 166 and 166us against 89us on the developer machine the published targets come from: 1.2x to 1.9x, and the two slowest walked through the 150us limit. That is the gate failing on the hardware, not on the code. Raises the factor to 2.0 and replaces the guess with the measurement in all three places that state it. The gate still catches a real regression: 2x on a developer machine lands at 4x of the published target on a runner. Signed-off-by: Sean Mauk <seanmauk@krakennetworks.com>
krakennetworks
approved these changes
Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the 1.0-readiness findings that were deferred out of #195. Ships as 0.10.0, not 1.0.0 — 1.0 waits on the readiness audit.
Merge order
Merge #197 first and let release-please cut 0.9.0 from the batch already on main. This branch then produces 0.10.0. It carries the same
release-please-config.jsonchange as #197 (identical content, so the merge is clean).What is in here
A rule pack that declared modules and fired nothing (
f290e27)A pack with
modules/but nofocus_ordercompiled fine, focused nothing, drained no agenda, and returned the default decision — silently.load_modules()now falls back to declaration order, and the benchmark refuses to report evaluation timings for a pack whoserule_traceis empty. The perf gate had been measuring an empty agenda: its generated pack was exactly this shape.The doc-source drift gate blocks the branch that caused the drift (
3d6702e)check_doc_sources.pyran only in the advisorydocs-qualityjob, so #195 landed source changes without re-verifying eight pages and the failure surfaced on an unrelated PR. The requireddocsjob now runs it scoped to the sources this branch touched (--changed-vs $BASE_SHA); the whole-repo sweep stays advisory, so a stale page still fails its own author rather than the next one.A declared public surface, and a policy behind it (
70a0e0f)__all__claimed 8 symbols while the docs taught imports from four other modules. It is now 26, resolving the attestation exports lazily (PEP 562) soimport fathomstill works without theattestationextra. NewVERSIONING.mdnames the covered surfaces and what a0.xminor and patch each mean;tests/test_public_surface.py(35 tests) holds the symbol block in that file to the package. Newdocs/reference/configuration.mddocuments all 13FATHOM_*variables, the gRPC TLS requirement, and the token scopes.SECURITY.md (
58bdd24)Was three lines promising support for 0.3.x. Now: support window, private GHSA reporting, response-time table, coordinated disclosure with a 90-day backstop, and explicit in/out-of-scope lists (
test:,type: raw, the two dev escapes, andpackages/fathom-editorare out).One evaluate response across every transport (
6d8f611)/v1/evaluatedropped the firing rule'sthen.metadata, and never populatedattestation_token— the REST app held an attestation service onapp.statebut built its engines without it, so the field was always null. gRPC carried neither field and could not distinguish "no rule decided" from a decision of"". Addsmetadatato the REST model, passes the service into stateless and session engines, marks protodecision/reasonoptional, and addsmetadata+attestation_tokento the proto. The Go and TypeScript clients carrymetadata; the Go gRPC client also reads back the token it used to discard.Audit lines that carry their signature (
36c8ef3)docs/concepts/audit-attestation.mdclaimed an exported audit line could not be modified without breakingverify_token, while the same page said two sections earlier that the JWT was deliberately kept offAuditRecord.AuditRecordnow carriesattestation_token, andChainedAttestationLoggainedwrite()so it satisfiesAuditSink—Engine(audit_sink=ChainedAttestationLog(path, service))writes evaluations into a hash-chained log where a deleted entry breaks linkage. The threat-model section now separates what the token covers from what it does not.Policy Studio, documented; README, corrected (
67e0159)The README listed one framework adapter where four ship, four rule packs where five do,
fathom-editoramong the SDKs, andpython -m fathom.studio.app— an entry point that stopped existing when the Studio moved to its own package. New how-to: Running Policy Studio — how to run it from a checkout (it is not on PyPI), its token gate, its five views, and the four things it is not.Verification
uv run pytest— 2186 passed, 4 skipped (the 2 skips are protoc-gated gRPC doc tests)ruff check/ruff format --check/mypy src/— cleango vet ./... && go test ./...,pnpm lint && pnpm test— cleanmkdocs build --strict— clean; all generated reference regenerated (OpenAPI, Postman, gRPC, Go, TS, Python SDK,llms*.txt)check_doc_sources.py --changed-vs origin/main— cleanStill needs your hands
pypi/npmenvironment protection rules (GitHub settings)NPM_TOKEN(npm-publish has never succeeded)CodeQLto main's required contextsOne Dependabot alert stays open and is not fixable here:
chromadb(critical, transitive through the optionalcrewaiextra, inuv.lockonly). The advisory covers<= 1.5.9and 1.5.9 is the newest release — there is no patched version to move to, and nothing in Fathom imports it.🤖 Generated with Claude Code
https://claude.ai/code/session_01Ro3WQQ1Dk76zrDmTEqygRA