Second-pass review of this session's work by another agent. It corrected several
real mistakes of mine and finished F06 gaps I had left open. Reviewed, verified
and committed as-is.
THE MISS THAT MATTERS
`job_scout/state/applications.json` — the authoritative application registry
introduced by F02 — was never actually tracked. `job_scout/state/*` in
.gitignore silently excluded it, so my checkpoint commit afdef7e claimed to add
it and did not. Only `application_cohort.json`, the GENERATED projection, was
version-controlled; the source of truth and the migration history backup were
not. .gitignore now carries explicit negations for both.
MY ERRORS, CORRECTED
- "kastellaun" was in CH_LOCATION_KEYWORDS. Kastellaun is in Rhineland-Palatinate.
I took it from a Bundeswehr reserve listing and filed it under Switzerland -
precisely the class of bug F12 existed to fix.
- The F12 fix only widened the keyword list; SmartRecruiters still discarded the
structured country, which was the concrete case the review named
(city=Baar, country=CH). The adapter now preserves it, and a COUNTRY_NAMES
mapping gives country precedence over a bare town name.
- F15 was applied to Taleo and Prospective only. Greenhouse still wrote
`updated_at` into `posted`; it now uses `published_at` and carries provenance.
- My negation guard `\b(no|not|...)\b[^.;]{0,60}$` was too loose - an unrelated
"not" within 60 characters could mask a real claim. Tightened to require the
negation to govern the term.
- F18's retirement of the macro-count rule missed "Four to six compact skills
lines" in the workflow; removed now.
F06 GAPS I LEFT OPEN, NOW CLOSED
Date-format validation on education/employment periods (the 2035-99 case named
in the review), evidence-structure validation, referential integrity on evidence
source files, and the missing-identity KeyError.
F10 TAKEN FURTHER, AND RIGHTLY
I demoted the US-GPA fields to *_orientation_only. They are now removed
entirely, with grade_output_rule saying what to do instead: use the German grade
and scale, and if a recipient requires a US GPA, use THEIR method and record it.
Keeping a number derived by a non-recognised method invites its use.
Also: the Capgemini omit-preference now sits in experience_capgemini.md itself,
alongside the instruction not to fill or explain the intentional gap.
BIS BASEL - REJECTED 2026-09-11
jr100429, applied 2026-07-10, no interview, no reason given. Recorded in the
registry with a dated event and in the decision log; the "prep interview brief
when screening lands" next action is cleared. This also settles the 2026-09-04
observation that the req had vanished from the BIS board - it was a real
rejection, not just a closed posting.
VERIFICATION I RAN MYSELF BEFORE COMMITTING
49/49 tests. Validator PASS with 0 advisory warnings. Package manifest and
registry views current. All 12 F12 location cases still correct, including
Kastellaun now rejecting. The F05 counter-proof still catches all 9 violations.
SBB, NVIDIA, RUAG and Kdo Cy resumes still scan clean. Diffs are net-additive
across every reference file - the deletions are replacements, not losses.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qej5SBvhcbGXHwLqNWiskM
120 lines
6.5 KiB
Python
120 lines
6.5 KiB
Python
"""Record applications without quotas; preserve outcome events and interview history."""
|
|
from __future__ import annotations
|
|
import argparse
|
|
import hashlib
|
|
import application_records as records
|
|
|
|
FIT_CLASSES = ("core", "adjacent", "stretch", "no-go")
|
|
CHANNELS = ("strong", "moderate", "weak")
|
|
|
|
|
|
def summary(data: dict, all_applications: bool = False) -> None:
|
|
cohort_id = data["cohort_metadata"]["cohort_id"]
|
|
applications = [row for row in data["applications"] if all_applications or row.get("cohort_id") == cohort_id]
|
|
print(f"Application record: {'all' if all_applications else cohort_id} ({len(applications)} applications; no quota)")
|
|
for field, values in (("fit_class", FIT_CLASSES), ("channel", CHANNELS)):
|
|
for value in values:
|
|
print(f" {field} {value}: {sum(row.get(field) == value for row in applications)}")
|
|
unknown = sum(row.get(field) is None for row in applications)
|
|
if unknown:
|
|
print(f" {field} unrecorded: {unknown}")
|
|
outcomes = sorted({row["outcome"] for row in applications})
|
|
print(" outcomes: " + ", ".join(f"{value}={sum(row['outcome'] == value for row in applications)}" for value in outcomes))
|
|
print(f" interview reached (including later withdrawal/rejection): {sum(records.interview_reached(row) for row in applications)}")
|
|
|
|
|
|
def add_application(data: dict, args: argparse.Namespace) -> None:
|
|
records.valid_date(args.date)
|
|
identity = args.id or "app-" + hashlib.sha256(f"{args.company.casefold()}|{args.job_id or args.role.casefold()}|{args.date}".encode()).hexdigest()[:12]
|
|
candidate = {
|
|
"id": identity, "company": args.company, "role": args.role,
|
|
"label": f"{args.company} — {args.role}", "application_date": args.date,
|
|
"fit_class": args.fit_class, "evidence_fit": args.evidence_fit,
|
|
"hard_gate": args.hard_gate, "channel": args.channel, "outcome": args.outcome,
|
|
"cohort_id": data["cohort_metadata"]["cohort_id"], "job_id": args.job_id,
|
|
"url": args.url, "fit_override_reason": args.fit_override_reason,
|
|
"rubric_version": args.rubric_version,
|
|
"next_action": args.next_action, "current_summary": args.outcome,
|
|
"events": [{"type": args.outcome, "date": args.date}],
|
|
}
|
|
for existing in data["applications"]:
|
|
same_job = args.job_id and existing.get("job_id") == args.job_id and existing["company"].casefold() == args.company.casefold()
|
|
same_url = args.url and existing.get("url") == args.url
|
|
same_application = existing["company"].casefold() == args.company.casefold() and existing["role"].casefold() == args.role.casefold() and existing.get("application_date") == args.date
|
|
if existing["id"] == identity or same_job or same_url or same_application:
|
|
if all(existing.get(key) == value for key, value in candidate.items() if key not in {"events", "id", "current_summary", "next_action", "outcome"}):
|
|
return
|
|
raise ValueError(f"Application already exists ({existing['id']}); use update instead")
|
|
data["applications"].append(candidate)
|
|
records.validate(data)
|
|
|
|
|
|
def update_application(data: dict, args: argparse.Namespace) -> None:
|
|
records.valid_date(args.date)
|
|
matches = [row for row in data["applications"] if row["id"] == args.id]
|
|
if not matches:
|
|
raise ValueError(f"Unknown application ID: {args.id}")
|
|
row = matches[0]
|
|
if row.get("application_date") and args.date < row["application_date"]:
|
|
raise ValueError("event date precedes application date")
|
|
outcome_dates = [event["date"] for event in row.get("events", [])
|
|
if (event.get("outcome") or event.get("type") in records.OUTCOMES) and event.get("date")]
|
|
outcome_dates.extend(row[key] for key in ("withdrawn", "rejection_date") if row.get(key))
|
|
if args.outcome and outcome_dates and args.date < max(outcome_dates):
|
|
raise ValueError("older outcome cannot replace current state; record it as a historical note")
|
|
event = {"type": args.event or args.outcome or "note", "date": args.date}
|
|
if args.note:
|
|
event["note"] = args.note
|
|
if args.outcome:
|
|
event["outcome"] = args.outcome
|
|
if event not in row.setdefault("events", []):
|
|
row["events"].append(event)
|
|
if args.outcome:
|
|
row["outcome"] = args.outcome
|
|
row["current_summary"] = args.note or args.outcome
|
|
row["next_action"] = args.next_action or ("Await response." if args.outcome in {"applied", "interviewing", "open"} else "Review recorded outcome before further action.")
|
|
elif args.next_action:
|
|
row["next_action"] = args.next_action
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
commands = parser.add_subparsers(dest="command", required=True)
|
|
view = commands.add_parser("summary")
|
|
view.add_argument("--all", action="store_true")
|
|
add = commands.add_parser("add")
|
|
for field in ("company", "role", "date"):
|
|
add.add_argument("--" + field, required=True)
|
|
add.add_argument("--id")
|
|
add.add_argument("--job-id")
|
|
add.add_argument("--url")
|
|
add.add_argument("--fit-class", choices=FIT_CLASSES, required=True)
|
|
add.add_argument("--evidence-fit", type=float, required=True)
|
|
add.add_argument("--channel", choices=CHANNELS, required=True)
|
|
add.add_argument("--hard-gate", choices=("pass", "fail", "unknown"), required=True)
|
|
add.add_argument("--outcome", choices=sorted(records.OUTCOMES), default="applied")
|
|
add.add_argument("--fit-override-reason")
|
|
add.add_argument("--rubric-version", default="critique-framework-2026-09-11")
|
|
add.add_argument("--next-action", default="Await response.")
|
|
update = commands.add_parser("update")
|
|
update.add_argument("--id", required=True)
|
|
update.add_argument("--date", required=True, help="Date of this event")
|
|
update.add_argument("--outcome", choices=sorted(records.OUTCOMES))
|
|
update.add_argument("--event", choices=("interview_invited", "interview_held", "assessment_passed", "contact_attempted", "contact_conversation", "referral_received", "note"))
|
|
update.add_argument("--note")
|
|
update.add_argument("--next-action")
|
|
args = parser.parse_args()
|
|
try:
|
|
if args.command == "summary":
|
|
summary(records.load(), args.all)
|
|
return
|
|
with records.transaction() as data:
|
|
(add_application if args.command == "add" else update_application)(data, args)
|
|
summary(data)
|
|
except (ValueError, OSError) as error:
|
|
parser.exit(2, f"ERROR: {error}\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|