← Files Career CentreARCHIVED FILE
scripts/build_schedule_prompt.py
6.99 KB · Sep 30, 2026 · 23:13 UTC
#!/usr/bin/env python3
"""Build a portable recurring-search instruction from a Career Passport."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any
from contracts import load_json, validate_career_passport
def build_schedule_prompt(passport: dict[str, Any]) -> str:
errors = validate_career_passport(passport)
if errors:
raise ValueError("Invalid Career Passport: " + "; ".join(errors))
automation = passport.get("automation")
if not isinstance(automation, dict) or not automation.get("enabled"):
raise ValueError("Career Passport has no enabled automation configuration")
preferences = passport["preferences"]
cadence = automation["cadence"]
local_time = automation["local_time"]
timezone = automation["timezone"]
timing = f"{cadence} at {local_time} ({timezone})"
pack_mode = automation["application_pack_mode"]
pack_instruction = (
"For verified Apply roles, create paired Word CV and cover-letter packs only after all document QA passes."
if pack_mode == "apply_roles"
else "Do not create application packs automatically; wait for my request."
)
locations = ", ".join(str(value) for value in preferences.get("locations", [])) or "the Passport geography"
directions = ", ".join(str(value) for value in preferences.get("target_directions", [])) or "the Passport target directions"
profile = passport.get("profile", {})
profile_snapshot = {
"headline": profile.get("headline"),
"location": profile.get("location"),
"work_rights": profile.get("work_rights"),
}
preference_snapshot = {
"target_directions": preferences.get("target_directions", []),
"locations": preferences.get("locations", []),
"remote_preference": preferences.get("remote_preference"),
"salary_minimum": preferences.get("salary_minimum"),
"currency": preferences.get("currency"),
"employment_types": preferences.get("employment_types", []),
"excluded_employers": preferences.get("excluded_employers", []),
"excluded_role_patterns": preferences.get("excluded_role_patterns", []),
}
evidence_snapshot = [
{
"evidence_id": item.get("evidence_id"),
"safe_wording": item.get("safe_wording"),
"restrictions": item.get("restrictions", []),
}
for item in passport.get("evidence", [])
if isinstance(item, dict)
]
history_snapshot = [
{
"company": item.get("company"),
"title": item.get("title"),
"exact_posting_url": item.get("exact_posting_url"),
"content_fingerprint": item.get("content_fingerprint"),
"recommendation": item.get("recommendation"),
"last_application_stage": item.get("last_application_stage"),
}
for item in passport.get("role_history", [])
if isinstance(item, dict)
]
snapshot = json.dumps(
{
"passport_updated_at": passport.get("updated_at"),
"profile": profile_snapshot,
"preferences": preference_snapshot,
"evidence": evidence_snapshot,
"role_history": history_snapshot,
},
ensure_ascii=False,
separators=(",", ":"),
sort_keys=True,
)
continuity_mode = automation.get("continuity_mode", "snapshot_only")
if continuity_mode == "task_context":
continuity_instruction = (
"Continuity mode: current-task context. Create this schedule from the existing Career Centre task and return each run "
"to that task. Use the task's existing conversation context, uploaded files, installed Career Centre plugin and latest "
"Career Passport available there. Reconcile role fingerprints and application history before browsing. State which "
"continuity inputs were available; never claim cross-account or unrelated-task memory. "
)
elif continuity_mode == "verified_persistent":
continuity_instruction = (
"Continuity mode: verified-persistent. Before browsing, load the latest valid Career Passport from the configured "
"persistent source and confirm its updated_at value. Reconcile its role fingerprints and application history, then save "
"the validated updated Passport where the next run will receive it. If either load or save is unavailable, return BLOCKED "
"with one recovery action; never reset to the embedded snapshot or claim continuity. "
)
else:
continuity_instruction = (
"Continuity mode: snapshot-only. Begin every result with: ‘Continuity: snapshot-backed alert. I used the Career Passport "
"captured when this schedule was created plus this run's results. The host did not supply an updated Passport from earlier "
"scheduled runs, so a role may repeat in a later alert.’ Suppress duplicates against the embedded role_history and within "
"this run. Use employer posting identity/external job ID first, then canonical employer plus normalised title and material "
"description similarity; do not use mutable remote/location wording as the primary key. Prefer roles explicitly posted or "
"updated within the latest scheduled interval. Do not print a run number, claim "
"cross-run deduplication, say the Passport was updated, or call a role new since the prior run. "
)
return (
f"Run {timing}. Invoke Career Centre explicitly. Use this embedded evidence-safe Career Passport snapshot as the candidate and history source: {snapshot}. "
f"{continuity_instruction}"
f"Search for {directions} in {locations}. Reconcile role fingerprints and application history before browsing. "
f"Return at most {automation['max_displayed_roles']} verified roles; every displayed role needs an exact open posting URL, "
"salary band and basis, employment type, Apply/Maybe/Skip decision, main match and main risk. "
"Use no more than four focused queries and inspect no more than twelve plausible postings per run; stop early rather than add filler. "
f"{pack_instruction} Never auto-submit or send an application. If the embedded snapshot or exact-link verification is unavailable, "
"return BLOCKED with one recovery action instead of guessing."
)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("passport", type=Path)
parser.add_argument("--format", choices=["text", "json"], default="text")
args = parser.parse_args()
passport = load_json(args.passport.resolve())
try:
prompt = build_schedule_prompt(passport)
except ValueError as exc:
print(json.dumps({"passed": False, "errors": [str(exc)]}, indent=2))
return 1
if args.format == "json":
print(json.dumps({"passed": True, "prompt": prompt}, indent=2))
else:
print(prompt)
return 0
if __name__ == "__main__":
raise SystemExit(main())
SHA-256: 8fc80cac95b354aa72bcb22c413e9e010f178f70a9d8bd91da9e10fb74526b31