{"id":5988,"date":"2026-10-12T02:57:40","date_gmt":"2026-10-12T05:57:40","guid":{"rendered":"https:\/\/tucumandevelopers.com\/index.php\/2026\/10\/12\/an-overlap-contract-before-any-shift-helper-split\/"},"modified":"2026-10-12T02:57:40","modified_gmt":"2026-10-12T05:57:40","slug":"an-overlap-contract-before-any-shift-helper-split","status":"publish","type":"post","link":"https:\/\/tucumandevelopers.com\/index.php\/2026\/10\/12\/an-overlap-contract-before-any-shift-helper-split\/","title":{"rendered":"An Overlap Contract Before Any Shift Helper Split"},"content":{"rendered":"<div>\n<div><\/div>\n<p>Read the comparisons before you rename any local symbol. The end minute is exclusive because abutting spans yield a zero raw window. Break credit is subtracted after the window, and it can erase a real overlap.<\/p>\n<p>Both of those rules stay intact in this pass. Do not replace the comparison if with a max call. Do not swap exclusive ends for inclusive ends.<\/p>\n<h2> <a name=\"numbered-workflow\" href=\"#numbered-workflow\"> <\/a> Numbered workflow <\/h2>\n<h3> <a name=\"1-name-the-axes-before-you-add-rows\" href=\"#1-name-the-axes-before-you-add-rows\"> <\/a> 1. Name the axes before you add rows <\/h3>\n<p>Write the three axes on one line in the test module. They are edge contact, midnight wrap, and break credit. A row that does not hit an axis does not earn a slot yet.<\/p>\n<h3> <a name=\"2-derive-booleans-from-the-listing-not-from-hope\" href=\"#2-derive-booleans-from-the-listing-not-from-hope\"> <\/a> 2. Derive booleans from the listing, not from hope <\/h3>\n<p>Compute each expected value with the same operators as the function. If a result differs from the hoped policy, record the current result. Do not correct the table during this characterization pass.<\/p>\n<h3> <a name=\"3-keep-one-assertion-per-case-id\" href=\"#3-keep-one-assertion-per-case-id\"> <\/a> 3. Keep one assertion per case id <\/h3>\n<p>Parameterize the rows so a failure prints the case id. Avoid a loop that catches errors and continues. A swallowed miss lets the helper land anyway.<\/p>\n<h3> <a name=\"4-run-the-suite-and-store-the-summary\" href=\"#4-run-the-suite-and-store-the-summary\"> <\/a> 4. Run the suite and store the summary <\/h3>\n<p>Run the listed pytest command before any edit. Store the passed count and the six case ids. The after-run must match that stored summary.<\/p>\n<h3> <a name=\"5-extract-only-the-raw-window-math\" href=\"#5-extract-only-the-raw-window-math\"> <\/a> 5. Extract only the raw window math <\/h3>\n<p>Move latest-start and earliest-end math into one helper. Leave break credit and the final comparison in overlaps. Do not rename fields, change 1440, or reorder arguments.<\/p>\n<h3> <a name=\"6-rerun-then-stop-the-pass\" href=\"#6-rerun-then-stop-the-pass\"> <\/a> 6. Re-run, then stop the pass <\/h3>\n<p>Re-run the same command on the same tree. If any case id flips, revert the helper. Do not edit the expected boolean to match a new diff.<\/p>\n<h2> <a name=\"hand-check-for-one-row\" href=\"#hand-check-for-one-row\"> <\/a> Hand check for one row <\/h2>\n<p>Use the one_minute_cross row as the hand check. Left span stays 540 to 601 because 601 exceeds 540. Right span stays 600 to 660 because 660 exceeds 600.<\/p>\n<p>Latest start is 600 because 600 exceeds 540. Earliest end is 601 because 601 is below 660. Raw window is 601 minus 600, which equals 1.<\/p>\n<p>Credit is 0 plus 0, so net stays 1. The boolean is true because net is greater than 0. Repeat this arithmetic for the other five rows before you commit the test file.<\/p>\n<h2> <a name=\"outcome-table-derived-from-the-listing\" href=\"#outcome-table-derived-from-the-listing\"> <\/a> Outcome table derived from the listing <\/h2>\n<p>These six booleans follow from the operators above. Treat them as a worksheet, then recompute them locally. A mismatch means the worksheet is wrong, not that the function moved.<\/p>\n<div>\n<table>\n<thead>\n<tr>\n<th>case_id<\/th>\n<th>left start, end, break<\/th>\n<th>right start, end, break<\/th>\n<th>raw<\/th>\n<th>credit<\/th>\n<th>overlaps<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>abut_exclusive<\/td>\n<td>540, 600, 0<\/td>\n<td>600, 660, 0<\/td>\n<td>0<\/td>\n<td>0<\/td>\n<td>false<\/td>\n<\/tr>\n<tr>\n<td>one_minute_cross<\/td>\n<td>540, 601, 0<\/td>\n<td>600, 660, 0<\/td>\n<td>1<\/td>\n<td>0<\/td>\n<td>true<\/td>\n<\/tr>\n<tr>\n<td>wrap_vs_morning<\/td>\n<td>1380, 60, 0<\/td>\n<td>0, 30, 0<\/td>\n<td>-1350<\/td>\n<td>0<\/td>\n<td>false<\/td>\n<\/tr>\n<tr>\n<td>wrap_vs_wrap<\/td>\n<td>1320, 60, 0<\/td>\n<td>1380, 90, 0<\/td>\n<td>120<\/td>\n<td>0<\/td>\n<td>true<\/td>\n<\/tr>\n<tr>\n<td>break_cancels_window<\/td>\n<td>540, 600, 30<\/td>\n<td>570, 630, 30<\/td>\n<td>30<\/td>\n<td>60<\/td>\n<td>false<\/td>\n<\/tr>\n<tr>\n<td>separated<\/td>\n<td>540, 600, 0<\/td>\n<td>660, 720, 0<\/td>\n<td>-60<\/td>\n<td>0<\/td>\n<td>false<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>The wrap_vs_morning row is the likely premature policy edit. Under this listing the morning span stays on day zero. The wrapped span lives at 1380 to 1500, so the windows miss.<\/p>\n<p>Characterization keeps that miss visible in the table. The break_cancels_window row shows a second preserved result. Thirty minutes of raw overlap disappear because both breaks are summed.<\/p>\n<p>The sum is larger than the window, so the boolean is false. A fairer credit rule belongs in a later pass. Leave that credit rule untouched in this pass.<\/p>\n<h2> <a name=\"test-file-that-encodes-the-table\" href=\"#test-file-that-encodes-the-table\"> <\/a> Test file that encodes the table <\/h2>\n<p>The test file below is a proposed artifact only. It has not been executed for this draft. Point it at the module only after you recompute the table. <\/p>\n<div>\n<pre><code><span>import<\/span> <span>pytest<\/span> <span>from<\/span> <span>shifts<\/span> <span>import<\/span> <span>Shift<\/span><span>,<\/span> <span>overlaps<\/span> <span>CASES<\/span> <span>=<\/span> <span>[<\/span> <span>(<\/span><span>\"<\/span><span>abut_exclusive<\/span><span>\"<\/span><span>,<\/span> <span>Shift<\/span><span>(<\/span><span>540<\/span><span>,<\/span> <span>600<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>Shift<\/span><span>(<\/span><span>600<\/span><span>,<\/span> <span>660<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>False<\/span><span>),<\/span> <span>(<\/span><span>\"<\/span><span>one_minute_cross<\/span><span>\"<\/span><span>,<\/span> <span>Shift<\/span><span>(<\/span><span>540<\/span><span>,<\/span> <span>601<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>Shift<\/span><span>(<\/span><span>600<\/span><span>,<\/span> <span>660<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>True<\/span><span>),<\/span> <span>(<\/span><span>\"<\/span><span>wrap_vs_morning<\/span><span>\"<\/span><span>,<\/span> <span>Shift<\/span><span>(<\/span><span>1380<\/span><span>,<\/span> <span>60<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>Shift<\/span><span>(<\/span><span>0<\/span><span>,<\/span> <span>30<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>False<\/span><span>),<\/span> <span>(<\/span><span>\"<\/span><span>wrap_vs_wrap<\/span><span>\"<\/span><span>,<\/span> <span>Shift<\/span><span>(<\/span><span>1320<\/span><span>,<\/span> <span>60<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>Shift<\/span><span>(<\/span><span>1380<\/span><span>,<\/span> <span>90<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>True<\/span><span>),<\/span> <span>(<\/span><span>\"<\/span><span>break_cancels_window<\/span><span>\"<\/span><span>,<\/span> <span>Shift<\/span><span>(<\/span><span>540<\/span><span>,<\/span> <span>600<\/span><span>,<\/span> <span>30<\/span><span>),<\/span> <span>Shift<\/span><span>(<\/span><span>570<\/span><span>,<\/span> <span>630<\/span><span>,<\/span> <span>30<\/span><span>),<\/span> <span>False<\/span><span>),<\/span> <span>(<\/span><span>\"<\/span><span>separated<\/span><span>\"<\/span><span>,<\/span> <span>Shift<\/span><span>(<\/span><span>540<\/span><span>,<\/span> <span>600<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>Shift<\/span><span>(<\/span><span>660<\/span><span>,<\/span> <span>720<\/span><span>,<\/span> <span>0<\/span><span>),<\/span> <span>False<\/span><span>),<\/span> <span>]<\/span> <span>@pytest.mark.parametrize<\/span><span>(<\/span> <span>\"<\/span><span>case_id,left,right,expected<\/span><span>\"<\/span><span>,<\/span> <span>CASES<\/span><span>,<\/span> <span>ids<\/span><span>=<\/span><span>[<\/span><span>row<\/span><span>[<\/span><span>0<\/span><span>]<\/span> <span>for<\/span> <span>row<\/span> <span>in<\/span> <span>CASES<\/span><span>],<\/span> <span>)<\/span> <span>def<\/span> <span>test_overlap_contract<\/span><span>(<\/span><span>case_id<\/span><span>,<\/span> <span>left<\/span><span>,<\/span> <span>right<\/span><span>,<\/span> <span>expected<\/span><span>):<\/span> <span>assert<\/span> <span>overlaps<\/span><span>(<\/span><span>left<\/span><span>,<\/span> <span>right<\/span><span>)<\/span> <span>is<\/span> <span>expected<\/span><span>,<\/span> <span>case_id<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<h2> <a name=\"commands-to-run-before-and-after\" href=\"#commands-to-run-before-and-after\"> <\/a> Commands to run before and after <\/h2>\n<p>Use one command so the summary stays comparable. Do not mix flags between the two runs. Version wording differs across runners, so compare case ids and the passed count.<\/p>\n<p>Install pytest in that disposable repo before the first run. Pin that dependency locally, not from a version stated here. Confirm your interpreter accepts the listed type hints before running. <\/p>\n<div>\n<pre><code>python <span>-m<\/span> pytest tests\/test_shift_overlap.py <span>-q<\/span> <span>--tb<\/span><span>=<\/span>line <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Run that command on the unchanged listing first. Confirm six case ids and zero failed assertions. Only then apply the extraction in the helper section.<\/p>\n<h2> <a name=\"the-only-edit-allowed-in-this-pass\" href=\"#the-only-edit-allowed-in-this-pass\"> <\/a> The only edit allowed in this pass <\/h2>\n<p>The helper below copies the window math without changing operators. The overlaps function still applies break credit and the same check. Public names and argument order stay as they were. <\/p>\n<div>\n<pre><code><span>def<\/span> <span>raw_overlap_minutes<\/span><span>(<\/span><span>left<\/span><span>:<\/span> <span>Shift<\/span><span>,<\/span> <span>right<\/span><span>:<\/span> <span>Shift<\/span><span>)<\/span> <span>-&gt;<\/span> <span>int<\/span><span>:<\/span> <span>a0<\/span><span>,<\/span> <span>a1<\/span> <span>=<\/span> <span>_span<\/span><span>(<\/span><span>left<\/span><span>)<\/span> <span>b0<\/span><span>,<\/span> <span>b1<\/span> <span>=<\/span> <span>_span<\/span><span>(<\/span><span>right<\/span><span>)<\/span> <span>latest_start<\/span> <span>=<\/span> <span>a0<\/span> <span>if<\/span> <span>a0<\/span> <span>&gt;<\/span> <span>b0<\/span> <span>else<\/span> <span>b0<\/span> <span>earliest_end<\/span> <span>=<\/span> <span>a1<\/span> <span>if<\/span> <span>a1<\/span> <span>&lt;<\/span> <span>b1<\/span> <span>else<\/span> <span>b1<\/span> <span>return<\/span> <span>earliest_end<\/span> <span>-<\/span> <span>latest_start<\/span> <span>def<\/span> <span>overlaps<\/span><span>(<\/span><span>left<\/span><span>:<\/span> <span>Shift<\/span><span>,<\/span> <span>right<\/span><span>:<\/span> <span>Shift<\/span><span>)<\/span> <span>-&gt;<\/span> <span>bool<\/span><span>:<\/span> <span>raw<\/span> <span>=<\/span> <span>raw_overlap_minutes<\/span><span>(<\/span><span>left<\/span><span>,<\/span> <span>right<\/span><span>)<\/span> <span>credit<\/span> <span>=<\/span> <span>left<\/span><span>.<\/span><span>break_min<\/span> <span>+<\/span> <span>right<\/span><span>.<\/span><span>break_min<\/span> <span>net<\/span> <span>=<\/span> <span>raw<\/span> <span>-<\/span> <span>credit<\/span> <span>return<\/span> <span>net<\/span> <span>&gt;<\/span> <span>0<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Review the diff for operator drift before you read style notes. Keep the original comparison form even when a max call looks equal. Ties match either way, but sameness is the point of this pass.<\/p>\n<h2> <a name=\"commit-boundary\" href=\"#commit-boundary\"> <\/a> Commit boundary <\/h2>\n<p>The characterization commit should contain the tests only. The extraction commit should contain the helper move only. Two commits make a flipped row obvious during review.<\/p>\n<h2> <a name=\"decision-table-for-the-next-commit\" href=\"#decision-table-for-the-next-commit\"> <\/a> Decision table for the next commit <\/h2>\n<div>\n<table>\n<thead>\n<tr>\n<th>signal you observe<\/th>\n<th>action in this pass<\/th>\n<th>action deferred<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>six ids green, diff is extraction only<\/td>\n<td>keep the commit<\/td>\n<td>none<\/td>\n<\/tr>\n<tr>\n<td>any case id flips<\/td>\n<td>revert the helper<\/td>\n<td>inspect the operator that moved<\/td>\n<\/tr>\n<tr>\n<td>wrap_vs_morning should become true<\/td>\n<td>do not edit the pin<\/td>\n<td>new test name, new policy commit<\/td>\n<\/tr>\n<tr>\n<td>break credit should ignore minutes outside the window<\/td>\n<td>do not edit the pin<\/td>\n<td>new test name, new credit rule<\/td>\n<\/tr>\n<tr>\n<td>a new axis appears, such as timezone offsets<\/td>\n<td>stop and add a row first<\/td>\n<td>no helper split until that row is green<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>Timezone offsets are absent from the listing on purpose. Adding them inside the extraction would mix a feature with a move. That mix defeats the before-and-after summary comparison.<\/p>\n<h2> <a name=\"where-an-optional-runner-fits\" href=\"#where-an-optional-runner-fits\"> <\/a> Where an optional runner fits <\/h2>\n<p>The workflow above is complete on a local checkout. A hosted runner is optional and is not required. Disclosure: This article was prepared as part of MonkeyCode&#8217;s product outreach.<\/p>\n<p>MonkeyCode&#8217;s free model access can draft extra candidate rows from the three axes. Accept a row only after you recompute it from the listing. Reject any draft that changes an expected boolean without a new case id.<\/p>\n<p>The free server option can run the same pytest command away from your laptop. This article does not state hardware, quotas, duration, or permanence. Confirm the current terms in the product documentation before you depend on that runner.<\/p>\n<p>Do not upload production rosters, employee names, or secrets with the example. The teaching module uses bare minute integers only. Keep real schedules out of both the prompt and the remote workspace.<\/p>\n<h2> <a name=\"who-should-not-use-this-approach\" href=\"#who-should-not-use-this-approach\"> <\/a> Who should not use this approach <\/h2>\n<p>Skip the pin when the boolean is a known defect. A characterization suite will argue for that bug. Ship the policy fix under a new test name instead.<\/p>\n<p>Skip it when overlaps reads a clock, a network, or a database. Those inputs will not stay frozen across runs. Add a seam or a stub before you trust a table.<\/p>\n<p>Skip it when the extract cannot land in its own commit. Mixed commits hide a flipped row inside formatting noise. Split the commits, or do not call the edit a refactor.<\/p>\n<h2> <a name=\"limits-of-the-sixrow-gate\" href=\"#limits-of-the-sixrow-gate\"> <\/a> Limits of the six-row gate <\/h2>\n<p>Six rows cover the named axes, not every minute pair. They will not catch a change that preserves these windows and breaks another pair. Add a row when a review finds a new axis.<\/p>\n<p>The preserved break rule can erase overlap that a payroll policy would keep. This pass documents that result and does not repair it. Repair is a behavior change and needs its own table.<\/p>\n<p>No timing numbers or hosted-runner benchmarks appear in this article. The example was not executed for this draft. Your local recompute is the gate, not this page.<\/p>\n<p>If a free MonkeyCode server is already on your account, rerun pytest there. Compare case ids with the laptop summary before you trust it. Ignore any draft row you did not recompute yourself.<\/p>\n<\/p><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Fuente: <a href=\"https:\/\/dev.to\/hackrs_6393\/an-overlap-contract-before-any-shift-helper-split-2lcm\">Art\u00edculo original<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Read the comparisons before you rename any local symbol. The end minute is exclusive because abutting spans yield a zero raw window. Break credit is subtracted after the window, and it can erase a real overlap. Both of those rules stay intact in this pass. Do not replace the comparison if with a max call. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":5987,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"webixso_pending_account_ids":""},"categories":[41],"tags":[],"class_list":["post-5988","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devto"],"jetpack_publicize_connections":[],"_links":{"self":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/5988","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/comments?post=5988"}],"version-history":[{"count":0,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/5988\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media\/5987"}],"wp:attachment":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media?parent=5988"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/categories?post=5988"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/tags?post=5988"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}