Uh oh!
There was an error while loading. Please reload this page.
Add DataModel.RebuildCommitHashes() for post-bootstrap rehash - #67
Add DataModel.RebuildCommitHashes() for post-bootstrap rehash#67myieye wants to merge 2 commits into
Conversation
Previously the duplicate-commit check ran before acquiring the repo lock, leaving a TOCTOU window where two concurrent Add calls could both see the commit as missing and proceed to insert it. https://claude.ai/code/session_01TziA3LymLmdxXxUgXywq4B
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR adds ChangesCommit Hash Rebuild Feature
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Actionable comments posted: 0 |
Walks every commit in chain order and recomputes Hash/ParentHash against the current Commit.Ids. The intended caller is FwLite's SQL-template- based project creation path: it seeds commits whose Ids get substituted at apply time, leaving the persisted hashes (computed against template- source Ids) stale. Calling this before the first sync brings the chain back in line. Local-only by contract — once any peer has observed a commit's hash, a rebuild would break sync. Takes the repo lock and wraps in a transaction to match other write paths. Test covers the recovery scenario: corrupt a mid-chain hash, watch AddChange reject further writes, then verify RebuildCommitHashes restores every Hash to GenerateHash(parentHash) and the chain accepts writes again. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
af27140 to
bf9698dCompareTemplated projects are local-only today (UploadProject still throws), so the per-project Guid_N hydration we shipped was protecting against a threat that can't manifest until cross-client template sync exists. The LexBox composite (ProjectId, Id) PK already covers the loud failure case (silent commit-Id collision on server insert). Static templates are concretely simpler: no projectId plumbing through ApplyAsync, no morph-types-commit-Id placeholder substitution, no rehash trigger, no Harmony submodule API surface needed. Easy to revert if either of the deferred triggers in TEMPLATE-FOLLOWUPS.md fires: - Sharing templated projects across clients - A LexBox feature that keys on Commit.Id alone What changed: - ProjectTemplate.ApplyAsync drops the projectId param and HydrateGuids/ placeholder-substitution/rehash steps; only WS placeholders are substituted at apply time. - template.sql is baked: Guid_N tokens replaced with deterministic UUIDv5s (namespace Guid.Empty), morph-types placeholder resolved to MorphTypesSeedCommitId(Guid.Empty), hash chain recomputed. - GenerateTemplate (dev tool) no longer tokenizes — drops TemplateGuidScrubbing.cs. - BakeStaticTemplate added as a skipped dev tool for re-baking if the template is regenerated and Guid_N tokens reappear. - CreateProjectRequest gains SeedMorphTypes (default true). Download path sets false: morph-types arrive via the server sync that runs inside AfterCreate, so the pre-seed would add a redundant commit that the inbound sync would just dup. Other call sites keep the default (import path needs morph-types before AfterCreate runs). - Harmony submodule pin reverted to develop's commit (96a75b26b); the RebuildCommitHashes API on PR sillsdev/harmony#67 is no longer called from lexbox. PR stays open as a known-working primitive for if/when ② in TEMPLATE-FOLLOWUPS.md fires. - TemplatedProjects_HaveDisjointCommitIds test removed (now they intentionally share Ids). - CreateProjectFromTemplateAppliesRequestedIdentity drops its per-project-Id morph-types-commit-Id assertion (no longer pertinent). TEMPLATE-FOLLOWUPS.md lays out the staged simplifications and the events that should trigger each next step. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Templated projects are local-only today (UploadProject still throws), so the per-project Guid_N hydration we shipped was protecting against a threat that can't manifest until cross-client template sync exists. The LexBox composite (ProjectId, Id) PK already covers the loud failure case (silent commit-Id collision on server insert). Static templates are concretely simpler: no projectId plumbing through ApplyAsync, no morph-types-commit-Id placeholder substitution, no rehash trigger, no Harmony submodule API surface needed. Easy to revert if either of the deferred triggers in TEMPLATE-FOLLOWUPS.md fires: - Sharing templated projects across clients - A LexBox feature that keys on Commit.Id alone What changed: - ProjectTemplate.ApplyAsync drops the projectId param and HydrateGuids/ placeholder-substitution/rehash steps; only WS placeholders are substituted at apply time. - template.sql is baked: Guid_N tokens replaced with deterministic UUIDv5s (namespace Guid.Empty), morph-types placeholder resolved to MorphTypesSeedCommitId(Guid.Empty), hash chain recomputed. - GenerateTemplate (dev tool) no longer tokenizes — drops TemplateGuidScrubbing.cs. - BakeStaticTemplate added as a skipped dev tool for re-baking if the template is regenerated and Guid_N tokens reappear. - CreateProjectRequest gains SeedMorphTypes (default true). Download path sets false: morph-types arrive via the server sync that runs inside AfterCreate, so the pre-seed would add a redundant commit that the inbound sync would just dup. Other call sites keep the default (import path needs morph-types before AfterCreate runs). - Harmony submodule pin reverted to develop's commit (96a75b26b); the RebuildCommitHashes API on PR sillsdev/harmony#67 is no longer called from lexbox. PR stays open as a known-working primitive for if/when ② in TEMPLATE-FOLLOWUPS.md fires. - TemplatedProjects_HaveDisjointCommitIds test removed (now they intentionally share Ids). - CreateProjectFromTemplateAppliesRequestedIdentity drops its per-project-Id morph-types-commit-Id assertion (no longer pertinent). TEMPLATE-FOLLOWUPS.md lays out the staged simplifications and the events that should trigger each next step. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Templated projects are local-only today (UploadProject still throws), so the per-project Guid_N hydration we shipped was protecting against a threat that can't manifest until cross-client template sync exists. The LexBox composite (ProjectId, Id) PK already covers the loud failure case (silent commit-Id collision on server insert). Static templates are concretely simpler: no projectId plumbing through ApplyAsync, no morph-types-commit-Id placeholder substitution, no rehash trigger, no Harmony submodule API surface needed. Easy to revert if either of the deferred triggers in TEMPLATE-FOLLOWUPS.md fires: - Sharing templated projects across clients - A LexBox feature that keys on Commit.Id alone What changed: - ProjectTemplate.ApplyAsync drops the projectId param and HydrateGuids/ placeholder-substitution/rehash steps; only WS placeholders are substituted at apply time. - template.sql is baked: Guid_N tokens replaced with deterministic UUIDv5s (namespace Guid.Empty), morph-types placeholder resolved to MorphTypesSeedCommitId(Guid.Empty), hash chain recomputed. - GenerateTemplate (dev tool) no longer tokenizes — drops TemplateGuidScrubbing.cs. - BakeStaticTemplate added as a skipped dev tool for re-baking if the template is regenerated and Guid_N tokens reappear. - CreateProjectRequest gains SeedMorphTypes (default true). Download path sets false: morph-types arrive via the server sync that runs inside AfterCreate, so the pre-seed would add a redundant commit that the inbound sync would just dup. Other call sites keep the default (import path needs morph-types before AfterCreate runs). - Harmony submodule pin reverted to develop's commit (96a75b26b); the RebuildCommitHashes API on PR sillsdev/harmony#67 is no longer called from lexbox. PR stays open as a known-working primitive for if/when ② in TEMPLATE-FOLLOWUPS.md fires. - TemplatedProjects_HaveDisjointCommitIds test removed (now they intentionally share Ids). - CreateProjectFromTemplateAppliesRequestedIdentity drops its per-project-Id morph-types-commit-Id assertion (no longer pertinent). TEMPLATE-FOLLOWUPS.md lays out the staged simplifications and the events that should trigger each next step. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
myieye
commented
Jun 2, 2026
Superseded by #68 ( |
Templated projects are local-only today (UploadProject still throws), so the per-project Guid_N hydration we shipped was protecting against a threat that can't manifest until cross-client template sync exists. The LexBox composite (ProjectId, Id) PK already covers the loud failure case (silent commit-Id collision on server insert). Static templates are concretely simpler: no projectId plumbing through ApplyAsync, no morph-types-commit-Id placeholder substitution, no rehash trigger, no Harmony submodule API surface needed. Easy to revert if either of the deferred triggers in TEMPLATE-FOLLOWUPS.md fires: - Sharing templated projects across clients - A LexBox feature that keys on Commit.Id alone What changed: - ProjectTemplate.ApplyAsync drops the projectId param and HydrateGuids/ placeholder-substitution/rehash steps; only WS placeholders are substituted at apply time. - template.sql is baked: Guid_N tokens replaced with deterministic UUIDv5s (namespace Guid.Empty), morph-types placeholder resolved to MorphTypesSeedCommitId(Guid.Empty), hash chain recomputed. - GenerateTemplate (dev tool) no longer tokenizes — drops TemplateGuidScrubbing.cs. - BakeStaticTemplate added as a skipped dev tool for re-baking if the template is regenerated and Guid_N tokens reappear. - CreateProjectRequest gains SeedMorphTypes (default true). Download path sets false: morph-types arrive via the server sync that runs inside AfterCreate, so the pre-seed would add a redundant commit that the inbound sync would just dup. Other call sites keep the default (import path needs morph-types before AfterCreate runs). - Harmony submodule pin reverted to develop's commit (96a75b26b); the RebuildCommitHashes API on PR sillsdev/harmony#67 is no longer called from lexbox. PR stays open as a known-working primitive for if/when ② in TEMPLATE-FOLLOWUPS.md fires. - TemplatedProjects_HaveDisjointCommitIds test removed (now they intentionally share Ids). - CreateProjectFromTemplateAppliesRequestedIdentity drops its per-project-Id morph-types-commit-Id assertion (no longer pertinent). TEMPLATE-FOLLOWUPS.md lays out the staged simplifications and the events that should trigger each next step. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Adds a public
DataModel.RebuildCommitHashes()API that walks every commit in chain order and recomputesHash/ParentHashagainst the current Commit.Ids. Intended for callers that have staged commits whose Ids changed after creation (e.g. applying a Guid-substituted SQL template), so the persisted hashes can be brought back in line before any sync validates them.Local-only by contract — once any peer has observed a commit's hash, the rebuild would break sync. Takes the repo lock + transaction to match other write paths.
Also pins
Verify.EntityFrameworkto the last v13 (v14+ pulls EF Core 10 abstractions, which collide with the EF 9.0.16 SQLite provider this repo stays on per the .NET 10 bump). Regenerates theDbContextTests.VerifyModelverified snapshot to reflect EF 9 (the previously-committed file hadProductVersion: 10.0.7baked in from the EF 10 churn before the rollback).Test plan
RebuildCommitHashesTests.RebuildCommitHashes_RestoresChainAfterHashesAreCorrupted— corrupts a mid-chain hash, watches AddChange reject further writes, then verifies RebuildCommitHashes restores every Hash toGenerateHash(parentHash)and the chain accepts writes again.RebuildCommitHashesTests.RebuildCommitHashes_IsNoOpOnEmptyChain— guards against an empty-chain regression.Downstream caller is sillsdev/languageforge-lexbox PR #2281, where it replaces a JSON-roundtrip hack that fabricated a synthetic Commit just to trigger the same internal rehash path.
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes