Ship happy-hour quota-preference windows and default user config (issue #69) #70
No reviewers
Labels
No labels
accessibility
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
P1
P2
P3
question
wontfix
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Creatidy/scarcity-router!70
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature/happy-hours-default-config"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements issue #69: happy-hour quota-preference windows and a single default user configuration location, reviewed as one bounded owner-workflow change.
1. Happy hours (D-035)
WeeklyHappyHourRulemirrors the blackout rule mechanics exactly — exactAvailabilityTargetidentity matching, explicit IANA zone, canonicalmon–sunweekday vocabulary, strictHH:MMwall times, half-open[start, end)local intervals, cross-midnight support — plus optional inclusivestart_date/end_datebounds because vendor campaigns are limited-time. Both schedule kinds share one matcher implementation, so boundaries can never drift.Semantics (never an eligibility bypass):
rule_idorder decides; blackout wins over an overlapping happy hour by stage order; the decision and--explainoutput name the governing rule and reason code.Serialized compatibility:
UserPolicygains the additive optional memberhappy_hours; documents without it round-trip byte-identically, and an empty set is never serialized. Envelopes and theSelectionDecisionfield set are unchanged.examples/selector-policy.jsonnow also carries the GLM-5.3-Flash campaign rule (zai-flash-campaign-night-sgt, daily 23:00–09:00 Asia/Singapore, 2026-09-03 → 2026-09-20, targetzai/glm-5.3-flash), matching the owner-provided campaign text.2. Default configuration location (D-036)
Exactly one config location:
$(XDG_CONFIG_HOME or ~/.config)/scarcity-router/selector-policy.json.uv tool installhas no post-install hook, so first use is the effective installation step) and via the newscarcity-router install-config [--force]command; an existing file is never silently overwritten; dir0o700, file0o600, no credentials ever.--selector-policy> default config > neutral (--neutral-policyignores the config for one run). MCP/REST: a request that suppliesselector_policywins; an omitted one runs under the server-configured default (ApplicationDependencies.default_policy), neutral when none resolves. No flags need to be added to the MCP command.scarcity_router/config.py; the wheel packages the example via the D-034 force-include mechanism asscarcity_router/default-selector-policy.json(no committed duplicate; sdist gains/examples/selector-policy.json; the package-checkexamples/tripwire gains exactly this one allowlisted exception, recorded in D-036).Validation
uv sync --only-dev,make check(972 tests OK),uv run basedpyright(0 errors / 0 warnings / 0 notes),git diff --check— all clean.make package-checkpasses end to end (wheel/sdist contents, isolateduv tool install, installed resource loading, MCP discovery, REST smoke).tests/test_happy_hours.py(schedule semantics, date bounds, ranking integration incl. blackout-overlap, gating and serialization),tests/test_config.py(XDG resolution, idempotent provisioning, permissions, precedence end-to-end,install-config). CLI-touching test modules run against a throwawayXDG_CONFIG_HOMEso the suite never reads or provisions the host config.Governance
selection-policy.md(new happy-hour section + amended ranking order),README.md(default configuration + availability policy),machine-interfaces.md,security.md,architecture.md,AGENTS.mduntouched.Workers/reviewers never merge: PR left open for human review.
Self-review remediation pushed (
aee0c24) — the end-user self-review found no merge blockers; per owner go-ahead, the three highest-value findings are folded into this PR as the one remediation round:~/.config/scarcity-router/selector-policy.jsonprintsnote: provisioned default user config: <path>once on stderr (JSON stdout stays clean; MCP/REST announce through the same channel at process start; existing files are never announced).render_select_humanprints oneHappy hour: rule … (reason)line for the selected candidate, so the "why did it pick this?" question no longer requires--explain/--json.SelectionDecisiongains the additive optional memberexpired_happy_hour_rules(serialized only when non-empty): rules whose weekly window would cover the evaluated instant but whose date bounds exclude it.--explainlists them under "Expired happy-hour rules" — after 2026-09-20 the campaign rule announces its own expiry instead of silently going inert. Explanation-only; no ranking or eligibility effect.Remaining self-review items stay DEFER (target-typo validation would change policy-failure behavior; provisioning race degrades one run with a warning; campaign end-boundary nuance is documented).
Validation re-run: 978 tests OK, basedpyright 0/0/0,
git diff --checkclean,make package-checkgreen end to end. D-035/D-036 amended in place (still unmerged, same PR).