Repository navigation
Queue work for the desk and retire the paid API jobs - #500
Conversation
8eb3353 to
9de3168
Compare
98fcd66 to
6c0061b
Compare
The desk queue lives on an orphan desk-queue branch: one JSON file per series under queue/, keyed by the slug of the series name, plus a README that init writes from the module so the format is documented in one place. GitHub Actions jobs add records with "add", from JSON lines or from options; the desk removes handled items with "remove". Each item gets a stable id from its source, kind, URL and payload, so the same finding queued twice is one item. A file keeps its 50 newest items and long payload strings are cut at 20,000 characters, flagged with "truncated". The module uses only the standard library, so any job can run it without the data tooling. clean_watch_title and the name lookup move here from desk_worklist.py, which now imports them, so the queue file key and the worklist's series matching read names the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr
check-conference-update.yml ran a paid-API triage on every changed page, then two agent jobs that edited the data. The triage passed 26% of events at 38% precision and 58% recall, and never saw the data, so it flagged dates already recorded and dropped extensions and moved dates. The workflow now validates the payload and writes it to the desk-queue branch unchanged; the desk judges it on its next run with the data and the live site in hand. The payload is read from the event file rather than interpolated or passed through the environment, so page text cannot reach the shell and a long diff cannot exceed the environment size limit. The new composite action desk-queue-write is the one place that writes to the queue: it creates the orphan branch when missing, adds records with utils/desk_queue.py, and retries a rejected push from a fresh copy of the branch, never with force. Ids are stable, so a retry adds nothing twice. scheduled-extension-checker.yml goes: the desk's worklist checks every deadline 1, 3 and 7 days after it passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr
The TBA enrichment keeps its deterministic link extraction: social accounts, sponsor pages and financial aid pages found on each conference's homepage. Deadlines, dates and time zones are now researched by the desk, so the full mode, its prompt and its API calls are removed, together with the --level option and the confidence handling only the API results needed. The workflow always runs the deterministic enrichment and no longer needs the API key. The canonical source workflow triggers it without a level input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr
The import asks a person to settle conflicts it cannot resolve: which of two values to keep, whether a later deadline is an extension, whether two similar names are one series. In CI no one is at a terminal, so query_yes_no took the default and the question was lost. query_yes_no now takes an optional context. Without a terminal it still returns the default, so the import stays deterministic, and it also records the question with the conference, year, field, both values and the source. With DESK_QUEUE_OUT set, the record is written as a JSON line, and canonical-source-update.yml adds the file to the desk queue, where the enricher answers it from the live site. The JSON-lines writer moves into tidy_conf/queue_out.py, shared with the link checker's redirect records. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr
The link check now records a link that ends on a different site instead of counting a 200 there as healthy, but nothing ran it on a schedule. redirect-scan.yml runs it every Sunday with DESK_QUEUE_OUT set and adds the records to the desk queue. The desk decides each one: a series that moved gets its new link, a lapsed or hijacked domain an archived copy. The scan is report-only. The link check's other edits stay with the writers that commit data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr
The canonical import asks the same standing questions on every run (an end date one day apart, "Brasil" and "Brazil"), and the redirect scan finds the same redirects every week. Ids only deduplicated while an item was queued, so once the desk had handled one, the next run queued it again. desk_queue.py remove now records each id in its file's "handled" list, the last 500 per file, and add reports a handled id instead of queueing it. A changed finding has a new id and is queued as usual. For a redirect the id leaves out the status and the final URL's query and fragment, which can change between checks. A queue file that is not valid JSON stopped every writer bound for it, and under set -e the canonical merge was never committed. add now moves such a file to <key>.json.bad and starts a new one; remove and list skip it. The composite action turns these messages and rejected records into workflow warnings, and the canonical workflow's queue step can no longer hold back the merge. Also from review: - the follow-up "Is this an extension?" question is no longer queued: the cfp question before it carries both deadlines, while its record gave the YAML cfp as the cfp_ext value; - the redirect scan's link check has a step timeout below the job's, so a slow scan still queues what it found; - canonical-source-update gets actions: write, which gh workflow run needs to start the TBA enrichment; - the action matches refs/heads/desk-queue exactly. desk.md step 8 now removes items only through desk_queue.py remove. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr
find_tba_conferences picked every entry whose cfp is in TBA_WORDS, which also holds "cancelled", so the job visited the homepage of a cancelled edition and could fill its sponsor or finaid link. It now leaves cancelled editions out. Entries with cfp None stay in: since the API mode is gone, the job only fills Bluesky, YouTube, Mastodon, sponsor and finaid links, and an edition with no public CfP still happens. On the current data the only change is PyCon Estonia 2026. The new tests fail on the previous code for the cancelled cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr
6c0061b to
7d6c428
Compare
|
Routine review 1. RequestNo linked issue, so the request is the PR body. It asks to stop every paid-API call from GitHub Actions by:
2. Checked against
3. Needs your decision
4. FindingsImportant: None. Nits:
5. Tests
6. Bounds
Generated by Claude Code |
…shes From the routine review of #500: - The manual check-conference-update run asks for a name such as "PyCon US 2027", but clean_watch_title took a year only from a changedetection suffix (" 2027 - Main"), so that input was queued with year null under queue/pycon-us-2027.json. A trailing year from 1900 to 2099 with no suffix now counts too, so it goes to queue/pycon-us.json with year 2027. Watch titles keep their suffix and are read as before; "Python 3000" and "PyCon 2027 Online" keep their names. - A comment next to the merge prompts in interactive_merge.py says that each prompt's text is part of its queued question's id, so rewording one queues every standing question again, once. The new tests fail on the previous code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr
|
Routine review Re-review of #500 at 1. RequestNo linked issue, so the request is the PR body. It is the same as last round:
This round adds
2. Checked against
3. Needs your decisionBoth questions from the earlier review still apply, and this push does not change them:
4. FindingsImportant: None. The earlier review had none to resolve, and What I checked for the new
5. Tests
6. Bounds
Generated by Claude Code |
#500 landed on main as the squash commit ce5df9f, whose tree is the old #500 head 87bc32e. Conflicts keep this branch's side, so the branch's diff against main is #501's own change (87bc32e..dd5a329). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CztuQqAuxRRJLkKiQYasnS
Brings in #500 and #501. The writers push with GITHUB_TOKEN, which cannot push this merge because main changed files under .github/workflows/, so it is merged by hand once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CztuQqAuxRRJLkKiQYasnS
Fourth PR in the desk stack, now based on
main(#499 is merged). GitHub Actions stops calling a paid API: the jobs that used one either collect work for the desk into a queue or keep only their deterministic part. After this PR, no workflow referencesANTHROPIC_API_KEY.The desk queue
utils/desk_queue.pyreads and writes the queue on an orphandesk-queuebranch, which is never merged. There is one JSON file per series underqueue/, keyed by the slug of the series name, and aREADME.mdthatinitwrites from the module, so the format is documented in one place. Each item gets a stable id from its source, kind, URL and payload, so the same finding queued twice is one item. When the desk removes an item, its id moves to the file'shandledlist. A writer that finds the same thing again (the import asks the same standing questions on every run) then skips it, while a finding that has changed gets a new id. A file keeps its 50 newest items and 500 handled ids, and payload strings are cut at 20,000 characters and flagged. A broken file is moved to.bad, so it can't stop the writers. The module uses only the standard library, so any job can run it..github/actions/desk-queue-writeis the one place that writes to the queue. It creates the branch when it is missing, adds records withdesk_queue.py add --jsonl, and retries a rejected push from a fresh copy of the branch, never with force. I tested it against a local remote: creating the branch, a repeat (no-op), an append, a push race (rejected once, then landed on top), a rejected record (a workflow warning, while the valid one still lands), and a re-add afterremove(reported as handled).Writers
check-conference-update.ymlturns a changedetection.io event into one queue record, with no triage in front. The payload is read from the event file, so page text never reaches the shell, and a long diff can't hit the environment size limit. The old pipeline (Haiku triage, two agent jobs,apply-filters) goes. Over 254 events the triage had 38% precision and 58% recall.canonical-source-update.yml: the import stays deterministic. In CI,query_yes_nostill takes the default answer. When the caller passes context, it also records the question: conference, year, field, both values and the source. WithDESK_QUEUE_OUTset, the record goes to the queue, where the enricher answers it from the live site. The JSON-lines writer is shared with the link checker intidy_conf/queue_out.py. A queue problem can't hold back the data merge (continue-on-error). The job also getsactions: write, which its existinggh workflow runcall to start the enrichment always needed.redirect-scan.yml(new, Sundays) runs the link check in report-only mode. It queues every link that now ends on another site, the case lychee can't tell apart from a healthy 200.Retired
scheduled-extension-checker.yml: the worklist checks every deadline 1, 3 and 7 days after it passes.utils/enrich_tba.pyand its workflow input. The deterministic link extraction stays, along with the code only it uses. The confidence handling, which only the API results could reach, is removed. Review items now end in "(kept existing)".cfp: Nonestay in: the job only fills Bluesky, YouTube, Mastodon, sponsor and finaid links, and an edition with no public CfP still happens. On the current data the only change is PyCon Estonia 2026.Needs your call
desk-queuewithout being lost (at most 50 per series).ANTHROPIC_API_KEYcan be deleted from the repository secrets once this PR is onmain.Checks
queue/pycon-us.jsonwith year 2027. Watch titles keep their suffix and read as before.uvrun gives 1175 passed, 0 failed.uvx ruff@0.4.4 check utils tests: clean.pre-commit runon every changed file: passes.actionlinton all workflows: clean.python utils/sort_yaml.py --skip_linksleaves_data/unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_01QzyF2ciB5jhvcTVpj4ErMr