Skip to content

Plans index

This page summarises every planning document in chronological order. Master plans decompose work into numbered phases, each with its own detailed plan file. Standalone plans track issues, follow-ups, or design decisions that do not require phased execution.

New plans should follow the structure in PLAN-TEMPLATE.md at the repo root. For pre-push audits of our own work see PUSH-AUDIT.md.

Master plans

Date Plan Intent Status Phases
2026-06-02 Automated SPICE test harness End-to-end SPICE test harness driving Uncalibrated Sextant via Ryll's control socket, with assertions against the visual digest and serial drain; replaces the OpenStack-dependent integration tests with a direct qemu/KVM lane Complete phase 1 (done), phase 2 (done), phase 3 (done), phase 4 (done), phase 5 (done), phase 6 (done), phase 7 (done), phase 8 (done)
2026-07-04 Rust SPICE proxy (kerbside-proxy) Replace the Python SPICE proxy with a Rust kerbside-proxy that talks tonic/gRPC over a UDS to the Python daemon, reuses ryll's shakenfist-spice-protocol crate, enforces L0+L1 firewall policy from day one, and ships inside the kerbside pip install via a maturin bin wheel Complete phase 1 (done), phase 2 (done), phase 3 (done), phase 4 (done), phase 5 (done), phase 6 (done), phase 7 (done), phase 8 (done)
2026-07-16 Consistency audit compliance Clear the backlog of open consistency issues filed by the daily shakenfist/development audit: four missing shared blocks in PUSH-AUDIT.md and PLAN-TEMPLATE.md, a vendored sfui copy two commits behind canonical, the retired comment addresser still deployed with contents: write on the pull request branch, and 70 files needing human review against a threshold of 5. The reading of those files is deliberately not tracked here -- decision 5 draws the boundary at the scaffolding, leaving #227 as the sole tracker, so this plan can complete while review-coverage is still failing. Surveyed 2026-08-29: two of the six findings did not survive contact with the tree -- skillsaw does run in CI and the audit's own checker is what is wrong, and the review backlog is 70 files rather than the 152 the issue claims. Promoted from a standalone plan that tracked only two GitHub settings checkboxes In progress phase 1 (complete, merged cbca9b1 -- the four shared blocks, the sfui stamp, and the settings closeout. audit-check.py against the branch goes from 6 failures to 3, leaving only phases 2-4. Two corrections landed during implementation: the plan had two block placements backwards, caught because the brief told the agent to read each block before placing it, and the master plan's own Execution table put Merged in the middle when the block it was adopting requires it last. The research step also killed the plan's assumption that the github-security audit covers the three settings ticked by hand in July: it does not check Dependabot at all, and returns pass when its API call fails), phase 2 (complete, merged 5f3c80c -- retire the comment addresser. The survey widened the blast radius twice over the master plan's sketch: pr-re-review.yml carries three references rather than one, and tools/shellcheck-wrap.sh carries a fourth nobody had noticed. One of the three is the stated reason for a live bot-comment guard, so the phase deletes the justification and keeps the guard, re-attributing it to pr-auto-review.yml, which still posts the comments that make it necessary. Every other measured criterion of the ci-review-automation audit already passes), phase 3 (complete, merged 16e6173 -- skillsaw CI detection. The survey found the sketch accurate in every particular, unusually. The change landed upstream as shakenfist/development PR 73 and nothing in kerbside moved; review there caught a false-positive the original regex would have introduced, where a word boundary matched the wrapped continuation line of a pip install, so four repositories would have passed on a mention rather than an invocation), phase 4 (complete, merged ade2788 -- review scope and session scaffolding. The survey corrected three of the sketch's claims: the backlog is 77 files not 70, the bulk is not docs/spice/, and the sketch was wrong to treat the review marks as freshly scoped work. A fourth survey finding, that no review mark had ever been signed, was itself wrong and was corrected on 2026-09-02: %G? reports N for a valid gitsign x509 signature in a clone without gpg.format set, and testing the commit objects directly finds 29 signed mark-adding commits since 2026-08-14 (30 until 2026-09-03, when the extra turned out to be a merge carrying GitHub's web-flow PGP signature rather than a gitsign attestation). The phase also absorbs review-scope-completeness, a check that arrived on 2026-08-30 and fails with 44 orphaned files, because settling scope after the grind would mean redoing part of it. Fixing scope took the in-scope count from 192 to 227 and the backlog from 77 to 104 -- worse-looking, correct, and eight better than predicted because weAudit had banked stamps on files that were out of scope at the time. The phase completes on the scaffolding: the reading itself is out of scope for this plan and is tracked by #227 alone, per decision 5), phase 5 (in progress -- diagram discipline and mermaid linting, for #370 and #381. The survey corrected the sketch's dates -- #370 was filed 2026-08-26 and is the pre-push audit issue phase 1 already worked on, refiled against a criterion that has since grown a block, and #381 was filed 2026-08-30 -- and confirmed that #370's body is stale, still describing phase 1's already-fixed finding when its only live one is the missing diagram-discipline block. The load-bearing result is that all nine diagram-bearing files already render, measured by running the upstream linter against develop, so this is a pure adoption with no remediation hiding behind the new lane. The lane needs a docker daemon and kerbside's only lint job runs on a static runner without one, so the shipped path-filtered workflow is taken rather than folded into the gate, and stays advisory: a path-filtered workflow that a ruleset requires never reports on a pull request it skips, and blocks it forever), phase 6 (not started -- push audit)
2026-07-17 Backend host_subject enforcement Restore hypervisor certificate subject pinning on the proxy's backend TLS leg, lost in the Rust proxy cutover: enforce spice-common host-subject matching semantics in ryll's shakenfist-spice-protocol verifier, adopt it in kerbside, and prove both accept and refuse paths in the direct-qemu CI lane Complete phase 1 (done, ryll PR #166), phase 2 (done, kerbside PR #114)
2026-07-19 Shaken Fist VDI console tokens Cross-repo plan (master in shakenfist's docs/plans/): per-instance console authorisation via short-lived Ed25519-signed tokens minted by Shaken Fist and validated offline by kerbside; kerbside side adds an /sf-console.vv exchange endpoint mirroring the Nova flow, a jti replay table, cluster-wide scraping, and host_subject for SF consoles Complete phase 5 (done), phase 6 (done), phase 7 (done, SF mint-path test; kerbside exchange lane moved to phase 9), phase 8 (done), phase 9 (done, lane green 2026-07-31, run 30608314535) — post-merge cross-repo e2e + kerbside exchange lane
2026-08-02 Two-tier CI Adopt the shakenfist/shakenfist two-tier CI flow: smoke gates (sanity, direct-qemu, Rust, single-node sf-e2e) on PRs; oVirt and Kolla/OpenStack move to a merge-queue tier; fix the oVirt lane to actually deploy kerbside; record the oVirt front-door architecture decision Complete phase 1 (done), phase 2 (done, promoted on four green nightlies), phase 3 (done, queue live 2026-08-09; first entry caught a real failure), phase 4 (done)
2026-08-02 Use case documentation One docs page per deployment permutation, each covering value proposition, how it works, and setup. The oVirt page landed 2026-08-10 as two-tier CI phase 4 and settles the format, the docs/use-cases/ directory and the index placement; five writable pages remain and Proxmox stays deferred until a source driver exists. Promoted from a standalone plan on 2026-09-18 when phase 1 was planned, which brings the mandatory push-audit phase with it In progress phase 1 (in progress -- the Shaken Fist page, chosen ahead of OpenStack so the format's second instance has the least unsettled ground under it: the VDI tokens plan is complete and sf-e2e gates it on every pull request, where the OpenStack deployment story is still moving upstream on Gerrit. The survey found the master plan's "remaining six pages are unblocked" wrong by its own table, the static-source row's precondition since met by the install rewrite, and the multi-cloud row's "stated nowhere" now one sentence and one bullet wide; all three corrected at source. It also found the index scaffolding already built -- docs/index.md carries a seven-row Use Cases table, so each page phase links an existing row rather than adding one. The load-bearing finding is not a staleness: docs/console-sources.md:67-146 already documents the Shaken Fist flow in eighty accurate lines, so the phase's central decision is what the page does not say, and six specifics are named in the definition of done as forbidden to duplicate), phases 2-5 (not started -- OpenStack, standalone/static, multi-cloud plus placement topologies, then the index slim-down and closeout), phase 6 (not started -- push audit)
2026-08-08 Convert the admin UI to sfui Make kerbside the second sfui consumer: vendor sfui into the static assets, rebuild the six templates on tokens and sfui components (theme toggle, tab-strip nav, brand chrome, details disclosures, morphdom polling), drop Bootstrap/jQuery/axios, fix the accumulated markup defects, and add template smoke tests; resolves #244 and #133 Complete phase 1 (done), phase 2 (done), phase 3 (done), phase 4 (done), phase 5 (done — consoles page: disclosures, flattened connect actions, two-step terminate, inline theme-aware icons; sfui gained the .sf-btn underline suppression), phase 6 (done — sessions/sources/audit pages: disclosure-panel accordion, CA cert disclosure, total_events footnote, shared two-step terminate include; all five pages now on base-sfui.html, template-only phase), phase 7 (done — meta refresh replaced by a 30s fetch-and-morph poll with an onBeforeElUpdated hook preserving open disclosures and the armed terminate button; failed polls pin a stale-since stamp instead of reloading), phase 8 (done — both terminate routes are POST behind the X-CSRF-TOKEN double-submit header, with a SameSite=Lax cookie covering the one destructive GET that cannot convert; the survey found two CI callers the master plan missed, cleared two it named in error, and split that remaining GET out as #319), phase 9 (done — teardown: 47 files and 8.4 MB of Bootstrap, jQuery, axios and logo.svg deleted along with the base.html nothing extended, base-sfui.html renamed into place, the pre-commit and review-scope exclusions pruned, and a StaticAssetReferenceTestCase added so a dangling /static/ reference fails the unit tests. The deletion and the rename were split into two commits, against the plan, because doing both to one path in one commit made git record a delete plus a modify rather than a rename; the survey also missed tools/preview-templates.py as a live reference. #244 is closed by hand, with a comment drafted in the phase plan)
2026-08-14 A working installation path: the compose demo docs/installation.md stops at acquisition, so a reader who follows it has software and no running system: it never mentions the two processes, the database, the TLS material, or a console source. Package the migrations and add kerbside db upgrade (a wheel install currently cannot create its own schema), write the missing etc/kerbside.conf.example, build a docker compose demo with a CI lane, and rewrite the page around it; resolves #3 Complete phase 1 (done 2026-08-14 — migrations moved into the package, kerbside db upgrade/downgrade and kerbside demo token added; verified from an installed wheel run outside any checkout. Fixed two logging defects found on the failure path, including alembic's env.py disabling every kerbside logger), phase 2 (done 2026-08-15 — etc/kerbside.conf.example covers all 34 Config fields, 8 live and 26 commented at their defaults, with no value that would work if pasted; tests pin coverage in both directions and the environment-beats-INI precedence that nothing previously held, both demonstrated to fail before being trusted. Verified from the real /etc/kerbside/kerbside.ini path in a container. Found and filed #313: a malformed INI exits zero, and percent-encoded database passwords reach it through configparser interpolation), phase 3 (done 2026-08-16 — demo/ brings up MariaDB, a disk-less qemu SPICE target and kerbside under one docker compose up; verified end to end with remote-viewer over TLS, every socket on 5900 and none on 5901, with the CA travelling in the .vv. The survey caught a qemu invocation that fails on QEMU 10, which the image then confirmed at 10.0.11; implementation found three more: the then-current release predated kerbside db upgrade so the install source defaulted to the checkout until v0.5.0 shipped it, Debian trixie keeps SPICE in qemu-system-modules-spice, and docker compose exec inherits nothing the entrypoint exported), phase 4 (planned 2026-08-17 — an advisory, path-filtered lane bringing the compose stack up against the PR and asserting a proxied SPICE session. The survey rewrote the draft: every one of its five line citations now points at unrelated content, its ryll feature-flag claim is backwards, its four-step proxy-wheel recipe is one step calling a script that did not exist when it was written, and #314 removed the reason for the cargo build entirely — the dev-inclusive floor in pyproject.toml resolves kerbside-proxy 0.5.1.dev1 from PyPI on its own. Also found that no kerbside workflow uses Docker at all, so this would be the first container build in CI and the phase now leads with a runner probe; and that renovate had just bumped the demo database a major version untested, which the survey verified by hand. Done 2026-08-18 — the lane brings the stack up against the pull request in 4.5 minutes and asserts 11 things about it, including that the session crossed the TLS port and not the plaintext one (5900=12, 5901=0, matching phase 3's manual count). The probe earned its place: the runner image had no docker at all, and Debian 12 supplies neither a new enough engine nor a docker compose v2 plugin, so the lane installs from Docker's own repository in 12 seconds. Review then found three real defects, every one an assertion that reported the wrong thing — set -e had made the hang branch dead code and silently skipped everything after it, the TLS-port check was a startup-log tautology that passed whenever the proxy merely started, and the docker short-circuit skipped the proxy configuration it existed to apply; testing those fixes found three more of the same family. Also caught and fixed a MariaDB root password reaching a downloadable CI artifact, from the redaction step that ran before log collection and so could never have scrubbed it. One item was deferred to phase 5 and is now settled: PR #351 demonstrated both directions of the path filter -- no run while it held only the planning commit, a run within seconds of docs/installation.md joining the diff), phase 5 (done 2026-08-22 — docs/installation.md now runs from pip install to a proxied console, via the two-process model, kerbside db upgrade, the TLS material and the eight settings with no useful default, then the compose walkthrough, then a handoff to docs/use-cases/. Running every command from a clean clone found four defects the prose had wrong, two of them mine: docker compose ps does not print the columns claimed, ss without -H counts its own header so the socket split read 13/1 rather than 12/0, pip install does not give you demo/ so the walkthrough now starts with the clone, and remote-viewer was never installed anywhere. Review then caught a fifth, and it was the load-bearing one: the page put pip install before the OS packages it depends on, which a container test confirmed cannot work — mysqlclient ships no wheel, so a clean pip install kerbside dies in pkg-config. Verifying the fix found that bindep.txt was itself incomplete, missing python3-dev/python3-devel, so tox -e bindep would have reported the dependency list complete while an install from it still failed in gcc; confirmed on both debian:trixie and rockylinux:10 and fixed here. Also closed phase 3's inbound-link gap in README.md, docs/index.md and ARCHITECTURE.md. Planned 2026-08-16, re-surveyed 2026-08-22 before implementation because the draft predated phase 4's completion. Phases 1-3 hold and nothing links inward to demo/ yet, as phase 3 review said; but the page had grown 16 lines from another plan's docs pass that the rewrite must preserve, four of the draft's line citations had rotted, the docs/index.md ordering question was already settled, and phase 4's deferred checkbox — proving the demo-compose path filter does not fire for docs outside installation.md — was missing from the draft entirely and is now step 5e, takeable only while the pull request holds the planning commit alone. The conf-example rename and the .claude/CLAUDE.md row are both decided against rather than left open)
2026-08-14 Rolling kerbside-proxy dev releases Publish PEP 440 dev releases of the kerbside-proxy wheel to PyPI when the binary's inputs change (path-filtered: rust/**, kerbside.proto), commit a dev-inclusive version specifier so unreleased git installs (notably upstream Kolla image builds) resolve a working binary with no upstream kolla changes, and add a proto-hash contract handshake between daemon and binary as the skew backstop Complete phase 1 (done, PR #314), phase 2 (done, PR #314), phase 3 (done, PR #314 — committed proto-hash constant, --contract-hash flag, startup refusal with KERBSIDE_SKIP_CONTRACT_CHECK escape hatch), phase 4 (docs done in PR #314; bootstrap wheel 0.4.1.dev184 published 2026-08-17 once the operator registered the trusted publisher, and the committed floor was verified against the live index to resolve it with no pre-release opt-in. Tail: 4b (patch175 simplification) withdrawn 2026-08-18 after measurement showed the fallback it would delete is inert — kolla's install_pip always passes --upgrade, which does not downgrade the newer dev wheel the committed floor already installed; the Gerrit recheck (4c) completed 2026-08-29 with every Gerrit review passing Zuul CI), phase 5 (done, PR #328 — survey found publishing denser than the master plan assumed, ~365 dev releases/year with 76% of triggers being Renovate dependency bumps. PyPI has no delete/yank API for any credential (Warehouse #12810 open and blocked), so this automates the watching rather than the deleting: a weekly credential-free storage monitor that files a tracking issue, Cargo.lock dropped from the publish trigger, and a manual pruning runbook), phase 6 (done, PR #375, 2026-08-29 — no critical, high or blocking findings. PUSH-AUDIT.md over the accumulated diff of phases 1-3, 4a and 5: 14b54f3^1..2e1fd43 scoped to the 40 paths those phases touched, 4,040 insertions. Survey found both audit scripts hard-code DIFF_BASE=develop, not just wave1.sh as the master plan said, so an unmodified run is a vacuous pass; that the Rust half of the range has no mechanical coverage at all; and one known wave-1 false positive, the two print() calls in tools/check-pypi-storage.py. The tooling step also caught wave 1's two fatal style checks never having inspected a line: a BRE grep -v '^\+\+\+' matched every added line rather than the diff headers, so $ADDED was empty for any diff at all. The audit produced five fixes — one correctness bug (get_binary_contract_hash() raised UnicodeDecodeError on a binary printing non-UTF-8, escaping its documented every-failure-returns-a-reason contract), two hardening fixes (an unanchored stamped-tree grep that could disagree with its two siblings, and an issue title interpolated into a jq program), and two documentation honesty fixes (the contract handshake is a compatibility check and not an integrity control; a git install resolves an unreviewed pre-release wheel and nothing on any install path verifies the build provenance attestations that are produced). Everything else is declined in writing in the master plan, and the finding that unattended OIDC publishes share a persistent runner pool with pull-request code is tracked as #374. The security review also refuted this phase plan's own claim that phases 3 and 4 describe the contract hash both ways)

Standalone plans

None at present. The last one, Use case documentation, was promoted to a master plan on 2026-09-18.

📝 Report an issue with this page