test(wfs): WFS read/write parity characterization tests (Decisions 1–9) run as a real manager persona - #377
Open
overfullstack wants to merge 61 commits into
Open
test(wfs): WFS read/write parity characterization tests (Decisions 1–9) run as a real manager persona#377overfullstack wants to merge 61 commits into
overfullstack wants to merge 61 commits into
Conversation
overfullstack
force-pushed
the
wfs/decision-1-9-revoman-tests
branch
3 times, most recently
from
July 1, 2026 05:40
3078406 to
5f9e635
Compare
…; login-as-sysadmin step)
…red-no-primary control
… javadoc for all 6 decisions
…) per final review
… admin alias)
The WFS parity acts already declared {{managerToken}}, but auth/login-as-sysadmin
aliased managerToken=adminToken, so every API-under-test call ran as admin. Mint a
real least-privilege WorkforceScheduling manager session instead and make it own the
policies + fixtures it books/reads against, matching Decision 9.
- auth/login-as-sysadmin: stop aliasing managerToken; set admin/access only and
unset managerToken so a broken manager login fails loud (no silent admin fallback).
- New auth/set-manager-password (17500) + auth/login-as-manager (18000, v64 SOAP)
after create-manager (17000): the manager gets its own session token.
- Point the 4 straggler acts at {{managerToken}} (2 Decision-8 get-available-resources
+ 2 Decision-3 schedule). skills-non-required-skill stays {{adminToken}} (Skill is a
setup object the manager can't create).
- Doc-only: fix two stale collection descriptions, de-dup an auth id, refresh the
AUTH_CONFIG javadoc, and add agent-readable descriptions to the collection folders.
- Enable both test classes (run manually against a provisioned WFS workspace org).
- Add the WFS parity test plan under docs/superpowers.
All 10 scenarios re-verified live as the manager persona; every verdict unchanged
(behaviors are caller-independent). Test logic untouched.
Investigation-grounded (5 read-only Core probes): 4=clean INVALID_INPUT input-validation reject; 4z=doc is WRONG (reschedule CAN leave no primary; the 'isPrimaryResource cannot be set for Delete' error fires only when a DeleteOperation entry carries the flag); 5=persist INVALID_FIELD reject, no auto-correct, no double-book; 2=field-match half not characterizable on 262 (OnField/ESO black box, OnSite shares read==write) so test the verified cheap-check promise.
…rrect/double-book)
…s doc) Arm A: isPrimaryResource on a DeleteOperation entry -> INVALID_INPUT/400 'isPrimaryResource cannot be set for delete.' (validateDeleteOperationFields payload-field guard). Arm B: delete the primary, no flag -> the primary-count validator allows zero primaries (validatePrimaryResourceCount only throws MultiplePrimary when primaryCount > 1), so there is NO no-primary rule -- which refutes the product doc. LIVE-OBSERVED on the 262 org: Arm B is rejected only by a downstream availability re-check (INVALID_INPUT 'The service resources are not available for the requested slot.' / SlotNotAvailable), never by a primary rule; 262 lacks the empty/no-op-reschedule availability short-circuit added in 264 (precommit 58140158). Characterized faithfully (availability, not no-primary) rather than forcing a Success the 262 org cannot produce. Clean two-resource schedule captures reschedCleanSaId for both arms.
…ision (final-review minors)
…roofs) All 4 read APIs (get-slots/candidates/available-slots/available-resources) + 2 write APIs (schedule/reschedule) share the loadSchedulableSlots engine over the 7 Common+InBusiness rules. Differential agreement matrix asserts read==write per rule + surfaces the 2 genuine divergences (reschedule no-op short-circuit; RequiredResources 262 NPE). onField/inField rules out of scope. jdwp double-assert where API verdict is ambiguous.
…v carry ReVoman.revUp accumulates env between kicks via mutableEnvCopyWithValuesOfType<String>() (ReVoman.kt:96) — only String values survive kick→kick. Four afterResponse scripts stored counts as raw JS numbers, so a count set in a non-last kick was dropped before the final rundown; getAsString returned null → testResourceLimitApptDistributionCapE2E failed (expected: 0 but was: null). Coerce with String(...) to match the parityRead* sibling idiom; also fixes the latent break in testShiftSharingModeSplitE2E (dec9Manager/CaseWorkerSlotCount). Verbose integrationTest logging: log4j2 com.salesforce.revoman → DEBUG (override with -Dlog.revoman.level=INFO; okhttp3/graalvm pinned WARN) + testLogging.showStandardStreams so the per-step execution trace streams live instead of burying in the stdout capture file.
Lift visiting-hours-op-hours-policy + visiting-hours-account-oh from source and conform to 262: strip WorkType SchedulingMethod/IsRegular, add ShiftUsage=Union param, link Account.OperatingHoursId via an admin-token PATCH step (WorkforceSchedulingManager has read-only FLS on that field). 4 acts prove a window OUTSIDE the account visiting hours (10-14) is pruned read (0 slots) AND rejected write, while an in-hours control returns >0 AND Success. Live PASS on 262.
…@tests; blank committed creds
…gine NPE) Plan hypothesized a read-prunes/write-crashes DIVERGENCE; live evidence refutes it. The READ path (GetAppointmentSlots) crashes with the SAME serviceTerritoryMembers NPE (HTTP 500 INTERNAL_SERVER_ERROR) the write path throws, because both share InBusinessGetCandidatesSlotsDataService.loadSchedulableSlots — so the 262 RequiredResources bug is read==write (a shared-engine crash), NOT a divergence. Control (resourceB required → demand satisfied) does not crash (HTTP 201, 0 slots), proving the crash is conditional on the non-required-helper input.
…valuating APIs) One MatchSkills violation (a required resource lacking the WorkType's skill) run through all 4 rule-evaluating read APIs (get-appointment-slots/candidates/available-slots/available-resources) plus the schedule write, asserting unanimous pruning/rejection — empirically proving they share the one loadSchedulableSlots engine so the per-rule read==write matrix generalizes to every API. Discovered live (2026-07-01): - get-appointment-candidates + get-available-slots return result[].candidates[] (not slots[]); get-available-slots with assignedResources dispatches to get-appointment-slots then converts. - get-appointment-candidates rejects isPrimaryResource in assignedResources (INVALID_INPUT 'isPrimaryResource is not supported.') so its body keeps only isRequiredResource; MatchSkills then prunes to 0 candidates (201). - get-available-resources runs the FULL 7-rule engine (getCandidatesProcessor.process): it takes no assignedResources and returns EVERY available resource, so the skilled resourceA survives while the unskilled resourceB is pruned/ABSENT — confirming it is NOT a rule-skipping subset. Reuses the Task-1 skills fixture/policy + the existing get-slots/schedule violating acts.
…PI self-defending)
…recompute NPE) Plan premise (no-op reschedule returns Success via SlotAvailabilityChecker:174-176 short-circuit) REFUTED by live+jdwp evidence. For a required-resource SA the short-circuit's resourcesHaveChanged is ALWAYS true — the existing SA's required ServiceResourceId set (18-char from SOQL) never equals the request's (15-char after the ESO request DTO truncation), so the branch is unreachable over REST. The reschedule then recomputes and 262 crashes HTTP 500 INTERNAL_SERVER_ERROR (ServiceTerritory.getServiceResourceIds() NPE) — a third 262 reschedule-recompute NPE variant. Test asserts the observed 500/NPE, not the refuted Success premise.
Each Decision-N scenario in the parity report now names the exact test method that pins it (WfsWritePathParityE2ETest / WfsReadPathParityE2ETest), plus a Test-method column in the summary table. Several doc scenarios (1a-d, A/B control pairs) collapse into a single method with multiple dimensions/arms; the inline tags make that mapping explicit.
…ash pinned, 15/18 cause open) Review flagged the 15-char-truncation mechanism claim as unverified and in tension with the fixture's 18-char wire id (0Hnxx...FrgCAE is 18 chars). The CRASH (HTTP 500 getServiceResourceIds NPE) is live-proven and remains asserted; the WHY-short-circuit- didn't-fire mechanism is downgraded to a flagged-open jdwp question. Assertions unchanged.
…+ setup act no-op prose
…t-circuit unreachable) jdwp breakpoint at SlotAvailabilityChecker:237 on a live no-op reschedule: existing SA required-resource id set = [0Hnxx0000004GB2CAM] (18-char, SOQL) vs request set = [0Hnxx0000004GB2] (15-char, ESO request DTO truncation) → equals=false → resourcesHaveChanged=true → short-circuit skipped → recompute → 262 crash. Confirms the mechanism (was softened to open); the repo's recurring 15/18-char ResourceId gotcha surfacing in the reschedule short-circuit comparison.
…4/4z/5 + rules-parity suite) Adds the WfsRulesParityE2ETest suite (8 tests) + Decisions 2/4/4z/5 to the report; records the headline (read==write holds, both predicted divergences refuted; get-available-resources = full engine not subset), the 3 262 slot-gen NPE bugs + 4z doc bug (handed off), and the jdwp-confirmed 15/18-char no-op-reschedule mechanism. Refreshes the at-a-glance table (28 rows) and coverage notes.
…adability review) Per readability review: added a 1-minute summary + plain-language glossary (defines 262/264, read/write, characterization test, crash vs rejected, etc.); de-jargoned the at-a-glance table into plain outcomes with a note that green = 'faithfully recorded' (not 'healthy', since some rows are pinned crashes); replaced code identifiers, error codes, and logic symbols (⟺/≠/==) throughout with plain English; unpacked the 15/18-char record-ID mechanism in words. Test-method names kept as code-font anchors for engineers. Content unchanged — readability only.
…n, highlight 4z deviation New section compares our live tests to the team's earlier manual observations (fetched from the shared docs): 4 and 5 CONFIRMED; 2 confirmed in part (field-match half not testable on 262); 4z is the one real DEVIATION — the observation's 'cannot reschedule without a primary' conclusion is wrong (validation allows zero primaries; the quoted error is a narrow field rule; the real stopper is an availability check) = doc bug. Also renamed all 'product doc' references to 'manual observations from the team' per user (these are team notes, not product docs).
…is on 262 New reschedule-delete-all act deletes BOTH assigned resources (no isPrimaryResource flag) to probe whether an empty-crew reschedule gets around the availability check and leaves a no-primary appointment (the end-to-end 4z proof Arm B could not deliver). LIVE-OBSERVED on 262: it does NOT. The delete-all reschedule is rejected with the SAME downstream availability re-check (INVALID_INPUT / HTTP 400 / SlotNotAvailable 'The service resources are not available for the requested slot.'), schedulingStatus null — no Success, no no-primary SA. The empty-effective-set short-circuit the Core func test testRescheduleAppointmentDeleteAllAssignedResources relies on is 264-only. Characterized faithfully as the observed rejection rather than a forced Success. New test method testRescheduleDeleteAllLeavesNoPrimaryE2E passes live.
…a-layer detail + Dec-9 sharing tension - 4z CORRECTION: earlier draft said 'delete every worker' gets around the availability check today — live test (testRescheduleDeleteAllLeavesNoPrimaryE2E) proves it does NOT on 262; all 3 removal paths are blocked by the same availability check. The empty-crew short-circuit is 264-only. Doc's conclusion = right outcome today, wrong reason, wrong next release. Added 4z-C table row. - Dec 5: added where the refusal happens — a DATA-LAYER save hook on the AssignedResource entity (fires on every write path, not just this API), gated by the multi-resource org pref. - Dec 9: added the case-worker tension — no read/write contradiction (can't book what you can't see; write reuses the same reads); the real issue is half-applied sharing within one operation. Added both product options (surface a reason / align the reads).
…ary is BLOCKED by availability (not crash, not persist) The under-guarded ≥2-workers-ZERO-primary shape the sibling delete arms never build (they shrink the crew to 1 or 0). New reschedule-demote-primary-two-crew act reschedules the clean two-resource SA with NO time change and TWO UpdateOperation entries flipping BOTH resourceA and resourceB to isPrimaryResource=false (isRequiredResource=true kept): effective crew = 2 workers, 0 primaries. LIVE-OBSERVED on 262: NOT Success and NOT a crash — rejected by the SAME downstream availability re-check (SlotNotAvailable) that blocks the delete-primary Arm B and the delete-ALL probe: INVALID_INPUT / HTTP 400 / top-level 'The service resources are not available for the requested slot.', schedulingStatus null. Does NOT trip the 15/18-char record-id resourcesHaveChanged bug (availability rejects first) and does NOT persist a no-primary crew. Doc still refuted at the validation layer (validatePrimaryResourceCount allows zero primaries); the availability rejection is a separate 262-only downstream gap (264's effective-set merge would find the surviving-crew slot). Asserted as OBSERVED.
…ilability, not crash/persist) testRescheduleDemotePrimaryTwoCrewNoPrimaryE2E: demote-to-2-workers-no-primary on 262 is BLOCKED by the availability check (HTTP 400 SlotNotAvailable) — corrects the earlier reasoned guess (crash). No no-primary crew persists today; the gap is that nothing GUARDS it (only availability blocks it), so the next-release final-crew rule is still needed.
…orrections Relocated from the scheduler-parity branch where a git add -A had inadvertently swept this WFS work. Adds testRescheduleDeleteAllWithPolicySucceedsE2E + reschedule-delete-all-with-policy collection, and 264-contrast doc corrections across the WFS parity report + test javadocs.
overfullstack
force-pushed
the
wfs/decision-1-9-revoman-tests
branch
from
July 6, 2026 07:31
4575c0e to
8f1f8da
Compare
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.
What
Off-core ReVoman integration tests that characterize live Salesforce 262 Workforce Scheduling behavior across six read/write parity decisions about how a non-required "helper" resource is treated (plus a read-path result cap and a shift-sharing gate). Each test asserts what 262 actually does today — including two genuine server crashes — with the 264 expectation documented per scenario, so the suite flips and alerts when 264 changes a behavior.
Two test classes supersede the old
WfsHelperFitnessE2ETest/WfsDoubleBookHelperE2ETest:WfsWritePathParityE2ETest— Decisions 1 (4 dims), 1.4, 1.5, 3.WfsReadPathParityE2ETest— Decisions 8, 9.The headline of the latest commit: real manager persona
The API-under-test calls for Decisions 1/1.4/1.5/3/8 now run as a real least-privilege Workforce Scheduling manager (its own SOAP session) instead of an admin-token alias — matching Decision 9, which always used real personas. The manager owns the policies + fixtures it books/reads against (a manager can't see another user's private shifts, so booking against admin-owned data would hit the very sharing gate Decision 9 is about).
auth/login-as-sysadminno longer aliasesmanagerToken=adminToken; it sets admin/access only andunsetsmanagerTokenso a broken manager login fails loud (no silent admin fallback).auth/set-manager-password+auth/login-as-manager(v64 SOAP) mint the real manager session aftercreate-manager.{{managerToken}}(2 Decision-8 + 2 Decision-3).skills-non-required-skillstays{{adminToken}}(Skill is a setup object the manager can't create).Decisions verified (live, as the manager persona — every verdict unchanged)
The two crashes (1.4, 3) are known 262 product defects, pinned down verbatim so the suite alerts the moment 264 fixes them.
Notes for reviewers
docs/superpowers/2026-06-30-wfs-parity-test-report.mddocuments every scenario (setup → rule isolated → exact assertion → recorded result) plus honest coverage gaps.