VERIFYFIRST // recipes Preflight checks for the moment before you call work done. ====================================================================== TASK: I deployed a static site Files copied to a web root, or pushed to a host that serves them. ====================================================================== 1. Hash the served bytes against the file on disk, from a client that has never requested the URL. $ curl -s https://HOST/path | md5sum # compare to md5sum of the local file guards against: NS-007, NS-058 2. Check which layer answered, not just that something did. $ curl -s -o /dev/null -w 'code=%{http_code} remote=%{remote_ip} age=%header{age}\n' URL guards against: NS-058, NS-059 3. Request a path that should not exist. A catch-all will answer 200 for it. $ curl -s -o /dev/null -w '%{http_code}\n' https://HOST/definitely-not-here guards against: NS-022, NS-043 4. Confirm the copy landed where you meant, not one directory deeper. $ ls the destination; rsync without a trailing slash nests the source directory guards against: NS-056 5. Fetch as a client would, following redirects and decompressing. $ curl -sL --compressed --fail URL | head guards against: NS-057, NS-016, NS-042 ====================================================================== TASK: I restarted a service A systemd unit, container or daemon was reloaded to pick up new code or config. ====================================================================== 1. Compare the unit's ExecStart on disk against the live process. $ systemctl show -p ExecStart -p MainPID UNIT && ps -p $MAINPID -o args= guards against: NS-005, NS-038 2. Confirm the binary the process is executing is the one you replaced. $ stat -Lc %i /proc/$MAINPID/exe # against stat -c %i /path/to/binary guards against: NS-038 3. Ask the socket, not the manager. Type=simple is active before execve. $ ss -ltnp 'sport = :PORT' guards against: NS-026, NS-037 4. Check it is not crash-looping between your observations. $ systemctl show -p NRestarts -p ExecMainStartTimestamp UNIT guards against: NS-027 5. Ask the running service what config it loaded, not the filesystem what it holds. $ For Caddy: the admin API's live config. For sshd: sshd -T as root. guards against: NS-008, NS-023 ====================================================================== TASK: I changed a configuration file A config, stylesheet, env file or manifest was edited. ====================================================================== 1. Read the resolved value, never the authored one. $ getComputedStyle(el).prop for CSS; sshd -T for sshd; the service's own live config otherwise guards against: NS-003, NS-023 2. Check nothing later in the file, or in an include, overrides it. $ Search the whole merged configuration for every occurrence of the key, not just yours. guards against: NS-003, NS-023 3. Check the parser agrees with you about types. $ python3 -c "import yaml,sys;[print(repr(k),repr(v),type(v).__name__) for k,v in yaml.safe_load(open(sys.argv[1])).items()]" FILE guards against: NS-046, NS-025 4. Make invisible characters visible before trusting a value. $ cat -A FILE # CRLF shows as ^M$ guards against: NS-061, NS-024 5. Confirm the file is actually tracked, and is the one being served. $ git status --short --ignored && git ls-files --error-unmatch FILE guards against: NS-060, NS-047, NS-062 ====================================================================== TASK: I checked that a page renders correctly A screenshot, headless capture or visual review was used to judge a page. ====================================================================== 1. Confirm the animation loop runs at all before judging any frame. $ let n=0; requestAnimationFrame(()=>n++); setTimeout(()=>console.log('rAF fired:', n), 1000) guards against: NS-001 2. Capture console errors. Inert and wrong look identical in an image. $ Collect page errors during load; an element that never initialised renders nothing. guards against: NS-002, NS-010 3. Check the fonts you designed against actually loaded. $ document.fonts.check('1em "Family Name"') guards against: NS-004 4. Compare element count against visible count. $ document.querySelectorAll('.reveal').length vs those with computed opacity above zero guards against: NS-010, NS-030 5. Confirm the capture used your device scale, colour scheme and stylesheet. $ devicePixelRatio, matchMedia('(prefers-color-scheme: dark)').matches, and print vs screen media guards against: NS-049, NS-050, NS-051 6. Wait for data, not for the load event. $ The load event fires before fetched content arrives; assert on the rendered rows. guards against: NS-052, NS-009 ====================================================================== TASK: I ran the test suite and it passed A test run reported success. ====================================================================== 1. Confirm the tests you think exist were actually collected. $ pytest --collect-only -q | tail -1 # compare the count against what you expect guards against: NS-017 2. Confirm the mocks still resemble the objects they stand in for. $ create_autospec(Real) raises on a method the real object no longer has; a bare Mock() does not. guards against: NS-018 3. Check the exit status came from the runner, not from a later command in the pipe. $ set -o pipefail, or read ${PIPESTATUS[@]} guards against: NS-015, NS-053 4. Check nothing swallowed a failure inside a loop. $ find -exec returns find's status, not the command's; use -print0 | xargs -0 guards against: NS-055 ====================================================================== TASK: I ran a script or pipeline A shell script, CI step or command pipeline completed. ====================================================================== 1. Read the status of the command you care about, not the last one. $ set -o pipefail; or echo ${PIPESTATUS[@]} guards against: NS-015 2. Confirm it did not exit early on an unrelated precondition. $ Under set -e a global validation step aborts before the step you wanted. guards against: NS-008 3. Confirm every child's status was collected. $ wait $pid per child; bare wait returns 0 regardless guards against: NS-053 4. Confirm it returned at all rather than waiting on a prompt. $ sudo -n true returns non-zero immediately when a password would be needed; wrap in timeout guards against: NS-014 5. Check redirections point where you think. $ cmd >f 2>&1 sends both to f; cmd 2>&1 >f does not guards against: NS-066, NS-054 6. Confirm it did not tear down the environment it was running inside. $ Compare the teardown target against $TMUX, the current container, the host you are on. guards against: NS-013 ====================================================================== TASK: I fetched or scraped a resource A URL was downloaded, an API called, or a page parsed. ====================================================================== 1. Check the status code came with a body worth having. $ curl -sS --fail --compressed URL >out; echo $? # 22 on 4xx/5xx, 18 on truncation guards against: NS-016, NS-042, NS-057 2. Parse the body even on 200. Some APIs report failure inside a success. $ Look for an errors array, an Error root element, or unprocessed items. guards against: NS-045, NS-020, NS-021 3. Check whether a cache answered. $ curl -sI URL | grep -iE '^(age|x-cache|cf-cache-status):' guards against: NS-058, NS-007 4. Prefer the canonical marker the document declares over any heuristic ranking. $ og:image, rel=canonical, structured data — not 'the largest image on the page' guards against: NS-006 5. Confirm credentials survived any redirect. $ curl -v shows the Authorization header dropped on a cross-origin hop. guards against: NS-044 6. Check large integers survived the parse. $ Round-trip the value; anything above 2^53 loses precision in a double. guards against: NS-025 ====================================================================== TASK: I read the logs and saw no errors Log output, journal or stdout was inspected for errors. ====================================================================== 1. Confirm the level you filtered on is the level the app writes. $ journalctl -u UNIT -o json | jq -r .PRIORITY | sort | uniq -c guards against: NS-067, NS-029 2. Confirm logging was configured before the first record. $ logging.basicConfig is a no-op once a handler exists; pass force=True guards against: NS-033, NS-034 3. Check nothing was dropped by rate limiting or rotation. $ Look for suppression notices; check the daemon is not writing to a deleted inode. guards against: NS-032, NS-028 4. Check output was flushed rather than lost with the process. $ A killed process discards unflushed stdout; run with unbuffered output. guards against: NS-031, NS-035 5. Check the journal survives a reboot before relying on it. $ journalctl --list-boots; /var/log/journal must exist for persistence guards against: NS-068 ====================================================================== TASK: The machine is slow, or something ran out of memory Memory, CPU or load average was inspected to explain slowness, a stall, or a container or process being OOM killed. ====================================================================== 1. Read the cgroup's limit, not the host's totals. $ cat /sys/fs/cgroup/memory.max and memory.events # free(1) is not scoped to the cgroup guards against: NS-063 2. Rank every process by footprint, not just the one that died. $ ps -eo rss,comm --sort=-rss | head -20 # count anything spawned in a loop guards against: NS-011 3. Check whether load is CPU work or uninterruptible sleep. $ ps -eo state= | sort | uniq -c # D means blocked on I/O, not running guards against: NS-064 4. Sample CPU over an interval rather than reading a lifetime average. $ Compare /proc/PID/stat utime+stime across a few seconds guards against: NS-065 5. Resolve a PID before killing anything matched by pattern. $ pgrep -af PATTERN and read it; your own command matches too guards against: NS-012, NS-040, NS-039, NS-036 ====================================================================== TASK: I published a package or release An artifact was uploaded to a registry, or a release was cut. ====================================================================== 1. Install the published artifact from the real index, in a clean environment. $ uvx --refresh-package NAME --from NAME==VERSION NAME # never test the local build guards against: NS-007 2. Confirm the index you queried is not a stale cache. $ A resolver holding an old index reports a published version as missing. guards against: NS-007 3. Confirm the tests actually gated the publish. $ Chaining commands is not a gate; only a non-zero exit that stops the script is. guards against: NS-015, NS-008 4. Confirm bundled copies match their source. $ diff -q every vendored copy against the original; copies drift silently. guards against: NS-062, NS-048 Full registry: https://verifyfirst.dev/all.txt