Skip to content

fix(hooks): freeze-guards skip system/seed writes — 23 boot errors + closed-won probability (2.2.2)#462

Merged
os-zhuang merged 1 commit into
mainfrom
fix/459-lifecycle-guard-system-writes
Jul 20, 2026
Merged

fix(hooks): freeze-guards skip system/seed writes — 23 boot errors + closed-won probability (2.2.2)#462
os-zhuang merged 1 commit into
mainfrom
fix/459-lifecycle-guard-system-writes

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #459. Highest-severity finding from the v2.2.1 QA dogfood; fixing before the ObjectStack 16 marketplace release.

Root cause

The opportunity_lifecycle and quote freeze-guards rejected any changed business field on a closed/accepted record. The seed re-applies rows on every reboot, and its dates (close_date: daysAgo(15), quote dates) re-evaluate to a new day each boot, so a re-seed legitimately changes close_date on already-closed opps → guard throws. Effects:

  • 23 BodyRunner errors on every reboot (log noise, looks broken to ops).
  • Closed-won probability stuck at the field default 10 — the guard blocked the seed from writing 100.

Fix

Both guards now fire only for genuine user edits (ctx.user?.id present). System / seed / backfill writes (no user) pass — matching this repo's system-write convention (case/lead hooks) and the guards' own intent ("only user edits to business fields"). A user editing a closed opportunity / accepted quote through the UI is still blocked (unchanged).

Verification

Rebooted on the existing persisted DB — the exact 23-error scenario:

  • BodyRunner errors: 23 → 0 (opportunity + quote).
  • All closed_won opps now report probability 100 (open stages keep stage-derived 25/40/60/80); confirmed via /api/v1/data/crm_opportunity.
  • verify (validate + typecheck + build + vitest 17/17) green.

🤖 Generated with Claude Code

…ors + closed-won probability (2.2.2)

Closes #459.

The opportunity_lifecycle and quote freeze-guards ran on every write to a
closed/accepted record. The seed re-applies rows on each reboot, and its
`close_date: daysAgo(15)` / quote dates re-evaluate to a *new* date every boot,
so the re-seed genuinely changes a guarded field on an already-closed record —
which the guard rejected. Result: 23 `BodyRunner` errors per boot, and the seed
could never set closed-won `probability` to 100 (it fell back to the field
default 10).

Both guards now fire ONLY for genuine user edits (`ctx.user?.id` present).
System / seed / backfill writes (no user) pass — matching this repo's
system-write convention (case/lead hooks) and the guards' own stated intent.
A user editing a closed opportunity / accepted quote via the UI is still blocked.

Verified: reboot on the existing (persisted) DB — the exact 23-error scenario —
now logs 0 BodyRunner errors, and all closed_won opps report probability 100
(open stages keep their stage-derived values). verify (validate/build/test 17/17)
green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies configuration labels Jul 20, 2026
@os-zhuang
os-zhuang merged commit 32d63e7 into main Jul 20, 2026
5 checks passed
@os-zhuang
os-zhuang deleted the fix/459-lifecycle-guard-system-writes branch July 20, 2026 18:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

configuration dependencies documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

opportunity_lifecycle freeze-guard rejects seed re-writes on closed deals → 23 boot errors + closed-won probability stuck at 10%

1 participant