Known technical debt
These are known, deliberate, and load-bearing. Do not “fix” any of them without discussion – each one is the honest interim for a problem whose real fix is a product decision, and several look like oversights precisely because the honest interim resembles one.
These are known, deliberate, and load-bearing — do not “fix” them without discussion:
introspectionScript.pyduplicates format-handling logic fromdataLoaders.py. Container scripts cannot import from the host.dictCtxabbreviates “context” although the style guide forbids abbreviating words shorter than eight characters. It is the per-hub context dictionary every route module receives, and its spelling is part of a registration signature used throughout the backend (fnRegisterAll(app, dictCtx)). Ruled an accepted, documented exception (ruling R4, 2026-10-02): neither style scanner flags it and it is not to be renamed in passing.tests/testStyleInvariants.pypins this entry.elin the frontend is NOT such an exception; it waits for a JavaScript naming lane and must not be mass-renamed piecemeal.DockerConnection.fnWriteFileViaTarno longer builds a tarball. It execs a fixed program as the container user and streams the bytes on stdin (vaibify/docker/confinedWrite.py), because aput_archiveextracts as root and follows symlinks the in-container agent planted. The name is kept because the mutation ledger, the falsification registry and sixty-odd call sites key on it; renaming it is a mechanical follow-up, not a behavior change.fnWriteTreeViaTarno longer usesput_archiveeither: it streams a tar to a fixed receiver program (the same module) that lands each member relative to directory descriptors, so a planted symlink is refused rather than followed.fnWriteFileViaTaris now a thin wrapper overfnWriteFileFromStream, the streamed funnel: the program copies stdin in 1 MiB chunks, fsyncs, refuses a stream whose length differs from the stated size, and can refuse to replace an existing file (atomically, by hard link, where the filesystem has one). The host connection has the same method.put_archivemust never come back for streaming; the streaming write is this one.Downloads no longer ask the daemon for an archive.
get_archiveanddocker cpread as root, so a component an agent swapped for a symlink after the path check redirected the read.DockerConnection.fiterReadFileConfinedandfiterReadDirectoryAsTarrun fixed programs as the container user (vaibify/docker/confinedRead.py); the host leg isvaibify/host/hostConfinedRead.py, held to the same contract bytests/testConfinedReadParity.py. A final symlink is followed only lexically and only inside the authorized root; a folder archive never follows a link and holds no member beneath one. The olderfiterStreamFile(daemon archive) still serves callers that have not moved.scriptFigureViewer.jswas not part of the 2026-01 frontend refactor. Kept as a single cohesive module.Re-export blocks exist across
pipelineRunner,pipelineServer,testGenerator, andsyncDispatcherfor backward compatibility. Callers should migrate toward canonical imports over time; do not delete the re-exports until external callers are updated.vaibify/reproducibility/githubWorkflow.pyis implemented, tested, and unreachable — no product code imports it. Kept rather than deleted because wiring it expands remote-execution surface, which is a product decision.tests/testOrphanedPublishMachinery.pyfails if the docs re-advertise it or if it gains a caller while still marked unreachable.condaPackagesis refused at validation rather than installed. The Dockerfile installs Miniforge for a non-pip package manager but has noconda installstep and no build argument carries the list, so accepting the field produced a container without the requested packages and said nothing. Wiring it is the honest fix; refusing is the honest interim.terminalContainment.pyand theterminaljournal kind also reconcile records written by EARLIER hub versions — release, the safe reaper and shutdown all settle terminal records throughfdictTerminateAndProveRecord— andtests/testTerminalContainmentLive.pykeeps the container process-group prover as the standing demonstration that it cannot see asetsiddescendant (tests/testHostTerminal.pycarries the host leg’s twin demonstration). This bullet used to say the in-memory registry was permanently empty; the terminal came back for containers on 2026-08-11 and for host projects on 2026-08-15, so live sessions register in production again.commitCarrier.fdictRequestDurableTaskCancelhas no caller and refuses everything. Kept deliberately: Python cannot interrupt a worker inasyncio.to_thread, so there is no honest generic cancel, and a reader who finds no function at all re-derives that from scratch — or writes one. The refusal is the answer, in the place the question is asked.The poison axis is NOT subsumed by the journal quarantine. A quarantine record survives a crash; it does not fence a socket that is open right now. Poison does both — the pipeline lane is refused at the gate and revalidated per frame — so the two are complementary, not redundant.
bAllowEmulationon an obtained project’s registry entry is frozen at conversion. The wizard offers the emulation checkbox only when the daemon was reachable while the wizard was open and its architecture differed from the pin, so a clone converted while Docker was unreachable can never consent later: no route updates the flag (fnUpdateImageSourceis called only for the overlay fields). The forward switch (switch-to-pinned-image) asks the question afresh, so switching to building and back is the workaround today; a route that updates the flag is the fix, once a researcher meets it.“Retire this container” is spelled out twice, and
vaibify destroymeans something narrower than the dashboard’s Delete. The stop-or-remove branch plus the keep-alive stop lives in bothregistryRoutes._fnExecuteStopandenvironmentDeletion._fbRemoveContainer, so both reach the lifecycle gateway’s primitives from outside it and the second copy raisedI_MUTATION_CAPABLE_OUTSIDE_GATEWAY_BUDGETby two. Homing the operation insidecontainerManagerwould collapse both call sites and lower that ratchet by three; it was not done alongside the delete feature because_fnExecuteStopis patched and source-inspected by name from four test modules, so the move needs its own change and its own reconfirmation. Separately, the CLI’svaibify destroyremoves only the workspace volume and optionally the image — not the container, the credentials volume, the other image tags, or the registry entry — so the CLI and the GUI now mean different things by “destroy”. Widening a CLI command’s blast radius is a product decision, so the divergence is recorded rather than quietly resolved.The memory watch never names the process an out-of-memory kill took. The kernel writes the victim’s name only to the Docker virtual machine’s own log, which a container can read only when it is privileged and shares the host’s process namespace – a container vaibify will not create to answer a display question. Guessing the victim by comparing process lists before and after would show a guess as fact, so the sentence says “a process” and asks the researcher to check what was running.
The memory watch keeps its incident history in the hub’s memory (
containerMemoryWatch). A hub restart forgets the kills of containers that have since been removed, and a running container’s kernel counter is reported again, as abeforeObservationincident whose time vaibify did not see. Persisting the history is possible; it was not done because a restart that loses history is honest about it, and a store on disk would need its own retention rules.A container terminal’s prompt reset (
S_PROMPT_RESET_RCFILEinterminalContainment.py) is appended after the hooks in the researcher’s~/.bashrc, but a hook that a tool adds toPROMPT_COMMANDlater, at runtime, runs after it. That is harmless: the reset is idempotent and hands on the exit status it received, so a later hook sees what it would have seen. Running it after every hook ever added would mean rewritingPROMPT_COMMANDon every prompt, which is a larger intrusion into the researcher’s shell than the defect it would cure.A live CPU or memory limit change (
resourceLimitApplication) runs under the per-container mutation lock, so it is serialized with guarded writes and with a start’s settlement, but not with a stop: the stop route holds no such lock (its own comment inregistryRoutesrecords why). The update is pinned to the container id a fresh inspect named, so it can only ever change that container, and a stop that lands mid-update makes the re-inspect fail and the outcome read “could not be applied”, never “applied”.A
vaibify connectorvaibify verifyshell is recorded on the host (~/.vaibify/cliShells/) and ended when its window closes or, once the CLI is provably dead, by the hub’s reaper, but it takes no container flock and writes no journal record, so it remains invisible to the quiescence claim: a project in which only a CLI shell has run still reports its quiescence as it did before. The record exists for cleanup, not for the claim; giving the claim sight of CLI execs is a later step that needs no lock to add.An interactive session in a container that no vaibify window or CLI opened cannot be attributed: vaibify cannot tell a researcher’s own
docker execor an editor’s remote shell from a leak, so the remnant panel lists it as possibly orphaned and never ends it on its own.The
setsidescape: a descendant that callssetsidleaves the session the containment record tracks, so every group-based proof, the dashboard terminal’s, the CLI shell’s and the scanner’s, can miss it. Whether the agent CLIs or their tool subprocesses do this has not been measured; measure before building anything on the answer.A
caffeinate -sthat is not in vaibify’s spawn ledger cannot be told from one the researcher started by hand, which the docker-status hint invites them to do. It is listed as possibly and removed only on the researcher’s confirmation, start-clock gated.A terminal or CLI shell orphaned by a hub that died abruptly runs until the next reaper pass, up to ten minutes, or until a hub is started. A dead-man switch in the container was considered and not built: the VM freezes while the host sleeps, so an in-container watcher would kill live sessions on wake, exactly when the researcher returns. The reaper logs how many it ends per pass; revisit if that is non-zero at more than one hub start in a month.
Source comments, docstrings and test oracles still cite design documents that are no longer in the tree: a path under
design/, or a label such as “design fact 28”, “D5”, “F7” or “I8”. The directory was removed on 2026-10-26 (planning documents, not reference documentation), and the citations were left in place, because one of them is in the frozencomparisonPolicyV1.py, which cannot change, and several falsification tests name these documents as the independent source of their expected values. Each file is in git history at4a15f03b; read one withgit show 4a15f03b:design/<name>.md. The files wereagentCouncil.md,agentCouncilContinuationPlan.md,agentCouncilMultiProviderImplementationPlan.md,agentCouncilPhase0Findings.md,agentCouncilRemediation.md,agentCouncilReviewCouncil.md,agentCouncilVerificationLanes.md,copiesLevel3MergeAndOrderedEndgame.md,cpuClassReproducibility.md,cpuClassReproducibilityImplementation.md,externalServices.md,level3AttainmentSequence.md,permanentArchivePromotion.mdandpermanentArchivePromotionImplementation.md.