Chapter 1 — Why a Contract Layer
Cross-OS automation has two shapes that fail. One is "OS-by-OS feature flags" —
if platform.system() == "Linux":branches scattered through every test. The other is "least common denominator" — strip away the platform-specific concepts until the API is too anaemic to be useful. The sysman tier rejects both with a contract layer: 52 cross-OS contracts that lock the semantics of each feature without committing to OS-specific mechanism.
This is the volume's foundational chapter. By the end, a
reader should know why a separately-versioned contract layer
exists and what it commits to. Chapter 2 walks the contracts
themselves; chapter 3 walks the substrate (featurebase)
that turns the contracts into runnable code.
The two failure modes
Most automation suites that span multiple OSes choose between two unworkable patterns.
Pattern A — OS-by-OS feature flags
def install_package(name: str) -> bool:
if platform.system() == "Linux":
if shutil.which("dnf"):
return _run_dnf_install(name)
elif shutil.which("apt-get"):
return _run_apt_install(name)
else:
raise RuntimeError("unknown Linux package manager")
elif platform.system() == "Darwin":
return _run_brew_install(name)
elif platform.system() == "Windows":
return _run_winget_install(name)
else:
raise NotImplementedError(platform.system())
The pattern looks reasonable in one place. It's a disaster
across thousands of test cases. The if platform.system()
ladders proliferate. The error-handling diverges. The
return shapes diverge — _run_dnf_install returns a tuple,
_run_brew_install returns a status enum, _run_winget_install
returns a parsed dict. Test code that calls
install_package has no idea what to expect; either every
caller learns every OS's quirks, or the function lies by
forcing a least-common-denominator return.
The cost shows up as drift. Six months in:
- One OS's branch tests for a different return type than another's because the developer who wrote that branch had different assumptions.
- One OS's branch silently swallows errors that another's bubbles, and a test passes on macOS while failing on Linux for the same scenario.
- One OS's branch handles the "package already installed" case as success; another raises. Test code rewrites the semantics every time.
Pattern B — Least common denominator
def install_package(name: str) -> bool:
"""
Returns True if the package install command exited cleanly.
"""
cmd = _platform_install_command(name)
return subprocess.run(cmd).returncode == 0
The pattern looks clean. It's also useless. "Did the install command exit cleanly" is not the question the test is asking. The test wants to know:
- Was the package already installed (skipped) or was it freshly installed (succeeded)?
- If a version was specified, did the right version land?
- If installation failed, was it a transient network blip or a permanent error like "package doesn't exist in any configured repository"?
- Was elevation required, and was it granted?
- Did the install have side effects that need to be undone if the test fails partway through?
A least-common-denominator API answers none of these. Every call site has to re-derive the answer from raw stdout / stderr / exit codes — and the call site doesn't know the OS-specific output formats.
The third option — a contract layer
The sysman tier picks neither pattern. It picks a contract layer: a set of cross-OS specifications that define the semantics of each feature without committing to OS-specific mechanism.
Concretely:
- Every feature has a markdown contract at
codecraftai-sysman-common/specs/contracts/<feature>/contract.md. - The contract specifies verb signatures (what methods exist, what arguments they take, what they return).
- The contract specifies invariants (what must be true before / during / after the call).
- The contract specifies result vocabulary (what
ActionStatusvalues are returned, when each applies). - The contract specifies elevation rules (which verbs elevate, which don't).
- The contract leaves mechanism to per-OS implementations.
A test calling package_manager.install("nginx") knows it
will get back a result with a status of SUCCESS, SKIPPED,
FAILED, or DRY_RUN — and knows what each means. The
test code is the same across OSes. The implementations
underneath differ wildly (dnf invocations vs. apt-get vs.
brew), but the test sees a uniform surface.
The 52-contract substrate
The portfolio ships 52 contracts — one per feature area.
The full list lives at
codecraftai-sysman-common/specs/contracts/README.md:385–510,
catalogued in three layers:
- Layer 1 (Infrastructure) — 1 contract:
command-execution. The substrate for everything above. - Layer 2 (Data) — 1 contract:
maintenance-reporting. Cross-feature reporting. - Layer 3 (Business Logic) — 50 contracts: firewall, service, package, network, encryption, certificate, user/group, kernel parameters, virtualization, container, ACLs, audit, authentication, backup, DNS server, mail transport, NFS, repository, samba, security compliance, storage, etc.
The chapter doesn't list all 52 by name (the catalogue does
that). What matters is the shape of the layering. A
Layer-3 contract like package-management calls into Layer
1's command-execution contract for actually running the
underlying command. Layer 2's maintenance-reporting
sits alongside, capturing the cross-feature concerns of
"what changed and when."
The five reference implementations
Five OSes are implemented to the contract:
codecraftai-sysman-linux-redhat— RHEL family (RHEL, CentOS Stream, Fedora). 46 features. 53 test files.codecraftai-sysman-linux-ubuntu— Ubuntu / Debian family. 40 features. 41 test files.codecraftai-sysman-macos— macOS (Apple Silicon and Intel). 49 features. 45 test files.codecraftai-sysman-windows— Windows 10/11 / Server. 49 features. 45 test files.codecraftai-sysman-uefi— UEFI firmware. Intentionally smaller — 5 features only.
Why five? Because these five OSes cover the bulk of production automation targets. The contract layer doesn't forbid implementations for other OSes — adding FreeBSD, Solaris, or AIX would follow the same pattern — but the maintenance burden of each implementation is real and the five shipped are what the portfolio actively maintains.
A note on the test counts. The volume's outline mentions
much higher numbers (e.g., "814 tests" for Ubuntu, "917 tests"
for Windows). Those are test invocation counts (individual
test_* functions) rather than test file counts. A single
test file typically holds a dozen or more test functions.
The chapter uses file counts in its summaries because they're
verifiable; readers should expect actual test invocations to
be 10–20× the file count.
Why these five are not the same
A reader might naively expect that ~50 contracts × 5 OSes should produce ~250 features × 5 implementations. The reality is messier:
- RHEL has features Ubuntu doesn't —
selinux-management(RHEL has SELinux; Ubuntu uses AppArmor),subscription-management(Red Hat Subscription Manager),cockpit-management,insights-management,tuned-management. - Ubuntu has features RHEL doesn't —
bootloader-management(covers EFI variants beyond RHEL's simpler boot story). - macOS doesn't ship some features — Linux-specific
concepts (
selinux-management,firewalld) have no macOS analogue. - Windows has unique concepts — Active Directory integration, Windows-specific service control, DSC.
The contract layer accommodates this through Convention 5
(chapter 9 walks the eleven conventions): when a feature has
no scriptable implementation on a given OS, the manager
returns *ActionResult with status=FAILED and a stable
detail token like unsupported_on_host_os_darwin. This
is the contract — a feature that's intentionally
unsupported on an OS still has a manager that responds
predictably.
What the contract layer commits to
A reader should understand what the contract layer locks down and what it leaves loose.
Locked
- Verb names. Every OS's
PackageManagerexposesinstall,remove,query,list_installed, etc. with exactly the names the contract specifies. - Verb signatures. Argument order, types, defaults.
- Result types. Every
*ActionResultsubclass hasstatus,detail,exit_code,stdout,stderr,commandfields (per Convention 2). - Status vocabulary.
SUCCESS,FAILED,SKIPPED,DRY_RUN— same four values across every feature on every OS. - Elevation rules. Reads never elevate; writes elevate per the manager's config (with the Homebrew / Convention 11 inversion as the well-known exception).
Loose
- Mechanism. How a manager actually invokes the
underlying tool. RHEL's
PackageManager.installrunsdnf install -y; Ubuntu's runsapt-get install -y; macOS's runsbrew install(withsudo -u). - Output parsing. Every OS's tool produces different text; per-OS code parses its own.
- Per-OS quirks. Apt's
DEBIAN_FRONTEND=noninteractive, Homebrew's root refusal, Windows' winget table-format fragility — all per-OS implementation concerns.
The split is what makes the layer work. A test author writing against the contract sees a uniform surface. An implementor handling a single OS focuses on that OS's specifics. Neither has to know about the other's concerns.
What the contract layer is not
Three misconceptions worth clearing up.
It is not a shared abstraction layer
The contract layer is not "an abstract base class everyone
inherits from." It is prose — markdown contracts that
specify the API in English plus tables. The shared base
class (BaseFeatureManager from chapter 3) implements some
of the contract uniformly, but the contract itself is text.
There is one deliberate exception that proves the rule. The
python-environment-management contract is implemented
once, as a concrete base class in codecraftai-sysman-common,
because the tools it drives (python -m venv, python -m pip)
do not vary by OS at all. Chapter 3 walks that case; the
point here is that the exception was made only after the
tool-level analysis showed there was nothing per-OS left to
write except paths and directory probes.
The reason: per-OS implementations vary so much in how they implement the contract that a code-level abstraction would either be too thin (forcing duplication) or too opinionated (forcing per-OS quirks into a shape that doesn't fit). Prose contracts let the contract specify what and let each OS's code decide how.
It is not generated
There is no code generator for the per-OS implementations.
Every per-OS file is hand-written to follow the contract.
The substrate (featurebase — chapter 3) provides shared
helpers, but the manager classes themselves are
hand-authored.
The reason: the OS-specific quirks (Homebrew root refusal, winget output parsing, launchd domain semantics) are genuinely irreducible. A code generator that tried to produce them would either (a) not produce them and force hand-edits, defeating the generator, or (b) become a prose-to-code translator that's harder to read than the direct hand-written code.
The chapter is honest: hand-writing ~50 contracts × 5 implementations is expensive. The portfolio justifies the cost by treating sysman as a foundational tier — once it's correct, every higher-tier test relies on it. The cost is amortised across the rest of the series.
It is not bidirectional
The contracts describe what the framework provides to test
code, not what test code provides to the framework. A
test that wants to install a package calls
PackageManager.install(...); the contract specifies that
call. The reverse — "what does test code have to do for the
framework to call its hooks?" — is a different concern
covered by the test framework (Volume 1).
How the discipline holds up
Three mechanisms keep the per-OS implementations aligned with the contracts:
Shared base classes
Every per-OS manager inherits from BaseFeatureManager (or
its featurebase family). The base class implements the
common scaffolding (__init__, _dispatch,
_wrap_result) so that anything that uses these helpers
follows the contract by construction. Chapter 3 walks the
base.
The 11 Implementation Conventions
Documented at
codecraftai-sysman-common/specs/contracts/README.md:75–364,
the eleven conventions cover the cross-OS patterns that
emerged from the Phase-3 implementation work. Things like:
- Convention 1:
ICommandRunneris the file-I/O seam. No separateIFileOperator. - Convention 4: OS-prefixed concrete type names
(
UbuntuPackageManager, etc.). - Convention 11: Elevation-model inversion when the underlying tool refuses root (Homebrew).
Chapter 9 walks all eleven in detail.
Per-OS test coverage
Every contract's "Tests" section prescribes minimum coverage (command-construction tests per write verb, idempotency tests, dry-run tests, elevation tests). Per-OS packages implement these tests. When a per-OS implementation drifts from the contract, the tests catch it.
The discipline isn't enforced by automated specification tooling. It's enforced by the combination of shared bases, documented conventions, and thorough tests. An engineer trying to skip the discipline runs into one of the three.
What this volume covers
The remaining nine chapters walk the layer in detail:
- Chapter 2 — the 52 contracts themselves. How they're organised, the EARS-inspired structure, three worked examples (package-management, service-management, network-configuration).
- Chapter 3 —
featurebaseand the per-OS implementation pattern.BaseFeatureManager,FeatureConfig,FeatureActionResult, the_dispatchhelper. - Chapters 4–8 — the five reference implementations, one per OS. RHEL, Ubuntu, macOS, Windows, UEFI. Each chapter walks the package's structure, key features, and OS-specific quirks.
- Chapter 9 — the 11 Implementation Conventions and the spec-driven development discipline.
- Chapter 10 — building cross-OS test parity. How the tests compose into a green-across-OSes campaign.
By the end of the volume, a reader should be able to:
- Read any contract and predict the per-OS implementation shape.
- Read any per-OS feature and identify which contract it implements.
- Add a new feature to an existing OS by writing the contract first and the implementation second.
- Add a new OS to the suite by implementing the existing contracts following the conventions.
The discipline is the volume's value. Five OSes × 50 features is a maintenance bill many automation suites would walk away from. The portfolio pays the bill because the contract layer makes the maintenance predictable — every test against the sysman tier behaves the same way regardless of OS.
What's next
Chapter 2 walks the 52 contracts: their structure, what they specify, and three worked examples that show the contract format end-to-end.