fix(outcome): use latest activity for follow-up quiet days (#726)

* fix(outcome): use latest activity for followup quiet days

* docs: reference PR 726 in unreleased fix

---------

Co-authored-by: Mads Lorentzen <madslorentzen17@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-11-10 20:18:15 +01:00
1 parent 453ccb9565
commit 5da702e8de
2 files changed
+10 -1

No files matched your search

+1 -1
View File
@@ -91,7 +91,7 @@ Also collect, without interrogating - one or two open questions are enough:
Enter this branch from the `followup` argument (Step 0) or from the offer under the open-pipeline table (Step 1.3). Standard practice is a brief, polite follow-up one to two weeks after applying, at most twice; this branch operationalizes that. Enter this branch from the `followup` argument (Step 0) or from the offer under the open-pipeline table (Step 1.3). Standard practice is a brief, polite follow-up one to two weeks after applying, at most twice; this branch operationalizes that.
**Candidates.** An application qualifies when its status is neither final nor `drafted`, the threshold has passed since its `date` (or since the last `followed up` marker in `notes`, if any), and it has fewer than **two** logged follow-ups. Parse dates defensively - skip rows whose dates do not parse and say so rather than guessing. Present qualifying applications as a table (company, role, days quiet, follow-ups sent, channel, contact person) and draft only for the ones the user picks. **Candidates.** An application qualifies when its status is neither final nor `drafted`, the threshold has passed since its `date` or the latest dated entry in `notes`, whichever is more recent (the same days-quiet calculation as Step 1.3), and it has fewer than **two** logged follow-ups. Count `followed up YYYY-MM-DD` markers only for that cap, not as the only activity that resets the quiet clock: a dated interview update or employer reply recorded by `/outcome` or `/gmail-sync` resets it too. Parse dates defensively - skip rows whose dates do not parse and say so rather than guessing. Present qualifying applications as a table (company, role, days quiet, follow-ups sent, channel, contact person) and draft only for the ones the user picks.
**Threshold.** The 10-day default is deliberately earlier than `/gmail-sync`'s 30-day staleness flag (its Step 9): that check is a read-only alarm that a row has been forgotten entirely; this branch is the proactive nudge while a reply is still plausible. The two numbers serve different moments, which is why they differ. **Threshold.** The 10-day default is deliberately earlier than `/gmail-sync`'s 30-day staleness flag (its Step 9): that check is a read-only alarm that a row has been forgotten entirely; this branch is the proactive nudge while a reply is still plausible. The two numbers serve different moments, which is why they differ.
+9
View File
@@ -64,6 +64,15 @@ class OutcomeFollowupBranchSpec(unittest.TestCase):
"spec lost the cap that stops the follow-up branch from nagging indefinitely", "spec lost the cap that stops the follow-up branch from nagging indefinitely",
) )
def test_followup_candidates_reuse_the_open_pipeline_quiet_clock(self):
text = COMMAND.read_text(encoding="utf-8")
branch = text.split("## Step 2b: Follow-Up Branch", 1)[1].split("## Step 2c:", 1)[0]
candidates = branch.split("**Candidates.**", 1)[1].split("**Threshold.**", 1)[0]
# The canonical spec defines candidacy: a dated interview/reply note
# must reset direct followup just as it resets the Step 1.3 table.
self.assertIn("latest dated entry in `notes`", candidates)
self.assertIn("whichever is more recent", candidates)
def test_threshold_contrast_with_gmail_sync_documented(self): def test_threshold_contrast_with_gmail_sync_documented(self):
text = COMMAND.read_text(encoding="utf-8") text = COMMAND.read_text(encoding="utf-8")
self.assertIn( self.assertIn(