You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The protection layer is 4,865 lines of non-test code in the tree, reachable from no command, with its design document no longer on main and no issue tracking it. This is that issue.
It also records a mistake made in #51, which removed one file of that layer.
What is in the tree
19 non-test files, 4,865 lines, against 20 test files and 2,759 lines:
The test coverage is real and passing. That is what has kept it invisible: unused cannot flag a function a test calls, so none of this appears in any gate.
None of it is reachable
ob --help lists no backup, restore, or protection command. Checking the entry points, each one appears only in the file that defines it:
Symbol
Production callers outside its own file
ApplyScheduledInstall
none
EnableProtection
none
AcquireProtectionLock
none
InstallProtectionCredentialFile
none
ValidateS3Target
none anywhere
BuildScheduledInstallPlan
none anywhere
cmd/ob-scheduled-runner is not built by anything. .goreleaser.yaml declares a single build, main: ./cmd/ob, and no Justfile recipe or workflow step compiles the runner. The design binds artifacts to a runner digest (Names.ProtectionRunnerPath, internal/onebox/runner_policy.go), and the binary that would satisfy it is in no release artifact.
The docs are honest about it
This is not a documentation gap. site/src/content/docs/status/capabilities.mdx states the position clearly, in two places:
Schema only, under a caution box reading "Your editor is ahead of the engine here. These three blocks are published in the JSON Schema and accepted by ob validate. Nothing behind them executes." — covering backup_targets and services.<name>.protection.
Not owned at all — "Backups. Onebox does not take them." and "Restore proof. No drill is run, and no service can currently prove a backup would restore."
docs/product.md and the README agree. ob doctor actively reports the absence of backups for every workload holding durable data.
So the product's public position is correct and consistent. What is missing is any record of the code.
The design document is not on main
The work was specified. openspec/changes/complete-oss-production-baseline/ held the proposal, design, task list, and seven spec files including specs/managed-data-protection/spec.md, whose purpose reads:
Defines trustworthy, driver-native protection for Onebox-run data services, including encrypted off-host backups, verified restores, restore drills, and evidence-based service-tier graduation.
That directory is absent from HEAD. It survives in tag v2026.8.0, so nothing is lost, but it is not reachable from main and nothing in the repository points at it:
git show v2026.8.0:openspec/changes/complete-oss-production-baseline/tasks.md
git show v2026.8.0:openspec/changes/complete-oss-production-baseline/specs/managed-data-protection/spec.md
tasks.md records 26 complete and 122 outstanding, of which 27 outstanding sit in sections 5–7 — the protection, restore, and image-publication slices. Those outstanding tasks are exactly the missing surface:
#33fix: make durable data mean one thing, and the backup gate satisfiable
—
gate correctness
#22's own description sets the expectation plainly: "Database backup and restore engines follow as separate slices." This is staged work that stopped between slices, not abandoned code.
#51 deleted internal/engine/protection_units.go (274 lines) plus its test, on the reasoning that ProtectionUnitTarget had no implementation outside an in-memory test double and no caller anywhere. That reasoning was accurate and the conclusion was wrong, for two reasons found only after the fact:
It was a listed deliverable of feat(protection): add lifecycle foundation #22. That PR enumerates "systemd units" among what it implements. Judged against "has a caller today" it looked like dead code; judged against the plan it was slice 1 of 3, waiting on the slices that install and invoke it.
git show f9ef6bc^:internal/engine/protection_units.go
git show f9ef6bc^:internal/engine/protection_units_test.go
The deletion also left the layer half-removed: one file gone, 4,784 lines of its siblings standing, on no consistent principle.
What to decide
The code is not the problem. The problem is that 4,865 lines of a specified, part-built subsystem sit in the tree with no issue, no spec on main, and a linter configuration that cannot see it — so the next person to run a dead-code pass reaches the same wrong conclusion #51 did, only for more of it.
Three coherent options:
A. Restore and track it as staged work. Bring protection_units.go back, bring openspec/changes/complete-oss-production-baseline/ back onto main (or a trimmed docs/design/ equivalent), and open one issue per outstanding slice. Cost is low and it makes the state legible. This is the recommendation, because #22 committed to the follow-on slices and the specification for them already exists.
B. Remove the layer. If protection is not being finished, remove all 4,865 lines and their tests, drop backup_targets and services.<name>.protection from the published schema, and delete the "Schema only" rows. This is defensible and honest, but it is a breaking schema change and discards work with a written design.
C. Freeze it deliberately. Keep the code, add a package-level comment on each file naming it as staged work behind managed-data-protection, and add a //go:build tag or an unused-exclusion entry so its status is stated in the code rather than inferred from test coverage. Cheapest, and it at least stops the next accidental deletion.
Whichever is chosen, two things should happen regardless:
The design document should be reachable from main, not only from a tag.
cmd/ob-scheduled-runner either gets built and shipped, or is removed. A binary that no release contains and no recipe compiles is not a dependency anything can rely on.
Verification
Every claim above was checked against the tree at 81d0ead:
Line counts from wc -l over the file set named in the table
Caller checks via rg -l '\bSymbol\(' internal/ cmd/ --type go -g '!*_test.go'
ob --help from the built binary
.goreleaser.yaml build list read directly
Spec content and task counts from git show v2026.8.0:openspec/...
PR scope from gh pr view 22, 23, 33
Related: #50 (the audit that surfaced the dead code), #59 (the sub-issue for the deletion), #56 and #57 (the systemd ownership gaps the deleted file addressed).
The protection layer is 4,865 lines of non-test code in the tree, reachable from no command, with its design document no longer on
mainand no issue tracking it. This is that issue.It also records a mistake made in #51, which removed one file of that layer.
What is in the tree
19 non-test files, 4,865 lines, against 20 test files and 2,759 lines:
internal/onebox/— evidence, state, envelopes, install, identity, resources, credentials, S3 target, active volumeinternal/app/—protection_schema.go,protection_artifacts.gointernal/engine/—protection_lock.go,scheduled_protection.go,protection_credentials.gointernal/journal/protection.gocmd/ob-scheduled-runner/main.goThe test coverage is real and passing. That is what has kept it invisible:
unusedcannot flag a function a test calls, so none of this appears in any gate.None of it is reachable
ob --helplists nobackup,restore, orprotectioncommand. Checking the entry points, each one appears only in the file that defines it:ApplyScheduledInstallEnableProtectionAcquireProtectionLockInstallProtectionCredentialFileValidateS3TargetBuildScheduledInstallPlancmd/ob-scheduled-runneris not built by anything..goreleaser.yamldeclares a single build,main: ./cmd/ob, and no Justfile recipe or workflow step compiles the runner. The design binds artifacts to a runner digest (Names.ProtectionRunnerPath,internal/onebox/runner_policy.go), and the binary that would satisfy it is in no release artifact.The docs are honest about it
This is not a documentation gap.
site/src/content/docs/status/capabilities.mdxstates the position clearly, in two places:ob validate. Nothing behind them executes." — coveringbackup_targetsandservices.<name>.protection.docs/product.mdand the README agree.ob doctoractively reports the absence of backups for every workload holding durable data.So the product's public position is correct and consistent. What is missing is any record of the code.
The design document is not on
mainThe work was specified.
openspec/changes/complete-oss-production-baseline/held the proposal, design, task list, and seven spec files includingspecs/managed-data-protection/spec.md, whose purpose reads:That directory is absent from
HEAD. It survives in tagv2026.8.0, so nothing is lost, but it is not reachable frommainand nothing in the repository points at it:tasks.mdrecords 26 complete and 122 outstanding, of which 27 outstanding sit in sections 5–7 — the protection, restore, and image-publication slices. Those outstanding tasks are exactly the missing surface:backup create,backup list,backup inspect, replay inspection, protectionstatusrestore preparerestore testHow it got here
feat(protection): add lifecycle foundationfeat(backup): add S3 target adapterfix: make durable data mean one thing, and the backup gate satisfiable#22's own description sets the expectation plainly: "Database backup and restore engines follow as separate slices." This is staged work that stopped between slices, not abandoned code.
What #51 got wrong
#51 deleted
internal/engine/protection_units.go(274 lines) plus its test, on the reasoning thatProtectionUnitTargethad no implementation outside an in-memory test double and no caller anywhere. That reasoning was accurate and the conclusion was wrong, for two reasons found only after the fact:X-Onebox-Application/-Environment/-Servicemetadata in each unit and refused to overwrite a unit whose filename matched but whose ownership did not.internal/engine/schedule.go, which survives and is live, matches on filename prefix alone — which is the substance of Four derived names use hyphen joins the naming contract forbids #56 and Stale timer sweep skips removal when disable fails #57.It is recoverable in full:
The deletion also left the layer half-removed: one file gone, 4,784 lines of its siblings standing, on no consistent principle.
What to decide
The code is not the problem. The problem is that 4,865 lines of a specified, part-built subsystem sit in the tree with no issue, no spec on
main, and a linter configuration that cannot see it — so the next person to run a dead-code pass reaches the same wrong conclusion #51 did, only for more of it.Three coherent options:
A. Restore and track it as staged work. Bring
protection_units.goback, bringopenspec/changes/complete-oss-production-baseline/back ontomain(or a trimmeddocs/design/equivalent), and open one issue per outstanding slice. Cost is low and it makes the state legible. This is the recommendation, because #22 committed to the follow-on slices and the specification for them already exists.B. Remove the layer. If protection is not being finished, remove all 4,865 lines and their tests, drop
backup_targetsandservices.<name>.protectionfrom the published schema, and delete the "Schema only" rows. This is defensible and honest, but it is a breaking schema change and discards work with a written design.C. Freeze it deliberately. Keep the code, add a package-level comment on each file naming it as staged work behind
managed-data-protection, and add a//go:buildtag or anunused-exclusion entry so its status is stated in the code rather than inferred from test coverage. Cheapest, and it at least stops the next accidental deletion.Whichever is chosen, two things should happen regardless:
main, not only from a tag.cmd/ob-scheduled-runnereither gets built and shipped, or is removed. A binary that no release contains and no recipe compiles is not a dependency anything can rely on.Verification
Every claim above was checked against the tree at
81d0ead:wc -lover the file set named in the tablerg -l '\bSymbol\(' internal/ cmd/ --type go -g '!*_test.go'ob --helpfrom the built binary.goreleaser.yamlbuild list read directlygit show v2026.8.0:openspec/...gh pr view 22,23,33Related: #50 (the audit that surfaced the dead code), #59 (the sub-issue for the deletion), #56 and #57 (the systemd ownership gaps the deleted file addressed).