DI DIEECDual Internal–External Expansion Computation 繁中

Passing tests is not clearance to ship

A candidate version has to satisfy schema validity, migration validity, API compatibility, runtime acceptance and release policy at once. None of them substitutes for another, and they are not averaged.

Release policy

Every one of these must hold. They are hard floors and a cost ceiling, not weights.

  • test_pass_rate ≥ 1.0
  • schema_valid_rate ≥ 1.0
  • migration_valid_rate ≥ 1.0
  • acceptance_success_rate ≥ 1.0
  • boundary_accuracy ≥ 1.0
  • false_closure_rate ≤ 0.0
  • false_commit_rate ≤ 0.0
  • mean_total_action ≤ 3.0

Why not one score

Because low action does not imply high boundary accuracy, and high success does not imply zero false commits. Compressing them into a single weighted number lets a candidate trade away a safety property for a cost improvement — which is exactly the trade the gate exists to refuse.

The case study: v1.0 certified five things nothing measured

v1.0's release certificate reads release_passed: true. That was true of the gate as wired. It was not true that the gate could see what it was certifying.

This is the project's own thesis turned back on itself, so it is worth stating plainly rather than burying in a changelog. All five defects were found by running the release on a Windows host — the release was packaged on POSIX, and every one of these is invisible there or invisible everywhere.

SQLiteSandbox leaked a connection on every call

sandbox.py used with self.connect() as c: in three places. That is sqlite3's transaction context manager — it commits or rolls back, and never closes. Every construction, put and get leaked an open connection plus the -wal and -shm sidecars WAL mode creates.

POSIX lets you unlink an open file, so it passed there. Windows raises WinError 32 when the temporary directory is cleaned up, which made v1.0 actually 70 passed / 5 failed on Windows rather than the recorded 75. This was a runtime handle leak, not only a test artifact.

The gate's test pass rate was the literal 1.0

The finalizer handed the gate a hardcoded 1.0. So no test failure could ever block a release — which is precisely how the five failures above shipped under a passing certificate. The policy demanded test_pass_rate = 1.0 and the finalizer supplied it by assertion.

The schema valid rate was likewise a literal

Also hardcoded 1.0, while a real measurement was available the whole time: the schema registry can validate every migrated certificate, and now does.

The API compatibility check could not fail

It compared the live manifest against a second freshly-built copy of itself. That is compatible by construction. Deleting ReleaseGate from the stable symbols and mangling a signature still returned compatible: true — verified, not assumed. It now compares against the frozen baseline on disk.

Release digests did not reproduce across platforms

Path.write_text defaults to newline=None, which translates \n to os.linesep. Every hashed artifact went through it, so identical content produced different digests depending on the build host, and a SHA256SUMS.txt generated on one platform failed on another. For a system that binds its evidence by digest, that is not cosmetic.

What v1.0.1 changed

The gate now measures what it certifies: pytest is executed and its counts read from the JUnit report, all 13,600 migrated certificates are validated against the registry, and the candidate manifest is compared against the frozen v1.0 baseline. Artifact writes pin LF, so regenerating the schema files on Windows now reproduces the frozen v1.0 files byte for byte.

Each fix has a regression test, and each test was verified to fail against v1.0's original code restored from the archive. The sandbox test deliberately uses tempfile.TemporaryDirectory rather than pytest's tmp_pathtmp_path tears down lazily, which is exactly why v1.0's own sandbox test passed on Windows while four suites using TemporaryDirectory failed.

The 25 frozen public symbols are unchanged. That is now confirmed by comparison against the stored baseline rather than asserted.

What the gate measures now

1.0test_pass_rate
1.0schema_valid_rate
1.0migration_valid_rate
1.0boundary_accuracy
0.0false_commit_rate
2.0259mean_total_action