Repository navigation
fix(casting): repair handwritten casting registries under lock with dry-run support [2/4] - #2252
Conversation
🟡 Impact Analysis — PR #2252Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affectedroot (1 file)
squad-cli (2 files)
squad-sdk (3 files)
tests (1 file)
|
🛫 PR Readiness Check
PR Scope: 📦🔧 Mixed (product + infrastructure)
|
| Status | Check | Details |
|---|---|---|
| ❌ | Single commit | 3 commits — consider squashing before review |
| ✅ | Not in draft | Ready for review |
| ✅ | Branch up to date | Up to date with tamirdresher-microsoft-stack-1-client-adapters |
| ❌ | Copilot review | No Copilot review yet — it may still be processing |
| ✅ | Changeset present | Changeset file found |
| ✅ | Scope clean | No .squad/ or docs/proposals/ files |
| ✅ | No merge conflicts | No merge conflicts |
| ✅ | Copilot threads resolved | 0 active Copilot thread(s) resolved (5 outdated skipped) |
| ✅ | CI passing | All checks passing |
Files Changed (7 files, +2029 −9)
| File | +/− |
|---|---|
.changeset/squad-2-0-multi-host-clients.md |
+1 −1 |
packages/squad-cli/src/cli/core/init.ts |
+17 −2 |
packages/squad-cli/src/cli/core/upgrade.ts |
+59 −6 |
packages/squad-sdk/src/casting/handwritten-registry.ts |
+932 −0 |
packages/squad-sdk/src/casting/index.ts |
+16 −0 |
packages/squad-sdk/src/index.ts |
+17 −0 |
test/casting-handwritten-repair.test.ts |
+987 −0 |
Total: +2029 −9
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
🏗️ Architectural Review
Automated architectural review — informational only. |
There was a problem hiding this comment.
🟡 Changes recommended
Identity preservation, history integrity, and archive symlink handling remain unresolved.
5 open issues
Preserve valid provenance IDs during normalization · New Require valid retirement timestamps before skipping repair · New Validate historical identity values against map keys · New Require registry, history, and manifest generations to agree · New Reject symlinked archive directories before writing · New
What changed in this PR
Adds handwritten casting-registry recovery to the SDK and upgrade flow as layer 2 of the Squad 2.0 stack.
Changes:
- Adds locked, transactional repair and roster backfill with archives and dry-run validation.
- Integrates recovery into upgrade and prevents init prompts in non-interactive sessions.
- Adds regression coverage and release notes.
| File | Description |
|---|---|
| test/casting-handwritten-repair.test.ts | Tests normalization, recovery, backfill, and dry runs. |
| packages/squad-sdk/src/index.ts | Exports repair APIs. |
| packages/squad-sdk/src/casting/index.ts | Exposes casting repair utilities. |
| packages/squad-sdk/src/casting/handwritten-registry.ts | Implements normalization, transactional repair, and backfill. |
| packages/squad-cli/src/cli/core/upgrade.ts | Integrates repair and preview reporting. |
| packages/squad-cli/src/cli/core/init.ts | Guards interactive prompts. |
| .changeset/squad-2-0-multi-host-clients.md | Documents recovery behavior. |
Review effort: Balanced
… repair - Validate each record against its existing map key first in normalizeHandwrittenCastingRegistry so valid provenance records (e.g. agents.belanna named B'Elanna with .squad/agents/belanna/ avatar) preserve their map key - Require valid retired_at on legacy retired records in isAcceptable so retired records missing retired_at reach normalization instead of early-exiting - Resolve snapshot agent values first in normalizeHandwrittenHistory and reject unknown agent values or disagreement with a recognized ID key - Require registry, history, and commit manifest generation metadata (transaction_id and revision) to agree before stripping and recommitting in recommitExistingPair - Reject symlinked handwritten-archive-<sha256> directories and archive target files via assertNotSymlink before creating or writing archives Refs #2239 Refs #2252 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c854ee92-c40c-4436-8a2c-901b1f781776
…m team.md (Layer 2) Carves Layer 2 of #2239 targeting tamirdresher-microsoft-stack-1-client-adapters and applies all Layer 2 review fixes: - Acquire the casting writer lock (with symlink/lstat guards) BEFORE reading registry.json, history.json, or registry-history.commit.json in repairHandwrittenCastingRegistry and backfillCastingRegistryFromTeam - Do not leave *.handwritten.bak clutter in .squad/casting/ by default (only when options.keepBackup === true is explicitly passed) - Restore the exact in-memory pre-repair snapshot to disk and rethrow if commitCastingRegistryPair fails (never fall back to writing a fresh cast or deleting registry-history.commit.json) - Never touch or rewrite valid canonical registries or registries with existing cast history - Validate handwritten registry repair in memory during `squad upgrade --dry-run` so dry-run previews succeed without mutating disk while still failing closed on corrupt/unrepairable registries - Guard interactive prompts in `squad init` via `isInteractiveSession()` Refs #2239 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c854ee92-c40c-4436-8a2c-901b1f781776
… backfill ids, roll-forward-safe rollback - H3: normalize handwritten registries per record — records that already parse as valid provenance records are preserved verbatim (all fields, timestamps, per-record universe); only incomplete records get missing fields synthesized. Preserve and bump the registry revision instead of resetting it. Archive pre-repair originals under handwritten-archive-<sha256>/ by default (keepBackup: false opts out) and report backupPath. - H4: backfillCastingRegistryFromTeam derives agent ids from the team.md Charter column (.squad/agents/<id>/charter.md) or a charter whose ## Identity name matches; rows with no provable folder fail the backfill closed with a dropped list. Fix the test that expected "b-elanna" for the belanna folder. - M8: wrapper-less shapes only accept record values carrying name/persistent_name/display_name/role; bare-string values and non-agent metadata are reported instead of guessed. Non-Latin names, duplicate names and id collisions no longer drop silently — RepairResult.dropped is populated and the repair fails closed; squad upgrade prints the reason and each dropped entry. - M7: commitPairWithRollback only restores the pre-repair snapshot when the commit failed before its journal became durable (never on CastingCommitInDoubtError or after a successful commit), restores via temp+fsync+rename, and attaches restore failures as cause. recoverCastingRegistryTransaction runs under the lock before the snapshot so pending journals roll forward first. - M9: single exported hasExistingCastHistory (empty arrays/objects are not history) replaces the CLI copy; a history.json holding one Init Mode snapshot consistent with the handwritten registry is normalized instead of blocking the repair. squad upgrade --dry-run previews the team.md backfill. - Low: strip UTF-8 BOM before JSON.parse; anchor the team.md universe regex to a line start / table cell and fail the backfill closed instead of inventing one; recommit of a manifest-mismatched pair bumps the revision and requires a readable history object. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 92888fd8-c715-4d1d-a60f-893a7a157841
… repair - Validate each record against its existing map key first in normalizeHandwrittenCastingRegistry so valid provenance records (e.g. agents.belanna named B'Elanna with .squad/agents/belanna/ avatar) preserve their map key - Require valid retired_at on legacy retired records in isAcceptable so retired records missing retired_at reach normalization instead of early-exiting - Resolve snapshot agent values first in normalizeHandwrittenHistory and reject unknown agent values or disagreement with a recognized ID key - Require registry, history, and commit manifest generation metadata (transaction_id and revision) to agree before stripping and recommitting in recommitExistingPair - Reject symlinked handwritten-archive-<sha256> directories and archive target files via assertNotSymlink before creating or writing archives Refs #2239 Refs #2252 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c854ee92-c40c-4436-8a2c-901b1f781776
ed9b3f7 to
1753d7a
Compare

Summary (Stack Layer 2 of 4 — splits #2239)
Adds
repairHandwrittenCastingRegistryandbackfillCastingRegistryFromTeamso repos with hand-authored.squad/casting/registry.jsonfiles normalize cleanly into the canonical committed registry/history pair.Review fixes included in Layer 2
acquireCastingRegistryLock(withlstatSyncsymlink guards on.squadand.squad/casting) before readingregistry.json,history.json,registry-history.commit.json, orteam.md..handwritten.bakfiles by default (keepBackupis opt-in).commitCastingRegistryPairfails (never falls back to a fresh cast).squad upgrade --dry-runon handwritten registries via in-memory validation without mutating disk or failing preflight.