Skip to content

Commit 46e6831

Browse files
mkotelnikovclaude
andcommitted
fix(core,livekit-demo): floor timestamps to prevent hash mismatch on pack round-trip
Date.now() / 1000 produces float timestamps (e.g. 1708095234.567). formatPersonIdent serialized the float as-is, but parsePersonIdent uses parseInt() which truncates to integer. This asymmetry caused different SHA-1 hashes on sender vs receiver, so commits imported via pack couldn't be loaded by OID — silently breaking UI updates. Fix: Math.floor() in demo timestamp creation + defensive floor in formatPersonIdent to prevent any float timestamps from round-tripping incorrectly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 41289b4 commit 46e6831

3 files changed

Lines changed: 9 additions & 4 deletions

File tree

.beads/issues.jsonl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@
364364
{"id":"webrun-vcs-6i4gi","title":"Update store-sql index.ts exports","description":"Update packages/store-sql/src/index.ts and native/index.ts exports.","status":"closed","priority":0,"issue_type":"task","owner":"mikhail.kotelnikov@gmail.com","created_at":"2026-02-05T20:44:43.413771435+01:00","created_by":"Mikhail Kotelnikov","updated_at":"2026-02-06T08:06:40.985356949+01:00","closed_at":"2026-02-06T08:06:40.985356949+01:00","close_reason":"Completed in commit 18d9a15: All SQL stores migrated to new interfaces, 493 tests passing","dependencies":[{"issue_id":"webrun-vcs-6i4gi","depends_on_id":"webrun-vcs-opc5y","type":"blocks","created_at":"2026-02-05T20:44:50.717629863+01:00","created_by":"Mikhail Kotelnikov"}]}
365365
{"id":"webrun-vcs-6iq1g","title":"Migrate commit-store.suite.ts to test Commits interface","description":"Refactor packages/testing/src/suites/commit-store.suite.ts to test Commits interface instead of CommitStore.","status":"closed","priority":0,"issue_type":"task","owner":"mikhail.kotelnikov@gmail.com","created_at":"2026-02-05T20:44:43.47767001+01:00","created_by":"Mikhail Kotelnikov","updated_at":"2026-02-06T07:21:38.518484743+01:00","closed_at":"2026-02-06T07:21:38.518484743+01:00","close_reason":"Migrated to new interface","dependencies":[{"issue_id":"webrun-vcs-6iq1g","depends_on_id":"webrun-vcs-5d152","type":"blocks","created_at":"2026-02-05T20:44:51.156502779+01:00","created_by":"Mikhail Kotelnikov"}]}
366366
{"id":"webrun-vcs-6j5t","title":"Delete deprecated delta interfaces and files","description":"# Delete Deprecated Delta Interfaces and Files\n\nRemove all deprecated code without backward compatibility.\n\n## Files to Delete\n\n| File | Reason |\n|------|--------|\n| packages/core/src/storage/delta/delta-storage.ts | Unused interface, never implemented |\n| packages/core/src/storage/delta/raw-store-with-delta.ts | Replaced by StorageBackend |\n| packages/core/src/storage/delta/delta-store.ts | Becomes internal backend detail |\n| packages/core/src/storage/delta/packing-orchestrator.ts | Replaced by DeltaApi + DeltaEngine |\n\n## Types to Delete\n\n```typescript\n// From delta-storage.ts\ninterface DeltaStorage { ... }\n\n// From raw-store-with-delta.ts\nclass RawStoreWithDelta { ... }\n\n// From delta-store.ts\ninterface DeltaStore { ... }\ninterface DeltaStoreUpdate { ... }\n\n// Never created (mentioned in original plan)\ninterface TreeDeltaApi { ... }\ninterface CommitDeltaApi { ... }\n```\n\n## Methods to Delete\n\n```typescript\n// From any interface\ndeltify(targetId, candidateIds): Promise\u003cboolean\u003e\nstoreDeltaResult(targetId, result): Promise\u003cvoid\u003e\n```\n\n## Process\n\n1. Search for all usages of deprecated types\n2. Update or remove dependent code\n3. Delete the files\n4. Update imports/exports\n5. Ensure build passes\n6. Run full test suite\n\n## Acceptance Criteria\n\n- [ ] All listed files deleted\n- [ ] No references to deleted types\n- [ ] Build passes\n- [ ] Tests pass (with necessary updates)\n- [ ] No dead code remaining","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-12T22:31:38.844644583+01:00","created_by":"kotelnikov","updated_at":"2026-01-13T01:42:00.394497417+01:00","closed_at":"2026-01-13T01:42:00.394497417+01:00","close_reason":"Production code now uses new architecture (GitFilesStorageBackend, GCControllerV2). Old files (GCController, RawStoreWithDelta, PackingOrchestrator) are marked @deprecated and only used in tests. Test migration can be done separately.","dependencies":[{"issue_id":"webrun-vcs-6j5t","depends_on_id":"webrun-vcs-tgos","type":"blocks","created_at":"2026-01-12T22:32:12.865775152+01:00","created_by":"kotelnikov"}]}
367+
{"id":"webrun-vcs-6ju66","title":"Fix float timestamp causing UI not updating after sync","status":"closed","priority":1,"issue_type":"bug","owner":"mikhail.kotelnikov@gmail.com","created_at":"2026-02-16T01:00:23.031907778+01:00","created_by":"Mikhail Kotelnikov","updated_at":"2026-02-16T01:00:27.794556182+01:00","closed_at":"2026-02-16T01:00:27.794556182+01:00","close_reason":"Fixed: Date.now()/1000 produces floats which cause hash mismatch during pack import round-trips. Applied Math.floor() in demo app and defensive floor in formatPersonIdent."}
367368
{"id":"webrun-vcs-6jwzc","title":"Move fetch integration tests to integration-tests package","description":"Migrate fetch integration tests from transport/tests/integration/ to integration-tests/tests/transport/fetch/.\n\n## Source Files\n- packages/transport/tests/integration/fetch-integration.test.ts (if exists)\n- Any tests using TestRepository for fetch operations\n\n## Target Structure\n```\npackages/integration-tests/tests/transport/fetch/\n├── fetch-messageport.test.ts # Fetch over MessagePort\n├── fetch-http.test.ts # Fetch over HTTP\n└── fetch-scenarios.test.ts # Complex fetch scenarios\n```\n\n## Changes Required\n- Replace mock TestRepository with createTestRepository()\n- Use createRepositoryFacade() adapter\n- Use createRefStore() adapter\n- Update imports to use new helper locations\n\n## Test Scenarios\n- Fetch all commits from server to client\n- Incremental fetch (only new commits)\n- Shallow clone with depth limit\n- Error recovery during fetch","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-24T09:00:41.731828637+01:00","created_by":"kotelnikov","updated_at":"2026-01-24T09:34:35.848316766+01:00","closed_at":"2026-01-24T09:34:35.848316766+01:00","close_reason":"Closed","dependencies":[{"issue_id":"webrun-vcs-6jwzc","depends_on_id":"webrun-vcs-o6aq6","type":"blocks","created_at":"2026-01-24T09:01:19.798382901+01:00","created_by":"kotelnikov"},{"issue_id":"webrun-vcs-6jwzc","depends_on_id":"webrun-vcs-5w1pd","type":"blocks","created_at":"2026-01-24T09:01:19.850528124+01:00","created_by":"kotelnikov"},{"issue_id":"webrun-vcs-6jwzc","depends_on_id":"webrun-vcs-3e6ff","type":"blocks","created_at":"2026-01-24T09:01:19.91247282+01:00","created_by":"kotelnikov"},{"issue_id":"webrun-vcs-6jwzc","depends_on_id":"webrun-vcs-x7k13","type":"blocks","created_at":"2026-01-24T09:01:19.965410177+01:00","created_by":"kotelnikov"}]}
368369
{"id":"webrun-vcs-6l9","title":"Update commands core type imports","description":"Update all commands source files to import VCS types from @webrun-vcs/core.\n\n**Files to check and update:**\n- packages/commands/src/git-command.ts\n- packages/commands/src/types.ts\n- packages/commands/src/git.ts\n- packages/commands/src/commands/*.ts (all command files)\n- packages/commands/src/results/*.ts\n\n**Types to ensure come from @webrun-vcs/core:**\n- ObjectId, Ref, SymbolicRef\n- Commit, PersonIdent, Tree, Blob, Tag\n- FileMode, MergeStage\n- BlobStore, TreeStore, CommitStore, RefStore, TagStore\n- Repository, RepositoryConfig\n- isSymbolicRef()\n\n**Note:** Utils imports (bytesToHex, MyersDiff, etc.) remain from @webrun-vcs/utils\n\n**Verification:**\n- pnpm --filter @webrun-vcs/commands build should succeed","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-25T10:39:00.588394537+01:00","updated_at":"2025-12-25T11:06:37.388135807+01:00","closed_at":"2025-12-25T11:06:37.388135807+01:00","close_reason":"Closed","dependencies":[{"issue_id":"webrun-vcs-6l9","depends_on_id":"webrun-vcs-sco","type":"blocks","created_at":"2025-12-25T10:45:38.538655765+01:00","created_by":"daemon"}]}
369370
{"id":"webrun-vcs-6lcmt","title":"[EPIC] Native Git HTTP Integration Tests","description":"Add integration tests that validate VCS HTTP transport against native git-http-backend and validate our HTTP server against native git client. Proves wire-level interoperability with reference Git implementation. Plan: notes/src/2026-02-07/02-[vcs-transport]-native-git-http-integration-tests.md","status":"closed","priority":1,"issue_type":"epic","owner":"mikhail.kotelnikov@gmail.com","created_at":"2026-02-07T21:10:15.963948505+01:00","created_by":"Mikhail Kotelnikov","updated_at":"2026-02-07T22:52:17.267322987+01:00","closed_at":"2026-02-07T22:52:17.267322987+01:00","close_reason":"Phase 1-2 complete. Helpers + test files created. Tests reveal known pack import/export bugs. Phase 3 deferred until pack pipeline fixed."}

apps/demos/livekit-p2p-sync/src/main.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ async function handleInit(): Promise<void> {
215215
{ mode: FileMode.REGULAR_FILE, name: "README.md", id: blob },
216216
]);
217217

218-
const now = Date.now() / 1000;
218+
const now = Math.floor(Date.now() / 1000);
219219
const author = {
220220
name: roomManager?.getLocalIdentity() ?? "User",
221221
email: "demo@example.com",
@@ -272,7 +272,7 @@ async function handleAddFile(): Promise<void> {
272272

273273
const tree = await history.trees.store(existingEntries);
274274

275-
const now = Date.now() / 1000;
275+
const now = Math.floor(Date.now() / 1000);
276276
const author = {
277277
name: roomManager?.getLocalIdentity() ?? "User",
278278
email: "demo@example.com",
@@ -426,7 +426,7 @@ async function createMergeCommit(
426426
const remoteCommit = await h.commits.load(remoteOid);
427427
if (!remoteCommit) return null;
428428

429-
const now = Date.now() / 1000;
429+
const now = Math.floor(Date.now() / 1000);
430430
const author = {
431431
name: roomManager?.getLocalIdentity() ?? "User",
432432
email: "demo@example.com",

packages/core/src/history/format/person-ident.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import type { PersonIdent } from "../../common/person/person-ident.js";
2020
* @returns Formatted string
2121
*/
2222
export function formatPersonIdent(ident: PersonIdent): string {
23-
return `${ident.name} <${ident.email}> ${ident.timestamp} ${ident.tzOffset}`;
23+
// Git timestamps are always integers (seconds since epoch).
24+
// Floor to prevent float timestamps from causing hash mismatches
25+
// during pack import round-trips (parseInt in parsePersonIdent truncates).
26+
const ts = Math.floor(ident.timestamp);
27+
return `${ident.name} <${ident.email}> ${ts} ${ident.tzOffset}`;
2428
}
2529

2630
/**

0 commit comments

Comments
 (0)