fix(seed): give the Acme AI governance workshop one schedule - #1687
Merged
Conversation
The workshop was seeded three different ways. The opportunity's `next_step` placed it "the week of close_date - 14d" (close_date is daysFromNow(30), so daysFromNow(16)); the `crm_event` row books it at daysFromNow(6); and the `crm_task` to schedule it was `not_started` and due daysFromNow(7) — a day after the meeting it asked someone to book. The event is now the single place the date is authored. It is the record with a real instant in the world (start, 90 minutes, location, attendee) and the only one another record derives from: the attendee builder dates the invitation daysAgo(max(daysAgo + invitedDaysBefore, 0)) = daysAgo(3), so the invite went out three days before boot and john.smith@acme.example.com has already answered `tentative`. Because sending that invitation IS the act of booking the workshop, the task is completed rather than re-dated — an open "schedule it" task beside a booked-and-invited meeting is the contradiction itself. It carries completed_date and due_date of daysAgo(3) plus the is_completed / progress_percent stamps the seed book already mirrors for a completed task. `next_step` now carries no date at all, absolute or relative: it states only what stays true however the event is scheduled (booked, invitation out) and leads with the work still open, the revised proposal. The event and the opportunity's close_date are untouched. Co-authored-by: Claude
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
os-steve
marked this pull request as ready for review
September 6, 2026 10:38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1660
One Acme workshop was seeded three different ways, and the task to book it was due a day after the meeting it asked someone to book.
What the three records said
crm_opportunityAcme Platform Upgradenext_stepsrc/data/sales.seed.tsclose_date - 14d"close_date=daysFromNow(30)⇒daysFromNow(16)crm_eventAcme — AI agent governance workshopsrc/data/service.seed.tsstatus: 'planned',daysAgo: -6daysFromNow(6)crm_taskAcme — schedule AI governance workshopsrc/data/service.seed.tsstatus: 'not_started',due_date: daysFromNow(7)daysFromNow(7)— a day after the workshopThe card attributed this to
sales.seed.ts; both contradicting records are inservice.seed.ts. Confirmed onorigin/mainand reflected in the file list.Which record is canonical, and why — the event
Not a preference. Two things were measured:
1. The event is the only record another record derives from. The attendee builder (
service.seed.ts) dates the invitation:For this row that is
max(-6 + 9, 0)=daysAgo(3). SoinvitedDaysBefore: 9means nine days before the event, and the invitation went out three days before the demo boots — a genuine past date, not the clamp floor. The card inferred the invite had gone out; this is the measurement behind it.2. Acme has already answered it. The event's sole attendee is
john.smith@acme.example.comwithresponse: 'tentative'. A reply only exists if there was an invite. That is an act that has happened and been answered — it outranks two dates nobody has acted on.By contrast
next_stephad no anchor of its own (its value was entirely parasitic onclose_date, which #1646 just made load-bearing for the account description, and which this PR does not touch), and the task's due date was an authored number nothing else read.What changed
status: 'completed',completed_dateanddue_datebothdaysAgo(3), withis_completed: true/progress_percent: 100mirrored the way the seed book already mirrors whattask_completionwould stamp (Send welcome package to Stark Medicalis the existing precedent, andcompleted_date_requiredis an error-severity validation, so the stamp is mandatory). Re-dating would have kept the contradiction and only moved it.next_stepcarries no date at all — absolute or relative. It states only what stays true however the event is scheduled (booked, invitation out) and leads with the work still genuinely open, the revised proposal, which the seed book already tracks as its ownnot_startedtask. TheMath.max(…, 0)clamp means a planned event's invitation is never in the future, so "the invitation is out" cannot go stale either.close_date. No new gate (AGENTS.md:431 — Acme's seeded account description states a renewal horizon that matches no seeded record #1646's ruling already declined a guard for exactly this shape).The story a reader now gets, opening the three records in any order: the workshop was booked three days ago, Acme has tentatively accepted, it happens in six days, and the only thing still owed on the deal is the proposal.
Seed guards — re-run, no movement
test/seed-consistency.test.ts,test/seed-validation-warnings.test.tsandtest/activity-seed-coverage.test.ts: 3 files, 60 tests, all passed. Neither pinned set moved.activity-seed-coveragewas the one at real risk — it walkst.status === 'completed' || t.is_completed === trueintobubbleTargets(), so a newly-completed task bubbleslast_activity_dateonto its account. Acme Corporation is alreadylast_activity_date: celtoday()`` from a held event two days ago, so both directions of that guard ("keeps held interactions away from the quiet accounts" and "authorstoday()on every account a seeded activity bubbles to") are satisfied unchanged.Movement this does cause
Two counts move by one, both intended, neither pinned by any test:
activity.dashboard.ts,filter: { is_completed: true }): 1 → 2task.view.ts,status in [not_started, in_progress]∧priority in [high, urgent]): 4 → 3 rows, still non-empty (Follow up with Acme on proposal,Acme — close out login-issues ticket before SLA,Prepare contract for Wayne Enterprises)Nothing else counts
not_startedtasks: the only other consumers are hooks/flows that write the status and theoverdue_tasksview, which filtersis_completed = falseand so excludes a completed task regardless of its due date.Token ratchet: zero cost.
check-source-token-ratchet.mjsdeclaresEXCLUDED = ['src/translations', 'src/data']— seed data is outside the ratchet entirely by ruling. Measured rather than assumed: reverting both files to the merge-base and re-running gives a byte-identical reading (~85,032/~37,963/~137,336), restore proven by blob-hash equality againstHEAD.Verification
pnpm verifyfully green — exit 0 across all eight stages:Note for #1661
The Acme contract card reads the two opportunities this PR touches. Nothing here changes an opportunity's identity, amount, stage or
close_date— onlynext_stepprose on Acme Platform Upgrade, which no longer dates the workshop. #1661 can treat the workshop's date as owned solely by thecrm_eventrow.🤖 Generated with Claude Code
https://claude.ai/code/session_018xtjdpZFjgWh4Ad9Wcx68J
Generated by Claude Code