We should publish two articles every morning at 9 AM, and avoid relying solely on scheduled triggers like “usually only runs once.” Retries, manual interventions, and deployment switches could all result in the same task being executed multiple times on the same day. The publishing system must ensure that the same batch of articles is executed multiple times without sending duplicate articles or changing the order.
This document discusses the workflow for publishing with both private draft sources and publicly built releases. The Cron Trigger is responsible for triggering the process, but not for providing complete business transactions. The execution and propagation times for specific services should be based on the current document.
First, separate the triggering events from the release plan.
Scheduling an event provides an opportunity to publish, and the release plan determines which day, which two articles, and which content can be made public. A triggered event can be re-run, but a plan cannot be changed due to a re-run. Assuming the first article has been successfully converted, and the second article copy failed, the next time should continue with the same plan, without taking two new articles from the remaining queue. The plan saves stable article identifiers and audit content fingerprints. After the editor makes changes, the content needs to be re-approved, rather than continuing to use the old plan to publish new content.
The plan should also be linked to the release policy version. Changes in the daily release quantity, start date, and category proportions should not be reinterpreted for previously completed batches. To reconcile, the original plan’s facts must be maintained, and future batches should use the new policy. Private document titles and content should not be entered into the public log, and scheduling records can be used with internal identifiers. The plan data itself also needs protection, as it may reveal unpublished content arrangements.
function batchId(instant) {
const parts = new Intl.DateTimeFormat('en-CA', {
timeZone: 'Asia/Hong_Kong',
calendar: 'gregory', numberingSystem: 'latn',
year: 'numeric', month: '2-digit', day: '2-digit',
}).formatToParts(instant);
const field = (type) => parts.find((part) => part.type === type)?.value;
const year = field('year');
const month = field('month');
const day = field('day');
if (!/^\d{4}$/.test(year || '') || !/^\d{2}$/.test(month || '')
|| !/^\d{2}$/.test(day || '')) {
throw new Error('Unsupported business date');
}
return `daily-publication:${year}-${month}-${day}`;
}
Do not use the local date of the build machine, and do not treat the execution time of each run as a batch ID. Scheduling expressions typically use UTC, and expressions corresponding to 9:00 AM in Hong Kong must be explicitly converted to a specific time zone. For regions with daylight saving time, a more careful calendar scheduling design is required.
The order and delimiters for displaying the format are not a persistent identity protocol and cannot rely on en-CA to always display YYYY-MM-DD. Instead, it explicitly serializes the year, month, and day obtained from formatToParts, using the Gregorian calendar and Latin numerals, and limits the business year to a four-digit range. Invalid dates or those outside the supported range will directly fail. It calculates the date identity for the initial plan, and when restoring across days, it still reads the previously saved business dates without re-selecting the current moment for batch processing.
Business dates are calculated based on the editor’s time zone and cannot be directly used as a Hong Kong plan identity. Triggers that occur across midnight and re-runs should still be associated with the correct plan, and summer time regions also require the definition of repeat time policies. The time provided by the scheduler only helps to identify the trigger; the editor decides which day the plan should be made public.
A State Machine Expressing Recovery Actions for Each Stage
A batch can be modeled as having the following stages: plan generated, assigned, public documents prepared, build verified, version deployed, and results confirmed. Each stage records the input fingerprint and the output version. If a stage fails, the last completed stage is retained. The transition between states cannot be solely determined by “command exit zero,” for example, after a successful deployment upload, it is still necessary to confirm the target domain and the specific article. Successful definition must align with the user-visible results, so that local preparation is not considered as a completed release.
The status should allow for recovery but not for reverting. Once the draft is prepared, any changes should be stopped and require a new review, rather than silently continuing with the original text. After deployment confirmation, the process should return the completed result without generating a new publication date. The status field can have more or fewer entries than the example, but the key is that there must be verifiable evidence for each external side effect. A single “done” boolean value cannot express partial success.
The lease is used to handle concurrent claims, and the duration is used to recover from crashed instances. The lease expiration does not guarantee that the old instance has stopped, and a fencing version or explicit coordination mechanism is needed to prevent two executors from writing to the same result. Tools such as database unique constraints, transactions, or durable objects have their own applicability, and it is important to first choose a reliable consistency foundation and then write the business state. A simple “read-then-write” approach does not provide mutual exclusion guarantees.
Two instances checking “not yet published” simultaneously might arrive at the same answer. After checking, the non-winning instance should write “not locked.” This can be achieved using database-level unique constraints, transactions, or a suitable single coordinator, ensuring that only one instance successfully claims a batch.
The status should distinguish between planned, claimed, publicly built, deployed successfully, and failed states. It should not permanently mark a state as “successful” before copying files, and it should not rely on a single boolean value to determine failure locations. Locks or leases should have expiration and recovery mechanisms to prevent tasks from being permanently held after a process crashes.
Idempotent keys should be associated with the same business input
When the same key is executed repeatedly from the same batch, the input should be the same. If the key is on the same day, but the document set changes, the system should reject and report the conflict, treating it as a normal retry, not a regular one. Recording the two documents, along with their summaries and target versions, allows you to determine whether it’s a continuation of known work or a new release request. While the date serves as an identifier, input consistency is what gives “send once” its clear meaning.
The process of writing to publicly accessible files uses a controlled temporary location, followed by verification before entering the build source. This ensures that no incomplete files are left behind in case of failure. If a target file already exists, its content is compared against the plan. If they are identical, the process can continue. If they differ, the process is stopped, and unknown modifications cannot be overwritten. Static artifacts can be redeployed, provided that the redeployment is of the same, verified set. The generation of random deployment identifiers should not change the identity of the article or the RSS identifier.
Local lock files can protect processes on the same machine, but cannot coordinate processes across multiple machines. Ordinary read and write operations on KV or object storage should not be assumed to have transactional locking semantics. Following the design of the tool’s actual guarantees, avoid extending single-machine demonstration results into a global concurrent commitment. For personal blogs, a single-controlled publishing executor may be sufficient, and a full distributed scheduling platform may not be necessary.
External deployments cannot be rolled back along with the database.
Database transactions cannot be automatically rolled back due to Cloudflare deployments or Git pushes. The deployment process is a cross-system operation and should record completed steps, and each step should be retryable. When generating artifacts, the same plan should be used, and repeating the deployment of the same artifact should maintain the content collection unchanged.
If deployment is successful but the status update fails, the next run should query the deployed version or the public list to reconcile, rather than selecting two articles directly. If submission is successful but the push fails, a clear warning and retry are required. The status “local files have been written” should not be interpreted as a successful publication.
Deployment call timeouts could indicate a failed upload, or the platform completing the deployment but losing the response. Before retrying, verify the deployment record and the public list to confirm whether the product has already been deployed. Since database transactions cannot undo publicly released content, the “published” status must reflect the external reality, not pretend that nothing happened by rolling back the database. Sensitive content that has been publicly released still requires cleanup and risk assessment; simply reverting the status to “draft” is not sufficient.
Git commits and pushes are handled separately. If a local commit is completed but the remote is unreachable, save the commit identity and trigger an alert, then resubmit the commit later without selecting the article. If the remote push is successful but deployment fails, you can continue deploying the same public input. At each step, check the current status against the plan to avoid manual intervention and automatic recovery overlapping. The final result that the user sees is the completion standard.
Log batch, stage, version, and error categories, but not the full text or authentication tokens. Alerts are triggered when manual intervention, failure, or a meaningful state change is required, rather than generating the same message every time. Upon completion of recovery, the verified version and the public article are provided, ensuring that maintainers can determine whether the operation has achieved its goal.
Even human intervention adheres to the same publishing agreement
Manual reprocessing cannot bypass the audit and batch identity checks, otherwise the automated system will be correct, but the manual entry may still generate duplicates. The reprocessing command should specify the business date, first displaying the saved plan and the current stage. Only if the recovery conditions are met should the process continue. When a new version is required, use a clear plan revision and re-audit, and do not rely on deleting the successful status to trigger the system to re-select. Manual and scheduled operations share the same core state logic, which can reduce the risk of inconsistencies between the two.
Pausing automatic publishing does not mean canceling the plan. Maintain the queue, starting rules, and previously published facts. When restoring, make a clear decision based on the policy regarding whether to retroactively approve content. During content optimization, changes to the content fingerprint should invalidate previous approvals. It is not sufficient to simply pause scheduling without checking the approval status after restoration. The scheduling switch controls whether content is activated, and the approval switch controls whether it can be published. Both conditions must be independently met.
The release records require backup and recovery testing. If the status file is lost but the online content still exists, the system cannot simply treat all unreleased articles as new and start over; instead, it should first reconcile based on the public list and remote submissions, and then rebuild the status. The priority of authoritative sources should be clearly defined. When there is a conflict between online facts and the local queue, manual judgment is required, and it should not be automatically overwritten. Use unique constraints to reduce redundancy, and use reconciliation to handle external facts that cannot be rolled back.
The alert for a failed batch on a single day can include the date, stage, and recovery suggestions, but not the private content. Continuous retries will not notify the same state without changes; instead, the status is only reported when the process is truly complete or requires manual selection. The goal of the system is to reliably complete the plan, not to continuously generate and display run logs. Automation can run quietly, but it must make failures visible, especially when remote pushes and deployments are not completed.
The final acceptance requires evidence of recovery for each failed injection point. On that particular day, there were only two, and they also needed to meet the criteria of being pre-defined, having content that aligns with the audit, having the correct timestamps, and not being part of the public output. Idempotency is not simply repeating the operation “without errors,” but rather ensuring that the business facts remain the same after repeated execution. This definition can apply to file conversion, Git, build, and deployment processes.
The preview plan cannot be executed silently. Maintainers can use a dry-run to check if the two planned tasks are scheduled for today. If the preview function also updates the tasks to “claimed,” the actual scheduler might think someone is actively working on them. Separating the planning, approval, saving, and execution of claimed tasks into distinct entries is crucial. Read-only previews can read existing plans but do not advance their status, generate Git commits, or trigger deployments. The term “dry-run” is not sufficient to guarantee that there are no side effects.
Test and save a snapshot of the synthesized state. After running the preview, compare the state, files, and external call counts. All three should remain unchanged. Then, execute the approved action, resulting only in the specified planned record. Finally, only then is public conversion allowed. If preview requires temporary rendering, write it to a separate temporary location and isolate it from the official build input, preventing a single view from becoming an implicit source for future releases.
The content between the plan and execution may change, so the “dry-run” output is not a permanent approved record. The report preserves the content fingerprint and policy version at the time of creation, and is compared again during execution. Any discrepancies should be verified again, rather than simply assuming that the “same” article is being used. This allows the author to safely check the next step without consuming their daily quota or accessing unpublished content.
Who can still add a claim after a claim has timed out?
The executing party, A, has claimed the batch and is preparing the output. The lease is about to expire. Party B has obtained a new lease and is resuming operations. It is not necessarily the case that A has stopped; it may simply be temporarily unable to contact the coordinating service. If both executing parties can create public documents or switch deployments, the lease itself cannot prevent double operations. It is necessary to use the claim number as a barrier condition, rejecting the old claim number when critical persistent states are updated, or choosing a coordinating architecture with clear serial semantics.
Does the external interface support conditional switching? This is a crucial limitation of the design. If the barrier can only prevent database writes but cannot prevent “Platform A” from continuing to use the platform, old versions could still overwrite the data. The executor should only prepare immutable artifacts, and a single, controlled publisher should handle the switch. Alternatively, the system should verify a reliable coordinated state before and after the switch. The specific solution depends on the platform’s capabilities and cannot claim that simply adding an integer to the record automatically protects against all external side effects.
The exercise uses two controlled actors, pausing Actor A until the lease expires, then allowing Actor B to proceed, and finally restoring Actor A. The old instance cannot change the final outcome, while the logs retain the reason for the refusal. This differs from a typical test where both instances start simultaneously: the latter only verifies the resolution of a claim conflict, while the former verifies that an actor that has previously held a right will not continue to disrupt the outcome. Concurrency safety should cover stale actors, not just new competitors.
The date and identity must align with the recovery goal.
The task is selected on a specific business day, and then re-run the next day. The batch ID cannot be recalculated based on the re-run time. The saved business date is used for recovery, and the current time is only used to determine whether it is allowed to continue. Manual re-runs explicitly specify the date, first displaying existing plans and stages, and then executing the approval action. There should be validation to prevent the transmission of future dates or crossing day boundaries, and to prevent any parameters from being prematurely revealed in future drafts.
During the pause, the system does not automatically resume previous drafts. This is an editorial policy, not a technical error caused by a scheduler failing to execute. When resuming, the system should generate future plans from the confirmed starting point, while retaining any unreleased drafts or explicitly marked “skipped” entries. It is not possible to directly convert unreleased drafts into completed drafts to improve the appearance of status numbers, nor is it possible to issue dozens of drafts at once as an implicit compensation. The system should adhere to the approved policies regarding reader pace, review status, and article identity.
The plan revisions and retries should be treated separately. When the dates for “A” and “B” are changed to “A” and “C,” the input set has also changed, and a clear revision and re-approval should be generated. It should also be checked whether “B” has already been published. If “B” has already been deployed, rolling back the local plan cannot delete external facts. The revision records should explain the actual changes that occurred, rather than having multiple contradictory versions of a “two-plan” document in history.
Reconciliation after Timeout Requires Independent Evidence
If the deployment call returns a timeout, first check the deployment logs, the product version, and the public list to determine if the batch has already been completed. Do not only request the homepage, as the homepage may be cached with an older version. Also, do not only rely on a successful upload in the backend, as the domain name may be associated with another project. Specifically, verify the two public addresses, RSS entries, and version lists to confirm which ones have been confirmed and which have not.
Stop automatic progression when discrepancies are detected during reconciliation. For example, if the platform shows a successful deployment, but the source contains only “A” and “B” with detail “B” missing, it should not automatically select “C” to fill the gap. First, either fix the product or ensure consistency, while retaining the original batch of recovered identities. Due to potential propagation delays, use bounded re-reads with a defined deadline. If the deadline is exceeded, manual judgment is required, and continuous polling should not be considered as completion.
Git remote status is also an independent fact. Once a commit identity is saved, a failed push only applies the public changes, and cannot be used to re-convert another set of files. The remote already contains the commit, but the local status has not been updated. First, verify the remote reference, then record the completion. Avoid using a single “done” field to represent files, Git, and deployment. This allows for accurate next steps only after some partial successes.
Private source read permissions do not extend to upload permissions.
To ensure proper selection, the execution tool must read all drafts. However, the upload scope only includes publicly accessible converted versions of approved batches. Separate checks are performed on the input, Git staging list, and deployment directory. Private indexes and backups are not included in any public scope. Directory names like “drafts” or “private” do not provide automatic protection; the actual enumeration of files by the tool determines the risk. Unique sentinel verification ensures that unselected articles are not present in the output or pending submission content.
The review process covers publicly visible elements such as titles, abstracts, code, and citations. Approval will not be granted simply because the text has been “optimized.” A single character can change the command risk, and old fingerprints in a batch are not valid for the current new text. After re-review, the plan or future scheduling approval record should be updated. Unscheduled documents will not automatically enter the daily selection after approval. Approval and scheduling are different permissions, and the status model should maintain the distinction.
Restoring from a backup also requires reconciliation with business data. If the backup is taken before deployment, but the live system is already publicly accessible, it’s not possible to restore and treat the batches as unexecuted, then re-select. First, read the reliable external version and the saved plan to determine which is the most up-to-date, and then fix the internal state. Backing up the protection file does not automatically mean that the restore process knows about external side effects.
Rehearsals can revert the internal state to a previous synthetic snapshot, while preserving records of the simulated deployment completion, and verifying recovery program prompts or identifying existing public facts. Do not delete the actual published state for testing purposes. The records should only contain necessary IDs and versions; private full text should not be copied to public storage for convenience. Handling process crashes and state rollback are two different recovery scenarios.
Separate accounting for “Launch Completion” and “Subscription Notification Completion”
Both articles have been published, but the notification emails failed to send. Therefore, the publication batch should not be reverted to an “unselected” state. The notification should clearly link the published version with the recipient’s identity, while retaining the statuses of “pending delivery,” “confirmed,” and “result unknown,” without invalidating the existing published fact with a single notification failure. Furthermore, the notification execution process should not re-select the two articles due to retries, as this would affect a different business process.
For example, in a group of three synthetic subscribers, two confirm and one times out. The approval and recovery strategy is only applied to the incomplete items. When downstream systems do not support idempotency or result verification, the risk of repeated notifications is accurately recorded. We cannot guarantee that emails will only be received once simply because the article publication itself is idempotent. Private content that has not yet been published is not included in the notification task; the task only references the confirmed public address. The notification system’s read permissions do not extend to all draft sources.
Completion Record: Specific Business Invariants
This section details the specific business invariants that must be maintained during the completion process.
Repeating the final process should ensure that the same two articles, with the same approved content, are published at a consistent time, and that there is no other private content or a verifiable public version. Simply increasing the total count by two does not prove that the selection is correct, and running without errors does not prove that the deployment is complete. These constraints should be used as an acceptance checklist, and failure should result in a date, stage, and next action. The reliability of automation comes from verifiable facts, not from the appearance of a regular pattern of triggers.
Coverage of Time, Concurrency, and Privacy Boundaries in the Exercise Matrix
All of the following scenarios should be reverted to the same two articles:
- Continuous operation of the same business day, two instances simultaneously claiming, crashing after claiming, crashing after building, and failure to write the status after deployment.
- Failure to update the public file, RSS, status record, and online version, in addition to the card list.
- If a draft is edited after failure, the process should be stopped to prevent further publication, demonstrating that the old review cannot approve new content.
Re-test the business date, scheduling delays, and manual overrides to confirm the policy’s clear selection of the target date. Time zone conversion can be tested using a fixed instant, without relying on the current time of the development machine. Since Hong Kong does not observe daylight saving time, this does not mean that all future regions can use a fixed UTC offset. Re-examine the calendar rules for extended time zones.
The draft sentinel scans the entire public output and Git pending commits. The scheduler has the authority to read private directories, but this does not mean it can upload the entire directory. Only the two articles that have passed the review in the publishing plan are published, while other drafts remain inaccessible. Reliable, scheduled publishing requires a state machine, external reconciliation, and content isolation; the absence of any of these could lead to “two articles per day” becoming uncontrollable and public.
References: Workers Cron Triggers, Durable Objects.