fix(apply): archive the job posting while /apply still holds it (#306) (#307)

/apply drafted two documents and a tracker row from the full posting, then
let the text die with the session. /outcome Step 3.2 tried to recover it by
re-fetching a `source` URL the spec itself expects to be dead, and a posting
pasted from an email or a PDF had no `source` to re-fetch at all.

Step 6b gains item 7: write the posting verbatim to
documents/applications/<company>_<role>/job_posting.md, never a re-fetch or a
reconstruction from memory. The folder is derived by citing /outcome Step 1.4
rather than restating the rule, so the two cannot drift. An existing file is
left alone and named in the report.

Step 0 and the /scrape path (job-application-assistant SKILL.md Step 1) now
retain the full posting text rather than a summary, so item 7 has something
verbatim to write.

Pinned by tests/test_apply_records_application.py.
This commit is contained in:
Jakob Stender Guldberg
2026-08-09 20:26:58 +02:00
committed by GitHub
parent e09d3eb37b
commit 0e1a895c4e
6 changed files with 81 additions and 8 deletions
+60
View File
@@ -190,5 +190,65 @@ class DraftedMeansDraftedToEveryReader(unittest.TestCase):
self.assertEqual(result.returncode, 0, f"lint_skills.py failed:\n{result.stdout}{result.stderr}")
class ApplyArchivesThePosting(unittest.TestCase):
"""Step 6b must also write the posting text it is holding to the archive."""
CASES = [
(APPLY, "## Step 0: Parse Input",
"full posting text verbatim",
"by Step 6b the model may hold only a summary, so the archive gets a "
"paraphrase - what /outcome Step 3.2 forbids"),
(APPLY, "### Step 6b: Record the Application",
"`documents/applications/<company>_<role>/job_posting.md`",
"the one moment /apply provably holds the posting is spent again, and "
"a pasted posting has no recovery path at all"),
(APPLY, "### Step 6b: Record the Application",
"never a fresh fetch",
"a model that no longer holds the text would re-fetch to comply, the "
"dead-URL path this whole item exists to avoid"),
(APPLY, "### Step 6b: Record the Application",
"`/outcome` Step 1.4",
"the derivation is no longer pinned to /outcome's, so a later edit to "
"either can silently orphan the archive"),
(OUTCOME, "## Step 1: Load State and Identify the Application",
"4. Derive the archive folder name",
"apply.md item 7 defers its folder derivation to /outcome Step 1.4 by "
"number; renumbering Step 1 leaves that citation dangling"),
(APPLY, "### Step 6b: Record the Application",
"**If the file already exists, leave it**",
"re-running /apply to refresh a CV would overwrite the posting that "
"was actually applied against"),
(APPLY, "### Step 6b: Record the Application",
"keeps the older posting",
"the leave-it rule would read as if the folder is always fresh, hiding "
"that a re-application to the same role collides with the old archive"),
(APPLY, "### Step 6b: Record the Application",
"left in place rather than written",
"the skip discards the current posting silently, and /interview preps "
"against the earlier application's posting"),
(APPLY, "### Step 6b: Record the Application",
"never reconstruct it from memory",
"a model that reached Step 6b without the text could satisfy none of "
"item 7's constraints, and would write a remembered posting instead"),
(SKILL, "### Step 1: Research & Evaluate Fit",
"full posting text verbatim",
"the /scrape path never runs /apply Step 0, so nothing stops it "
"compressing the posting before Step 3b archives it"),
(SKILL, "### Step 3b: Record the Application",
"same posting archive",
"the /scrape path reaches Step 3b without running /apply, and its "
"closed enumeration of Step 6b's rules would omit the archive write"),
(OUTCOME, "## Step 3: Archive the Application Materials",
"if it already exists, leave it",
"/outcome would overwrite /apply's archived posting with a re-fetch, "
"the dead-URL branch the /apply write exists to avoid"),
]
def test_posting_is_archived_where_every_reader_looks(self):
for path, heading, needle, why in self.CASES:
with self.subTest(file=path.name, rule=needle):
self.assertIn(needle, section(path, heading), why)
if __name__ == "__main__":
unittest.main()