Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
e085da1
test(06-01): add failing tests for pointer file helpers and pool dir …
FileSystemGuy Jul 4, 2026
01fe661
feat(06-01): add pointer file + pool dir name helpers on submission_c…
FileSystemGuy Jul 4, 2026
1d5ca23
docs(06-01): add SUMMARY.md for pointer-file + pool-dir-name helpers …
FileSystemGuy Jul 4, 2026
7a9b917
test(06-02): add failing tests for LegacyLayoutDetected per D-63
FileSystemGuy Jul 4, 2026
eb413cb
test(06-02): add failing tests for pool capture-or-verify rewrite
FileSystemGuy Jul 4, 2026
7984310
test(06-02): add failing UX-01 source-guard test asserting retired re…
FileSystemGuy Jul 4, 2026
e0ad6e8
feat(06-02): rewrite capture_or_verify_code_image for content-address…
FileSystemGuy Jul 4, 2026
04cb93b
chore(06-02): delete retired-behavior tests aligned with CAPVER-03 + …
FileSystemGuy Jul 4, 2026
4b30182
docs(06-02): add SUMMARY.md for content-addressed pool + pointer rewrite
FileSystemGuy Jul 4, 2026
8c010c4
refactor(06-03): retire legacy results_dir/code_image.py capture path…
FileSystemGuy Jul 4, 2026
7b0c07f
test(06-03): update tests/integration/test_canonical_layout_end_to_en…
FileSystemGuy Jul 4, 2026
c4eb1a6
docs(06-03): add SUMMARY.md and advance STATE for legacy code_image r…
FileSystemGuy Jul 4, 2026
6ab22cd
test(06-04): add integration coverage for pool capture fresh tree (SC…
FileSystemGuy Jul 5, 2026
05cf758
test(06-04): add integration coverage for pool image reuse (SC-2)
FileSystemGuy Jul 5, 2026
6ad455d
test(06-04): add integration coverage for source-change captures new …
FileSystemGuy Jul 5, 2026
90cd049
test(06-04): add integration coverage for cross-mode dedup (SC-4)
FileSystemGuy Jul 5, 2026
ffb5566
test(06-04): add integration coverage for per-org isolation (SC-5)
FileSystemGuy Jul 5, 2026
7338ab4
test(06-04): add D-66 concurrent capture integration test
FileSystemGuy Jul 5, 2026
366355e
docs(06-04): add SUMMARY.md
FileSystemGuy Jul 5, 2026
bbc63fa
docs(06): add VERIFICATION.md — phase 6 PASS 10/10 REQ-IDs
FileSystemGuy Jul 5, 2026
91219e6
docs(06): mark phase 6 verified in STATE.md
FileSystemGuy Jul 5, 2026
fcccc6f
feat(07-01): extend conftest with legacy_tree_factory fixture
FileSystemGuy Jul 5, 2026
298d07b
test(07-01): add Wave-0 xfail integration test stubs for migration sc…
FileSystemGuy Jul 5, 2026
89e1ba6
test(07-01): add Wave-0 xfail unit and structural test stubs
FileSystemGuy Jul 5, 2026
4a111c1
feat(07-02): add HandEditedCodeImage exception in code_image.py + str…
FileSystemGuy Jul 5, 2026
56ff458
feat(07-02): create legacy_migration.py with full migration coordinat…
FileSystemGuy Jul 5, 2026
be185a0
feat(07-03): wire Phase 7 pre-check into main.py:224 (D-70) + wiring …
FileSystemGuy Jul 5, 2026
abbedc6
test(07-03): populate Wave-0 stubs — 6 structural + 9 unit tests (all…
FileSystemGuy Jul 5, 2026
474d33c
test(07-03): populate Wave-0 stubs — 8 MIG-01 behavioral integration …
FileSystemGuy Jul 5, 2026
111e005
test(07-03): populate Wave-0 stubs — 6 MIG-03 + 2 multi-org integrati…
FileSystemGuy Jul 5, 2026
f9dc367
feat(07-04): populate test_migration_idempotency.py — 6 MIG-02 tests …
FileSystemGuy Jul 5, 2026
9d4996f
docs(07): force-add Phase 7 SUMMARY + VERIFICATION artifacts; mark ph…
FileSystemGuy Jul 5, 2026
8d485fd
docs(08): capture phase context — D-80..D-93 locked
FileSystemGuy Jul 5, 2026
8ad7de4
docs(08): create phase 8 plan — 3 plans, waves 1-3, CHECK-01..05
FileSystemGuy Jul 5, 2026
de7c7e7
feat(08-01): create PoolStructureCheck with CHECK-01..04 methods
FileSystemGuy Jul 5, 2026
7f83075
feat(08-01): remove STRUCT-06 and update top-level check for v1.1 poo…
FileSystemGuy Jul 5, 2026
2134f65
feat(08-01): wire PoolStructureCheck into main.py; remove --reference…
FileSystemGuy Jul 5, 2026
3a91962
feat(08-02): add resolve_run_pool_image helper to helpers.py
FileSystemGuy Jul 5, 2026
7829fed
feat(08-02): implement D-89 CHECK-05 pool-walk in training and vdb ch…
FileSystemGuy Jul 5, 2026
597b70a
test(08-03): unit tests for PoolStructureCheck CHECK-01..04 + fix CHE…
FileSystemGuy Jul 5, 2026
0fdfb0c
test(08-03): integration tests for Phase 8 success criteria SC-1..SC-5
FileSystemGuy Jul 5, 2026
cfc74d9
docs(08): add Phase 8 VERIFICATION.md
FileSystemGuy Jul 5, 2026
16eefab
docs(08): force-add Phase 8 VERIFICATION + update STATE.md to complete
FileSystemGuy Jul 5, 2026
913da94
docs: update ROADMAP.md — phase 8 in progress (2/3 plans executed)
FileSystemGuy Jul 6, 2026
3867e85
chore: remove .planning/ artifacts from PR diff
FileSystemGuy Jul 6, 2026
b507946
Merge remote-tracking branch 'origin/main' into fix/651
FileSystemGuy Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions mlpstorage_py/submission_checker/checks/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
CodeImageError,
MissingHashFile,
MalformedHashFile,
PointerMalformed,
_read_pointer,
_pool_dir_name,
_read_hash_file,
)


Expand Down Expand Up @@ -347,6 +351,59 @@ def _check_code_image_layered(
return valid


# ---------------------------------------------------------------------------
# resolve_run_pool_image (Phase 8 D-89, CHECK-05)
# ---------------------------------------------------------------------------

def resolve_run_pool_image(
run_leaf: Path,
results_dir: Path,
orgname: str,
log,
) -> tuple[Path, dict]:
"""Resolve run leaf → pool image path + hash data.

Steps 1-4 of the D-89 CHECK-05 flow. Steps 5-6 (REFERENCE_CHECKSUMS lookup
and upstream-identity comparison) are caller responsibility.

Raises FileNotFoundError if .mlps-code-image is absent, PointerMalformed if
malformed, MissingHashFile / MalformedHashFile if .code-hash.json is absent
or invalid.

Args:
run_leaf: Path to the per-timestamp run directory that holds the
``.mlps-code-image`` pointer file.
results_dir: The top-level submission root (``args.input``). The pool
lives at ``<results_dir>/<orgname>/code-<hash8>/`` per D-89/D-62.
orgname: Organization name (directory name under the top-level pool
root, e.g. ``"acme"``).
log: Logger object passed through to ``_read_pointer`` and
``_read_hash_file``.

Returns:
``(pool_image_path, hash_data)`` where ``pool_image_path`` is the
resolved ``Path`` to the pool image directory and ``hash_data`` is the
parsed ``.code-hash.json`` dict (keys: ``hash``, ``algorithm``,
``captured_at``, ``mlpstorage_version``, ``git_sha``).

Raises:
FileNotFoundError: ``.mlps-code-image`` absent in ``run_leaf``.
PointerMalformed: File present but not a valid ``md5-tree-v2:<hex32>``.
MissingHashFile: ``.code-hash.json`` absent in the resolved pool image.
MalformedHashFile: ``.code-hash.json`` present but invalid.
"""
# Step 1-2: read pointer; propagate FileNotFoundError and PointerMalformed.
_alg, full_hash = _read_pointer(run_leaf, log)

# Step 3: resolve pool image path.
pool_image_path = results_dir / orgname / _pool_dir_name(full_hash)

# Step 4: read hash file; propagate MissingHashFile and MalformedHashFile.
hash_data = _read_hash_file(pool_image_path, log)

return pool_image_path, hash_data


# ---------------------------------------------------------------------------
# _pair_checkpoint_runs
# ---------------------------------------------------------------------------
Expand Down
150 changes: 128 additions & 22 deletions mlpstorage_py/submission_checker/checks/training_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@
from ..dlio_summary_helpers import cluster_total_host_memory_gb
from ..loader import SubmissionLogs
from ..rule_registry import rule
from pathlib import Path

from .helpers import (
_check_filesystem_separation,
_check_code_image_layered,
read_fs_separation_sidecar,
resolve_run_pool_image,
)
from ..tools.code_image import (
verify_image_self_consistent,
PointerMalformed,
MissingHashFile,
MalformedHashFile,
)
from ..tools.code_checksum import compute_code_tree_md5
from ..constants import REFERENCE_CHECKSUMS

# Shared with the in-process verifier (mlpstorage_py.rules.run_checkers.training)
# so both checkers stay in lockstep about which dotted-keys the mlpstorage
Expand Down Expand Up @@ -681,32 +692,127 @@ def node_capability_consistency_check(self):

@rule("3.6.1", "trainingClosedSubmissionChecksum")
def closed_submission_checksum(self):
"""For CLOSED submissions, verify code directory MD5 checksum.
"""For CLOSED training submissions, verify per-image code checksum (D-89, CHECK-05).

(Rules.md 3.6.1)

Phase 4 CD-04: delegates to the shared
``helpers._check_code_image_layered`` helper so the §3.6.1 and §5.6.1
rules enforce an identical layered model (self-consistency +
upstream-identity) without duplicating the implementation across
check classes. STRUCT-06 (§2.1.6) keeps its own inline implementation
because it has additional surrounding logic (per-leaf walker, the
``expected is None`` warning) that does not belong in the helper.

Walk-up: ``self.path`` is the per-leaf training path
(``<root>/closed/<orgname>/results/<system>/training/<model>``). The
CLOSED ``code/`` lives at ``<root>/closed/<orgname>/code/``, four
levels above ``self.path`` (model → type → system → results →
``<orgname>``). Missing ``code/`` is NOT logged here — STRUCT-06
already owns the VALS-01 missing-code/ violation under §2.1.6, so
re-firing under §3.6.1 would double-count.
Phase 8 Plan 02: replaces the legacy code/-walk with the full D-89
per-image pool-walk. For each run leaf, reads ``.mlps-code-image`` to
resolve the pool image, runs self-consistency verification, then looks up
``REFERENCE_CHECKSUMS[mlpstorage_version]`` for upstream-identity check.

Walk-up: ``self.path`` is
``<root>/closed/<orgname>/results/<system>/training/<model>``.
Run leaves live at ``self.run_path/<datetime>/``. From a datetime leaf:
parents[5] → ``<orgname>`` (submitter_path)
parents[7] → ``<root>`` (results_dir / pool root parent)
"""
# TODO(Phase8-Plan2): replaced in Plan 08-02 with per-image pool-image
# lookup (D-89). The legacy code/ walk and get_reference_checksum() call
# are removed; Plan 08-02 implements the full CHECK-05 flow using
# _read_pointer → pool image resolution → verify_image_self_consistent
# → REFERENCE_CHECKSUMS[mlpstorage_version] per-image lookup.
return True # TODO(Phase8-Plan2): replaced in Plan 08-02
if self.mode != "training":
return True
if self.submissions_logs.loader_metadata.division != "closed":
return True

valid = True
# D-87: emit the "unknown version" warning at most once per pool image.
if not hasattr(self, "_warned_pool_images"):
self._warned_pool_images: set[str] = set()

for _summary, _metadata, ts in self.submissions_logs.run_files:
run_leaf = Path(self.run_path) / ts

# Determine submitter_path and root_path by walking up from run_leaf.
# run_leaf depth from root: <root>/closed/<orgname>/results/<sys>/training/<model>/run/<ts>
# parents[5] = <orgname> parents[7] = <root>
run_leaf_path = Path(run_leaf)
try:
submitter_path = run_leaf_path.parents[5]
root_path = run_leaf_path.parents[7]
except IndexError:
self.log_violation(
"3.6.1", "trainingClosedSubmissionChecksum", str(run_leaf),
"unexpected directory depth; cannot resolve pool root",
)
valid = False
continue

orgname = submitter_path.name

# Steps 1-4 (D-89): resolve run leaf → pool image + hash data.
try:
pool_image_path, hash_data = resolve_run_pool_image(
run_leaf_path, root_path, orgname, self.log,
)
except FileNotFoundError:
self.log_violation(
"3.6.1", "trainingClosedSubmissionChecksum", str(run_leaf),
"run leaf %s has no .mlps-code-image pointer.",
run_leaf,
)
valid = False
continue
except PointerMalformed as e:
self.log_violation(
"3.6.1", "trainingClosedSubmissionChecksum", str(run_leaf),
"%s", str(e),
)
valid = False
continue
except (MissingHashFile, MalformedHashFile) as e:
self.log_violation(
"3.6.1", "trainingClosedSubmissionChecksum", str(run_leaf),
"%s", str(e),
)
valid = False
continue

# Step 5 (D-89): self-consistency check on pool image.
try:
self_ok = verify_image_self_consistent(pool_image_path, self.log)
except Exception as e:
self.log_violation(
"3.6.1", "trainingClosedSubmissionChecksum", str(pool_image_path),
"%s", str(e),
)
valid = False
continue
if not self_ok:
self.log_violation(
"3.6.1", "trainingClosedSubmissionChecksum", str(pool_image_path),
"pool image %s: self-consistency check failed",
pool_image_path,
)
valid = False
continue

# Step 6 (D-89): version-keyed upstream-identity check (D-86/D-87).
mlpstorage_version = hash_data.get("mlpstorage_version")
expected = REFERENCE_CHECKSUMS.get(mlpstorage_version)

if expected is None:
# D-87: warn once per pool image, then pass.
pool_key = str(pool_image_path)
if pool_key not in self._warned_pool_images:
self._warned_pool_images.add(pool_key)
self.warn_violation(
"3.6.1", "trainingClosedSubmissionChecksum", str(pool_image_path),
"mlpstorage_version %s not in REFERENCE_CHECKSUMS; "
"upstream-identity check skipped (self-consistency still ran).",
mlpstorage_version,
)
continue

# Step 7: compute tree MD5 and compare against reference.
digest = compute_code_tree_md5(str(pool_image_path), self.log)
if digest != expected:
self.log_violation(
"3.6.1", "trainingClosedSubmissionChecksum", str(pool_image_path),
"pool image %s: MD5 %s does not match "
"REFERENCE_CHECKSUMS[%s] = %s",
pool_image_path, digest, mlpstorage_version, expected,
)
valid = False

return valid

@rule("3.6.2", "trainingClosedSubmissionParameters")
def closed_submission_parameters(self):
Expand Down
Loading
Loading