Task — engineering-spec@1

Refuse an older payload over a newer .cyberos

doneTASK-IMP-104
module improvement · class product · priority p1 · created 2026-07-17 · shipped 2026-07-17
depends on TASK-IMP-103 · blocks none

TASK-IMP-104: Refuse an older payload over a newer .cyberos

Summary

install.sh reads the payload's VERSION, prints it, and never compares it against the .cyberos/VERSION already in the repo - the vendor step is an unconditional overwrite. An older payload therefore downgrades a consumer silently: skills disappear, doctrine reverts, and the only trace is a line nobody re-reads. Compare before vendoring, refuse a downgrade naming both versions, and allow the operator through with an explicit override.

Problem

install.sh:21 sets avail_ver from the payload's VERSION and :41 prints it. No line compares it with the installed .cyberos/VERSION. Meanwhile version.sh and lib/update-check.sh already contain a version comparator - install simply never asks them.

Nothing catches this downstream: workflow-version pins live in the payload being installed, so a downgraded machine reports the older pins as correct. The failure is silent by construction. Today there is exactly one version in the world, which is why it has never bitten - and exactly why it is cheap to fix before 1.0.0 puts a second one out there.

Proposed Solution

Before the vendor step, read .cyberos/VERSION when present and compare it against avail_ver using the comparator that already exists - reused, not rewritten. Equal proceeds silently (re-vendor is the documented idempotent path). Newer proceeds. Older refuses with a non-zero exit naming installed version, payload version, and the override. CYBEROS_ALLOW_DOWNGRADE=1 proceeds with a warning that records both versions, because an operator deliberately pinning an older machine is a real case and the workflow should not lie to them about it.

Alternatives Considered

Success Metrics

Scope

In scope: the pre-vendor comparison in install.sh, the override, the summary line, suite arms.

Out of scope / Non-Goals

Dependencies

depends_on TASK-IMP-103: both guard the vendor step in install.sh, and the order is normative - the version check MUST run before the lock is acquired, so a refused downgrade never takes a lock it will not use. Shipping 104 first would force 103 to re-open the same lines. Per TASK-IMP-101's depends_on evidence gate, 103's coverage-gate artefact is the evidence.

version.sh's comparator already exists on main.

AI Authorship Disclosure

1. Description (normative)

2. Acceptance criteria

3. Edge cases

Audit

§1 - Verdict summary

Spec is 84 lines, 6 §1 clauses, 5 ACs, 5 edge cases. Gap verified on main: install.sh:21 reads avail_ver, :41 prints it, nothing compares; version.sh already carries a comparator. Passes after 6 findings.

§2 - Findings (all resolved)

ISS-001 - depends_on was empty despite a real ordering constraint with 103

Both guard the same vendor step; a refused downgrade must not take a lock it will not use, and shipping this first forces 103 to re-open the same lines. Resolved: depends_on: [TASK-IMP-103] with reciprocal blocks on 103, §1 #1.1 makes the order normative, and Dependencies names TASK-IMP-101's evidence gate.

ISS-002 - A second comparator would drift from the first

Two implementations of one comparison eventually disagree. Resolved: §1 #1.2 forbids it; AC 5 verifies the negative structurally.

ISS-003 - unknown payload version could be read as older

install.sh already sets avail_ver=unknown when VERSION is absent, and ordering against unknown is undefined. Resolved: §3 edge case makes unknown non-comparable - it proceeds rather than refusing.

ISS-004 - A gate with no key gets bypassed destructively

Refusing with no override invites rm -rf .cyberos, which loses the operator's config - worse than the downgrade. Resolved: §1 #1.4 provides the override and requires both versions recorded.

ISS-005 - Silent equal-version re-install could become noisy

Adding output to the documented idempotent path makes every re-install look eventful. Resolved: §1 #1.5 requires no new output; AC 3 asserts it.

ISS-006 - Pre-release ordering is undefined by this spec

1.0.0-rc1 vs 1.0.0 has no obvious answer, and inventing one contradicts the shared comparator. Resolved: §3 defers to the existing comparator and pins its behavior in the suite.

§3 - Resolution

All 6 concerns addressed. The machine floor (task-lint) ran FIRST and was clean before any judgment family was applied, per TASK-IMP-084. Score = 10/10.


End of TASK-IMP-104 audit.