All notes

Committed, Pushed, Live: Three Different Completion States

Close the release loop with evidence for the exact source revision, remote branch, artifact, and public response.

English edition, edited for length. Read the complete Chinese original.

Let’s assume a website modification has been submitted to Git, and the maintainer has announced “deployment is complete.” However, the reader still sees the old title. This could be due to a failed push, deployment to a different directory, or the new version being live while the browser is still using the old content. Despite their similar appearance, these situations require completely different solutions.

This article breaks down local commits, remote pushes, build processes, Cloudflare deployment, and user path validation. The example uses Astro static site generation, and does not involve database migrations or stateful services. The commands are used to illustrate verification methods. Before performing a push, deployment, or rollback, you must still verify the repository, branch, and Cloudflare target. The checklist is not an automated authorization for any production environment.

1. First, define the evidence required for each completion status.

A local commit saves a source-code snapshot. A push makes the commit available through the specified remote Git reference. A build transforms source into assets, and deployment delivers a particular artifact to the platform. Live verification establishes that the user’s access path returns the expected result. Success at any one stage cannot serve as evidence for the remaining stages.

Instead of just keeping a single screenshot of a successful release, you can create a small record for each release.

repository: botao/taotao
branch: main
source_commit: <full Git commit identifier>
worker: taotao-dev
deployment_version: <version identifier returned by the platform>
verified_url: https://taotao.dev/writing/verify-deployment/
verified_at: <actual verification time and time zone>

The placeholders must originate from this specific operation and cannot be copied from previous outputs to create new evidence. This record indicates the source, target, and observation time; it does not automatically guarantee the accuracy or security of the content. If the release process is divided into preparation and completion stages, it should also explain which checks have not been passed, to prevent tools from simply marking a task as verified based solely on “deployment interface success.”

2. Review the scope of changes before submitting to avoid accidentally incorporating someone else’s modifications.

First, verify the current directory and branch. Then, check for differences. Uncommitted changes in the shared workspace may not all belong to the current task. Modifications to articles, dependency upgrades, and domain name configurations may originate from different tasks, and should be understood separately. Directly discarding unfamiliar changes to keep the workspace clean will disrupt collaboration.

git status --short --branch
git diff --check
git diff -- src/content/writing/verify-deployment.md
git diff --cached --stat
git diff --cached

Unstaged and staged differences are displayed separately. The last two commands inspect what will actually enter the commit; the working-tree diff is not a substitute. Filenames alone are insufficient: an ordinary-looking configuration file may contain private addresses, tokens, or the wrong production target. Once the scope is understood, explicitly stage the intended files rather than unconditionally adding the whole directory.

Build directories, installed dependencies, temporary screenshots, and private drafts typically stay out of source history. Lock files pin dependency resolution and should not be ignored merely because tools generate them. If a secret has entered history, deleting it now and adding an ignore rule does not retract distributed copies. Handle credentials and historical copies according to the actual exposure; that is a different task from an ordinary release rollback.

3. Verify the current commit, not an arbitrary test result

A passing test is meaningful only for the input it tested. Editing source afterward and citing the previous result as evidence for those edits separates the evidence from the commit. Run the relevant checks before committing, record HEAD after committing, and ensure that the content intended for deployment has not changed further.

If builds from an uncommitted working tree are allowed, the commit identifier describes only the base version, not the complete input. The report must also preserve the understood differences or artifact identity instead of presenting HEAD as the entire source. Conversely, a publisher requiring a clean working tree must stop when it finds additional changes. It must not automatically stage, commit, or discard another person’s work to continue. Workflows can choose different policies, but their evidence must stay within what those policies actually establish.

npm run check
npm test
npm run build
git rev-parse HEAD
git status --short

The specific method of whether the project is built before or after submission can be chosen based on the workflow. The key is to clearly demonstrate the relationship between the source code being verified and the deployed source code. If the testing process generates non-ignored files, it is necessary to distinguish between normal output and source code changes. You cannot simply delete all newly generated files to pass a clean check, nor can you secretly introduce unknown files into the deployment.

Content sites also need artifact-level checks. Article bodies, titles, RSS, and sitemaps are generated, and correct source files do not guarantee that every consumer follows the same rules. Check that unpublished private drafts are absent from those artifacts. For public articles, confirm that the intended routes and content exist. Type checking alone establishes neither promise.

4. Upstream references and real remote data are two layers of evidence.

Local branches may track incorrect remote branches, or even branches that don’t exist on the upstream. Before pushing, verify the configuration. Do not assume the account and destination based solely on the repository directory name. When multiple GitHub accounts share a machine, also verify the SSH host aliases and corresponding identities. Do not assume that all repositories have write permissions based on a single default authentication.

git remote -v
git branch --show-current
git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}'
git rev-list --count '@{upstream}..HEAD'

The last command counts how many commits local HEAD is ahead of the current upstream tracking reference. Zero means that the local view is not ahead; it does not mean the command has just contacted the network remote. If someone else changed the remote branch and the local tracking reference has not been refreshed, this check cannot establish the remote’s latest state.

If there’s no upstream, first decide on the target branch and then establish the connection; if the remote is out of sync, first read and understand the historical differences. Regular releases should not be forced to push just to eliminate errors. Forcing changes can overwrite other people’s history, and should only be a clearly authorized collaborative action, not a default fix after a failed check.

5. Ensure that the results of the push are associated with the specified branch.

Execute the push after confirming the target, and save the command results:

git push
git rev-list --count '@{upstream}..HEAD'
git ls-remote --heads origin refs/heads/main

The third command reads the actual remote branch; adjust the remote and branch name for the project. Compare the returned identifier with the expected commit and confirm which reference you checked. If collaborators have added later commits, do not require the remote to equal local HEAD forever. Establish whether your commit is part of the target history so legitimate advancement is not mistaken for a lost push.

When a push is rejected, it’s important to distinguish between authentication, authorization, network issues, and historical conflicts. Network timeouts can also lead to unknown results. The next step should be to read the target state rather than immediately changing the credentials or overwriting the history. Using successful output, remote and local tracking checks together provides a more reliable conclusion than relying on a single count.

Changes to standard website content will not automatically be deployed due to GitHub branch updates unless the project has been specifically configured with the corresponding CI. Both manual and automated deployments have their own entry points. When reporting “pushed,” the system should not treat a pipeline that has not been triggered as having run, nor should it record the expected outcome of a status check as the actual outcome.

How to determine if your commit is still valid when working remotely

Assume that a maintainer’s commit is A, and another person has already pushed B to the target branch during a subsequent check. If the remote identifier no longer equals A, it doesn’t automatically mean that A hasn’t been pushed. Instead, you need to read the corresponding target history and determine whether A is present in its ancestor chain. Before comparing, it’s crucial to confirm that the remote, branch, and locally read target identifiers correspond to the same observation. You should not treat subsequent commits on another branch as the production target history.

A shallow clone or incomplete object database may lack ancestry information. If an ancestry query cannot confirm the relationship, distinguish genuine absence from insufficient local evidence. Do not immediately force-push A to “fix” it. Reading the needed target history is diagnosis; overwriting the target branch is a collaborative change with different authorization requirements. Record the observed remote identifier and time so later advancement can explain differences between observations.

Successful pushes and long-term retention are not the same thing. Later, others may rewrite branches or delete references, but the previous observations remain valid, but no longer represent the current goal. Normal release records should preserve verified sources and results, and continued branch protection depends on the corresponding repository policies. Do not attempt to make a single push command prove that a commit will never disappear from any remote history.

6. Pin the build inputs, then verify the upload directory

For an Astro static site, build output normally resides in dist/, and Wrangler’s static-assets directory must point there too. Uploading an old build directory may successfully produce a deployment version while omitting every latest article. Deployment success does not detect that you selected the wrong input.

An automated environment can install dependencies and build the output from specified commit and lock files. The manual process also requires recording the current source code status, Node and dependency conditions, and confirming that no other source files have been modified simultaneously after the build. The article structure, configuration, images, and generated index are all inputs, and it is not sufficient to only examine the main content file to determine the origin of the entire output.

npm ci
npm run check
npm test
npm run build

These steps should be performed in a suitable workspace; if inconsistencies already exist in the workspace, first understand the cause. Verify the results against at least one target article, a content list, an RSS feed, and a non-existent path strategy. Pay particular attention to ensuring that private data is not inadvertently introduced through search indexes or copy scripts; the security boundary should be based on the final uploaded content.

Even with identical source code, verification of build inputs is still necessary.

A lock file fixes part of dependency resolution, not the entire environment. The Node.js version, environment configuration, fetched external data, and build-tool behavior can still change the generated pages. A layout deriving canonical URLs from environment variables can produce different URLs from the same commit in preview and production. That may be intentional or a misconfiguration; compare the actual values with the intended environment.

Artifact identity helps distinguish “what was uploaded” from “what the source is.” A hash confirms only that the compared bytes match, not that they are safe or correct. If you hash an archive, packaging timestamps can make archives with otherwise identical content differ. If you record a file manifest, define which files and paths participate. Choose a method sufficient to trace this project, rather than creating precise-looking identifiers that nobody knows how to recompute.

When reverting, it’s important to retain a previously accepted artifact. This can reduce the reliance on external build inputs and variables. Whether or not to do this depends on your storage and platform processes. If you rebuild from an older commit, you should clearly state that the resulting artifact may be different from the one produced in that year, especially if external resources have changed. You cannot simply assume that a new build with the same Git commit is equivalent to the previously validated artifact.

7. Verify the deployment version and domain name binding separately.

Before deploying with Wrangler, verify the account, Worker name, static-assets directory, and custom domain. The same local project can deploy to different accounts or environments, so a version identifier alone does not establish that production has changed. Recording the target provides necessary troubleshooting input, not an optional formality.

npx wrangler whoami
npx wrangler deploy

The deployment commands in the example will modify the remote state and are executed only on authorized targets. If the results of the command cannot be verified, first read the platform version and deployment status, and then decide whether to retry. While blindly repeating may not be as complex as database writes for static resources, it can still confuse version history or upload subsequent local changes together.

When the default address returns the new version and the official domain returns the old version, it’s important to continue checking the domain binding, routing, and access links. The root domain and www may be handled by different rules, and a successful TLS connection doesn’t guarantee that the same application is being returned. The status displayed in the control panel only indicates a part of the platform configuration status; the actual content of the official URL indicates where the user has actually ended up.

Protect completion records when two releases overlap

Suppose release A builds slowly and release B starts later but deploys first. When A finally uploads, the site may revert to A’s content even though both commands report success. Testing one deployment in isolation does not expose this race. The release system needs serialization, a condition allowing only the intended target version to advance, or an explicitly accepted order. Do not assume that the last task started will be the last version deployed.

Completion records must not be overwritten by an older task finishing late. Associate verification conclusions with the deployment version and expected source, and confirm that the observed content belongs to that version. If another release has replaced yours, report that replacement or verify again; a successful GET alone does not prove your artifact is still live. A lock is only a coordination mechanism. Version conditions must still protect the result after a lock expires or a task resumes.

The manual process for static blogs can be simplified with a more straightforward agreement: only one person should perform deployments at a specific time, and before starting, they should confirm that no other deployments are in progress. After completing the deployment, the version and official address should be saved as evidence. While this agreement cannot automatically prevent errors, it is more understandable than having no rules at all. When automation is needed, the agreement can be implemented as observable status checks, rather than relying on a platform guarantee like “Everyone should not be operating simultaneously.”

8. Both the status code and the content identity require positive and negative examples.

The check should not only access the homepage. For this modification, choose a distinct content identifier, such as a new title or a new section, and then retrieve the specified article. Also, check that non-existent paths and private draft paths are not being successfully masked by pages.

curl -I https://taotao.dev/writing/verify-deployment/
curl -I https://taotao.dev/rss.xml
curl -I https://taotao.dev/writing/not-a-real-article/
curl -I https://taotao.dev/.drafts/index.html

HEAD is useful for quickly inspecting status and headers; checking the body requires GET. An article route that returns success but displays the homepage violates the routing contract. An RSS response containing XML but missing the latest articles reveals a content-consumer error. Negative paths must not return private text. Absence from navigation is not a substitute for checking that private content cannot be accessed.

The browser also needs to check for resource loading issues, long code lines, incorrect line breaks in mobile titles, and broken links. While the network response is correct, the fixed navigation still obscures the page and affects readability. Regarding cached content, it’s important to first compare the deployed version with the response, rather than directly applying the results of a forced refresh to all visitors. Furthermore, the refresh cycle of third-party reading apps is not entirely controlled by this website.

9. Rolling back is a new release, not a deletion of history.

Static blogs don’t have a business database rollback, making it relatively simple to restore old content. However, you still need to decide which version to restore. You can rebuild the deployment from a known commit, or restore according to the platform’s supported versioning process. The choice depends on whether the version is traceable and on the current platform configuration. Before executing the restore command, verify the capabilities and target, and don’t rely on memory.

After rollback, preserve the failed version and the reason, then recheck the production domain, articles, and feeds. The Git branch may still contain newer commits while the live site serves an older version. That state is not forbidden, but it must be clearly recorded. Future maintainers need to know about the difference, or the next automatic deployment could publish the faulty content again.

Database migrations, external messaging, and payment functionalities within dynamic applications cannot be rolled back along with static versions, making it impossible to apply the lessons learned from restoring this platform to all services. Even content websites, where external text has been scraped or referenced, will not be automatically reverted; when factual corrections are needed, it may be necessary to provide transparent explanations rather than simply reverting to older files.

Determining which segment to restore based on the failure window.

Failure before pushing means the remote stage has not been established; do not deploy in parallel to bypass source verification. When a build fails, preserve its precise error and source version instead of uploading stale artifacts left in the directory. A deployment-request timeout can leave the result unknown: inspect the platform and production content before deciding to retry. A verification failure also requires distinguishing an application error from an observation-path error. Incorrect content already being live and a temporarily unreachable certificate or network require different responses.

A well-structured recovery report should clearly document the final verified stage, the target identity, any outstanding checks, and the next steps. Avoid simply resetting all failures and re-running, as this could inadvertently carry over changes from another workspace. Also, avoid relying solely on the last successful evidence, as this would leave the recovery process empty and ineffective. Each recovery should explicitly state “Which source, which output, and which deployment are currently being processed,” to prevent state conflicts.

Git has pushed the reference to the remote, but this does not mean that the external attachment has also been transferred.

If a repository uses submodules or separate large-file storage, the main repository’s commits record references or pointers. The content required for release has additional retrieval boundaries. A matching main-branch identifier alone cannot prove that a clean environment can rebuild every resource. This project does not use those mechanisms; other repositories must check their actual dependencies.

Verify that the target source is obtained from a controlled clean environment, and check that all attachments are complete. Do not copy private credentials into the repository to fix access issues. Missing attachments should prevent the deployment of anything that relies on them, rather than silently replacing placeholders and then reporting that the original source is now live.

10. Stages Confirmed by the Report and Remaining Risks

Begin a completion report with the last established stage, followed by its source, target, and evidence. If a local commit exists but the remote cannot be read, preserve the commit identity and read failure. The next step is to check the specified remote, not to claim “everything is live.” If a deployment version exists but the production article does not match, retain the entry-point response and check domain binding and delivery. Do not choose another source to conceal the discrepancy.

Distinguish executed checks from pending checks and include the observation time. A GET proves what that endpoint returned then; it does not guarantee the same result on another network, in every cache, or after future deployments. Decide whether uncovered areas need further verification or monitoring according to risk. Do not hide them all behind the word “verified.”

Each state should end with corresponding evidence: the source commit, observed remote reference, artifact identity, platform version, and actual production response. These let future maintainers continue the same release rather than rerun every command and produce another result that cannot be reconciled with the original task.

References