Skip to content

refactor: consolidate the tools on shared/, and split triage.py - #64

Closed
Bilb wants to merge 5 commits into
feat/contributor-pr-digestfrom
refactor/shared-consolidation
Closed

Bilb wants to merge 5 commits into
feat/contributor-pr-digestfrom
refactor/shared-consolidation

Conversation

@Bilb

@Bilb Bilb commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #59, which created shared/. This finishes the job: what the tools still duplicated moves into shared/, the zendesk scripts stop reaching it through triage.py, and triage.py is split into the pieces the other scripts actually need.

shared/

  • shared.text: clip, squash, window_label, undash_english. Pure string work that prompts, ticket fields and Discord lines all did.
  • shared.state.Tracker: binds a digest's schema version, item key, activity field and retention once. The new/changed/unchanged partition and the record-building both digests carried side by side now live here.
  • shared.discord.delivered_ids: what a partial post covered, so the state records only what Discord accepted.
  • get_env(default=) for settings that are optional with a fallback.
  • shared.testing: raise_for_status on the fake response, a context-manager fake session, and an Env swap, so no test file defines its own.

zendesk_triage

note_reply, resolve_reviews and deploy/alert.py imported the 1900-line classifier to reach a retry loop, a marker format or the CLI's name; relay imported it and used nothing. They now import what they use:

module holds
zendesk.py the API session, search and its 1000-result ceiling, one ticket, its comments and users, the one PUT every write is, the markers, who the customer is on a channel integration, what an imported store review looks like
claude_cli.py run_json, and try_run_json for an enrichment that hands the failure back instead of exiting (three copies of the catch-SystemExit idiom collapse into it)
transcript.py the English transcript end to end: translate, render, write, attach

triage.py keeps the queries, taxonomy, dedup, review filter, analysis and rendering: 1091 lines, from 1913.

Two behaviour changes on purpose: the three comment fetchers differed only in page size, order and whether a failure exits or skips the ticket, so they are one fetch_comments with those as parameters; and note_reply --model now goes through resolve_api_model like the triage's does, so sonnet works for both rather than one.

crowdin

download_translations_from_crowdin.py had its own retry loop: 429 only, no 5xx, int(Retry-After) that crashed on the date form the RFC allows, no session. It now uses shared.retry on one session, with the export downloads on a second unauthenticated session because they are served from a signed URL on another host. Arguments parse in main instead of at import, which is what let it get tests. Every argument the workflow passes is unchanged.

CI

Nothing ran the test suites before. tests.yml runs all six on pull requests, one job per directory because two requirements files pin requests differently. sogs_moderation runs on the system interpreter with a --system-site-packages venv, since python3-session-util ships as a deb from deb.session.foundation rather than a wheel.

Testing

Every suite locally: shared 63, github_prs, zendesk_triage 444, deploy, crowdin 23. sogs_moderation needs the apt package, so the CI job is its first run.

Left for later, deliberately: a shared.crowdin for the API base, token lookup and pagination copied across three crowdin scripts, and sogs_moderation, whose signed requests cannot be retried generically.

…her fakes

shared.text holds clip, squash and window_label, the string work every tool
does before it talks to anything. shared.state.Tracker binds a digest's
schema version, item keys and retention once, so the partition and the
record-building both digests carry live in one place, and
shared.discord.delivered_ids names what a partial post covered. get_env
takes a default for settings that are optional with a fallback, and the
fakes gain raise_for_status, a session context manager and an Env swap so
no test file has to define its own.
…Tracker

note_reply, resolve_reviews and the alert reached the shared helpers as
attributes of triage, so a 1900-line classifier was the import path for a
retry loop; relay imported it and used nothing. Each now imports shared
itself, and the tests take their fakes from shared.testing rather than from
test_triage.

The digest and the triage carried the same new/changed/unchanged partition,
state wrappers and window label side by side. Both now configure a
shared.state.Tracker and take clip, squash and window_label from
shared.text; the crowdin report's private clip and its unused snippet go
the same way.
…t of triage.py

note_reply, resolve_reviews and the alert imported the 1900-line classifier
to reach a ticket fetch, a marker format or the CLI's name. Those now live in
modules of their own:

  zendesk.py     the API session, search and its 1000-result ceiling, one
                 ticket, its comments and users, the one PUT every write is,
                 the markers, who the customer is on a channel integration,
                 and what an imported store review looks like
  claude_cli.py  run_json, and try_run_json for an enrichment that hands the
                 failure back instead of exiting
  transcript.py  the English transcript: translate, render, write, attach

The three comment fetchers in triage.py differed only in page size, order
and whether a failure exits or skips the ticket; those are now parameters of
one fetch_comments. note_reply's model flag goes through resolve_api_model
like the triage's does, so an alias works for both. undash_english is pure
text and moves to shared.text.

triage.py keeps the queries, the taxonomy, the dedup, the review filter, the
analysis and the rendering: 1091 lines, from 1913.
The script's own retry loop retried 429 only, crashed on a Retry-After given
as a date, and re-sent the bearer token per call with no session. It now goes
through shared.retry on one session, with the export downloads on a second,
unauthenticated one: they are served from a signed URL on another host.

Arguments were parsed at import time and the functions read module globals,
which is why the script had no tests. The client is an object now, the
arguments parse in main, and the printers come from generate_shared next
door instead of being spelled out again. Every argument the workflow passes
is unchanged.
Nothing ran them before. One job per directory, because two of the
requirements files pin requests differently. sogs_moderation runs on the
system interpreter with a venv that can see it, since session_util ships as
a deb built against that interpreter rather than as a wheel.
@Bilb Bilb closed this Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant