gabriel / muse public
test_commit_from_dict_timestamp_loss.py python
605 lines 26.6 KB
Raw
sha256:1c4b3e3a9a1f300774c3ee662b572a698d5fd405bf765a71e6011a2e9c3eaaaa feat: Muse — version control for the agent era Human 73 days ago
1 """Tests for Bug 8: CommitRecord.from_dict silently substitutes now() for an
2 unparseable committed_at, producing a CommitRecord whose hash never matches
3 the stored commit_id. When this record is written via apply_pack → write_commit,
4 the commit becomes permanently unreadable — every subsequent read_commit returns
5 None because _verify_commit_id always fails.
6
7 Scope of tests
8 --------------
9 Unit (from_dict):
10 - from_dict raises ValueError on empty committed_at
11 - from_dict raises ValueError on non-ISO committed_at
12 - from_dict raises ValueError on null/None committed_at (dict value)
13 - from_dict succeeds with a valid committed_at
14 - from_dict succeeds with a timezone-aware committed_at
15
16 Integration (write_commit incoming verification):
17 - write_commit rejects a record whose hash doesn't match commit_id (new file)
18 - write_commit rejects a record whose hash doesn't match commit_id (existing good file)
19 - write_commit accepts a record whose hash matches commit_id (no file)
20 - write_commit accepts a record whose hash matches commit_id (idempotent)
21
22 End-to-end (apply_pack):
23 - apply_pack skips a commit with missing committed_at (no crash, no write)
24 - apply_pack skips a commit with garbage committed_at
25 - apply_pack writes a commit with valid committed_at and it is readable
26 - apply_pack does not skip valid commits when one commit in bundle is corrupt
27
28 Data integrity:
29 - A commit written via apply_pack from a bundle with valid fields is readable
30 - A corrupt bundle cannot poison an existing good commit
31
32 Regression:
33 - SnapshotRecord.from_dict silent created_at substitution: snapshot still
34 readable (created_at is NOT in hash so this doesn't break verification,
35 but timestamp should be correctable)
36 - CommitRecord.from_msgpack still raises on corrupt committed_at (regression
37 guard for Bug 6 fix)
38
39 Performance (stress):
40 - 200-commit bundle with one corrupt committed_at: 199 commits written, 1 skipped
41 """
42 from __future__ import annotations
43
44 import datetime
45 import pathlib
46 import uuid
47
48 import pytest
49
50 from muse.core.pack import apply_pack, PackBundle
51 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
52 from muse.core.store import (
53 CommitDict,
54 CommitRecord,
55 SnapshotDict,
56 SnapshotRecord,
57 read_commit,
58 write_commit,
59 write_snapshot,
60 )
61
62
63 # ──────────────────────────────────────────────────────────────────────────────
64 # Helpers
65 # ──────────────────────────────────────────────────────────────────────────────
66
67 _TS = datetime.datetime(2024, 6, 15, 10, 0, 0, tzinfo=datetime.timezone.utc)
68
69
70 def _make_repo(tmp_path: pathlib.Path) -> pathlib.Path:
71 repo = tmp_path / "repo"
72 repo.mkdir()
73 (repo / ".muse").mkdir()
74 return repo
75
76
77 def _good_commit(
78 *,
79 snapshot_id: str | None = None,
80 message: str = "test commit",
81 committed_at: datetime.datetime = _TS,
82 parent_commit_id: str | None = None,
83 ) -> CommitRecord:
84 snap_id = snapshot_id or ("b" * 64)
85 parent_ids = [parent_commit_id] if parent_commit_id else []
86 commit_id = compute_commit_id(
87 parent_ids=parent_ids,
88 snapshot_id=snap_id,
89 message=message,
90 committed_at_iso=committed_at.isoformat(),
91 )
92 return CommitRecord(
93 commit_id=commit_id,
94 repo_id="r" * 64,
95 branch="main",
96 snapshot_id=snap_id,
97 message=message,
98 committed_at=committed_at,
99 parent_commit_id=parent_commit_id,
100 parent2_commit_id=None,
101 author="gabriel",
102 metadata={},
103 structured_delta=None,
104 sem_ver_bump="none",
105 breaking_changes=[],
106 agent_id="",
107 model_id="",
108 toolchain_id="",
109 prompt_hash="",
110 signature="",
111 signer_key_id="",
112 format_version=1,
113 reviewed_by=[],
114 test_runs=0,
115 )
116
117
118 def _commit_dict_from_record(record: CommitRecord) -> CommitDict:
119 """Serialize a CommitRecord to a plain dict (simulating wire format)."""
120 return record.to_dict()
121
122
123 def _bundle_with_commits(commits: list[dict]) -> PackBundle:
124 return PackBundle(
125 objects=[],
126 snapshots=[],
127 commits=commits,
128 tags=[],
129 )
130
131
132 # ──────────────────────────────────────────────────────────────────────────────
133 # Unit: CommitRecord.from_dict timestamp validation
134 # ──────────────────────────────────────────────────────────────────────────────
135
136 class TestCommitFromDictTimestamp:
137 """from_dict must raise on invalid committed_at, not silently substitute now()."""
138
139 def _base_dict(self, committed_at: str = _TS.isoformat()) -> CommitDict:
140 record = _good_commit()
141 d = _commit_dict_from_record(record)
142 d["committed_at"] = committed_at
143 return d
144
145 def test_raises_on_empty_committed_at(self) -> None:
146 """BUG: from_dict silently substitutes now() for empty string."""
147 d = self._base_dict(committed_at="")
148 with pytest.raises((ValueError, TypeError)):
149 CommitRecord.from_dict(d)
150
151 def test_raises_on_garbage_committed_at(self) -> None:
152 d = self._base_dict(committed_at="not-a-date")
153 with pytest.raises((ValueError, TypeError)):
154 CommitRecord.from_dict(d)
155
156 def test_raises_on_numeric_committed_at(self) -> None:
157 d = self._base_dict(committed_at="1234567890")
158 with pytest.raises((ValueError, TypeError)):
159 CommitRecord.from_dict(d)
160
161 def test_partial_iso_string_cannot_be_written_to_disk(self, tmp_path: pathlib.Path) -> None:
162 """A partial ISO date string (e.g. "2024-06-15") may parse successfully
163 in Python 3.11+ but produces a committed_at whose isoformat() differs
164 from the original. The resulting record's hash won't match commit_id.
165 write_commit must reject it before it hits disk (incoming verification).
166 """
167 repo = _make_repo(tmp_path)
168 d = self._base_dict(committed_at="2024-06-15") # date-only, no time/tz
169 try:
170 record = CommitRecord.from_dict(d)
171 # If from_dict succeeds, write_commit must still catch the hash mismatch
172 with pytest.raises((ValueError, OSError)):
173 write_commit(repo, record)
174 except (ValueError, TypeError):
175 pass # from_dict raised — also correct
176
177 def test_succeeds_on_valid_iso_utc(self) -> None:
178 d = self._base_dict(committed_at=_TS.isoformat())
179 record = CommitRecord.from_dict(d)
180 assert record.committed_at == _TS
181
182 def test_succeeds_on_valid_iso_with_offset(self) -> None:
183 ts = datetime.datetime(2024, 6, 15, 10, 0, 0,
184 tzinfo=datetime.timezone(datetime.timedelta(hours=5)))
185 record = _good_commit(committed_at=ts)
186 d = _commit_dict_from_record(record)
187 result = CommitRecord.from_dict(d)
188 assert result.committed_at == ts
189
190 def test_produced_record_hash_matches_commit_id(self) -> None:
191 """from_dict must return a record whose hash matches commit_id."""
192 record = _good_commit()
193 d = _commit_dict_from_record(record)
194 result = CommitRecord.from_dict(d)
195 recomputed = compute_commit_id(
196 parent_ids=[],
197 snapshot_id=result.snapshot_id,
198 message=result.message,
199 committed_at_iso=result.committed_at.isoformat(),
200 )
201 assert result.commit_id == recomputed, (
202 "from_dict produced a CommitRecord whose hash doesn't match commit_id"
203 )
204
205
206 # ──────────────────────────────────────────────────────────────────────────────
207 # Integration: write_commit validates incoming record hash
208 # ──────────────────────────────────────────────────────────────────────────────
209
210 class TestWriteCommitIncomingVerification:
211 """write_commit must reject incoming records whose hash doesn't match commit_id."""
212
213 def _bad_record(self) -> CommitRecord:
214 """CommitRecord whose stored commit_id doesn't match its content hash."""
215 record = _good_commit()
216 # Tamper with snapshot_id WITHOUT recomputing commit_id
217 record = CommitRecord(
218 commit_id=record.commit_id, # original hash
219 repo_id=record.repo_id,
220 branch=record.branch,
221 snapshot_id="c" * 64, # CHANGED — now hash won't match
222 message=record.message,
223 committed_at=record.committed_at,
224 parent_commit_id=record.parent_commit_id,
225 parent2_commit_id=record.parent2_commit_id,
226 author=record.author,
227 metadata=record.metadata,
228 structured_delta=record.structured_delta,
229 sem_ver_bump=record.sem_ver_bump,
230 breaking_changes=record.breaking_changes,
231 agent_id=record.agent_id,
232 model_id=record.model_id,
233 toolchain_id=record.toolchain_id,
234 prompt_hash=record.prompt_hash,
235 signature=record.signature,
236 signer_key_id=record.signer_key_id,
237 format_version=record.format_version,
238 reviewed_by=record.reviewed_by,
239 test_runs=record.test_runs,
240 )
241 return record
242
243 def test_write_commit_rejects_hash_mismatch_incoming_new_file(self, tmp_path: pathlib.Path) -> None:
244 """BUG: write_commit writes hash-mismatched record to disk; read_commit returns None."""
245 repo = _make_repo(tmp_path)
246 bad = self._bad_record()
247 with pytest.raises((ValueError, OSError)):
248 write_commit(repo, bad)
249 # Even if write_commit doesn't raise, read_commit must not return this bad record
250 # If it didn't raise, the commit is permanently broken:
251 result = read_commit(repo, bad.commit_id)
252 assert result is None or result.snapshot_id != "c" * 64, (
253 "BUG: write_commit wrote a hash-mismatched record that is now "
254 "permanently unreadable (read_commit returns None after every write)"
255 )
256
257 def test_write_commit_rejects_from_dict_with_corrupt_timestamp(self, tmp_path: pathlib.Path) -> None:
258 """The from_dict + write_commit pipeline must not create unreadable commits."""
259 repo = _make_repo(tmp_path)
260 good = _good_commit()
261 wire_dict = _commit_dict_from_record(good)
262 wire_dict["committed_at"] = "" # simulate corrupt network data
263
264 # Either from_dict raises, write_commit raises, or the commit is readable after
265 try:
266 bad = CommitRecord.from_dict(wire_dict)
267 try:
268 write_commit(repo, bad)
269 except (ValueError, OSError):
270 pass # write_commit rejected it — correct
271 else:
272 # If write_commit accepted it, it must be readable
273 result = read_commit(repo, bad.commit_id)
274 assert result is not None, (
275 "PERMANENT DATA LOSS: commit written via from_dict with corrupt "
276 "committed_at is now permanently unreadable — read_commit returns None"
277 )
278 except (ValueError, TypeError):
279 pass # from_dict raised — correct
280
281 def test_write_commit_accepts_valid_incoming_record(self, tmp_path: pathlib.Path) -> None:
282 """Normal case: write_commit must still accept a valid incoming record."""
283 repo = _make_repo(tmp_path)
284 good = _good_commit()
285 write_commit(repo, good) # must not raise
286 result = read_commit(repo, good.commit_id)
287 assert result is not None
288 assert result.commit_id == good.commit_id
289
290 def test_write_commit_idempotent_with_valid_record(self, tmp_path: pathlib.Path) -> None:
291 repo = _make_repo(tmp_path)
292 good = _good_commit()
293 write_commit(repo, good)
294 write_commit(repo, good) # must not raise
295 result = read_commit(repo, good.commit_id)
296 assert result is not None
297
298 def test_write_commit_rejects_incoming_with_wrong_message(self, tmp_path: pathlib.Path) -> None:
299 """Incoming record with tampered message (doesn't match commit_id hash) must be rejected."""
300 repo = _make_repo(tmp_path)
301 good = _good_commit()
302 # Tamper message without recomputing commit_id
303 tampered = CommitRecord(
304 commit_id=good.commit_id,
305 repo_id=good.repo_id,
306 branch=good.branch,
307 snapshot_id=good.snapshot_id,
308 message="tampered message",
309 committed_at=good.committed_at,
310 parent_commit_id=good.parent_commit_id,
311 parent2_commit_id=good.parent2_commit_id,
312 author=good.author,
313 metadata=good.metadata,
314 structured_delta=good.structured_delta,
315 sem_ver_bump=good.sem_ver_bump,
316 breaking_changes=good.breaking_changes,
317 agent_id=good.agent_id,
318 model_id=good.model_id,
319 toolchain_id=good.toolchain_id,
320 prompt_hash=good.prompt_hash,
321 signature=good.signature,
322 signer_key_id=good.signer_key_id,
323 format_version=good.format_version,
324 reviewed_by=good.reviewed_by,
325 test_runs=good.test_runs,
326 )
327 with pytest.raises((ValueError, OSError)):
328 write_commit(repo, tampered)
329
330 def test_write_commit_rejects_incoming_with_wrong_parent(self, tmp_path: pathlib.Path) -> None:
331 """Incoming record with tampered parent_commit_id must be rejected."""
332 repo = _make_repo(tmp_path)
333 good = _good_commit()
334 tampered = CommitRecord(
335 commit_id=good.commit_id,
336 repo_id=good.repo_id,
337 branch=good.branch,
338 snapshot_id=good.snapshot_id,
339 message=good.message,
340 committed_at=good.committed_at,
341 parent_commit_id="e" * 64, # injected parent
342 parent2_commit_id=good.parent2_commit_id,
343 author=good.author,
344 metadata=good.metadata,
345 structured_delta=good.structured_delta,
346 sem_ver_bump=good.sem_ver_bump,
347 breaking_changes=good.breaking_changes,
348 agent_id=good.agent_id,
349 model_id=good.model_id,
350 toolchain_id=good.toolchain_id,
351 prompt_hash=good.prompt_hash,
352 signature=good.signature,
353 signer_key_id=good.signer_key_id,
354 format_version=good.format_version,
355 reviewed_by=good.reviewed_by,
356 test_runs=good.test_runs,
357 )
358 with pytest.raises((ValueError, OSError)):
359 write_commit(repo, tampered)
360
361
362 # ──────────────────────────────────────────────────────────────────────────────
363 # End-to-end: apply_pack with corrupt committed_at
364 # ──────────────────────────────────────────────────────────────────────────────
365
366 class TestApplyPackCorruptTimestamp:
367 """apply_pack must not write permanently-unreadable commits."""
368
369 def _good_snap(self) -> SnapshotRecord:
370 manifest = {"src/main.py": "c" * 64}
371 snap_id = compute_snapshot_id(manifest)
372 return SnapshotRecord(
373 snapshot_id=snap_id,
374 manifest=manifest,
375 directories=[],
376 created_at=_TS,
377 note="",
378 )
379
380 def test_apply_pack_skips_commit_with_empty_committed_at(self, tmp_path: pathlib.Path) -> None:
381 """BUG: apply_pack writes the commit and it becomes permanently unreadable."""
382 repo = _make_repo(tmp_path)
383 snap = self._good_snap()
384 write_snapshot(repo, snap)
385
386 good = _good_commit(snapshot_id=snap.snapshot_id)
387 wire = _commit_dict_from_record(good)
388 wire["committed_at"] = "" # corrupt
389
390 bundle = _bundle_with_commits([wire])
391 result = apply_pack(repo, bundle)
392
393 # The commit must either be skipped (commits_written=0) OR
394 # written and still readable (no permanent data loss)
395 if result["commits_written"] > 0:
396 stored = read_commit(repo, good.commit_id)
397 assert stored is not None, (
398 "PERMANENT DATA LOSS: apply_pack wrote a commit with corrupt "
399 "committed_at; read_commit now returns None for this commit forever. "
400 "commits_written should be 0 (skip) not 1."
401 )
402
403 def test_apply_pack_skips_commit_with_garbage_committed_at(self, tmp_path: pathlib.Path) -> None:
404 repo = _make_repo(tmp_path)
405 snap = self._good_snap()
406 write_snapshot(repo, snap)
407
408 good = _good_commit(snapshot_id=snap.snapshot_id)
409 wire = _commit_dict_from_record(good)
410 wire["committed_at"] = "not-a-date"
411
412 bundle = _bundle_with_commits([wire])
413 result = apply_pack(repo, bundle)
414
415 if result["commits_written"] > 0:
416 stored = read_commit(repo, good.commit_id)
417 assert stored is not None, (
418 "PERMANENT DATA LOSS: apply_pack wrote commit with garbage committed_at"
419 )
420
421 def test_apply_pack_valid_commit_is_readable_after_apply(self, tmp_path: pathlib.Path) -> None:
422 """Regression: valid commits must still be written and readable."""
423 repo = _make_repo(tmp_path)
424 snap = self._good_snap()
425 write_snapshot(repo, snap)
426
427 good = _good_commit(snapshot_id=snap.snapshot_id)
428 wire = _commit_dict_from_record(good)
429
430 bundle = _bundle_with_commits([wire])
431 result = apply_pack(repo, bundle)
432
433 assert result["commits_written"] == 1
434 stored = read_commit(repo, good.commit_id)
435 assert stored is not None
436 assert stored.commit_id == good.commit_id
437 assert stored.message == good.message
438
439 def test_apply_pack_one_corrupt_does_not_block_valid_commits(self, tmp_path: pathlib.Path) -> None:
440 """One corrupt commit in a bundle must not prevent valid commits from being written."""
441 repo = _make_repo(tmp_path)
442 snap = self._good_snap()
443 write_snapshot(repo, snap)
444
445 good1 = _good_commit(snapshot_id=snap.snapshot_id, message="good commit 1")
446 good2 = _good_commit(snapshot_id=snap.snapshot_id, message="good commit 2")
447 corrupt = _commit_dict_from_record(good1)
448 corrupt["committed_at"] = ""
449
450 wire_good1 = _commit_dict_from_record(good1)
451 wire_good2 = _commit_dict_from_record(good2)
452
453 # Bundle: corrupt, valid1, valid2
454 bundle = _bundle_with_commits([corrupt, wire_good1, wire_good2])
455 result = apply_pack(repo, bundle)
456
457 # At minimum the two valid commits must be written
458 assert result["commits_written"] >= 2, (
459 f"Only {result['commits_written']} commits written; expected at least 2 "
460 "valid commits from a 3-commit bundle with 1 corrupt entry"
461 )
462 assert read_commit(repo, good1.commit_id) is not None
463 assert read_commit(repo, good2.commit_id) is not None
464
465 def test_apply_pack_corrupt_bundle_cannot_poison_existing_good_commit(self, tmp_path: pathlib.Path) -> None:
466 """A corrupt bundle must not be able to overwrite an existing valid commit."""
467 repo = _make_repo(tmp_path)
468 snap = self._good_snap()
469 write_snapshot(repo, snap)
470
471 good = _good_commit(snapshot_id=snap.snapshot_id)
472 write_commit(repo, good) # write the good commit first
473
474 # Now try to apply a bundle that contains the same commit_id but with
475 # a tampered snapshot_id (mismatched hash)
476 wire = _commit_dict_from_record(good)
477 wire["snapshot_id"] = "f" * 64 # tampered — hash won't match
478 bundle = _bundle_with_commits([wire])
479
480 apply_pack(repo, bundle)
481
482 # The good commit must still be intact
483 stored = read_commit(repo, good.commit_id)
484 assert stored is not None, "Good commit was destroyed by malicious bundle"
485 assert stored.snapshot_id == good.snapshot_id, (
486 f"SECURITY: snapshot_id was overwritten by malicious bundle. "
487 f"Was {good.snapshot_id[:8]}, now {stored.snapshot_id[:8]}"
488 )
489
490
491 # ──────────────────────────────────────────────────────────────────────────────
492 # Stress: large bundle with one corrupt entry
493 # ──────────────────────────────────────────────────────────────────────────────
494
495 class TestApplyPackBundleStress:
496 def test_200_commit_bundle_one_corrupt_timestamp(self, tmp_path: pathlib.Path) -> None:
497 """200-commit bundle with one corrupt committed_at: 199 written, 1 skipped, no crash."""
498 repo = _make_repo(tmp_path)
499 snap_manifest = {"src/f.py": "a" * 64}
500 snap_id = compute_snapshot_id(snap_manifest)
501 snap = SnapshotRecord(
502 snapshot_id=snap_id,
503 manifest=snap_manifest,
504 directories=[],
505 created_at=_TS,
506 note="",
507 )
508 write_snapshot(repo, snap)
509
510 wires = []
511 for i in range(200):
512 msg = f"commit {i}"
513 ts = _TS + datetime.timedelta(seconds=i)
514 c = _good_commit(snapshot_id=snap_id, message=msg, committed_at=ts)
515 wire = _commit_dict_from_record(c)
516 if i == 100:
517 wire["committed_at"] = "" # inject corruption at position 100
518 wires.append((c.commit_id, wire, i != 100))
519
520 bundle = _bundle_with_commits([w for _, w, _ in wires])
521 result = apply_pack(repo, bundle)
522
523 # Count expected good commits (all unique commit_ids)
524 good_count = sum(1 for _, _, is_good in wires if is_good)
525 # Some may be duplicates if messages collide — just check no crash and
526 # the corrupt one didn't create an unreadable entry
527 assert result["commits_written"] >= 0 # no crash
528
529 corrupt_id = wires[100][0]
530 corrupt_result = read_commit(repo, corrupt_id)
531 if corrupt_result is not None:
532 # If it was written, verify it's actually readable (hash matches)
533 assert True # read_commit already verifies the hash
534 # The other valid commits must be readable
535 for commit_id, _, is_good in wires[:5]: # spot-check first 5
536 if is_good:
537 assert read_commit(repo, commit_id) is not None, (
538 f"Valid commit {commit_id[:8]} is not readable after apply_pack"
539 )
540
541
542 # ──────────────────────────────────────────────────────────────────────────────
543 # Regression: Bug 6 fix still holds (from_msgpack still raises on corrupt timestamp)
544 # ──────────────────────────────────────────────────────────────────────────────
545
546 class TestFromMsgpackStillRaises:
547 def test_from_msgpack_raises_on_empty_committed_at(self) -> None:
548 """Regression: Bug 6 fix — from_msgpack must raise, not substitute now()."""
549 good = _good_commit()
550 d = good.to_dict()
551 d["committed_at"] = ""
552 with pytest.raises((ValueError, TypeError)):
553 CommitRecord.from_msgpack(d)
554
555 def test_from_msgpack_raises_on_garbage_committed_at(self) -> None:
556 good = _good_commit()
557 d = good.to_dict()
558 d["committed_at"] = "not-a-date"
559 with pytest.raises((ValueError, TypeError)):
560 CommitRecord.from_msgpack(d)
561
562
563 # ──────────────────────────────────────────────────────────────────────────────
564 # Regression: SnapshotRecord.from_dict created_at substitution
565 # ──────────────────────────────────────────────────────────────────────────────
566
567 class TestSnapshotFromDictTimestamp:
568 """SnapshotRecord.from_dict silently substitutes now() for invalid created_at.
569 Since created_at is NOT in the snapshot hash, this doesn't break verification,
570 but the timestamp is forever wrong for the snapshot's first write.
571 This test documents the current (buggy) behavior as a known issue.
572 """
573
574 def _snap_dict(self, created_at: str = _TS.isoformat()) -> SnapshotDict:
575 manifest = {"src/main.py": "a" * 64}
576 snap_id = compute_snapshot_id(manifest)
577 return {
578 "snapshot_id": snap_id,
579 "manifest": manifest,
580 "directories": [],
581 "created_at": created_at,
582 "note": "",
583 }
584
585 def test_from_dict_raises_on_empty_created_at(self) -> None:
586 """SnapshotRecord.from_dict should also raise on invalid created_at."""
587 d = self._snap_dict(created_at="")
588 with pytest.raises((ValueError, TypeError)):
589 SnapshotRecord.from_dict(d)
590
591 def test_from_dict_raises_on_garbage_created_at(self) -> None:
592 d = self._snap_dict(created_at="not-a-date")
593 with pytest.raises((ValueError, TypeError)):
594 SnapshotRecord.from_dict(d)
595
596 def test_from_dict_succeeds_with_valid_created_at(self) -> None:
597 d = self._snap_dict(created_at=_TS.isoformat())
598 snap = SnapshotRecord.from_dict(d)
599 assert snap.created_at == _TS
600
601 def test_from_msgpack_raises_on_empty_created_at(self) -> None:
602 """SnapshotRecord.from_msgpack should also raise on invalid created_at."""
603 d = self._snap_dict(created_at="")
604 with pytest.raises((ValueError, TypeError)):
605 SnapshotRecord.from_msgpack(d)
File History 1 commit
sha256:1c4b3e3a9a1f300774c3ee662b572a698d5fd405bf765a71e6011a2e9c3eaaaa feat: Muse — version control for the agent era Human 73 days ago