Z.ai temporary campaign policy: date-bounded blackouts for the Sep 25 – Oct 7 all-day off-peak campaign (D-059, #154) #155

Merged
adrian.tkacz merged 1 commit from policy/zai-temp-campaigns into develop 2026-09-28 12:30:06 +00:00
Member

Closes #154. Decision record: D-059 in docs/decisions.md.

Incorrect previous behavior

The shipped default selector policy (examples/selector-policy.json, provisioned on first use) modeled Z.ai peak hours as a standing weekly blackout zai-peak-hours-sgt (Mon–Fri 14:00–18:00 Asia/Singapore, reason preserve_zai_offpeak). During the vendor's temporary all-day off-peak campaign the blackout still fired in every nominal peak window, so an otherwise eligible request constrained to provider=zai was rejected outright. Demonstrated against origin/develop @ 81b8e63 with the shipped policy at the injected instant 2026-09-25T14:00:00+08:00 and requirement hard_constraints.required_provider=zai:

selected: null (no selection)
reason_codes: ['no_eligible_candidate']
excluded: glm-5.3 | blackout rule: zai-peak-hours-sgt | reason_code: preserve_zai_offpeak   (×3 variants)
excluded: glm-5.3-flash | blackout rule: zai-peak-hours-sgt | reason_code: preserve_zai_offpeak

With this branch, the same instant/request yields selected: zai/glm-5.3/low (selected_balanced), no policy_blackout exclusions, and expired_blackout_rules: ['zai-peak-hours-sgt', 'zai-peak-hours-sgt-post-campaign'] (explanation only).

Official Z.ai sources (fetched 2026-09-28; semantic authority)

  • Plan Update Announcement: "From September 25 to October 7, 2026, all-day usage will be charged at the off-peak rate." · "Peak hours: Monday to Friday, 14:00–18:00 Singapore Standard Time (UTC+8)." · GLM-5.3: "1× during off-peak hours and 3× during peak hours"; GLM-5.3-Flash: "0.4× during off-peak hours and 1.2× during peak hours."
  • GLM-5.3-Flash Usage Campaign: "Campaign period: September 3, 2026 to October 7, 2026"; daily "from 23:00 to 09:00 the following day" Singapore Time; via ZCode/AutoClaw "Zero quota consumption for unlimited usage"; via other supported agents "Available quota is doubled based on your plan's standard quota rules"; "This campaign takes effect only in ZCode version 3.10 and later."; 5 hours/week participation cap; "This campaign applies only to GLM-5.3-Flash."
  • No official source contradicted the task facts.

Two independent campaigns — kept separate

  1. All-day off-peak campaign (Sep 25 – Oct 7, 2026) — a provider-wide rate-class override: effective_rate_class = off_peak all day, i.e. GLM-5.3 at 1× and Flash at 0.4× even in nominal peak windows. This is the mandatory correction: it removes the wrong preserve_zai_offpeak exclusion.
  2. GLM-5.3-Flash Usage Campaign (Sep 3 – Oct 7, 2026, nightly 23:00–09:00 SGT) — a Flash-specific benefit whose economics depend on the execution method (zero quota via ZCode ≥ 3.10/AutoClaw, doubled quota via other agents). The two overlays are never collapsed into one multiplier; composition during an ordinary weekday peak: GLM-5.3 3×→1×; Flash via ordinary agent 1.2×→0.4×; Flash via eligible ZCode in the nightly window → zero nominal burn (subject to the weekly cap).

Exact temporal semantics implemented

  • WeeklyBlackoutRule gains the same optional inclusive local calendar bounds start_date/end_date (YYYY-MM-DD, in the rule's zone, start_date <= end_date) that WeeklyHappyHourRule has had since D-035 — one shared implementation (_v_date_bounds, _date_bounds_contain, _date_bounds_expired) so the rule kinds cannot drift at boundaries. A rule whose weekly window covers an instant while its bounds do not does not block; bounds are inclusive on both ends; the all-day campaign is encoded as calendar dates because the official definition is date-granular (no hour-level boundary is published).
  • The shipped default encodes the campaign as the dated gap between two otherwise-identical blackout halves: zai-peak-hours-sgt (end_date: 2026-09-24) and zai-peak-hours-sgt-post-campaign (start_date: 2026-10-08). Boundary behavior: Thu Sep 24 15:00 SGT blocked; Fri Sep 25 00:00 and 14:00 SGT not blocked; Wed Oct 7 14:00 SGT not blocked; Thu Oct 8 14:00 SGT blocked again — automatic expiry, no configuration change on Oct 8.
  • zai-flash-campaign-night-sgt is extended to end_date: 2026-10-07 (inclusive; the last covered night is Oct 7 02:30 SGT).
  • Explanation symmetry with D-035: WeeklyBlackoutRule.is_date_expired_at plus the additive SelectionDecision.expired_blackout_rules member (sorted; serialized only when non-empty; listed by --explain) name parked rules. Additive, machine-interface v1 (D-039/D-035/D-057 precedent): documents without the new keys load and round-trip byte-identically.
  • Asia/Singapore is the policy calendar; evaluation converts caller-supplied timezone-aware instants into the rule's zone; the clock is injected (select_model(evaluated_at=…) / simulate evaluated_at override); no test depends on the current date.

Why the permanent baseline peak schedule is preserved

The campaign is temporary; the ordinary Mon–Fri 14:00–18:00 SGT peak economics return on Oct 8. Peak logic is not disabled globally and "always off-peak" is not substituted for the baseline: only the dated window suspends the blackout, and the standing schedule resumes without human action. Ranking policy is untouched — no model is newly preferred because it is cheaper; only eligibility economics and explanation output changed.

What is deliberately NOT automated (Flash/ZCode boundary)

The Flash campaign's zero-quota benefit is an attribute of the execution method (ZCode ≥ 3.10 / AutoClaw), not of Z.ai or Flash globally. The selector has no authoritative per-request execution-source identity or version — D-053 sources are administrator grants bound to workers, not request attestation — so modeling client == "zcode" would be fabricated certainty and would violate harness-independence. Accordingly: the nightly rule stays a ranking preference (D-035 semantics), never an eligibility or zero-cost claim; null/unknown source never yields a zero-quota assumption; capacity exhaustion still excludes (the weekly 5 h/week cap is exactly the D-035 "campaign small print" case); GLM-5.3 is never targeted. If execution-source attestation ever becomes authoritative, that is a separate decision.

Regression tests added (deterministic, injected clock)

  • tests/test_resource_policy.py: blackout date-bound validation (ordering/format), round-trip incl. pre-D-059 documents unchanged, inclusive bounds on both ends, one-sided bounds, UTC→local-calendar-date conversion, suspension inside the gap, is_date_expired_at semantics, naive-instant rejection.
  • tests/test_selection_app.py: example-policy loads two dated halves; last pre-campaign peak still blocks; campaign first day / first minute / last day select Z.ai with no policy_blackout exclusions; weekend inside campaign unchanged (no double discount); provider=zai-constrained request survives campaign peak (the #154 regression); ordinary peak resumes Oct 8; expired_blackout_rules values per phase; --explain lists the parked rule.
  • tests/test_happy_hours.py: extended campaign window boundaries (before Sep 3, overlap with the all-day campaign, inclusive last night Oct 7, first excluded night Oct 8), GLM-5.3 never targeted.
  • Policy-level invariants re-asserted by existing tests: blackout wins over overlapping happy hour; exhaustion still excludes inside a window; knowledge class still dominates; preference never bypasses hard constraints.

Test/check results

  • uv sync --only-dev — OK.
  • make check — full suite 2128 tests OK. One full-suite run showed a single failure in test_e2e_execution.CancellationTests.test_scenario_11_client_disconnect_propagates_cancel; it passes in isolation and is the documented pre-existing load-sensitive race (issue #143), untouched by this diff. Re-run: clean.
  • uv run basedpyright — 0 errors, 0 warnings, 0 notes.
  • git diff --check — clean.

Documentation

docs/selection-policy.md (blackout date bounds + "Current vendor campaigns" subsection with sources), README.md (shipped-default description), docs/decisions.md (D-059). No catalog ratings, capability claims, ranking semantics or breaking serialized-contract changes.

UX impact: positive — an otherwise eligible Z.ai request is no longer wrongly rejected during the campaign; a parked blackout is now visible (expired_blackout_rules + --explain "Expired blackout rules") instead of going silent; preserve_zai_offpeak explanations continue to name the exact rule and reason outside the campaign.

Per repository governance: PR left open for human review; not merged; develop not promoted; nothing deployed.

Closes #154. Decision record: **D-059** in `docs/decisions.md`. ## Incorrect previous behavior The shipped default selector policy (`examples/selector-policy.json`, provisioned on first use) modeled Z.ai peak hours as a **standing** weekly blackout `zai-peak-hours-sgt` (Mon–Fri 14:00–18:00 Asia/Singapore, reason `preserve_zai_offpeak`). During the vendor's temporary all-day off-peak campaign the blackout still fired in every nominal peak window, so an otherwise eligible request constrained to `provider=zai` was rejected outright. Demonstrated against `origin/develop` @ `81b8e63` with the shipped policy at the injected instant `2026-09-25T14:00:00+08:00` and requirement `hard_constraints.required_provider=zai`: ``` selected: null (no selection) reason_codes: ['no_eligible_candidate'] excluded: glm-5.3 | blackout rule: zai-peak-hours-sgt | reason_code: preserve_zai_offpeak (×3 variants) excluded: glm-5.3-flash | blackout rule: zai-peak-hours-sgt | reason_code: preserve_zai_offpeak ``` With this branch, the same instant/request yields `selected: zai/glm-5.3/low` (`selected_balanced`), no `policy_blackout` exclusions, and `expired_blackout_rules: ['zai-peak-hours-sgt', 'zai-peak-hours-sgt-post-campaign']` (explanation only). ## Official Z.ai sources (fetched 2026-09-28; semantic authority) - [Plan Update Announcement](https://docs.z.ai/devpack/notice/usage-revision.md): "From September 25 to October 7, 2026, all-day usage will be charged at the **off-peak rate**." · "Peak hours: Monday to Friday, 14:00–18:00 Singapore Standard Time (UTC+8)." · GLM-5.3: "1× during off-peak hours and 3× during peak hours"; GLM-5.3-Flash: "0.4× during off-peak hours and 1.2× during peak hours." - [GLM-5.3-Flash Usage Campaign](https://docs.z.ai/devpack/notice/event-glm-5.3-flash.md): "Campaign period: September 3, 2026 to October 7, 2026"; daily "from 23:00 to 09:00 the following day" Singapore Time; via ZCode/AutoClaw "Zero quota consumption for unlimited usage"; via other supported agents "Available quota is doubled based on your plan's standard quota rules"; "This campaign takes effect only in ZCode version 3.10 and later."; 5 hours/week participation cap; "This campaign applies only to GLM-5.3-Flash." - No official source contradicted the task facts. ## Two independent campaigns — kept separate 1. **All-day off-peak campaign (Sep 25 – Oct 7, 2026)** — a provider-wide rate-class override: `effective_rate_class = off_peak` all day, i.e. GLM-5.3 at 1× and Flash at 0.4× even in nominal peak windows. This is the mandatory correction: it removes the wrong `preserve_zai_offpeak` exclusion. 2. **GLM-5.3-Flash Usage Campaign (Sep 3 – Oct 7, 2026, nightly 23:00–09:00 SGT)** — a Flash-specific benefit whose economics depend on the **execution method** (zero quota via ZCode ≥ 3.10/AutoClaw, doubled quota via other agents). The two overlays are never collapsed into one multiplier; composition during an ordinary weekday peak: GLM-5.3 3×→1×; Flash via ordinary agent 1.2×→0.4×; Flash via eligible ZCode in the nightly window → zero nominal burn (subject to the weekly cap). ## Exact temporal semantics implemented - `WeeklyBlackoutRule` gains the same optional inclusive local calendar bounds `start_date`/`end_date` (`YYYY-MM-DD`, in the rule's zone, `start_date <= end_date`) that `WeeklyHappyHourRule` has had since D-035 — one shared implementation (`_v_date_bounds`, `_date_bounds_contain`, `_date_bounds_expired`) so the rule kinds cannot drift at boundaries. A rule whose weekly window covers an instant while its bounds do not **does not block**; bounds are inclusive on both ends; the all-day campaign is encoded as calendar dates because the official definition is date-granular (no hour-level boundary is published). - The shipped default encodes the campaign as the **dated gap** between two otherwise-identical blackout halves: `zai-peak-hours-sgt` (`end_date: 2026-09-24`) and `zai-peak-hours-sgt-post-campaign` (`start_date: 2026-10-08`). Boundary behavior: Thu Sep 24 15:00 SGT blocked; Fri Sep 25 00:00 and 14:00 SGT not blocked; Wed Oct 7 14:00 SGT not blocked; Thu Oct 8 14:00 SGT blocked again — automatic expiry, no configuration change on Oct 8. - `zai-flash-campaign-night-sgt` is extended to `end_date: 2026-10-07` (inclusive; the last covered night is Oct 7 02:30 SGT). - Explanation symmetry with D-035: `WeeklyBlackoutRule.is_date_expired_at` plus the additive `SelectionDecision.expired_blackout_rules` member (sorted; serialized only when non-empty; listed by `--explain`) name parked rules. Additive, machine-interface v1 (D-039/D-035/D-057 precedent): documents without the new keys load and round-trip byte-identically. - Asia/Singapore is the policy calendar; evaluation converts caller-supplied timezone-aware instants into the rule's zone; the clock is injected (`select_model(evaluated_at=…)` / simulate `evaluated_at` override); no test depends on the current date. ## Why the permanent baseline peak schedule is preserved The campaign is temporary; the ordinary Mon–Fri 14:00–18:00 SGT peak economics return on Oct 8. Peak logic is **not** disabled globally and "always off-peak" is **not** substituted for the baseline: only the dated window suspends the blackout, and the standing schedule resumes without human action. Ranking policy is untouched — no model is newly preferred because it is cheaper; only eligibility economics and explanation output changed. ## What is deliberately NOT automated (Flash/ZCode boundary) The Flash campaign's zero-quota benefit is an attribute of the **execution method** (ZCode ≥ 3.10 / AutoClaw), not of Z.ai or Flash globally. The selector has no authoritative per-request execution-source identity or version — D-053 sources are administrator grants bound to workers, not request attestation — so modeling `client == "zcode"` would be fabricated certainty and would violate harness-independence. Accordingly: the nightly rule stays a **ranking preference** (D-035 semantics), never an eligibility or zero-cost claim; `null`/unknown source never yields a zero-quota assumption; capacity exhaustion still excludes (the weekly 5 h/week cap is exactly the D-035 "campaign small print" case); GLM-5.3 is never targeted. If execution-source attestation ever becomes authoritative, that is a separate decision. ## Regression tests added (deterministic, injected clock) - `tests/test_resource_policy.py`: blackout date-bound validation (ordering/format), round-trip incl. pre-D-059 documents unchanged, inclusive bounds on both ends, one-sided bounds, UTC→local-calendar-date conversion, suspension inside the gap, `is_date_expired_at` semantics, naive-instant rejection. - `tests/test_selection_app.py`: example-policy loads two dated halves; last pre-campaign peak still blocks; campaign first day / first minute / last day select Z.ai with no `policy_blackout` exclusions; weekend inside campaign unchanged (no double discount); `provider=zai`-constrained request survives campaign peak (the #154 regression); ordinary peak resumes Oct 8; `expired_blackout_rules` values per phase; `--explain` lists the parked rule. - `tests/test_happy_hours.py`: extended campaign window boundaries (before Sep 3, overlap with the all-day campaign, inclusive last night Oct 7, first excluded night Oct 8), GLM-5.3 never targeted. - Policy-level invariants re-asserted by existing tests: blackout wins over overlapping happy hour; exhaustion still excludes inside a window; knowledge class still dominates; preference never bypasses hard constraints. ## Test/check results - `uv sync --only-dev` — OK. - `make check` — full suite **2128 tests OK**. One full-suite run showed a single failure in `test_e2e_execution.CancellationTests.test_scenario_11_client_disconnect_propagates_cancel`; it passes in isolation and is the documented pre-existing load-sensitive race (issue #143), untouched by this diff. Re-run: clean. - `uv run basedpyright` — 0 errors, 0 warnings, 0 notes. - `git diff --check` — clean. ## Documentation `docs/selection-policy.md` (blackout date bounds + "Current vendor campaigns" subsection with sources), `README.md` (shipped-default description), `docs/decisions.md` (**D-059**). No catalog ratings, capability claims, ranking semantics or breaking serialized-contract changes. **UX impact:** positive — an otherwise eligible Z.ai request is no longer wrongly rejected during the campaign; a parked blackout is now visible (`expired_blackout_rules` + `--explain` "Expired blackout rules") instead of going silent; `preserve_zai_offpeak` explanations continue to name the exact rule and reason outside the campaign. Per repository governance: PR left open for human review; not merged; `develop` not promoted; nothing deployed.
Author
Member

Independent review outcome (bounded review lifecycle)

  • Reviewed head (frozen): e7ee4b8d5375a770fffe4013b56bdce9752c61e2 — unchanged during review; no commits pushed after review started.
  • Reviewer: independent exact-head review on GLM-5.3-Flash$max; every finding independently re-confirmed by a separate confirmer that read the code at the reviewed head.
  • Verdict: APPROVED — 0 MERGE_BLOCKER, 2 DEFER (both verified).
  • UX impact: none (positive) — the #154 failure (constrained provider=zai request during the campaign returning no_eligible_candidate / preserve_zai_offpeak) was reproduced on base and confirmed fixed at the reviewed head; --explain gains the truthful "Expired blackout rules" section; old policy documents load and round-trip unchanged; the additive decision member is serialized only when non-empty, so existing REST/MCP consumers see no change; happy-hour validation error messages are byte-identical to base (verified by execution).
  • Security classification: not security-critical; no Daybreak gate required per issue #154.
  • Full report artifact: "PR #155 review report" (published by the review run).

DEFER-1 — cross-midnight window tail vs. single-day date bounds (documentation clarification)

scarcity_router/policy.py:336 (with policy.py:302-308, policy.py:1153-1160). For a cross-midnight rule with single-day bounds, the after-midnight tail falls on the next calendar date and is treated as outside the bounds (Mon 17:00→Tue 03:00 with bounds Mon..Mon blocks Monday evening but not Tuesday 00:00–03:00). This is inherited, tested D-035 semantics that D-059 deliberately mirrors via the shared helpers. The confirmer additionally notes the interaction is live in the shipped default: zai-flash-campaign-night-sgt (23:00→09:00 nightly, bounds 2026-09-03..2026-10-07) truncates the vendor's final nightly window at 2026-10-08T00:00 SGT and over-admits the 00:00–09:00 tail on 2026-09-03. Effect is ranking-preference-only (never eligibility/quota claims), the calendar-date encoding is the D-059-recorded resolved interpretation of the vendor's date-granular definition. Deferred action: spell the interaction out in docs/selection-policy.md in a future docs change.

DEFER-2 — ended blackout half stays permanently "expired" in explain output

examples/selector-policy.json:19 (with selector.py:1948-1954, selection_app.py:918-926). After Oct 8 the ended half zai-peak-hours-sgt (end_date 2026-09-24) remains date-expired and is listed under "Expired blackout rules" in every Mon–Fri 14:00–18:00 SGT decision/explain. Output is truthful and harmless (explanation-only, matches D-059's deliberate signal; confirmed empirically at 2026-09-25, 2026-10-15 and 2027-06-15). Deferred action: consolidate the shipped default back to a single standing rule after the campaign ends — a policy-file edit, no code change.

Final verification

No blockers identified and no remediation performed, so final verification confirms the existing gate on the unchanged reviewed head: make check full suite 2128 tests OK (clean re-run; one earlier run exhibited the pre-existing documented load flake #143, which passes in isolation), uv run basedpyright 0 errors, git diff --check clean, origin/develop..HEAD contains exactly one non-merge commit limited to the 10 in-scope files.

Human merge gate: open — this PR is not merged by agents.

## Independent review outcome (bounded review lifecycle) - **Reviewed head (frozen):** `e7ee4b8d5375a770fffe4013b56bdce9752c61e2` — unchanged during review; no commits pushed after review started. - **Reviewer:** independent exact-head review on `GLM-5.3-Flash$max`; every finding independently re-confirmed by a separate confirmer that read the code at the reviewed head. - **Verdict:** **APPROVED** — 0 MERGE_BLOCKER, 2 DEFER (both verified). - **UX impact:** none (positive) — the #154 failure (constrained `provider=zai` request during the campaign returning `no_eligible_candidate` / `preserve_zai_offpeak`) was reproduced on base and confirmed fixed at the reviewed head; `--explain` gains the truthful "Expired blackout rules" section; old policy documents load and round-trip unchanged; the additive decision member is serialized only when non-empty, so existing REST/MCP consumers see no change; happy-hour validation error messages are byte-identical to base (verified by execution). - **Security classification:** not security-critical; no Daybreak gate required per issue #154. - Full report artifact: "PR #155 review report" (published by the review run). ### DEFER-1 — cross-midnight window tail vs. single-day date bounds (documentation clarification) `scarcity_router/policy.py:336` (with `policy.py:302-308`, `policy.py:1153-1160`). For a cross-midnight rule with single-day bounds, the after-midnight tail falls on the next calendar date and is treated as outside the bounds (Mon 17:00→Tue 03:00 with bounds Mon..Mon blocks Monday evening but not Tuesday 00:00–03:00). This is inherited, tested D-035 semantics that D-059 deliberately mirrors via the shared helpers. The confirmer additionally notes the interaction is **live** in the shipped default: `zai-flash-campaign-night-sgt` (23:00→09:00 nightly, bounds 2026-09-03..2026-10-07) truncates the vendor's final nightly window at 2026-10-08T00:00 SGT and over-admits the 00:00–09:00 tail on 2026-09-03. Effect is ranking-preference-only (never eligibility/quota claims), the calendar-date encoding is the D-059-recorded resolved interpretation of the vendor's date-granular definition. Deferred action: spell the interaction out in `docs/selection-policy.md` in a future docs change. ### DEFER-2 — ended blackout half stays permanently "expired" in explain output `examples/selector-policy.json:19` (with `selector.py:1948-1954`, `selection_app.py:918-926`). After Oct 8 the ended half `zai-peak-hours-sgt` (end_date 2026-09-24) remains date-expired and is listed under "Expired blackout rules" in every Mon–Fri 14:00–18:00 SGT decision/explain. Output is truthful and harmless (explanation-only, matches D-059's deliberate signal; confirmed empirically at 2026-09-25, 2026-10-15 and 2027-06-15). Deferred action: consolidate the shipped default back to a single standing rule after the campaign ends — a policy-file edit, no code change. ### Final verification No blockers identified and no remediation performed, so final verification confirms the existing gate on the unchanged reviewed head: `make check` full suite 2128 tests OK (clean re-run; one earlier run exhibited the pre-existing documented load flake #143, which passes in isolation), `uv run basedpyright` 0 errors, `git diff --check` clean, `origin/develop..HEAD` contains exactly one non-merge commit limited to the 10 in-scope files. Human merge gate: open — this PR is not merged by agents.
adrian.tkacz deleted branch policy/zai-temp-campaigns 2026-09-28 12:30:07 +00:00
Sign in to join this conversation.
No description provided.