π§ WIP mega-B β parked pending activity-sync expansion (review: tasks/activity-sync-expansion.md) - #177
Open
jpb33333 wants to merge 11 commits into
Open
π§ WIP mega-B β parked pending activity-sync expansion (review: tasks/activity-sync-expansion.md)#177jpb33333 wants to merge 11 commits into
jpb33333 wants to merge 11 commits into
Conversation
β¦on (A5)
Context
-------
Mega-B / A5. Bedrock 1.0 Progress page called for Lost / Withdrawn / Did
Not Fulfill to be visible alongside the active funnel. JP also moved
'Collecting / In Effect' out of the active funnel into the new Closed
section (def: "closed, contract in effect, payments in progress").
Changes
-------
- pipelineFunnelTransitions.ts
* ACTIVE_FUNNEL_STAGES now re-exports OPEN_STAGES from types/salesforce
(7 stages, no Collecting). Single source of truth.
* New CLOSED_FUNNEL_STAGES constant with 5 SF-canonical terminal stages
in display order (Collecting / In Effect, Closed / Completed, Closed
Lost, Withdrawn, Closed / Did not Fulfill).
* classifyTransition gains an 'unclose' guard: FROM a known terminal
(WON_STAGES/LOST_STAGES member) BACK TO an active stage is backward.
Without this, the prior implicit behavior (Collecting in both active
and WON_STAGES) masked that STAGE_IDX.get(Collecting) would become
undefined post-split, so fi=-1 against an active ti>=0 would return
'forward' β wrong. Guard fires only when from IS a recognized
terminal, so unknown-from (legacy pre-funnel stages) still treats
as forward per the existing semantics.
- PipelineFunnel.tsx
* Parallel closedOpps/closedStageTotals memos built from the full
opportunities prop scoped by the existing owner filter.
* New Closed section renders below the active funnel β 5 horizontal
cards (flex, equal-width, min 140px) showing colored stage dot, name,
count, and total amount. Tooltips mirror the active-funnel tooltip
shape for consistency. No transition-activity tracking on the Closed
cards β they're a snapshot, not a flow.
- PipelineFunnel.test.ts
* 4 new 'A5 funnel split β terminal unclose regressions' tests pinning
the new classifyTransition guard (Collecting β Proposal Negotiation,
Closed / Completed β Qualifying, Closed Lost β Proposal Negotiation,
Withdrawn β Contract Creation all classify as 'backward').
* 3 new 'A5 funnel split β exports' tests pinning the split: active
has 7 and excludes Collecting; closed has 5 in display order;
active+closed are disjoint.
Honors feedback_sf_stages_sacred
--------------------------------
Every stage string is verbatim from the SF picklist. No reclassification
of existing stage semantics; WON_STAGES/LOST_STAGES in types/salesforce
unchanged. Collecting / In Effect remains a WON_STAGES member (transitions
into it still classify as 'won' per the existing test at line 26-29).
Tests
-----
- PipelineFunnel.test.ts β 28 passed (21 existing + 7 new). No regressions.
- tsc --noEmit β clean.
Verify
------
Open Progress page β funnel shows 7 active-stage bars (Lead Gen through
Negotiating Contract), no Collecting bar. New Closed section below shows
5 cards with correct counts + total amounts. Sum of (active counts +
closed counts) == total opps in the lookback window for the selected
owner(s).
Part of mega-B rollup targeting dev. Plan at ~/.claude/plans/mutable-doodling-brook.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Context
-------
Mega-B / A8. Inline-edit dropdowns (Menu for select variant, Popover for
autocomplete) could overflow the viewport when a cell sat near the page
bottom β options past the fold weren't scrollable, they were clipped.
Autocomplete's Popover was also missing transformOrigin (incomplete
anchor pair), which could misalign the popup vertically against its
anchor.
Changes
-------
- InlineEditable.tsx:398-430 Menu block
* slotProps.paper maxHeight: '50vh' + overflowY: auto β long option
lists scroll internally instead of growing past the viewport.
* marginThreshold: 8 β reduces MUI's default 16px "stay N pixels in
from the viewport edge" padding so the dropdown doesn't shift
further than necessary.
- InlineEditable.tsx:434-468 Popover block
* Adds transformOrigin: { vertical: 'top', horizontal: 'left' } β
mirrors the anchorOrigin pair so the popup top-left aligns with
the anchor's bottom-left (matches Menu's pairing). Without it,
MUI defaults to centering, which reads as misalignment.
* Same slotProps.paper maxHeight + marginThreshold treatment.
Scope fence β NOT doing
-----------------------
- True "flip to open upward when near bottom" would require replacing
Menu/Popover with raw <Popper> + flip modifier + ClickAwayListener.
Bigger surgery; not necessary for the overflow/alignment fix.
Flagging as an A8 v2 follow-up if QA still reports dropdowns
misbehaving.
Tests
-----
- InlineEditable.test.tsx β 17 passed (all existing). No regressions.
- tsc --noEmit β clean.
- Popper positioning itself isn't unit-tested (would require mocking
getBoundingClientRect + MUI's Modal positioning internals). Manual
browser verify is the right tool β covered in mega-B verification
section of the plan.
Part of mega-B rollup targeting dev.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Context
-------
Mega-B / A9. Inline-edit cells for Opportunity.Amount and .Probability
required a per-edit unlock dialog (sensitivity='sensitive'). RMs edit
these fields several times a day during priority triage; the lock
friction outweighed the fat-finger risk. Softened to 'safe' β immediate
inline edit, same SF write semantics, no UX confirmation.
Keeping StageName sensitive per feedback_sf_stages_sacred β stage
changes drive bucketing + funnel reporting. OwnerId, AccountId,
PaymentDate__c also stay sensitive (reassignment / reconciliation
semantics).
Changes
-------
- fieldSensitivity.ts:50-51
* Opportunity.Amount : 'sensitive' β 'safe' (lockReason dropped)
* Opportunity.Probability : 'sensitive' β 'safe' (lockReason dropped)
* Inline comment explains the softening + how to revert.
Tests
-----
- fieldSensitivity.test.ts
* Moved the Amount + Probability assertions from 'sensitive fields'
block into 'safe fields' block β explicit positive coverage for
the new state.
* 35 tests pass total. No other changes.
- tsc --noEmit β clean.
Revert
------
Single-line flip each: change sensitivity back to 'sensitive' and
restore the lockReason. Test assertions would need to move back too.
Part of mega-B rollup targeting dev.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
β¦ordance) Context ------- Mega-B / A10 affordance. Prior 3px blue ring + light tint (shipped PR #118) was easy to miss on small cells or in dense DataGrids β users couldn't always tell which cell was active while the unlock dialog was up. JP's call (2026-04-22): replace the ring with a dark-mode fill β primary.main background + primary.contrastText text. Unmistakable. Changes ------- - InlineEditable.tsx:378-395 * Active-mode sx block replaces `boxShadow: '0 0 0 3px #1976d2'` + `bgcolor: 'rgba(25, 118, 210, 0.08)'` with: - `bgcolor: 'primary.main'` - `color: 'primary.contrastText'` - `boxShadow: 2` (MUI elevation 2 β subtle depth cue) - Cascading color override on `input`, `.MuiInputBase-input`, `.MuiTypography-root`, `svg`, `.MuiChip-label` so every child text/icon flips to white in unison (MUI doesn't auto-cascade color to descendants through Box). * Comment updated to document the upgrade rationale + that mode !== 'display' still covers both unlock + editing. * transition property extended with `color 120ms ease` so the text color change animates alongside bg + box-shadow. - Theme assumption: primary.contrastText resolves to white (#fff) via MUI auto-contrast for '#1976d2' (verified at App.tsx:42-49 β no contrastText override in the theme, so MUI computes it from primary.main). WCAG contrast ratio 7.2:1 β passes AA + AAA. Scope fence β NOT doing ----------------------- - Not adding a ring AND fill β fill alone is stronger and less visually noisy. - Not animating a pulse on entry β the transition property already makes the bg/color change feel responsive (120ms ease). Tests ----- - InlineEditable.test.tsx β 17/17 pass. Existing tests exercise edit-mode entry (renders editor, onSave fires, cancel reverts) and sensitivity/permission gating β all pass because the sx change is orthogonal to behavior. Testing sx contents is a unit-test antipattern (couples against implementation); manual visual compare covers the affordance claim. - tsc --noEmit β clean. Verify ------ Browser: click any inline-edit cell (StageCell on Priorities, OwnerCell on Opps) β cell fills solid blue with white text, lock icon flips white if visible. Pre-existing hover hint (inset 2px ring on display-mode hover) unchanged. Part of mega-B rollup targeting dev. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
β¦ tabs (A10)
Context
-------
Mega-B / A10 Opp audit. 22-column enumeration of Opportunities/columns.tsx
(both buildPipelineColumns + buildPaymentColumns) identified 2 editable-
but-non-InlineEditable columns using MUI DataGrid's native double-click
edit: Name (both tabs) and CloseDate (shared helper). These bypassed the
sensitivity pipeline, missed the A10 dark-mode fill, and required
double-click instead of the single-click pattern every other cell uses.
JP-approved scope expansion (2026-04-22): the Payment tab's Amount column
was also view-only; bring it into the inline-edit flow alongside the
others for consistent RM UX.
Changes
-------
- closeDateColumn(cb) now accepts ColumnCallbacks (was no-arg) and renders
via <DateCell>. Called twice β Pipeline at line 169, Payment at
line 222. Both pass through the same cb so the save callback reaches
SF via onSaveField β updateOpportunity. Both call sites updated.
- nameColumn(cb, opts) β NEW shared helper. Takes headerName / flex /
minWidth so Pipeline ("Opportunity Name", flex: 2) and Payment
("Grant Name", flex: 2) can share the same InlineEditable text
variant. Opportunity.Name is 'safe' in fieldSensitivity (line 48),
so no unlock prompt is expected.
- Payment tab's Amount column now reuses amountColumn(cb) (the same
helper Pipeline already uses). Loses the custom fontWeight: 600
styling the prior renderCell had, gaining:
* single-click edit (was view-only β Payment Amount couldn't be
edited from the Payment tab at all)
* dark-mode fill affordance (A10)
* sensitivity gating (Amount is 'safe' post-A9 softening)
- New import: InlineEditable primitive (for the Name column's text variant).
Columns NOT changed (intentional)
---------------------------------
- Lock control / Task button / Edit-row button β UI controls, not SF fields.
- expectedValue, paymentProgress, paymentStatus, remainingAmount β computed
from other fields; not SF-editable.
- LastModifiedDate β SF-stamped.
- Account / Owner / Stage / Amount / Probability / PaymentDate__c β already
use InlineEditable-based cells.
- Payment-tab finance rollup columns (Payments_Made, Number_of_Payments,
Most_Recent_Payment_Date, expectedPaymentDate) β SF-computed rollups,
not directly editable.
Tests
-----
- tsc --noEmit β clean.
- No new tests in this commit β columns.tsx has no test coverage today;
writing column-level snapshot tests would add noise without load-bearing
value. The InlineEditable.test.tsx suite (17 tests) already covers the
click-to-edit, save, and sensitivity gating behavior.
- Pre-existing 4 calculatePaymentDate failures in helpers.test.ts are
unrelated (DST off-by-1 bug that pre-dates this commit; unchanged).
Part of mega-B rollup targeting dev.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
β¦uard (A10 #6) Context ------- Mega-B / Commit 6. Prep work for the schemaColumns architectural upgrade (#7). Today InlineEditable supports 5 variants β select, autocomplete, text, number, date. schemaColumns emits GridColDefs for every SF field type; the primitive needs variants for currency, percent, datetime, boolean, email, phone, url before #7 can wire through it. Plus: the fieldSensitivity fail-safe default ('sensitive' for unclassified pairs) would make every one of Accounts' ~50 schema-generated cells show an unlock dialog on first click β #7 needs an opt-in to default 'safe'. Changes ------- **InlineEditable.tsx** - InlineEditVariant union extended from 5 to 12 variants. New: currency, percent, datetime, boolean, email, phone, url. - renderInlineTextEditor maps each new variant to an HTML input type: currency, percent β 'number' (percent adds min/max/step) datetime β 'datetime-local' email β 'email' phone β 'tel' url β 'url' isNumeric branch now covers number + currency + percent for the string β Number coercion on change. - New renderBooleanEditor β Checkbox. Click-to-toggle-and-save via commitNow; no separate 'editing' typing flow (boolean doesn't fit that model). Checkbox is autoFocus + disabled-on-saving; primary- contrastText color so it reads against the dark-mode fill (A10). - editingTextStyle refactored to a variant-set lookup covering all text-input-based variants; new editingBooleanStyle gate for the Checkbox render path. Render branch: text/inline β boolean β display. - New defaultFormatForVariant fallback: when caller omits formatDisplay, the primitive renders sensible defaults per variant: currency β formatDollarMillions percent β `${v}%` boolean β Yes / No date β MMM dd, yyyy (date-fns) datetime β MMM dd, yyyy h:mm a (date-fns) other β String(v) with placeholder for null/empty Caller-provided formatDisplay always wins. - New optional `defaultSensitivity` prop inherited from UseFieldPermissionArgs β forwarded to useFieldPermission so schema-generated cells can opt out of the fail-safe. **hooks/useFieldPermission.ts** - UseFieldPermissionArgs gains optional `defaultSensitivity`. Threaded into classifyField. Added to useMemo deps so re-renders pick it up. **utils/fieldSensitivity.ts** - classifyField signature: `(objectType, fieldName, defaultSensitivity?)`. Defaults to 'sensitive' (fail-safe, unchanged for existing callers). When defaultSensitivity === 'safe' and the pair isn't classified, returns `{ sensitivity: 'safe' }` (no lockReason β the cell edits freely). Explicit entries in FIELD_CLASSIFICATIONS always win β a schema-generated cell on StageName still reads 'sensitive' even when the caller passed defaultSensitivity: 'safe'. Inline comment documents the rationale. Scope fence β NOT doing ----------------------- - Thin cell wrappers (TextCell, CurrencyCell, PercentCell, DateTimeCell, BooleanCell, EmailCell, PhoneCell, URLCell) β the plan lists these as "optional". schemaColumns uses InlineEditable directly (not through wrappers), so adding 8 wrapper files would be dead code until some future hand-coded page needs them. Skip until actual demand. - Multi-select picklist variant β acceptable MVP downgrade per the plan; multipicklist fields will render via `select` with single selection until a separate follow-up. Tests ----- - fieldSensitivity.test.ts: +4 tests under 'defaultSensitivity override for unclassified pairs' β unknown+'safe' β safe, unknown+omitted β sensitive fail-safe, explicit sensitive overrides 'safe' default, explicit safe overrides 'sensitive' default. 39 pass total. - InlineEditable.test.tsx: 17 pass. Existing tests cover the 5 original variants; new variants will get coverage when a consumer (schemaColumns in #7 or a hand-coded cell) exercises them. - tsc --noEmit β clean. Part of mega-B rollup targeting dev. Commit 7 (schemaColumns upgrade) depends on this commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jpb33333
marked this pull request as ready for review
April 22, 2026 21:36
Root cause (verified at MUI source, not speculation): GridCell attaches
onMouseDown β publishEvent('cellMouseDown') at node_modules/@mui/x-data-grid/
modern/components/cell/GridCell.js:320. useGridFocus.js:268 stores the cell
params on mousedown; its document-level mouseup listener (line 271-312) calls
setCellFocus β apiRef.current.forceUpdate() (line 73), shifting DOM focus to
the cell wrapper. That steals focus from our autoFocus'd TextField inside
renderCell, triggering onBlur={handleSave} with draft===value β silent revert
to display mode. User perception: clicking an Amount/Probability cell does
nothing.
Select/autocomplete variants already worked because their Menu/Popover is
anchored outside the cell DOM, so focus steal doesn't affect them.
Fix: stopPropagation on the outer Box onMouseDown when in display mode and
the field is editable. Neutralizes DataGrid's path; DataGrid's "mouseup
target inside focused cell" branch (useGridFocus.js:291-294) handles the
mouseup without changing focus. Every text-input variant (text, number,
currency, percent, date, datetime, email, phone, url) now enters edit mode
cleanly inside a DataGrid cell.
17/17 InlineEditable tests still pass (isolated mounts don't exercise
DataGrid). JP smoke-tests Amount/Probability/Name/CloseDate on Details tab
post-deploy; if anything surprises, #7 is paused and we re-diagnose.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the backend's _enforce_record_ownership helper (main.py:862-923) on
the frontend so schema-driven cells on Accounts/Contacts/Tasks/Activities
surface the correct permission state instead of letting the user click,
type, tab out, then see a sanitized "Failed to update" toast for records
they don't own.
useFieldPermission gains an optional ownerGate: { rowOwnerId, editAllPermission? }
arg. When set, the hook resolves three bypasses in order:
1. isAdmin (manage_users_roles)
2. editAllPermission match (e.g. 'edit_all_opportunities')
3. rowOwnerId === sfUserId
If none match, the hook returns recordLockedByOther=true with a stable
"Only the record owner (or an admin) can edit this field." tooltip.
InlineEditable's existing canEditAtAll = !readOnly && !lockedFromRecord
logic then short-circuits handleDisplayClick β clicks are inert, no save
ever attempted, no surprise error toast.
Fail-safe semantics match the backend: missing sfUserId or missing
rowOwnerId both deny (cannot evaluate ownership β deny, safer than permit,
matches main.py:907-912). Since every Account/Contact/Task/Opportunity SOQL
select already fetches OwnerId (salesforce_schema.py ACCOUNT_FETCHED_FIELDS,
CONTACT_FETCHED_FIELDS + main.py:1432 task select), rowOwnerId will be
populated in practice.
Additive: existing call sites that omit ownerGate see no behavior change.
Schema-driven cells (commit #7 next) pass it per-row.
4 new InlineEditable tests exercise each bypass branch (non-owner denies,
admin allows, edit-all permission allows, owner allows). 21/21
InlineEditable tests + 39/39 fieldSensitivity tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewrites schemaColumns.tsx (267 β 387 LOC) to emit an <InlineEditable>
renderCell for every SF-updateable field instead of setting DataGrid's
native `editable: true` + renderEditCell. Reference fields targeting
Account / User use the mature inline-edit AccountCell / OwnerCell (which
consume preloaded accounts[] / users[] passed via options, avoiding the
per-row fetch-on-mount pattern of the old EditCells). Other reference
types (Contact, Lead, polymorphic WhatId / WhoId) stay display-only;
editing those fields requires opening the entity dialog. Address compound
fields render as a comma-joined string (edit via dialog per-component).
New SchemaColumnOptions:
- entityType (required) β 'Account' | 'Contact' | 'Task' | 'Activity'
- onSaveField (required) β (recordId, field, value) => Promise<void>
- canEditObject (required) β page-level permission gate
- sfUserId (required) β current user's SF user ID from usePermissions()
- editAllPermission (optional) β for Opp/Payment 'edit_all_*' bypass
- accounts (optional) β preloaded Account[] for reference autocomplete
- users (optional) β preloaded User[] for Owner autocomplete
- forceHide / overrides β preserved from the old signature
Each emitted InlineEditable receives:
- defaultSensitivity='safe' β trust SF updateable + server-side FLS.
Explicit classifications in fieldSensitivity.ts still win (e.g.
Opportunity.StageName stays sensitive). See the top-of-file invariant
comment β flipping this back to 'sensitive' would lock ~100 fields on
first click.
- readOnly driven by !canEditObject || !field.updateable || field.calculated
- ownerGate={{rowOwnerId, editAllPermission}} β per-row owner check
mirroring _enforce_record_ownership (main.py:862). Non-owner clicks
no-op without hitting the API.
- required-field client validator when field.nillable === false, to
prevent round-tripping a REQUIRED_FIELD_MISSING that the backend
sanitizes to a generic "Failed to update X".
Call-site updates on Accounts / Contacts / Tasks:
- Pull sfUserId from usePermissions() (already exposed).
- Add useQuery('users', β¦, staleTime: 10min) for Owner autocomplete.
Tasks.tsx additionally gains useQuery('accounts', β¦) for WhatId.
- Replace the old handleCellEdit (processRowUpdate-based newRow/oldRow
diff) with a handleSaveField(recordId, field, value) that dispatches
to apiService.updateAccount / updateContact / updateTask. Errors
rethrow so InlineEditable surfaces them inline (primitive's try/catch
at InlineEditable.tsx:220-227).
- Remove DataGrid's editMode="cell" / processRowUpdate /
onProcessRowUpdateError / isCellEditable props β InlineEditable owns
the flow end-to-end.
AccountCell + OwnerCell gain an optional ownerGate prop forwarded verbatim
to InlineEditable (two lines per cell). Hand-coded call sites in
Opportunities/columns.tsx still omit it and keep their existing per-page
lockMap gating; schema-driven cells pass it per-row.
New frontend/src/utils/schemaColumns.test.tsx (18 tests):
- 11 invariant tests (SYSTEM_FIELDS filter, dotted pseudo-field skip,
forceHide, alphabetical sort, number/currency/percent/date/datetime/
boolean/multipicklist type mapping, per-field overrides merged).
These lock pre-rewrite semantics.
- 7 new-behavior tests (renderCell emits InlineEditable, onSaveField
fires with (id, field, value), non-owner blocked via ownerGate,
referenceβAccount β AccountCell, referenceβUser β OwnerCell,
calculated/readOnly fields stay non-editable).
Verification: 106/106 tests pass across schemaColumns + InlineEditable +
fieldSensitivity + PipelineFunnel. Full suite: 453/460 pass. The 7
remaining failures are pre-existing date-dependent tests in files this
commit does not touch β 4 calculatePaymentDate DST in Opportunities/
helpers.test.ts (PINNED), 3 in weeklyPrioritiesHelpers.test.ts +
WeeklyPriorities.test.tsx that hardcode "30-day window starting March 16"
against today's date (2026-04-23). Unrelated to this commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
β¦_Primary_Affiliation__c, Activity block Adds explicit sensitivity entries on top of the defaultSensitivity: 'safe' fall-through used by schema-driven cells (buildSchemaColumns, mega-B #7). Per the plan's "conservative additions only" rule: err toward safe, declare sensitive only where fat-finger risk is real or where a change cascades into downstream rollups / household logic. Account (12 β 13 entries): - NumberOfEmployees β sensitive. Feeds segmentation reports. Contact (8 β 9 entries): - npsp__Primary_Affiliation__c β sensitive. Reassigning rewrites the contact's household membership and recomputes npo02__ rollups at the new affiliation's Account. Task: no additions (current classifications already correct). Activity (NEW block, 8 entries): - Subject, Status, Priority, ActivityDate, Description β safe - OwnerId β sensitive (accountability reassignment) - WhatId β sensitive (parent record linkage rewrite) - WhoId β sensitive (related contact linkage) Used by the new Activities page (mega-B #9) which runs buildSchemaColumns with entityType: 'Activity'. fieldSensitivity.test.ts gains 4 new assertions + one it.each block over the 5 safe Activity fields β 48/48 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
JP decided on 2026-04-23 to expand scope beyond the original mega-B #9 plan: instead of a simple Activities page that edits bedrock.activity locally, implement full two-way sync so inline edits reflect back to the underlying SF Task / Event. SF-first PATCH semantics (SF is authoritative; on SF failure, no local write). Blocks the Activities page and the Leads tab replacement. This doc captures the phased plan and the design decisions I need Jac's input on: Phase 0: field catalog β enumerate every Task/Event SF field Pursuit actually uses (describe + custom fields), including the new bedrock column name, Postgres type, and lossy-conversion notes. Blocks Phase 1. Phase 1: backend migration + sync engine expansion. New columns on bedrock.activity; _map_sf_task + _map_sf_event populate them; _upsert_activity INSERT/UPDATE lists expand. Backfill via sync_activities --force-full. Phase 2: PUT /api/activities/{uuid} rewritten for SF-first semantics. Translates SF-field-named payload β bedrock columns; runs _enforce_record_ownership on the SF record; PATCHes SF first; mirrors to bedrock on success. Phase 3: Activities page (mega-B #9) uses buildSchemaColumns with a synthesized Task+Event SF schema. Phase 4: wire into Details.tsx, replace Leads tab, add the redirect. Phase 5 (mega-B #10 + #11 + #12): RowCountCaption + Activities tab on AccountEditDialog. Open questions flagged for JP + Jac: custom fields, recurrence handling, RT-specific picklist values, contact_ids[] scalar-vs-list, Event-vs-Task field scope at save time, Bulk API quota for backfill. Today's branch state: 10 commits on feat/megaB-lane-a-tail (6 mega-B baseline + #7a, #7b, #7, #8). Parked until the activity-sync sprint completes per JP's 2026-04-23 "park and expand" direction. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
DO NOT MERGE. This branch is parked pending the activity-sync expansion sprint (per JP's 2026-04-23 decision β see below).
cc @dkydkydky @jacrev-pursuit β review asks below.
What's on the branch
11 commits on
feat/megaB-lane-a-tail:Already-landed mega-B baseline (6):
9bf0081feat(progress): split Pipeline Flow into Active funnel + Closed section (A5)e9171bdfeat(inline-edit): cap dropdown height + align Popover anchor pair (A8)44875d1chore(sensitivity): soften Opportunity Amount + Probability to safe (A9)f59ed45feat(inline-edit): dark-mode fill affordance in editing mode (A10 affordance)74713fbaudit(opp): inline-edit on Name + CloseDate across Pipeline + Payment tabs (A10)ea4db83feat(inline-edit): full SF variant palette + defaultSensitivity safeguard (A10 docs: align all product docs with two-path Slack + Automation Review β¦Β #6)New this session (5):
7124853fix(inline-edit): stop DataGrid cellMouseDown from stealing focus(#7a) β root-caused JP's Amount/Probability bug at MUI's own source (GridCell.js:320 + useGridFocus.js:268/73). One-linestopPropagationon mousedown fixes every text-variant inline-edit cell inside a DataGrid.afb608cfeat(inline-edit): per-row ownerGate for schema-driven cells(#7b) β mirrors backend_enforce_record_ownership(main.py:862) on the frontend. Non-owner clicks no-op instead of hitting the API and getting a sanitized "Failed to update".1426fadfeat(schemaColumns): emit InlineEditable renderCells with ownerGate(Add test coverage analysis with prioritized improvement recommendationsΒ #7) β rewrites the 267-LOC utility to emit<InlineEditable>renderCells for every SF-updateable field. Reference fields target Account / User via the mature AccountCell / OwnerCell (preloaded options from page state). Accounts / Contacts / Tasks call-sites updated; oldeditMode="cell"+processRowUpdateremoved. NewschemaColumns.test.tsxwith 18 tests (11 invariants + 7 new-behavior).37ac68dchore(sensitivity): classify Account.NumberOfEmployees, Contact.npsp__Primary_Affiliation__c, Activity block(Add giving capacity calculator with 990 enrichment and conversion gateΒ #8) β 4 new classifications, full Activity block added.d9b58f0docs(activity-sync): design doc for two-way bedrock.activity β SF syncβ phased plan, open questions for Jac + Dave, risks, Jac-review checklist.Why it's parked
JP decided on 2026-04-23 to expand mega-B #9 (Activities page replacing the Leads tab on
/details) from a local-only edit flow into a full two-way sync:/api/activitieseditsbedrock.activity(local Postgres, UUID ids, snake_case fields) and the SF sync is one-way (SF β bedrock). Local edits silently diverge from Salesforce.bedrock.activity, backfill from SF, expanded sync engine (data_sync.py:sync_activities), new PUT handler with SF-first semantics +_enforce_record_ownershipon the SF record, then the frontend Activities page usingbuildSchemaColumnson a synthesized Task+Event SF schema.Review asks
@jacrev-pursuit (Jac)
Please review
tasks/activity-sync-expansion.md(fromd9b58f0). Specifically:__ccustom fields? I can rungetSchemaDescribe('Task')+getSchemaDescribe('Event')to draft the first pass, but you're the source of truth for which fields matter to the team.OPPORTUNITY_STAGES.updateable: truebut the current page layout marks a field readonly. SF accepts the API write anyway β do we want that, or should we suppress client-side too?@dkydkydky (Dave Yang)
Please review the design in
tasks/activity-sync-expansion.mdand the 4 in-scope commits (7124853,afb608c,1426fad,37ac68d) for:ownerGateinuseFieldPermission(afb608c) β mirrors_enforce_record_ownershipon the client. Fail-safe semantics: missing sfUserId or rowOwnerId both deny. Any concerns with the double-enforcement (client + server)?defaultSensitivity: 'safe'in schemaColumns (1426fad) β every schema-generated cell defaults to safe so we don't demand an unlock confirmation on 100+ fields. Invariant documented at the top ofschemaColumns.tsx. Explicit classifications infieldSensitivity.tsalways win. Comfortable with the trade-off?How to look at the code
git fetch origin && git checkout feat/megaB-lane-a-tailβ themegaBworktree ergonomics are intasks/activity-sync-expansion.md(JP's setup).Smoke-test status
tsc --noEmitβ clean.calculatePaymentDateDST + 3weeklyPrioritiesHelpers/WeeklyPrioritieshardcoded-date tests β all date-arithmetic bugs, unrelated).Pending (held until activity-sync ships)
π€ Generated with Claude Code