Repository navigation
Description
docs/roadmap.md says that materialising on persistent hardware is where the runtime becomes another project's; three milestones renumbered today keep persistence, reconciliation and durable volumes inside feint. Both texts are current, neither cites the other, and the decision that would say which superseded which is filed (#196) but not written. In six months a reader of roadmap.md will see "its scheduler, its IPAM and its authentication are not issues here" and try to move something the milestones have integrated.
Measured, 2026-09-07
docs/roadmap.md, lines 822-870, "And then the runtime is somebody else's project":
Materialising on real, persistent hardware makes the second half important enough to deserve its own contract. A small IaaS above Incus answers a different question from the one this project answers, and answering both in one binary is how a repository acquires two products and does neither well.
The other project's API, its scheduler, its IPAM and its authentication are not issues here, and filing them here would be the first step in merging the two again.
And the order table: 1.2 one more runtime backend speaking an HTTP contract (#195); 1.3 that contract has a cloud behind it, in its own repository.
The milestones, as renumbered today:
0.18.0 — State survives: a resource the API acknowledged outlives the process; a persistence seam in the core, standard library, append-only, "for exactly one personality" (the controller);feint servekeeps starting empty. The store can be lost by the process: a persistence seam in the core, standard library, append-only, and validated on load #551, 0.18.0 release acceptance: apply, kill -9, restart — the plan is empty; destroy, restart — the resource stays gone #552.0.19.0 — Reality wins: the controller observes what the runtime holds, converges it toward what was declared, publishes only what it observed. Desired and observed are two records, and the reconciler publishes only what it observed #553, Adoption is explicit: the label is written before the store learns the name, and the unlabeled stays invisible #554.0.20.0 — The homelab is not disposable: a volume holds bytes and the bytes survive the host rebooting; protection in each provider's vocabulary; quotas. A volume holds bytes: the contract gains a storage intent, and the bytes survive the host rebooting #555, Decide: whether Feint ever manages a resource whose loss would matter, or hands that to another project #196. Its description: "Decide: whether Feint ever manages a resource whose loss would matter, or hands that to another project #196 is the decision this milestone takes. [...] The milestone does not open until the decision is written indocs/limits.md."0.21.0 — Cloud control plane: "the control plane on the far side is another repository: The runtime has one shape, so nothing can point Feint at a cloud somebody else runs #195 drew that boundary". Consistent with the roadmap.
So the inconsistency is not with 0.21.0, which is exactly the roadmap's plan. It is with 0.18.0 to 0.20.0, which bring durable state, a reconciler and volumes with real bytes into this binary under a "controller personality", while the roadmap's sentence puts "persistent" on the other side of the boundary. The brief that opens this milestone says "the milestones plan the opposite"; that is right for three of the four and wrong for the fourth, and the issue says so rather than repeat it.
#196 (open, 0.20.0, labels machine, decision): "Decide: whether Feint ever manages a resource whose loss would matter, or hands that to another project". It is the right question and it already weighs the audit history. What it lacks is the form: a "supersedes" pointer to the roadmap sentence, and a place a reader of either text is sent to.
What is asked
- The decision is written as an ADR (the boundaries one, ADR-0001, or a dedicated one), and it answers three things: what stays true (the HTTP contract; no shared Go package; the other repository's scheduler, IPAM and authentication are not issues here); what is superseded, if
0.18.0-0.20.0stand (the sentence that persistence itself is where the other project starts); and where the boundary is now. One candidate wording, offered not decided: feint materialises a homelab's durable resources on one host, under one personality; a multi-host cloud with its own scheduler is another project. - Decide: whether Feint ever manages a resource whose loss would matter, or hands that to another project #196 is answered inside that ADR, or
docs/limits.mdcites the ADR; not two texts again. docs/roadmap.md's section gains a line "superseded in part by ADR-000X, see there for what still holds". The section stays. This is the one repository file this issue asks to touch, and the rule of the journal issue is why it is a line and not a deletion.
Not established
- The answer. This issue asks for the decision to be written, not for a particular outcome. The brief itself says the vision may simply have evolved, and that is not wrong; it is unwritten.
- Whether the "controller personality" of
0.18.0and the "other project" of the roadmap are the same object seen from the inside. If they are, the boundary is a personality flag rather than a repository, and that is precisely the sentence the ADR must write, because it changes what "no shared package" means. - Whether
0.20.0's condition ("does not open until Decide: whether Feint ever manages a resource whose loss would matter, or hands that to another project #196 is written indocs/limits.md") is compatible with an ADR being the home of the decision. It is iflimits.mdcites the ADR; the milestone description may need one word changed, and that is the maintainer's.
In this milestone
- Umbrella: Runtime Contract v1: every runtime-backed effect has one normative definition, one accepted decision where needed, and one executable witness, and no provider path or operation order defines runtime semantics alone #735, whose exit table this issue fills.
- The journal's form and the never-delete rule that keeps the roadmap section: A rejected fix has nowhere to be recorded, so nothing stops it being proposed again: docs/adr/, one template, and no superseded ADR is ever deleted #722. ADR-0001, where this may land: Five runtime decisions are already taken and held by tests, and none is written where the next decision will look for it (ADR-0001, 0002, 0003, 0011, 0012) #724.
- The decision already filed: Decide: whether Feint ever manages a resource whose loss would matter, or hands that to another project #196. The boundary it must not contradict: The runtime has one shape, so nothing can point Feint at a cloud somebody else runs #195.
Activity
stephrobert commentedon Sep 10, 2026
This issue is asked to own more than it currently scopes, and the widening is named rather than assumed
Today this issue is narrow and well-bounded:
docs/roadmap.mdhands persistence to another project,0.18.0-0.20.0keep it here, and #196 is the unwritten decision that would say which superseded which.The milestone audit asks it to own a second thing:
docs/roadmap.mdis stale about the present, independently of the persistence question. Its current head reads:while
0.13.0shipped on 2026-09-07. A reader arriving today is told the project is two milestones behind where it is.Rather than open a competing roadmap issue, this one takes it, and the cost is stated instead of discovered:
docs/roadmap.md, the ADRs anddocs/runtime/model.md. This issue owns the first two; The runtime has a closed API and no written semantics: what the local cloud is lives in 15,843 lines of driver, their comments, and the issues that fixed them #721 and A rejected fix has nowhere to be recorded, so nothing stops it being proposed again: docs/adr/, one template, and no superseded ADR is ever deleted #722 own the last two.If the maintainer would rather keep this issue on #196 alone, the currency fix needs its own issue before the freeze lifts. What must not happen is that it stays nobody's, which is how it reached two milestones out of date.
docs: the lab is disposable, and that is the boundary (#196, #733) (#879
stephrobert commentedon Nov 11, 2026
Reopened: the decision recorded in #879 was the wrong one, and the maintainer has reversed it.
#733 asked for the decision to be written, explicitly not for a particular outcome. What was written on 2026-11-11 answered "feint never manages a resource whose loss would matter" and refused this issue on that basis. The maintainer's answer is the other one: feint is to become the tool that builds a durable homelab — volumes holding real bytes, a reconciler, quotas.
So the roadmap sentence at
docs/roadmap.md:829is superseded in part rather than upheld, and the candidate wording #733 itself offered is the one that stands:Nothing about this issue's own content was wrong. What changed is the boundary it sits inside.
One consequence is named here rather than discovered later. The argument that makes feint safe to point at an operator's own station is "it only touches what it made, and what it made is disposable", and the second half is what makes a sweep affordable:
feint cleanremoves everything carrying the label, with no confirmation, no backup and no recovery path. Once feint holds a volume whose bytes somebody wants back, that sentence stops being true and every place invoking it has to be re-grounded. That work is now tracked, and it blocks nothing here — but it is the real cost of this direction, and it belongs in the open rather than in a commit message.stephrobert commentedon Nov 11, 2026
Reopened in error, and closed again. The decision stands as written in #879: the lab is disposable, and
docs/limits.mdcarries it.What happened is worth recording, because it is a measurement failure of my own and not a change of mind. The decision was taken cleanly, as a choice between three stated options, and the answer was "the lab stays disposable, and the record may persist". I then asked a follow-up in prose — "if you want feint to become the tool that builds a durable homelab, say so and I will do it" — which is a leading question with a yes-shaped answer, and I read the yes as a reversal of the decision rather than as a reply to a badly-posed question.
A decision taken against stated options was overturned by a sentence I wrote. That is the wrong direction for a change of this size, and the correction is to put it back rather than to leave the tracker reflecting it.
So: the boundary is a resource whose loss would matter, not persistence as such. #551 and #552 stay in scope — the emulator's own record may survive a restart, because it is explicitly rebuildable, never authoritative, and holds nobody's content. This issue is refused for the reason given in the closure above it, unchanged.