<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Tim Stacey — Field notes</title>
  <subtitle>Field notes on testing and quality engineering by Tim Stacey.</subtitle>
  <link href="https://tim.sillysamoyed.com/atom.xml" rel="self"/>
  <link href="https://tim.sillysamoyed.com/blog"/>
  <id>https://tim.sillysamoyed.com/blog</id>
  <updated>2026-07-17T00:00:00.000Z</updated>
  <author><name>Tim Stacey</name></author>
  <entry>
    <title>The Internet Runs on Remote Work. Your CEO Says It Doesn't Work</title>
    <link href="https://tim.sillysamoyed.com/blog/internet-runs-on-remote-work"/>
    <id>https://tim.sillysamoyed.com/blog/internet-runs-on-remote-work</id>
    <updated>2026-07-17T00:00:00.000Z</updated>
    <summary>The largest engineering projects in history are distributed by design, yet executives keep insisting distributed work fails. What the RTO research, the real estate money trail, and one Australian policy contradiction actually show.</summary>
    <category term="RemoteWork"/>
    <category term="RegionalAustralia"/>
    <category term="OpenSource"/>
    <category term="TestAutomation"/>
    <category term="FutureOfWork"/>
  </entry>
  <entry>
    <title>Where Slack Put AI Agents in Its E2E Testing Stack</title>
    <link href="https://tim.sillysamoyed.com/blog/slack-agentic-e2e-testing-stack"/>
    <id>https://tim.sillysamoyed.com/blog/slack-agentic-e2e-testing-stack</id>
    <updated>2026-07-15T00:00:00.000Z</updated>
    <summary>Slack ran more than 200 end-to-end workflows through AI agents to map where intent-driven tests beat brittle scripts and where the price per run keeps scripts in charge.</summary>
    <category term="TestAutomation"/>
    <category term="Playwright"/>
    <category term="AgenticTesting"/>
    <category term="E2ETesting"/>
    <category term="AI"/>
  </entry>
  <entry>
    <title>Your Cucumber Suite Is a Test Script in a Plain-English Costume</title>
    <link href="https://tim.sillysamoyed.com/blog/cucumber-test-script-costume"/>
    <id>https://tim.sillysamoyed.com/blog/cucumber-test-script-costume</id>
    <updated>2026-07-13T00:00:00.000Z</updated>
    <summary>The value in BDD lives in the conversation before the code, not the .feature file; a Cucumber suite that skips that conversation becomes a brittle test script wearing plain English.</summary>
    <category term="TestAutomation"/>
    <category term="BDD"/>
    <category term="Cucumber"/>
    <category term="SDET"/>
    <category term="QualityEngineering"/>
  </entry>
  <entry>
    <title>Two k6 Load Models Measure Two Different Systems</title>
    <link href="https://tim.sillysamoyed.com/blog/k6-closed-vs-open-load-models"/>
    <id>https://tim.sillysamoyed.com/blog/k6-closed-vs-open-load-models</id>
    <updated>2026-07-09T00:00:00.000Z</updated>
    <summary>A closed-model k6 test eases off the moment the server slows; an open-model test holds the request rate and shows you the tail your users will hit.</summary>
    <category term="k6"/>
    <category term="PerformanceTesting"/>
    <category term="LoadTesting"/>
    <category term="Grafana"/>
    <category term="CoordinatedOmission"/>
  </entry>
  <entry>
    <title>Your CI's AI agent was running on a standing credential</title>
    <link href="https://tim.sillysamoyed.com/blog/ci-ai-agent-standing-credential"/>
    <id>https://tim.sillysamoyed.com/blog/ci-ai-agent-standing-credential</id>
    <updated>2026-07-07T00:00:00.000Z</updated>
    <summary>Running Copilot CLI in GitHub Actions used to need a stored personal access token; the CLI now reads the run built-in GITHUB_TOKEN, a credential that expires with the job.</summary>
    <category term="GitHubActions"/>
    <category term="DevSecOps"/>
    <category term="CICD"/>
    <category term="Copilot"/>
    <category term="DevOps"/>
  </entry>
  <entry>
    <title>Let a flake-rate metric build your quarantine list</title>
    <link href="https://tim.sillysamoyed.com/blog/flake-rate-metric-quarantine-list"/>
    <id>https://tim.sillysamoyed.com/blog/flake-rate-metric-quarantine-list</id>
    <updated>2026-07-05T00:00:00.000Z</updated>
    <summary>A hand-kept flaky-test list goes stale the day you save it. Score each test flake rate in Prometheus and a query keeps the quarantine list current for you.</summary>
    <category term="FlakyTests"/>
    <category term="TestAutomation"/>
    <category term="CICD"/>
    <category term="Prometheus"/>
    <category term="TestObservability"/>
  </entry>
  <entry>
    <title>k6 2.1 Ships a Feature-Flag System for Experimental Behavior</title>
    <link href="https://tim.sillysamoyed.com/blog/k6-2-1-feature-flags"/>
    <id>https://tim.sillysamoyed.com/blog/k6-2-1-feature-flags</id>
    <updated>2026-07-02T00:00:00.000Z</updated>
    <summary>k6 2.1 adds a feature-flag system: opt into experimental behavior with --features, list what exists with k6 features, and switch trend metrics to native Prometheus histograms.</summary>
    <category term="k6"/>
    <category term="PerformanceTesting"/>
    <category term="LoadTesting"/>
    <category term="Grafana"/>
    <category term="Observability"/>
  </entry>
  <entry>
    <title>Playwright 1.61 Tests Passkey Logins Without a Hardware Key</title>
    <link href="https://tim.sillysamoyed.com/blog/playwright-1-61-passkey-testing"/>
    <id>https://tim.sillysamoyed.com/blog/playwright-1-61-passkey-testing</id>
    <updated>2026-06-30T00:00:00.000Z</updated>
    <summary>Playwright 1.61 ships a virtual authenticator that registers passkeys in-test, so the passkey login button gets E2E coverage without a security key.</summary>
    <category term="Playwright"/>
    <category term="TestAutomation"/>
    <category term="WebAuthn"/>
    <category term="Passkeys"/>
    <category term="QA"/>
  </entry>
  <entry>
    <title>Checkout v7 refuses the pwn request by default</title>
    <link href="https://tim.sillysamoyed.com/blog/checkout-v7-refuses-pwn-request"/>
    <id>https://tim.sillysamoyed.com/blog/checkout-v7-refuses-pwn-request</id>
    <updated>2026-06-25T00:00:00.000Z</updated>
    <summary>actions/checkout v7 refuses to fetch fork pull request code under pull_request_target, closing the most copied half of the pwn request attack.</summary>
    <category term="GitHubActions"/>
    <category term="CICD"/>
    <category term="DevSecOps"/>
    <category term="SupplyChainSecurity"/>
    <category term="DevOps"/>
  </entry>
  <entry>
    <title>A pull request title ran shell commands in Nx's pipeline</title>
    <link href="https://tim.sillysamoyed.com/blog/pr-title-ran-shell-commands"/>
    <id>https://tim.sillysamoyed.com/blog/pr-title-ran-shell-commands</id>
    <updated>2026-06-25T00:00:00.000Z</updated>
    <summary>A PR title carried shell commands, a run step echoed it, and the runner obeyed. Bind untrusted input to an env var and quote it.</summary>
    <category term="GitHubActions"/>
    <category term="CICD"/>
    <category term="DevSecOps"/>
    <category term="SupplyChainSecurity"/>
    <category term="DevOps"/>
  </entry>
  <entry>
    <title>Your API requests belong in Git, not Postman's cloud</title>
    <link href="https://tim.sillysamoyed.com/blog/drop-postman-http-files"/>
    <id>https://tim.sillysamoyed.com/blog/drop-postman-http-files</id>
    <updated>2026-06-24T00:00:00.000Z</updated>
    <summary>A private Postman collection does two jobs badly. Send the quick check to cURL and a .http file, the endpoint tests to Playwright, and let git hold both.</summary>
    <category term="APITesting"/>
    <category term="Playwright"/>
    <category term="DeveloperExperience"/>
    <category term="cURL"/>
    <category term="SoftwareDevelopment"/>
  </entry>
  <entry>
    <title>Stop seeding your Playwright suite from the login form</title>
    <link href="https://tim.sillysamoyed.com/blog/stop-seeding-from-the-login-form"/>
    <id>https://tim.sillysamoyed.com/blog/stop-seeding-from-the-login-form</id>
    <updated>2026-06-18T00:00:00.000Z</updated>
    <summary>Drive the login form to seed every test and one selector change reddens the whole suite. Authenticate through the API and keep one test on the form.</summary>
    <category term="Playwright"/>
    <category term="TestAutomation"/>
    <category term="APITesting"/>
    <category term="CICD"/>
    <category term="QA"/>
  </entry>
  <entry>
    <title>Your coverage gate rewards tests that assert nothing</title>
    <link href="https://tim.sillysamoyed.com/blog/coverage-gate-rewards-empty-tests"/>
    <id>https://tim.sillysamoyed.com/blog/coverage-gate-rewards-empty-tests</id>
    <updated>2026-06-16T00:00:00.000Z</updated>
    <summary>Line coverage counts execution, not checking. A test that runs your code and asserts nothing still passes an 85 percent gate.</summary>
    <category term="TestAutomation"/>
    <category term="CodeCoverage"/>
    <category term="MutationTesting"/>
    <category term="SoftwareTesting"/>
    <category term="CICD"/>
  </entry>
  <entry>
    <title>Five steps to a GitHub merge queue that does not stall</title>
    <link href="https://tim.sillysamoyed.com/blog/github-merge-queue-five-steps"/>
    <id>https://tim.sillysamoyed.com/blog/github-merge-queue-five-steps</id>
    <updated>2026-06-14T00:00:00.000Z</updated>
    <summary>A merge queue tests each PR against the ones ahead of it before it lands. Five setup steps keep it moving instead of stalling the day you turn it on.</summary>
    <category term="GitHubActions"/>
    <category term="CICD"/>
    <category term="MergeQueue"/>
    <category term="DevOps"/>
    <category term="DeveloperExperience"/>
  </entry>
  <entry>
    <title>GitLab 19.0 moves the secrets manager into the pipeline</title>
    <link href="https://tim.sillysamoyed.com/blog/gitlab-native-secrets-manager"/>
    <id>https://tim.sillysamoyed.com/blog/gitlab-native-secrets-manager</id>
    <updated>2026-06-11T00:00:00.000Z</updated>
    <summary>GitLab 19.0 bundles OpenBao as a native secrets store, so you stop running a standalone Vault just to feed your pipelines.</summary>
    <category term="GitLab"/>
    <category term="CICD"/>
    <category term="DevSecOps"/>
    <category term="SecretsManagement"/>
    <category term="DevOps"/>
  </entry>
  <entry>
    <title>Where Test Health Belongs: CI Logs or an Observability Backend</title>
    <link href="https://tim.sillysamoyed.com/blog/test-health-ci-logs-vs-observability"/>
    <id>https://tim.sillysamoyed.com/blog/test-health-ci-logs-vs-observability</id>
    <updated>2026-06-09T00:00:00.000Z</updated>
    <summary>Your suite emits pass rate and flake count every run, then buries them in a CI log nobody scrolls; export them over OTLP and a dashboard catches the rot.</summary>
    <category term="TestAutomation"/>
    <category term="OpenTelemetry"/>
    <category term="TestObservability"/>
    <category term="CICD"/>
    <category term="DevOps"/>
  </entry>
  <entry>
    <title>Retrying a flaky test deletes the evidence of a real bug</title>
    <link href="https://tim.sillysamoyed.com/blog/retries-hide-real-bugs"/>
    <id>https://tim.sillysamoyed.com/blog/retries-hide-real-bugs</id>
    <updated>2026-06-07T00:00:00.000Z</updated>
    <summary>A bug that fails one run in four passes CI 99.6 percent of the time under three retries. Quarantine the test instead and keep the signal.</summary>
    <category term="TestAutomation"/>
    <category term="FlakyTests"/>
    <category term="CICD"/>
    <category term="SoftwareTesting"/>
    <category term="DevOps"/>
  </entry>
  <entry>
    <title>Three caching changes that take 80% off a GitHub Actions build</title>
    <link href="https://tim.sillysamoyed.com/blog/github-actions-cache-strategy"/>
    <id>https://tim.sillysamoyed.com/blog/github-actions-cache-strategy</id>
    <updated>2026-06-04T00:00:00.000Z</updated>
    <summary>A cached ~/.npm drops a cold Node install from four minutes to thirty seconds, and two more cache changes take the rest of the pipeline down with it.</summary>
    <category term="GitHubActions"/>
    <category term="CICD"/>
    <category term="DevOps"/>
    <category term="SoftwareDevelopment"/>
    <category term="TestAutomation"/>
  </entry>
  <entry>
    <title>My resume site ships behind 460 tests</title>
    <link href="https://tim.sillysamoyed.com/blog/resume-site-behind-460-tests"/>
    <id>https://tim.sillysamoyed.com/blog/resume-site-behind-460-tests</id>
    <updated>2026-06-04T00:00:00.000Z</updated>
    <summary>I set the direction and Claude Code wrote the code and the tests; 247 unit tests and 213 browser tests are how I trust a site I never hand-wrote.</summary>
    <category term="Astro"/>
    <category term="StaticSite"/>
    <category term="Playwright"/>
    <category term="ContinuousIntegration"/>
    <category term="TestAutomation"/>
  </entry>
  <entry>
    <title>GitHub Actions parallel steps and the matrix jobs you can retire</title>
    <link href="https://tim.sillysamoyed.com/blog/github-actions-parallel-steps"/>
    <id>https://tim.sillysamoyed.com/blog/github-actions-parallel-steps</id>
    <updated>2026-06-02T00:00:00.000Z</updated>
    <summary>Three matrix jobs for lint, type-check, and unit tests pay three runner boots and an artifact handoff for concurrency that parallel steps fold back into one job.</summary>
    <category term="GitHubActions"/>
    <category term="CICD"/>
    <category term="DevOps"/>
    <category term="TestAutomation"/>
    <category term="SoftwareDevelopment"/>
  </entry>
  <entry>
    <title>Contract Testing vs End-to-End: Where Integration Bugs Belong</title>
    <link href="https://tim.sillysamoyed.com/blog/contract-testing-vs-e2e"/>
    <id>https://tim.sillysamoyed.com/blog/contract-testing-vs-e2e</id>
    <updated>2026-06-01T00:00:00.000Z</updated>
    <summary>A contract test catches a renamed field in seconds; a 20-minute E2E suite catches it after booting six services. Put each test where it earns its minutes.</summary>
    <category term="ContractTesting"/>
    <category term="Microservices"/>
    <category term="APITesting"/>
    <category term="TestAutomation"/>
    <category term="CICD"/>
  </entry>
  <entry>
    <title>k6 Script Authoring calibrates load tests to live traffic</title>
    <link href="https://tim.sillysamoyed.com/blog/k6-script-authoring-live-telemetry"/>
    <id>https://tim.sillysamoyed.com/blog/k6-script-authoring-live-telemetry</id>
    <updated>2026-05-26T00:00:00.000Z</updated>
    <summary>Grafana Assistant reads your telemetry, finds endpoints by real RPS and p95, and generates a k6 script that inherits that profile.</summary>
    <category term="PerformanceTesting"/>
    <category term="k6"/>
    <category term="Grafana"/>
    <category term="TestAutomation"/>
    <category term="DevOps"/>
  </entry>
  <entry>
    <title>When AI can write every test, what ships to CI is the job</title>
    <link href="https://tim.sillysamoyed.com/blog/playwright-ai-test-explosion"/>
    <id>https://tim.sillysamoyed.com/blog/playwright-ai-test-explosion</id>
    <updated>2026-05-24T00:00:00.000Z</updated>
    <summary>AI-generated Playwright tests flake under 1.5%. The new problem is test explosion, and coverage intent is still yours to define.</summary>
    <category term="Playwright"/>
    <category term="TestAutomation"/>
    <category term="SoftwareTesting"/>
    <category term="AI"/>
    <category term="CICD"/>
  </entry>
  <entry>
    <title>One click to fix a failing GitHub Actions run</title>
    <link href="https://tim.sillysamoyed.com/blog/github-copilot-fixes-failing-ci"/>
    <id>https://tim.sillysamoyed.com/blog/github-copilot-fixes-failing-ci</id>
    <updated>2026-05-21T00:00:00.000Z</updated>
    <summary>Fix with Copilot puts a cloud agent on the failure: it investigates, pushes a fix, reruns CI, and tags you for review.</summary>
    <category term="GitHubActions"/>
    <category term="CICD"/>
    <category term="TestAutomation"/>
    <category term="DevOps"/>
    <category term="SoftwareDevelopment"/>
    <category term="Playwright"/>
  </entry>
  <entry>
    <title>90% use AI in the IDE; the pipeline is another story</title>
    <link href="https://tim.sillysamoyed.com/blog/ai-cicd-adoption-gap"/>
    <id>https://tim.sillysamoyed.com/blog/ai-cicd-adoption-gap</id>
    <updated>2026-05-19T00:00:00.000Z</updated>
    <summary>JetBrains data: daily AI in the editor, almost none in CI/CD. The trust gap closes when AI reduces noise instead of adding it.</summary>
    <category term="CICD"/>
    <category term="DevOps"/>
    <category term="TestAutomation"/>
    <category term="SoftwareDevelopment"/>
    <category term="AITesting"/>
  </entry>
  <entry>
    <title>Bitbucket Agentic Pipelines automates the chores</title>
    <link href="https://tim.sillysamoyed.com/blog/bitbucket-agentic-pipelines"/>
    <id>https://tim.sillysamoyed.com/blog/bitbucket-agentic-pipelines</id>
    <updated>2026-05-17T00:00:00.000Z</updated>
    <summary>Define an agent block in bitbucket-pipelines.yml, scope it, tie it to an event. It drafts the docs and the coverage gaps; you review.</summary>
    <category term="Bitbucket"/>
    <category term="DevOps"/>
    <category term="CICD"/>
    <category term="TestAutomation"/>
    <category term="SoftwareDevelopment"/>
    <category term="Playwright"/>
  </entry>
  <entry>
    <title>Playwright 1.59 turns failures into reviewable evidence</title>
    <link href="https://tim.sillysamoyed.com/blog/playwright-1-59-healer-agent-ci"/>
    <id>https://tim.sillysamoyed.com/blog/playwright-1-59-healer-agent-ci</id>
    <updated>2026-05-14T00:00:00.000Z</updated>
    <summary>The 1.59 agents plus screencast and browser.bind shift your job from chasing selectors to reviewing what the Healer did.</summary>
    <category term="Playwright"/>
    <category term="TestAutomation"/>
    <category term="AITesting"/>
    <category term="QA"/>
    <category term="CI"/>
  </entry>
  <entry>
    <title>k6 2.0 moves load-test authoring into the CLI</title>
    <link href="https://tim.sillysamoyed.com/blog/k6-2-ai-performance-testing"/>
    <id>https://tim.sillysamoyed.com/blog/k6-2-ai-performance-testing</id>
    <updated>2026-05-12T00:00:00.000Z</updated>
    <summary>Grafana previewed k6 2.0 at GrafanaCON 2026: AI authoring in the CLI, an MCP server, and a Playwright-to-k6 converter.</summary>
    <category term="PerformanceTesting"/>
    <category term="TestAutomation"/>
    <category term="k6"/>
    <category term="Grafana"/>
    <category term="AI"/>
  </entry>
  <entry>
    <title>The locator tax nobody puts in the budget</title>
    <link href="https://tim.sillysamoyed.com/blog/playwright-mcp-locator-tax"/>
    <id>https://tim.sillysamoyed.com/blog/playwright-mcp-locator-tax</id>
    <updated>2026-05-12T00:00:00.000Z</updated>
    <summary>Broken-test triage is a staffing decision disguised as a process one. Here is the cost, and where AI self-healing pays it back.</summary>
    <category term="EngineeringLeadership"/>
    <category term="SoftwareEngineering"/>
    <category term="TestAutomation"/>
    <category term="DevProductivity"/>
    <category term="QualityAssurance"/>
  </entry>
  <entry>
    <title>Playwright agents and the new QA skills gap</title>
    <link href="https://tim.sillysamoyed.com/blog/playwright-ai-agents"/>
    <id>https://tim.sillysamoyed.com/blog/playwright-ai-agents</id>
    <updated>2026-05-10T00:00:00.000Z</updated>
    <summary>Playwright v1.56 put a Planner, Generator, and Healer in the test runner. The interesting part is what it asks of the engineers who own the suite.</summary>
    <category term="Playwright"/>
    <category term="SoftwareTesting"/>
    <category term="AI"/>
    <category term="TestAutomation"/>
    <category term="QualityAssurance"/>
  </entry>
</feed>
