Repository navigation
Configurable i3 emulator + per-configuration compliance CI - #43
Merged
Merged
Conversation
…builder The mock i3 shape was hard-coded to scalar-i3-50-3, so the emulator could not represent any other valid i3 configuration despite the cross-repo contract advertising EMULATOR_SLOT_COUNT / EMULATOR_DRIVE_COUNT / EMULATOR_PROFILE / EMULATOR_OCCUPANCY_PERCENT. This adds a ScalarI3Profile builder: - build_scalar_i3_config(profile) generalizes slots, drive mix, LTO generation (incl. LTO-9=18TB), occupancy, IE slots, and partitions, keeping the 1,bay,slot addressing and one-cleaning-slot-per-drive rules. - Fail-fast validation: 1<=drives<=6 (documented i3 max), known LTO gens, slot_count >= drives+1, occupancy 0..100, partition_count in 1..drive_count. - scalar_i3_active_config() reads the EMULATOR_* env knobs (EMULATOR_PROFILE named or parsed scalar-i3-<slots>-<drives>, with per-field slot/drive/occupancy overrides), defaulting to the canonical default when unset. - scalar_i3_default_config() output is byte-identical to the historical literal (locked by a golden unit test); the default's drive list is preserved verbatim. - The barcode helpers stay bound to the DEFAULT config on purpose so bootstrap's demo NAS seeding remains deterministic across profiles. NAMED_PROFILES ships representative valid configs (min 1-drive, default, 100-slot, max 6-drive, LTO-9, 2-partition) for the compliance suite + CI. No consumers repointed yet; behavior is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012K5Zjc2bggcyJtFbz2D4Pw
…imulator Repoint the /aml response builders (aml_state.py, 11 sites) and the mock robotics backend (scenarios.scalar_i3_default) from scalar_i3_default_config() to scalar_i3_active_config(), so both the control-plane responses and the simulated library reflect the EMULATOR_*-selected shape. With no env set active == default, so behavior is unchanged. bootstrap.py is deliberately NOT repointed (its demo NAS seeding stays bound to the default barcodes). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012K5Zjc2bggcyJtFbz2D4Pw
test_14_emulator_profile and the physical-elements bay test derived expected slots/drives/generations from the hard-coded 50/3 shape; derive them from scalar_i3_active_config() instead so they hold under any emulator profile (still assert 50/3 by default). Also generalize the media-generation check to "every data media generation is readable by some drive" so single-generation profiles (LTO-9-only, 1-drive) pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012K5Zjc2bggcyJtFbz2D4Pw
Parameterized over NAMED_PROFILES: asserts each config is internally consistent (slot/drive counts, 1,bay,slot addressing uniqueness, one cleaning slot per drive at top ids, occupancy-derived data count, capacity-by-generation incl LTO-9, data generations readable by drives, partition bookkeeping); asserts invalid configs fail fast; and boots the app under scalar-i3-25-1 / scalar-i3-50-6 to confirm /aml/library + /aml/library/inventory + /aml/drives report the selected shape end-to-end. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012K5Zjc2bggcyJtFbz2D4Pw
Add an i3-config-<profile> matrix job to i3-emulator-compliance.yml that boots the emulator once per named profile (scalar-i3-25-1, -50-3, -100-3, -50-6, -50-3-lto9, -50-4-p2) with EMULATOR_PROFILE set (server + pytest both observe the shape) and runs the i3 core suite + test_18 against each, failing the build if any configuration is non-compliant. The existing suite=core/operations/system matrix is preserved. Add test_18_config_profiles to the emulator-change-gates pre-boot gate, and document in emulator_contract/README.md that the in-repo app now honors the EMULATOR_* shape knobs (default scalar-i3-50-3; >6 drives fails fast) so the emulated i3 behaves like a real i3 across configurations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012K5Zjc2bggcyJtFbz2D4Pw
…s; defer partitions Reviewer (adversarial) found two proven blockers and three smaller issues: - BLOCKER: EMULATOR_PROFILE=scalar-i3-50-3 dropped the canonical drive_overrides and regenerated drive metadata (load counts drifted) — selecting the default profile by name no longer reproduced the default. Fixed: a named profile with no per-field override is returned verbatim (drive_overrides preserved). Byte-identical again. - BLOCKER: the 2-partition profile orphaned partition-2 media from the AML layer (media counts 28->25, single partition served) because the parity-gated route serializer (routes_aml_partitions._partition_slots) assumes one partition. Rather than rework a parity-gated surface under this change, the multi-partition axis is DEFERRED: reverted the aml_state partition builder to its original single-partition form (kept the scalar_i3_active_config rename) and removed all multi-partition code from the i3_config builder. The compliance matrix now ships the three fully-working axes (drive count, LTO generation, slot/module scaling); multi-partition is documented as awaiting AML route-serializer work. - Clear fail-fast messages naming the offending EMULATOR_* variable on non-integer input. - Demo NAS seeding now guards on the ACTIVE backend's barcodes (identical to default for every shipped profile), so a low-occupancy override can't seed phantom cartridges. - test_18 fixture ordering fixed so the default context is restored after monkeypatch reverts. Added regression tests: named-default reproduces the default; non-integer knob names the variable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012K5Zjc2bggcyJtFbz2D4Pw
There was a problem hiding this comment.
Pull request overview
This PR makes the in-repo Quantum Scalar i3 emulator configuration-driven (via contracted EMULATOR_* knobs) instead of being hard-coded to a single library shape, and adds CI coverage to ensure each supported profile boots and passes the i3 compliance suite.
Changes:
- Introduces a validated
ScalarI3Profilebuilder and an env-drivenscalar_i3_active_config()for generating deterministic i3 shapes (slots/drives/occupancy/LTO gen). - Repoints AML state + simulator scenario wiring to reflect the active profile across
/aml/*responses and the mock robotics backend. - Adds new i3 compliance tests and a GitHub Actions matrix job that boots the emulator per profile and runs the i3 suite against each configuration.
Reviewed changes
Copilot reviewed 11 out of 43 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
openblade/simulator/i3_config.py |
Replaces hard-coded i3 config with a profile-based builder plus env-driven active profile selection. |
openblade/api/aml_state.py |
Switches AML default state generation to use the active i3 config so /aml/* reflects selected shape. |
openblade/simulator/scenarios.py |
Builds the mock library backend from the active i3 config (so simulator backend shape follows env). |
openblade/bootstrap.py |
Seeds demo catalog cartridges based on the active emulator media set to avoid phantom cartridges in small/low-occupancy profiles. |
tests/unit/test_i3_config_builder.py |
Adds unit coverage for default determinism, env precedence, and fail-fast invalid configs. |
tests/i3/test_18_config_profiles.py |
Adds i3 compliance coverage for profile invariants and live /aml reflection under non-default profiles. |
tests/i3/test_14_emulator_profile.py |
Updates existing i3 tests to validate against the active profile rather than assuming 50 slots / 3 drives. |
tests/integration/test_aml_diagnostics.py |
Makes bay/slot expectations scale with the active profile slot count. |
openblade/emulator_contract/README.md |
Documents that the in-repo app honors EMULATOR_* shape knobs and describes the new CI coverage. |
.github/workflows/i3-emulator-compliance.yml |
Adds an i3-config-<profile> matrix job to boot + test each shipped i3 profile. |
.github/workflows/emulator-change-gates.yml |
Adds the new config profile test file to existing emulator change gates. |
Comment on lines
+23
to
+33
| from openblade.simulator.i3_config import ( | ||
| _CAPACITY_BY_GENERATION, | ||
| NAMED_PROFILES, | ||
| ScalarI3Profile, | ||
| build_scalar_i3_config, | ||
| ) | ||
|
|
||
| pytestmark = pytest.mark.i3 | ||
|
|
||
| _COORD_RE = re.compile(r"\A1,[12],\d+\Z") | ||
|
|
Comment on lines
505
to
509
| def scalar_i3_mock_library_barcodes(*, include_cleaning: bool = True) -> list[str]: | ||
| # Bound to the DEFAULT config on purpose: bootstrap.py seeds the demo NAS | ||
| # catalog from fixed VOLxxxL9 barcodes via a membership guard, so this must | ||
| # stay deterministic regardless of the active emulator profile. | ||
| media = scalar_i3_default_config()["media"] |
Comment on lines
66
to
68
| def scalar_i3_default() -> tuple[MockLibraryBackend, MockLTFSBackend]: | ||
| config = scalar_i3_default_config() | ||
| config = scalar_i3_active_config() | ||
| partition = config["partition"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
The in-repo Quantum i3 emulator (
openblade.api.main:app, backend=mock) was hard-coded to one library shape (scalar-i3-50-3), even though the cross-repo contract advertisesEMULATOR_SLOT_COUNT/EMULATOR_DRIVE_COUNT/EMULATOR_PROFILE/EMULATOR_OCCUPANCY_PERCENT— so it couldn't represent any other valid i3 configuration and was non-compliant with its own contract.This makes the emulator honor those knobs and adds CI that boots the emulator once per supported configuration and runs the i3 suite against each, so the emulated i3 behaves like a real i3 across the range of supported shapes. The same
tests/i3suite runs against a real i3 (I3_TEST_MODE=real), so both are held to one behavioral contract.Configuration axes (shipped, in CI)
Boot with
EMULATOR_PROFILE=<name>(orscalar-i3-<slots>-<drives>, with per-fieldEMULATOR_SLOT_COUNT/EMULATOR_DRIVE_COUNT/EMULATOR_OCCUPANCY_PERCENToverrides). Matrix profiles:scalar-i3-25-1,scalar-i3-50-6(6 = documented i3 max, perdocs/sharding.md; >6 fails fast).scalar-i3-50-3-lto9(LTO-7/8/9 = 6/12/18 TB).scalar-i3-100-3(with correct1,bay,slotaddressing).scalar-i3-50-3is byte-identical to before (golden-locked).Deferred: a multi-partition axis is not shipped — the parity-gated AML partition route serializer (
routes_aml_partitions._partition_slots) assumes a single partition over the absolute slot range, so a 2-partition library can't be served faithfully until that route work lands. Documented inemulator_contract/README.md.How it works
openblade/simulator/i3_config.py— a validatedScalarI3Profilebuilder (build_scalar_i3_config,scalar_i3_active_config,NAMED_PROFILES); fail-fast on invalid configs.openblade/api/aml_state.py+scenarios.py— repointed toscalar_i3_active_config()so/aml/*responses and the mock robotics backend reflect the selected shape (unchanged when no env is set)..github/workflows/i3-emulator-compliance.yml— newi3-config-<profile>matrix job.Review (T3)
Reviewed by the adversarial reviewer subagent. It found two proven blockers — both fixed:
scalar-i3-50-3by name dropped the canonical drive list → now returned verbatim (byte-identical).Plus: clear fail-fast messages naming a bad
EMULATOR_*var; demo NAS seeding guards on the active backend (no phantom cartridges under low occupancy).Verified
ruffclean on all changed files. 125 tests green (contract gatestest_15/16/17, aml operations/system/diagnostics, builder +test_18); all 5 shipped profiles boot a real server and serve every/amlendpoint with no 5xx; default/amloutput byte-identical tomaster.Pre-existing and out of scope (unchanged by this PR): repo-wide
ruff/mypydebt in untouched files, andvalidate_cross_repo_contract.pyalready red onmaster(missingOPENBLADE_EMULATOR_IMAGEpin indocker-compose.yml).🤖 Generated with Claude Code
https://claude.ai/code/session_012K5Zjc2bggcyJtFbz2D4Pw