Course-launch batch schedule: one confirmed cohort launch-and-schedule enqueues eligible announcements
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 35/100
- issue の種類
- 機能追加
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
調査の方向性
Start with courses/services/launch_batch.py and its focused test courses/tests/test_course_launch_batch_schedule.py, then inspect templates/studio/course_launch.html and the OpenAPI/CLI entry points. Run the focused Django and CLI tests first. Done means preview makes no changes, confirmed execution enqueues each eligible revision once, and Studio/API/CLI expose identical skip reasons; the issue is already marked implemented and QA-accepted.
索引モデルが issue の本文から書いたものです。
説明
Course-launch batch schedule (dependent follow-up to #467)
Raw reporter intent preserved: user requires cohort launch to GENERATE AND SCHEDULE automatically (linked to module/project STARTs); sending stays explicit/scheduled. Core #467 generate() creates only unarmed draft proposed times (armed: 0, no dispatch) requiring per-row approve+schedule. This issue adds the missing one-action batch scheduling without changing core semantics.
Groomed spec (PM, 2026-10-17)
Normative links
- #467 (prerequisite core increment): templates/publications, Course defaults, cohort overrides, native starts, unarmed generation, per-row approve/schedule/send, Relay
coursessimulation, Telegram quarantine, dual manage+send authority, CLI auth/parity. - Specs 04/05/06 + open-decisions §10/§11 (Relay), §19 pattern (no secret retention); playbooks
02-course-launch-social-system.md(module/project announcements),01-course-platform-setup.md(Monday 23:00 UTC, copy-previous outline). - Ownership: website canonical scheduling (C-LAUNCH-1); no CMP change; no new scheduler (reuse
community_base.jobsleaseddispatch_after_commit).
Scope (website repo only, additive)
- One explicit confirmed privileged batch action (
launch_and_scheduleor equivalent name): Studio + Admin API + CLI parity, samesendcapability (dual manage+send authority),confirmed: truerequired,Idempotency-Keyrequired. - Two-step UX: preview/dry-run first (eligible count + per-row skip reasons, no mutations), then confirmed execute. Studio shows batch section separately from per-row approve/schedule; CLI offers
--dry-run(default preview) +--confirmedexecute. - Eligibility: only generated rows with immutable template version, known future anchor (
starts_atpresent, proposedanchor+offset > now), status draft/approved (not settled/dispatching/submitted/ambiguous, not cancelled), live cohort + current curriculum. Everything else skipped with explicit reason (missing_start,past_due,settled,cancelled,blocked_destination,stale_revision). - Effect per eligible row: approve current copy (set
approved_revision = revisionafter preview render passes) + enqueue one durable scheduled job at proposed time via the exact single-schedule dispatch path (same idempotency key schemeannouncement:<id>:<revision>, same revision fencing). Returns per-rowenqueued/skipped{reason}+ batch summary counts. Rejected naive/missing/past never enqueued; no catch-up. - Safety preserved: simulation default (non-prod or flag-off → simulated, never live); Relay sender
coursesonly; Telegram uncertain-ack quarantine (no auto-retry); no secret/destination persistence beyond setting key; audit redacted; preference enforcement unchanged (enrolled +email_course_updates). - Idempotency/revision: same-key replay returns prior batch result without duplicate jobs; per-row stale revision → skip with reason (never overwrite another operator's edit); assignment/template change mid-batch fences via existing reconcile (disarm) path.
- Docs/tests: runbook section (batch vs per-row, eligibility table, dry-run example), OpenAPI + registry entries, focused Django tests, CLI tests, desktop+mobile screenshots of batch preview + result (reason codes visible).
Non-goals
- No change to #467 core semantics:
generate()stays preview-only/unarmed; per-row approve/schedule/send/cancel/reschedule unchanged; no renaming drafts as auto-scheduled. - No batch immediate-send (
send nowstays per-row explicit only); no past-due catch-up; no resurrecting cancelled/settled rows via batch. - No new scheduler infra, worker, or cron; no production sender config/deploy; no live/real-user sends; no CMP/import changes; no RBAC redesign (existing dual-authority + global scope).
- No mass-assignment or existence leaks; support role still excluded from bodies/recipients.
Dependencies
- Depends on #467 (must land first or coordinator sequences branches; this work must not mutate the frozen v5/v6 core diff). Blocked while #467 gates (broad run + Argon final + PM accept) are pending.
Acceptance criteria
- Batch preview (dry-run) lists eligible + skipped-with-reason per cohort with zero mutations and zero jobs; confirmed execute with
Idempotency-Keyenqueues exactly the eligible future rows once each (job per row, proposed time = anchor+offset). - Missing/past/settled/cancelled/blocked rows are skipped with explicit reasons; past rows never dispatched; cancelled rows stay cancelled (explicit per-row recovery only).
- Same-key replay returns identical batch result with no duplicate jobs; stale-revision rows skip (409-style reason) without overwriting.
- Permission: dual manage+send (or site_admin) can execute; course-only or email-only principals get 401/403 denial with no existence leak and no partial enqueue (atomic per-row, reported).
- Simulation: dev/flag-off run enqueues simulated jobs only (no live Relay/Telegram IO); live path unchanged and still requires authorized release/config (human step).
- Studio/API/CLI parity: Studio batch section + API action + CLI command produce identical results;
--dry-rundefault,--confirmedrequired to execute;--json/help/exit codes consistent with existing CLI. - Screenshots: desktop+mobile batch preview + post-execute states showing enqueued/skipped reasons, no error/debug/broken layout.
- No
.env/tokens/DB/screenshots committed; scratch under.tmp/; migration-drift clean (no schema change expected; if any, nullable additive + null-preserving backfill rule from #467 applies).
Django/integration scenarios
- Live cohort, all starts future → preview N eligible/0 skipped → confirm → N jobs, each row approved+scheduled, single idempotency keys, replay → same result, zero duplicates.
- Mixed cohort (one missing start, one past start, one cancelled, one dispatched) → preview 1 eligible + 4 skipped with reasons → confirm → 1 job only.
- Dual-role principal succeeds; single-role principals denied with zero side effects.
Browser scenarios (desktop + mobile)
- Studio batch preview → confirm → result list (enqueued + skipped reasons); denied principal sees safe denial; empty cohort (no versions/starts) shows guidance, not silent no-op.
Repository/operations scenarios
- Focused Django + CLI tests for batch contract; Playwright smoke/core as selector demands; versioned verification plan with base/head + digests; no live sends, secrets, or cloud mutations.
Roles
- Engineer: launch-web (after #467 lands / coordinator sequences). Tester: independent (Argon or next). PM acceptance: after tester passes; overall #467+#468 goal closes only when both shipped.
QA Review (Argon QA launch-qa2, 2026-10-17 — ACCEPT)
Verdict
- Status:
ACCEPT(QA2-468-FINAL-ACCEPT) - Reviewer: Independent Final QA (
launch-qa2, sessionb51faf34-e16a-4107-bf14-38f70c545c4c, engine Argon) - Frozen Source Bundle:
/home/alexey/git/dtc-website-wt-course-launch-integrated/.tmp/launch-integrated19/(19 changed paths overHEAD054f233d7e8192f832064603c109fdd3f8b02a15)source-manifest.jsonSHA-256:8aff8b24354cbd18f2d82a3465605b5fa3548724faa745404bd415de49b1fb6etracked.patchSHA-256:216a9d516e1d26e6e36db6e2de2024e9cc2c2c163bd765a32a5caff229c73e23courses/services/launch_batch.pySHA-256:172cd9afa30a4a7e827367b43a2d671026becc7b72c8a2505ead358278e60c64courses/tests/test_course_launch_batch_schedule.pySHA-256:fca3a933192bb27b15dddd3905cf9afac64400ab99c0081c50ba2c634ab4295ctemplates/studio/course_launch.htmlSHA-256:3126690b23c9e4ac6039a3d001c9d1488e23c88828e581b33b0f5c942bbe2743
Verification Plan & 7-Component Full Report Validation
- Author
verification-plan.jsonDigests:- Raw file
sha256sum:8b87b6e264aca497c09de15181197c0630aea856062727b9a00ace8df500604d - Canonical
sha256_json(plan)(report.plan_sha256atci/verification.py:879):88b49abb90bf640c0cd64eccb120103ffe7ccc02ce5154c14da16d0a49a09b60
- Raw file
- Tester
verification-plan.jsonDigests:- Raw file
sha256sum:564f363a407ee9584ba038c435b9aaea35f27aa8561805a8c8699224f72515b0 - Canonical
sha256_json(plan):bae325f0b5587984b60713df03a5a4e27369d091fb3656b86f9fabe51a6114df
- Raw file
- Shared Plan Digests (Identical Across Author & Tester Plans):
source_tree.manifest_sha256:21ff298bb27c21e141ceed93b28d1ac4eb596c1deb01ce83add00a91a126c621render.sha256:7af0b74118152b92e9234ad0631e2e47062f1d3b8650ca909a2b623a574c6d6bgraph_sha256:bb924cbe4e46a273f1cea46f5e85f1b9b0711e7ff8b1100b0162bc306de1186bverification_state_sha256:897e2b7b5ef6ff645bec50901772143e103e6a839cc34ab1e67253bb247a34fb
- Completed Report (
/home/alexey/git/dtc-website-wt-course-launch-integrated/.tmp/verification-468-final19b/):verification-report.jsonSHA-256:1c753cde5e9e05c6c25761f94ebacb35315d162bf2d31c6fda7610edc28d994d(Verdict: success;validate-report+validate-evidence-directory+ all 7validate-evidencechecks PASS)verification-report.mdSHA-256:f731e453913b8443d60a9e21c4673a43a9c3726dfe331767a3e71acc06d604a3- Explanation of
invalid_evidence: [{"component": "screenshots", "reason": "render_inputs_changed"}]: Inci/verification.py:899-905,report["invalid_evidence"]is a mandatory historical plan-selection diagnostic derived from[component for component, item in plan["components"].items() if item["disposition"] == "rerun" and item["reason"] in INVALID_EVIDENCE_REASONS]. Becausescreenshotsis always planned withdisposition: "rerun"andreason: "render_inputs_changed"when render inputs change (and"render_inputs_changed" in INVALID_EVIDENCE_REASONS),validate_reportstrictly enforces thatinvalid_evidencecontainsscreenshots:render_inputs_changedeven after the freshscreenshotsrerun (evidence_id: f7af1e6b7496964f395cf82ff34a0b62741f8c0b1f2fb01ed8259df297682582,result: "success") passes inbuckets.rerun. - Container Ambient Environment Verification:
container-environment.json(0a099d7b17b7c90044c908e065e8e20e1fddbfcc77e3a9b3330502c2071da55d) matchescomponent_environment_fingerprint("container")(environment.sha256 = e0764a1d065452d57f9347633766dfe0464f35a8193cb9e365e1d928bb77f9f5) andplan.components.container.environment.sha256from the normal runner (scripts/ci.py) without any forced fingerprint override (13container assertions passed,status: "pass").
- 7 Component Evidence Envelopes (
all result=success, failed=0):selector-evidence.json:f149d6c3afc297247495ccf375651479d54bb0dfda02c791d57dea47e4876624(changed_paths=19,direct_nodes=9,downstream_nodes=10)quality-evidence.json:90e19ca53dce7d4943c664c9b31b0e85cc66cccbaedf37d4c8ffae2c6792ca45(tests=765, passed=760, skipped=5, failed=0)django-evidence.json:805ce02a97c90ba00f6646c9a9b5fa38b792d2b0a065d4962a7dafac51432cc6(tests=4358, passed=4352, skipped=6, failed=0)playwright-evidence.json:5cf56448ef09f1b1beb88074cf2c0bfadec39fba97c273881dfeeeeaf2434334(attempted=352, passed=352, failed=0, rerun=0, quarantined=0)container-evidence.json:9690c151db030bf7d681a6f84406136322836e923333935adf49a2d117bc8255(assertions=13, failed=0)screenshots-evidence.json(producer_role=tester):0c0a06b4edda64c9e046fea489aa817b796ca710a81b3df4719ab2e6308d89c4(captures=8, assertions=8, failed=0,screenshots.json3166e1ab17c4b5c9937d088b8c9f1c4aee5b9b906394bdd57f26ed04ba8b5290)evidence_validation-evidence.json:bdefae542ad4777406921b6a3083b17566bb9104d642558cbd57b3bff31d665d(tests=765, passed=760, skipped=5, failed=0)
Independent Functional, Security & Adversarial Verification
- Two-Step Preview & Confirmed Batch Execution:
preview_cohort_launch_scheduleis strictly read-only (0CourseLaunchAnnouncement,0JobIntent,0ManagementIdempotencyRecord, and0ManagementAuditLogwrites) and returns a Django-signedpreview_tokenbindingcohort_id,template_state_sha256, and each eligible row'skey,revision,anchor_starts_at, andproposed_scheduled_for.confirm_cohort_launch_schedulerequires dualcourses.manage_course_launch+courses.send_course_launch_announcementauthority (checked before idempotency reservation or row mutation),confirmed=True, a valid signedpreview_token, and anIdempotency-Key. Legacy client-supplied preview maps (expected_revisions/preview_revisions/eligible_keys) are rejected with400.
- Lock Ordering, Template State Fencing & Prospective Null-Revision Guard:
- Confirmed execution acquires locks in canonical order
Course -> existing CourseLaunchTemplateAssignment -> Cohort -> existing CohortAnnouncementPlan -> CourseLaunchAnnouncement (by PK), raising409 Conflictcleanly inside the atomic transaction when parent rows disappear ortemplate_state_sha256(effective assignment, published template version, channel config, destination key, or resolved destination) drifts between preview and confirm. - Prospective rows (
revision is Noneinpreview_token) require currentrevision == 1,approved_revision is None, rawsubject_template/body_templatematching the published template version,context_overrides == {}, andcohort.announcement_context == default_cohort_announcement_context(cohort); any post-preview edit skips the row asstale_revision.
- Confirmed execution acquires locks in canonical order
- Eligibility, Durable JobIntent Scheduling & Idempotent Replay:
- Only future-anchored (
anchor + offset > now) draft/approved rows on active non-self-paced cohorts with valid destinations are approved and enqueued via_enqueue_scheduled_dispatch(idempotency_key = announcement:<id>:<revision>,scheduled_for = recomputed anchor + offset). - Rows with missing/past starts, manual edits, cancelled/settled/already-scheduled states, or blocked destinations are safely skipped with explicit reasons (
missing_start,past_due,stale_revision,cancelled,settled,already_scheduled,blocked_destination,not_in_preview) without re-arming or catch-up. - Same-key idempotent replay returns the stored response (
replayed: True) without creating duplicateJobIntentrows. Non-production / flag-off runs remain strictly simulated (simulated_only).
- Only future-anchored (
- Studio / Admin API / CLI Parity:
- Verified across 78 Django course-launch tests (
32intest_course_launch_batch_schedule.py+46existing course-launch regressions) and 2 live-server HTTP CLI Playwright tests (playwright_tests/test_course_launch_cli.py), exercising both--dry-runpreview and--confirmed --preview-token <TOKEN> --idempotency-key <KEY>execution.
- Verified across 78 Django course-launch tests (
Visual & Screenshot Evidence (Inspected via Image Viewer)
- 8 Graph-Required Screenshots (
/home/alexey/git/dtc-website-wt-course-launch-qa2/.tmp/verification/evidence/screenshots/):courses-courses-desktop-1440x900.png:5f5a9a3e4429b4fb40ede2c4f439b4ae3cdcc8e53d8fc0a6445116b2f12dd470courses-courses-mobile-390x844.png:19822e0448354cf6d7c53e1e9f4730cce88fb6670a63307ee1fa0b13c7c0042chome-home-desktop-1440x900.png:1bd927557c21aa4428854564341d32f18cea1bcc119608aa6bb380da7e0878d3home-home-mobile-390x844.png:8f64b0818a23a3593ea3510c52100ccbbf634fdb17fb630df79e92c3686a5f59studio-studio-sign-in-desktop-1440x900.png:84253046e0b9865295302aec4670292e631c0ee0af8edc671233ef9f513f4b27studio-studio-sign-in-mobile-390x844.png:1a10b294003b658f444baa72fe327d0cd3127e291bbfad3fe00f69e2a5677440verification-missing-not-found-desktop-1440x900.png:d22f14f0225528d6abe09ab2d20e6159d3aa93013752c6d8858170093ad06314verification-missing-not-found-mobile-390x844.png:9a22572bab68bf58f92bd06a025090ad70a0cc2ab0f5901f72db46439e4048ca
- 4 Studio Batch Preview & Post-Confirm Screenshots (
/home/alexey/git/dtc-website-wt-course-launch-qa2/.tmp/screenshots/issue-468/,manifest.json86d41e9990b7d8fe8b26fc2877966e29998d64b787be66661e5d91d6f48b1503):batch-preview-desktop-1440x900.png:3e5739975166c6ec5e44b6d8c8a600504b50c8ba7b17e99d8ea205d96b7a7e66batch-preview-mobile-390x844.png:06c81c9fc65b5094f0925d4e27c24d1a101f64d0a2282041f26f2579d3421c0bbatch-confirmed-desktop-1440x900.png:666414f273591fe7d0e63709ce943cb5232a818c71c1bd4f5240577131996a56batch-confirmed-mobile-390x844.png:8850c331ea99d23b0ab8854029eb064bcdab627bd926610d92a00ec8cf563828
Rollout Boundary
- Code and verification evidence are complete and accepted on the frozen 19-file bundle over
054f233d7e8192f832064603c109fdd3f8b02a15. No live announcements or cloud deployments were triggered during QA; production deployment, live sender credentials, andCOURSE_ANNOUNCEMENTS_ENABLEDactivation (website/settings/announcements.py:12) remain explicit human-operated release steps.
- 主要言語
- Python
- スター
- 0
- フォーク
- 0
- PR マージ指標
- 30日以内にマージされた PR はありません
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートなし
- コントリビューションガイドなし
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
DataTalksClub/website のほかの issue
-
bug infra operations P1 testing
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
DataTalksClub/website#329 ·
-
bug content data-migration documentation events operations P1
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
DataTalksClub/website#327 ·
-
bug frontend P0 testing
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100
DataTalksClub/website#300 · コメント 7 件 ·
-
bug data-migration events P0 testing
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
DataTalksClub/website#295 · コメント 6 件 ·
-
admin courses data-migration email enhancement integration operations P1
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
DataTalksClub/website#467 · コメント 3 件 ·
DataTalksClub/website の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
webrecorder/warcio#209 ·
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
Jeffy123-zhu/logspecter#20 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
メンテナーはふだん 1 日以内に返信