{"_id":"@connorpham6499/ai-qa","_rev":"10-69b6c607bec408c89bc33e9ca74fe175","name":"@connorpham6499/ai-qa","dist-tags":{"latest":"0.10.0"},"versions":{"0.1.0":{"name":"@connorpham6499/ai-qa","version":"0.1.0","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.1.0","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"08ebf0cfae076a83649c0bd00da40ee8692fbb7a","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.1.0.tgz","fileCount":65,"integrity":"sha512-ejTnMcpF1WzDy+W+Lt1jXpZP80ZHvmYggk7bdVcXVZKPlaAiF4+c5E5XWmwkLuZUf3Bdag4JftqDShJPabKfyQ==","signatures":[{"sig":"MEYCIQD/tTwlMqorG5oGjcWvrJ5gBEtfDHC+nvJ2TVvJrzOn9gIhAN7OnBtGjDogH+zV8app3yWzAfKJhLDA4138JuS8HxX1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":840032},"type":"module","engines":{"node":">=20"},"gitHead":"189a45d0b97f4e57dac20ea9792080f4692a3600","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"10.8.2","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.1.0_1788681746943_0.8452652397682667","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@connorpham6499/ai-qa","version":"0.1.1","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.1.1","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"8dec67b705330a67dfba72d680ca45df06529703","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.1.1.tgz","fileCount":65,"integrity":"sha512-cX4RAWs6fkIK60JDJcgokFl2gJ53WA+iobfx5jvOMA++7ROENo9dhH3+yn6LVLz3MYuwNKxsXUBYNmAuqc0nwQ==","signatures":[{"sig":"MEQCIBs15QdLEnwPNfhjOkaeEw5+kbSrdF15LxsB0HmJIdJOAiBDmdb1X1mWaNaRCYNILPmXQMqG6KQ3YENLPqNrQAK/pw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":841364},"type":"module","engines":{"node":">=20"},"gitHead":"c8544bebce29acbfe129bddaaa68d5e2f79fcc80","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"10.8.2","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.1.1_1788683732325_0.9848007801061351","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@connorpham6499/ai-qa","version":"0.2.0","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.2.0","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"704d4a256761cd5274a05cc6c834a588da7101b5","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.2.0.tgz","fileCount":65,"integrity":"sha512-kjLn5bAMDiLHKtwsO2ouKUmQHXtTUtNrOsgTOTx67z4xaEtoh0sFUbAsOjbVY+nJQEFVK6muFoH4EndsERrQ3A==","signatures":[{"sig":"MEYCIQDJ35O9Kwfi5eMxLoDIct08JK8uigyLevknxRpkuCHoUAIhAKZr0LmmGGCPFlipm2dmDP/U8mZySCkHnoczEbgqKvFi","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":863819},"type":"module","engines":{"node":">=20"},"gitHead":"1fb9cdb4cdc32a2873d37f403cb5ad9b49c2ea5c","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"10.8.2","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.2.0_1788685907720_0.12145829753185056","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@connorpham6499/ai-qa","version":"0.3.0","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.3.0","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"1fd2b15060980df8ecc3d3e8ee10c77d7d09c6e5","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.3.0.tgz","fileCount":67,"integrity":"sha512-qraPedjFWi4hpN9NWIgXPJxp2csaGrWq7kCyNK0djh2I4MHKzF63ZVfW7zAwmKVvinYfwgLpYiFDzvDMxYFsNg==","signatures":[{"sig":"MEUCIHKO30DCRzCe+XSUc14+aVTEGV9TpsVN6WfM8MffUCnRAiEAnsBiRbIq58NcAXo/fSBzz/2ImXFZ6VkPU9FDdiD022c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":898799},"type":"module","engines":{"node":">=20"},"gitHead":"0dabc8c7f669fdccb22ce70d471d943d6a12c75e","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"10.8.2","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.3.0_1788687276597_0.9839543919140286","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@connorpham6499/ai-qa","version":"0.4.0","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.4.0","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"19a1111b4f75093fe1a84b5ae9b425f8f0a8e9f3","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.4.0.tgz","fileCount":61,"integrity":"sha512-SulohsUzL4uMbH7Ns2IZofhFIpQr0LW9Ix2CkJLYKj3xSJT1VfZvvZKSqbJV2xXPpAhxnX8Y9PIhnJead4Tc4Q==","signatures":[{"sig":"MEYCIQDsZgvulkMITOGY4ywPHnZubkXydd/TFPPH7gChULt83wIhAJb0yld+mibsrnOoTVnz3Bc0QPGRU/dcnKi4oUG/gB91","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":679623},"type":"module","engines":{"node":">=20"},"gitHead":"66496b6bacfbf121f2d3d8f5c2ade232e9b19cb5","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"10.8.2","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.4.0_1788687808772_0.6262790927016848","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@connorpham6499/ai-qa","version":"0.5.0","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.5.0","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"e7e03752f1d7610333a2c34b4cc1776a0ad83a5f","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.5.0.tgz","fileCount":61,"integrity":"sha512-wJzfflytjQlS5Z/yakaZZ5Thl54UgzXZkflseUW5K7VQ+Vwd4SasZmGRyqTdUsxZac6ZZzJcEdRcpDQq2bM7wQ==","signatures":[{"sig":"MEYCIQCAgYuN63+CJjQroleImlH5zO8yo5ZnAC/U/gKrQkoRaQIhAL/QUxiME5SkVELkFAJce5XVpS2C9+MVaCV/tltEol1x","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":695380},"type":"module","engines":{"node":">=20"},"gitHead":"07af2b2ab16f419d438a23d20a79d8e7aa19d115","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"10.8.2","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.5.0_1788692204942_0.2964981554435242","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"@connorpham6499/ai-qa","version":"0.5.1","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.5.1","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"c2f859019db3dcc4f59755bb9e8eb2b056d49338","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.5.1.tgz","fileCount":61,"integrity":"sha512-rvV3ghseCWjCypu+6dZrtbC9dk2zCe3VuTD3MrytsTLcKqdBUK2dmONh09NvPbe/NXp2QnkrDbCv2CXsVKPrmw==","signatures":[{"sig":"MEYCIQCAAynqCAgPbBLVQXelgqH2MG/OX7J8Y/xELHAT+OOPQgIhAMMB19dNgljwpWq5czJcFQ09GXlOE/vaorWUk2uik61n","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":708715},"type":"module","engines":{"node":">=20"},"gitHead":"6ea2cc230e696db6719dfc62cbc9e0fe7bb033da","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"10.8.2","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"20.19.6","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.5.1_1788692876203_0.8194550349555829","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@connorpham6499/ai-qa","version":"0.6.0","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.6.0","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"4e450045e38d0e6fdec8a233429d0ac69acedd90","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.6.0.tgz","fileCount":61,"integrity":"sha512-CdP+VuR6GerpSXsnddd7NNihyycDK1DGa+ixFdPU+L+dTWE8yz/cc9w4+8x5SSt6p77hruYYfSvAUxYyHahcIw==","signatures":[{"sig":"MEUCIBBLFCpZ22HyWSYsv10yRjul90fHLd7JCOSCp+y2rYTbAiEA1rSpxGmQYaanihHsWS9RikLbD8KO2Lkr6YNNMQoxZdE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIERLXvG+uyoUweNwyqT1Puse8zM1Cyrd2fQ0Go8nGWxcAiEA5CiERFuL06GMoB2mwKi080jqVLfvcUyozGftI6KbDME=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":722090},"type":"module","engines":{"node":">=20"},"gitHead":"806f69807bd0cb82552eb9ca43e0687d17124edc","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"11.12.1","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.6.0_1790045503187_0.1271209534146338","host":"s3://npm-registry-packages-npm-production"}},"0.9.1":{"name":"@connorpham6499/ai-qa","version":"0.9.1","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"license":"MIT","_id":"@connorpham6499/ai-qa@0.9.1","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"homepage":"https://github.com/connorpham/ai-qa#readme","bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"bin":{"ai-qa":"bin/ai-qa.mjs"},"dist":{"shasum":"ea9b9acbaafc20b7a8489488bbdf27796a168ded","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.9.1.tgz","fileCount":65,"integrity":"sha512-fIurSSSp2cmZDmbarZSJN4a9es6qXa4PlFl0fZUvukwGrm59C/R4weZxjsW9cDS8KsU1pIDJx6izBRVH7ra5WQ==","signatures":[{"sig":"MEQCIHGxRTL9A3kTp9SDhjd/0ql7gJ9RNcr5nB5z5Ouzw4sTAiB8VRulHDPdlZ5jcrSwlZ4g2+h+rioYlcmNjEHxo4MlVg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCzsHRmCHgo8RzaD4e2BJcwNdE57+0Qn7epdfBHj2V2YQIgeY6oHMKuv8Fmpiz0uxGON4pMt148Sf9S9j+kE1AZ/i4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":794502},"type":"module","engines":{"node":">=20"},"gitHead":"0681a7fc17b889f6057688617a6ece822ed823c1","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"11.12.1","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-qa_0.9.1_1790056877144_0.8367517665640838","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"_id":"@connorpham6499/ai-qa@0.10.0","bin":{"ai-qa":"bin/ai-qa.mjs"},"bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"dist":{"shasum":"21a1ece0b2975e6bc31967f34d4e0fe9a5b7fcd5","tarball":"https://registry.npmjs.org/@connorpham6499/ai-qa/-/ai-qa-0.10.0.tgz","fileCount":65,"integrity":"sha512-ylXoqoHzL7V+EVMemnas8prCSYeTr4vYzRSvdlfLL5mbvRCr4Tmy5zDASvoLJ0yvum6ThOnCfTz7pHz7nV/t1A==","signatures":[{"sig":"MEUCIBJff4kpGFl/ZVUfl2GSSeRCPs3pmhsG0HQhyh49fKeYAiEA5SCTUqeK+NH1HLFF7Zu3XEaW3o8f9SQeJd4Tu3CBSdc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEWLucA7qPW6NDxVEDEmGrbI+H7QYgaZo/VGPDuLOrSwAiB1FCT1sDnqkN9zyqWRND/7MUqmBE6uRzAHhmoYUPOxlw=="}],"unpackedSize":824393},"name":"@connorpham6499/ai-qa","type":"module","engines":{"node":">=20"},"gitHead":"3d1fd9a8abdb9ff083c386418b4730819f891f85","license":"MIT","scripts":{"test":"node tests/conformance.mjs && node tests/e2e.mjs","selftest":"node src/cli/scan.mjs --selftest","prepublishOnly":"npm test"},"version":"0.10.0","_npmUser":{"name":"connorpham6499","email":"phamchicong0604@gmail.com"},"homepage":"https://github.com/connorpham/ai-qa#readme","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"_npmVersion":"11.12.1","description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","directories":{},"maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-qa_0.10.0_1790059264108_0.0982705394587362"}}},"time":{"created":"2026-09-06T08:02:26.809Z","modified":"2026-09-22T06:41:04.407Z","0.1.0":"2026-09-06T08:02:27.123Z","0.1.1":"2026-09-06T08:35:32.484Z","0.2.0":"2026-09-06T09:11:47.870Z","0.3.0":"2026-09-06T09:34:36.721Z","0.4.0":"2026-09-06T09:43:28.919Z","0.5.0":"2026-09-06T10:56:45.095Z","0.5.1":"2026-09-06T11:07:56.386Z","0.6.0":"2026-09-22T02:51:43.278Z","0.9.1":"2026-09-22T06:01:17.248Z","0.10.0":"2026-09-22T06:41:04.214Z"},"bugs":{"url":"https://github.com/connorpham/ai-qa/issues"},"license":"MIT","homepage":"https://github.com/connorpham/ai-qa#readme","keywords":["ai","qa","testing","agents","claude-code","cursor","test-evidence","onboarding","playwright","quality-gates"],"repository":{"url":"git+https://github.com/connorpham/ai-qa.git","type":"git"},"description":"An AI QA engineer you can drop into any codebase. It onboards like a new hire — works out what it needs to know, says what is missing — then verifies tickets against the spec with evidence a non-programmer can read.","maintainers":[{"name":"connorpham6499","email":"phamchicong0604@gmail.com"}],"readme":"# ai-qa\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/connorpham/ai-qa-assets/main/hero.png\" alt=\"ai-qa — an AI QA engineer you can drop into any codebase\" width=\"880\">\n</p>\n\n**An AI QA engineer you can drop into any codebase.**\n\nIt onboards the way a person does — works out what it can, says plainly what it\ncannot find, asks the team the rest — and then verifies tickets against the spec\nwith evidence a non-programmer can read in two minutes.\n\nIt reports defects. It never fixes them, and it never invents an expected value.\n\n```bash\nnpx @connorpham6499/ai-qa scan     # grade this repo: could a QA test it, and would their verdicts mean anything?\nnpx @connorpham6499/ai-qa init     # install the QA lane (terminal wizard, or --ui for the browser)\nnpx @connorpham6499/ai-qa doctor   # prove every gate still works — green doctor is the definition of installed\n```\n\nThe command it installs is `ai-qa`, so once it is a devDependency the scope\ndrops off: `npx ai-qa scan`. The published name is scoped because npm reserves\nthe bare `ai-qa` — it is too close to an unrelated `aiqa` package.\n\nThen, inside your agent: `/onboard` · `/qa` · `/triage` · `/regress`.\n\n---\n\n## Contents\n\n- [The problem this exists for](#the-problem-this-exists-for)\n- [The whole system at a glance](#the-whole-system-at-a-glance) — the diagram\n- [End to end: from an empty repo to a verdict](#end-to-end-from-an-empty-repo-to-a-verdict) — the run, step by step\n- [Inside `/qa`: how one ticket is verified](#inside-qa-how-one-ticket-is-verified) — the flowchart\n- [With a written spec, and without one](#with-a-written-spec-and-without-one--the-two-paths) — **the two paths** (documented vs undocumented repos)\n- [`scan`](#ai-qa-scan--the-mirror-before-you-commit-to-anything) · [`init`](#ai-qa-init--a-setup-that-already-read-your-repo) · [the four workflows](#the-four-workflows)\n- [Reading tickets](#reading-tickets--jira-backlog-github-or-files) · [Acceptance criteria](#acceptance-criteria) · [The spreadsheet everyone reads](#the-spreadsheet-everyone-else-reads)\n- [Gates that can go red](#gates-that-can-actually-go-red) · [The mind of the tester](#the-mind-of-the-tester) · [Rules the lane will not bend](#rules-the-lane-will-not-bend)\n- [Environments](#environments--local--dev--stg--prod) · [Working language](#the-working-language--en--vi) · [Surfaces](#surfaces) · [Agent tools](#agent-tools)\n- [Updating safely](#updating-safely) · [Layout](#layout) · [Requirements](#requirements) · [Tests](#tests)\n\n---\n\n## The problem this exists for\n\nAsk an AI agent to test a feature and you get a confident paragraph. Ask what it\nactually ran and the answer is usually: it read the code, saw a function that\nlooked right, and wrote \"verified\".\n\nThe deeper problem is older than AI. **A QA engineer joining a project cannot\ntest anything until they know what \"correct\" means here** — and on most teams\nthat knowledge is not written down. It lives in three people's heads. So the new\ntester quietly adopts whatever the code currently does as the expected value,\nand from that moment nobody is checking anything.\n\nai-qa attacks both:\n\n1. **It arrives knowing nothing, and admits it.** `/onboard` tags every line it\n   produces as `[OBSERVED]` (with a file citation), `[INFERRED]` (with the\n   reasoning shown), `[TOLD BY <who>, <date>]`, or `[UNKNOWN]`. A dossier with no\n   UNKNOWNs after a first pass is not thorough — it is fabricated.\n2. **It refuses to invent an oracle.** No written spec for an area? Then a\n   verification there reports *differences*, never *defects*, and the report says\n   so in its first lines. That refusal is the product.\n\n---\n\n## The whole system at a glance\n\nFour moving parts, and one rule connecting them: **nothing is a verdict until a\ngate that can go red has passed.** The CLI installs a lane into your repo; your\nagent reads the doctrine and drives the product; every claim it makes is checked\nby a gate and captured as evidence a stranger can read.\n\n```mermaid\nflowchart TB\n    subgraph repo[\"📁 Your repository\"]\n        code[\"source code\"]\n        spec[\"docs/ — the written spec<br/>(the oracle: what 'correct' means)\"]\n        tickets[\"tickets<br/>Jira · Backlog · GitHub · files\"]\n    end\n\n    subgraph cli[\"⚙️ ai-qa CLI &nbsp;·&nbsp; bin/ai-qa.mjs\"]\n        scan[\"scan — grade readiness\"]\n        init[\"init — install the lane\"]\n        doctor[\"doctor — prove gates green\"]\n        update[\"update — re-render safely\"]\n    end\n\n    subgraph lane[\"🔧 The installed lane &nbsp;·&nbsp; .ai-qa/ + docs/qa/\"]\n        doctrine[\"docs/qa/method/ — the QA doctrine<br/>(how a tester thinks)\"]\n        gates[\".ai-qa/scripts/ — the gates<br/>(each ships a --selftest)\"]\n        config[\"aiqa.config.yaml — the contract<br/>(url · specs · accounts · surfaces)\"]\n    end\n\n    subgraph agent[\"🤖 Your AI agent &nbsp;·&nbsp; Claude Code / Cursor / …\"]\n        onboard[\"/onboard\"]\n        qa[\"/qa\"]\n        triage[\"/triage\"]\n        regress[\"/regress\"]\n    end\n\n    evd[\"📦 evd/&lt;TICKET&gt;/ — the evidence pack<br/>report · manifests · screenshots · xlsx\"]\n\n    scan --> init\n    init --> lane\n    doctor -. verifies .-> gates\n    lane --> agent\n    agent -. reads .-> doctrine\n    agent -. reads .-> spec\n    agent -. fetches .-> tickets\n    agent -. runs .-> gates\n    agent ==> evd\n\n    classDef store fill:#FFF7E6,stroke:#E0A800,color:#3A2E00;\n    classDef tool fill:#E8F0FE,stroke:#3B6FD6,color:#0B2D66;\n    classDef out fill:#E9F9EE,stroke:#2E9E52,color:#0B3D1E;\n    class repo,lane store;\n    class cli,agent tool;\n    class evd out;\n```\n\n| Part | Lives in | What it is |\n|---|---|---|\n| **The CLI** | `bin/`, `src/` | `scan` · `init` · `doctor` · `update`. Installs and verifies the lane; never tests your product itself. |\n| **The doctrine** | `core/doctrine/` → `docs/qa/method/` | How a real tester thinks — personas, boundaries, security probes, accessibility, how to write a case and a report. The agent reads it at the moment each is needed. |\n| **The gates** | `core/scripts/` → `.ai-qa/scripts/` | Small programs that go **red** on a bad verification: missing evidence, a write to the database, a secret in a log, a pack that skipped security. Each proves itself with `--selftest`. |\n| **The workflows** | `core/workflows/` → your agent tool | `/onboard` · `/qa` · `/triage` · `/regress`, rendered into whatever your agent discovers natively. |\n| **The evidence** | `evd/<TICKET>/` | The output: a report a non-programmer reads in two minutes, plus the machine-checked pack behind it. |\n\n---\n\n## `ai-qa scan` — the mirror, before you commit to anything\n\nRead-only. No network. Works in repos without ai-qa installed. Always exits 0.\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/connorpham/ai-qa-assets/main/scan.png\" alt=\"ai-qa scan readiness scorecard — six gates scored, with the questions a new tester would ask\" width=\"880\">\n</p>\n\n<details>\n<summary>The same scorecard as plain text</summary>\n\n```\n  QA readiness  ~/work/shop\n  38/100  grade D   next.js, react · web+database\n\n  RUN       ████████████░░░░░░  13/20  Can I start it?\n  ACCESS    ██████░░░░░░░░░░░░   5/15  Can I get in?\n  ORACLE    ░░░░░░░░░░░░░░░░░░   0/25  Do I know what 'correct' means?\n  SURFACE   ███████████░░░░░░░   9/15  Do I know what to test?\n  COVERAGE  ███████░░░░░░░░░░░   6/15  What is already covered?\n  PROCESS   ██████░░░░░░░░░░░░   5/10  How does work arrive and leave?\n\n  What I would have to ask a human before I could test this\n  — these are the questions /onboard puts to the team, one at a time —\n\n  ? [ORACLE] Where is the written description of correct behaviour? Without it every\n    verdict is my opinion against the developer's — and I will not guess an expected value.\n  ? [ACCESS] Which test accounts exist, one per role? A verdict with no actor is\n    untraceable — half of all UI bugs are role-shaped.\n```\n\n</details>\n\n**ORACLE carries the most weight (25 points) on purpose.** Tests written against\nnothing verify nothing, so a repo with a great test suite and no specification\nstill scores badly — which is exactly the situation where a new tester cannot\ntell a feature from a defect.\n\nThe score is not the point. **The gap list is** — it is a to-do list for the\nteam, phrased as the questions a new colleague would actually ask.\n\n## `ai-qa init` — a setup that already read your repo\n\nThe scan runs first, so the wizard is short and its defaults are already right.\nIt asks only what your repo cannot answer, and writes nothing until you approve\nthe summary.\n\n```\nai-qa init          # terminal wizard\nai-qa init --ui     # the same questions in a browser form on 127.0.0.1\nai-qa init --yes    # accept every detected default (CI-safe)\n```\n\nA value it cannot detect is left **empty**, and empty is not a default — it is a\ndeclared unknown that the workflows report as a blocker. A guessed URL that\nhappens to be wrong costs more than an admitted blank.\n\nInstalls into `.ai-qa/` (gates, profiles, manifest), `docs/qa/` (the dossier and\nmethod), and whatever each chosen agent tool natively discovers.\n\n## The four workflows\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/connorpham/ai-qa-assets/main/workflows.png\" alt=\"The four workflows: /onboard, /qa, /triage, /regress\" width=\"880\">\n</p>\n\n| | What it does |\n|---|---|\n| **`/onboard`** | Arrive knowing nothing. Read everything that exists, draft the dossier, batch the unknowns into a short interview, then **prove the answers** by bringing the app up and walking one journey per role. Publishes `docs/qa/onboarding.md`, a risk map, three runnable charters, and a readiness verdict naming what is still missing and who owes it. |\n| **`/qa`** | Verify one ticket against the spec. **Read the ticket for ambiguity first** and ask before testing. Derive expected values **with citations**, design 2–5 cases chosen by risk, **walk each one as a named persona with a move a real user makes** — the double-click, the Back after Save, the paste with a trailing space — run them for real, capture named and annotated evidence, record what was noticed but not asked about, cross-check every claim, pass the machine gate, get falsified by a fresh challenger, publish a report a non-programmer can read. |\n| **`/triage`** | Turn \"it's broken\" into something a developer can fix today: reproduce first-hand, narrow to the minimal conditions, separate observation from theory, dedup, assign severity by consequence, file with numbered steps and evidence. |\n| **`/regress`** | Build a suite people still run in six months. Promote the journeys past verifications left behind, rank by consequence × likelihood, **prove every case can fail**, quarantine flakes with a deadline, and report coverage as what is protected — never as a percentage. |\n\n---\n\n## End to end: from an empty repo to a verdict\n\nThe whole lifecycle is five commands and four slash-workflows. You run the CLI\nonce to install and prove the lane; after that you live inside your agent.\n\n```mermaid\nflowchart LR\n    A[\"1 · scan<br/>grade readiness<br/>(read-only)\"] --> B[\"2 · init<br/>install the lane<br/>answer what the repo can't\"]\n    B --> C[\"3 · doctor<br/>prove every gate<br/>can still go red\"]\n    C --> D[\"4 · /onboard<br/>learn the repo,<br/>interview the team,<br/>write the dossier\"]\n    D --> E[\"5 · /qa TICKET<br/>verify against the spec,<br/>with evidence\"]\n    E --> F{\"defect<br/>found?\"}\n    F -->|yes| G[\"/triage<br/>reproduce, narrow,<br/>file a fixable bug\"]\n    F -->|no| H([\"ready to release\"])\n    E --> I[\"/regress<br/>promote the journey<br/>into a lasting suite\"]\n\n    classDef cli fill:#E8F0FE,stroke:#3B6FD6,color:#0B2D66;\n    classDef flow fill:#FFF7E6,stroke:#E0A800,color:#3A2E00;\n    classDef done fill:#E9F9EE,stroke:#2E9E52,color:#0B3D1E;\n    class A,B,C cli;\n    class D,E,G,I flow;\n    class H done;\n```\n\n**Step by step, with the commands you actually type:**\n\n```bash\n# ── 1. Look before you leap. Read-only, no network, works on any repo. ──\nnpx @connorpham6499/ai-qa scan\n#   → a 0–100 readiness score and the exact questions a new tester would ask.\n#     ORACLE weighs most: tests written against nothing verify nothing.\n\n# ── 2. Install the lane. The scan already ran, so the wizard is short. ──\nnpx @connorpham6499/ai-qa init          # or: init --ui  (a browser form)\n#   → writes .ai-qa/ (gates), docs/qa/ (doctrine + dossier skeleton),\n#     aiqa.config.yaml (the contract), and your agent's native workflow files.\n#     A value it cannot detect is left EMPTY — a declared unknown, not a guess.\n\n# ── 3. Prove the install. Green doctor is the definition of \"installed\". ──\nnpx ai-qa doctor\n#   → runs every gate's --selftest. A gate that cannot fail does not exist.\n```\n\nThen, inside your agent (Claude Code, Cursor, …):\n\n```text\n/onboard          → reads the repo, asks what it cannot find in ONE batched\n                    interview, proves the answers by bringing the app up and\n                    walking one journey per role, and publishes:\n                      docs/qa/onboarding.md   the dossier (every line tagged\n                                              OBSERVED / INFERRED / TOLD / UNKNOWN)\n                      docs/qa/charters.md     runnable exploratory charters\n                      a readiness verdict naming what is still missing, and who owes it\n\n/qa SHOP-142      → verifies one ticket against the spec and publishes:\n                      evd/SHOP-142/REPORT.md          a two-minute, jargon-free report\n                      evd/SHOP-142/*_testcases.xlsx   the six-sheet workbook, images embedded\n                      a comment on the ticket, and a proposed status move\n\n/triage           → turns \"it's broken\" into a bug a developer can fix today\n/regress          → promotes what /qa proved into a suite people still run in six months\n```\n\nThe rule that never bends: **every verdict comes from a run that happened this\nsession, checked against the written spec, and proved by evidence a gate has\ninspected.** No spec for an area? The report says so in its first line, and calls\nits own verdict an opinion — [that refusal is the product](#the-problem-this-exists-for).\n\n---\n\n## Inside `/qa`: how one ticket is verified\n\n`/qa` is eight phases (V0–V7). The shape that matters: **design by risk, run for\nreal, then have a *fresh* agent try to break the verdict before it is final** —\nand if the challenger finds a hole, the loop goes back and runs again.\n\n```mermaid\nflowchart TB\n    V0[\"V0 · Resolve the ticket<br/>fetch it for real · pin the commit · check status\"]\n    V1[\"V1 · Derive what SHOULD happen<br/>read the spec + schema · cite every expected value\"]\n    V2[\"V2 · Design 2–5 cases by risk<br/>acceptance · boundary · whole-screen<br/>write-readback · security · exploratory\"]\n    V3[\"V3 · Create missing data<br/>through the product, under the write gate\"]\n    V4[\"V4 · Run for real<br/>browser / API / database · capture named evidence\"]\n    V5[\"V5 · Cross-check<br/>every claim in the ticket → a file that proves it\"]\n    V6{\"V6 · Challenger<br/>a fresh agent tries to<br/>FALSIFY the verdict\"}\n    V7[\"V7 · Report + gates<br/>evidence gate · xlsx · comment on the ticket\"]\n    DONE([\"A verdict a non-programmer<br/>reads in two minutes\"])\n\n    V0 --> V1 --> V2 --> V3 --> V4 --> V5 --> V6\n    V6 -->|hole found| V4\n    V6 -->|holds up| V7 --> DONE\n\n    classDef phase fill:#FFF7E6,stroke:#E0A800,color:#3A2E00;\n    classDef check fill:#FDECEC,stroke:#D64545,color:#5A1414;\n    classDef done fill:#E9F9EE,stroke:#2E9E52,color:#0B3D1E;\n    class V0,V1,V2,V3,V4,V5,V7 phase;\n    class V6 check;\n    class DONE done;\n```\n\nTwo of these are the whole point of the tool. **V1** refuses to invent an\nexpected value — no spec, no defect, only a reported *difference*. **V6** hands\nyour finished verdict to an agent with empty context and tells it to prove you\nwrong: wrong role, a difference that is really about data, a boundary never\ntested, evidence that does not show what its caption claims. Both cards go in\n`debate.md`, and agreement reached without a run that actually executed is\n`UNCLEAR`, not `PASS`.\n\n---\n\n## With a written spec, and without one — the two paths\n\nThis is the fork that decides everything, so ai-qa makes you face it on purpose.\n**Where does \"correct\" come from here?** If a written spec exists, a verification\ncan call a divergence a *defect*. If nothing is written, it cannot — and the\nhonest thing is to say so, not to quietly adopt whatever the code does today as\nthe expected value. ai-qa takes the second path as seriously as the first: an\nhonest *difference* with an owner beats an invented *defect* every time.\n\n```mermaid\nflowchart TB\n    Q{\"Does this area have a<br/>WRITTEN spec / acceptance criteria?\"}\n    Q -->|\"Yes — an oracle exists\"| HAS\n    Q -->|\"No — nothing written\"| NONE\n\n    subgraph HAS[\"✅ Project WITH documentation\"]\n        direction TB\n        H1[\"Point aiqa.config.yaml → oracle.specs<br/>at the docs\"]\n        H2[\"/qa derives each EXPECTED value<br/>and CITES it to a section\"]\n        H3[\"Divergence from the spec = a DEFECT\"]\n        H4[\"Verdict: PASS / FAIL, with a severity\"]\n        H1 --> H2 --> H3 --> H4\n    end\n\n    subgraph NONE[\"⚠️ Project WITHOUT documentation\"]\n        direction TB\n        N1[\"oracle.specs left EMPTY<br/>— a declared unknown, not a guess\"]\n        N2[\"/qa will NOT invent an expected value\"]\n        N3[\"It reports DIFFERENCES, never defects<br/>+ consistency findings: inconsistent with its<br/>own other screen, its last release, the law\"]\n        N4[\"The report says so in line 1:<br/>'compares against nothing written' —<br/>the verdict is labelled an opinion\"]\n        N5[\"The questions it had to ask become<br/>your spec's to-do list → write it, re-verify\"]\n        N1 --> N2 --> N3 --> N4 --> N5\n    end\n\n    classDef q fill:#E8F0FE,stroke:#3B6FD6,color:#0B2D66;\n    classDef good fill:#E9F9EE,stroke:#2E9E52,color:#0B3D1E;\n    classDef warn fill:#FFF3E0,stroke:#E0A800,color:#3A2E00;\n    class Q q;\n    class HAS good;\n    class NONE warn;\n```\n\nThe same distinction runs through every command:\n\n| | 📗 Project **with** documentation | 📙 Project **without** documentation |\n|---|---|---|\n| Where \"correct\" comes from | the written spec, **cited** section by section | nothing written yet — it has to be decided by a human |\n| What `ai-qa scan` shows | a high **ORACLE** score | ORACLE near **0** — printed as the number-one gap, because it weighs most (25 pts) |\n| What `/onboard` does | maps each area to its spec in the dossier's §4 oracle map | tags the area `[UNKNOWN]` and turns it into the first question for the team |\n| The config line | `oracle.specs: [docs/spec/…]` | `oracle.specs: []` — an honest blank the workflows report as a blocker |\n| What `/qa` produces | **defects** — PASS / FAIL with severity and citation | **differences** and consistency findings, each with an owner — and the verdict is `BLOCKED`, not a PASS: the gate refuses `ORACLE: NONE` under a PASS |\n| The way forward | verify tickets against the spec | let the questions ai-qa asks *become* your first written spec — then the area flips to the left column |\n\n**You do not need documentation to start** — you need to be honest about not\nhaving it. The most valuable output on an undocumented repo is not a verdict; it\nis the precise list of questions a careful tester would have to ask before any\nverdict could mean anything. That list is your spec, half-written.\n\n> Practical tip: even a one-paragraph acceptance criterion in the ticket, or a\n> `docs/qa/` note capturing a decision the moment it's made, moves an area from\n> the right column to the left. ai-qa reads Markdown specs, Jira/Backlog\n> acceptance criteria, and a design source (Figma) as oracles — start with\n> whatever exists.\n\n---\n\n## Reading tickets — Jira, Backlog, GitHub, or files\n\nThe lane fetches the ticket itself instead of working from what someone pasted\ninto chat:\n\n```bash\npython3 .ai-qa/scripts/tracker.py check\npython3 .ai-qa/scripts/tracker.py get SHOP-142 --out evd/SHOP-142/ticket.md\npython3 .ai-qa/scripts/tracker.py comment SHOP-142 --body-file evd/SHOP-142/REPORT.md\npython3 .ai-qa/scripts/tracker.py attach SHOP-142 evd/SHOP-142/TC_*/*_boxed.png --record evd/SHOP-142/index.md\npython3 .ai-qa/scripts/tracker.py transition SHOP-142 \"Done\"\n```\n\n| Provider | Config (`tracker.base_url` / `tracker.project`) | Environment |\n|---|---|---|\n| `markdown` | — | none; tickets are files in `docs/qa/tickets/` |\n| `jira` | `https://acme.atlassian.net` · `SHOP` | `JIRA_EMAIL`, `JIRA_API_TOKEN` |\n| `backlog` | `https://acme.backlog.com` · `SHOP` | `BACKLOG_API_KEY` |\n| `github` | — · `owner/repo` | `GITHUB_TOKEN` |\n\n**The split is deliberate.** Base URL and project key are coordinates, not\nsecrets, so they live in the committed config where a reviewer can see them.\nCredentials only ever come from the environment; `init` adds their **names** to\n`.env.example`, and `.env` itself is in `.gitignore`.\n\nA `markdown` ticket is a file with the title on the first line and a block of\n`Key: value` lines under it — `Status`, `Assignee`, `Reporter`, `Type`,\n`Labels`. Blank lines inside that block are fine; the block ends at the first\nline of prose, and a `Status:` further down the file is prose, not a field. If\none is found there, the readiness note says so rather than reporting the ticket\nas having no status:\n\n```markdown\n# Apply the gold-tier loyalty discount at checkout\n\nStatus: Ready for QA\nAssignee: dev-nguyen\n\n## Acceptance criteria\n…\n```\n\nFour properties the selftest proves against a live local server, not a mock:\n\n- **A secret never reaches disk or a log.** Backlog authenticates with\n  `?apiKey=` in the query string, so every URL that could be printed goes\n  through a redactor — including error messages this module did not raise.\n- **Missing credentials are BLOCKED (exit 2), never FAILED (exit 1).** A\n  verification that could not start is a different outcome from one that ran and\n  found a defect, and conflating them turns a broken laptop into a false bug\n  report.\n- **Jira's ADF descriptions are flattened to text.** A verify sheet quoting\n  `{'type': 'doc', ...}` is a verification working from garbage.\n- **A field the file declares is read.** A blank line under the title used to\n  end the header block, so a ticket marked `Status: Ready for QA` came back with\n  no status — and `/qa` then called it `BLOCKED (not delivered)`. The fixture\n  now includes the blank line, because that is how everyone writes markdown.\n\nThe fetched ticket is written with a banner saying it is **data, not the\noracle**, plus an honest readiness note: whether it carries acceptance criteria\nat all, and whether its status actually means \"delivered\". A ticket that is only\nprose gets told so — *\"the description is prose; prose describes an intention,\nit does not say what to check\"* — because that is the moment a verification\neither gets an oracle or quietly invents one.\n\n## The spreadsheet everyone else reads\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/connorpham/ai-qa-assets/main/xlsx.png\" alt=\"The five-sheet workbook: Summary, Test Cases, Defects, Traceability, Evidence\" width=\"880\">\n</p>\n\nThe markdown pack is the record. But the people a verdict is *for* — a project\nmanager, a client, an auditor, whoever signs off — mostly do not open markdown,\nand a report nobody opens has the same value as a verification nobody ran.\n\n```bash\npython3 .ai-qa/scripts/xlsx_export.py --evd evd/SHOP-142            # writes evd/SHOP-142/SHOP-142_testcases.xlsx\npython3 .ai-qa/scripts/xlsx_export.py --evd evd/SHOP-142 --strict   # exit 1 while anything is still undeclared\npython3 .ai-qa/scripts/xlsx_export.py --evd evd/SHOP-142 --lang vi  # headers follow project.language by default\n```\n\nSix sheets, laid out to **ISO/IEC/IEEE 29119-3** with an IEEE-829-style field\nset, so nobody has to be taught how to read it:\n\n| Sheet | The question it answers on sight | Standard |\n|---|---|---|\n| **Summary** | Is this done? **How many defects, and how bad is the worst one?** | §8 test status report |\n| **Test Cases** | What exactly was tested, as whom, against which expected value, and what happened? | §7 test case specification |\n| **Defects** | What is wrong, how much does it hurt, who fixes it? One row per failed case, ranked worst-first. | §9 incident report |\n| **Traceability** | Which requirement does each case actually check — and which cases check nothing? | requirement traceability matrix |\n| **Evidence** | Where is the proof for every row above? Clickable, relative to the workbook. | — |\n| **Evidence images** | Each case as a plain **Given / When / Then** scenario, then **every step's screenshot embedded in order** — so a reader who opens nothing else sees what was *done*, step by step, not only how it ended. | — |\n\nThe severity ladder is not invented for the spreadsheet: **Blocker / Critical /\nMajor / Minor** come from `docs/qa/method/severity.md`, the same file the report\ncites, and a conformance test fails if the two ever disagree. Each level prints\nwith its definition and its handling rule next to the count, so a reader who has\nnever met this team still knows what \"Major\" obliges anyone to do.\n\n**Three things it refuses to do**, and they are the reason it can be forwarded\nwithout a covering note:\n\n1. **It never invents a value.** A field the pack does not declare prints\n   `NOT DECLARED` in grey and is listed again under *declared unknowns* on the\n   summary. A failed case with no `SEVERITY:` is counted as ungraded — never\n   quietly coloured Major because Major is the usual answer.\n2. **It adds nothing.** Every cell traces to a line in the pack. No score, no\n   weighting, no opinion of its own. Fix the pack and re-export; never edit the\n   workbook, or the two stop agreeing and only one of them has evidence behind\n   it.\n3. **It says so when the pack contradicts itself** — a verdict asserting a\n   defect with no failed case, a `PASS` over a failed one, an `ORACLE: NONE`\n   that makes every verdict in the file an opinion.\n\nWritten with nothing but the Python standard library — no `openpyxl`, no\n`pandas`, nothing to install. Byte-deterministic too: the same pack always\nproduces the same file, so a workbook whose bytes changed is telling you the\nevidence changed.\n\n## Gates that can actually go red\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/connorpham/ai-qa-assets/main/gates.png\" alt=\"The three exit codes every gate agrees on: 0 green, 1 a real finding, 2 BLOCKED\" width=\"880\">\n</p>\n\nEvery gate ships its own `--selftest` that mutates a passing fixture and asserts\neach mutation turns it red. A gate that has never failed does not exist.\n\n| Gate | Refuses |\n|---|---|\n| `evd_check.py` | Missing actor, precondition, entry path, reload check, boundary case, annotation, severity, or challenger card. A case folder called `TC_2` and nothing else, a screenshot carrying another case's number, an index that no longer matches the folders. Catches \"planned 5 cases, ran 1\". A report that does not say which environment produced the verdict. A case with no screen is evidenced by a read-only query, a command record, or a recorded request/response pair — the artefacts this toolchain actually writes — here or in one folder per call. An `EXPECTED` or `ACTUAL` that is only a judgement word — \"works as expected\", \"failed\" — because that is a wish, not a value. A pack that never declares whether **security** and **accessibility** were in scope — the two lenses skipped in silence more than any other — instead of naming the case that covered each or waiving it with a reason. **Every expected value must cite the document it was read out of** — a section number with no document, a file that does not exist, or a document this project never declared as an oracle is a red, and so is `ORACLE: NONE` under a PASS. **It opens the images**: a screenshot that is not a PNG or JPEG, one too small to be a screen, one screenshot filed under two step names, and a `_boxed` image identical to the shot it was drawn from are all reds — the cheapest forgeries, and the ones a prose-only gate cannot see. A `COMMIT:` that resolves to no commit in this repository is a red. A project checklist (`docs/qa/checklists.md`) omitted in silence is a red. An accessibility citation with no measurable UI fidelity checks is a red. Bypassing specs by putting `FLOOR` on every case is a red. **The project's own acceptance checklist is mapped item by item** — every id in `docs/qa/checklists.md` gets a case or a waiver with a reason, and a declaration that names no ids is refused. **Accessibility is decided by numbers** — a contrast ratio or a target size, not the word \"label\". **`FLOOR` names one of seven outcomes no specification permits**, never free text. **`debate.md` is read, not counted**: a weak spot named in advance, a challenge pointing at a case, and a resolution. **69 mutations, each proven to go red.** |\n| `evd_index.py` | Writes the case table into `evd/<TICKET>/index.md` from the case records, so `what was tested here` is answered by the folder itself — and cannot drift from it. `xlsx_export.py` reads that table for each case's one-line title. |\n| `db_verify.py` | Any write — including one hidden inside a CTE, behind a comment, or batched after a `SELECT`. **7 reads allowed, 18 writes refused.** |\n| `api_check.mjs` | Silent assertion failures; a token reaching an evidence file; an unreachable host being reported as a failure rather than as BLOCKED. Writes the command it ran and what it asserted into `cmd_verify.md`, so the case can be re-run without anyone retyping it. |\n| `annotate.py` | An \"annotation\" with no box and no caption — that is a copy. |\n| `tracker.py` | A credential reaching an evidence file or an error message; a missing token being reported as a failed verification rather than a blocked one. |\n| `browser.mjs` | Falling back to headless when Playwright is missing. That is a BLOCKED run with an install command. |\n| CI (`.github/workflows/aiqa-evidence.yml`) | Seeded on install, then yours. Runs the evidence gate and the strict export on every PR that touches `evd/`, plus both selftests. It is the only enforcement in the lane that does not run inside an agent — the difference between *the gate passed* and *an agent told me the gate passed*. |\n| `xlsx_export.py` | A guessed severity, a citation nobody wrote, a conclusion the pack does not support, a missing environment, a cell of terminal escape codes that would make the workbook unopenable. **18 honesty mutations, each proven to be reported in the file itself.** |\n\n**Every gate uses the same exit codes**: `0` green · `1` a real finding · `2`\nBLOCKED, the run could not start. Conflating 1 and 2 is how a laptop with no\nbrowser installed gets reported as a broken product, so conformance checks that\nthe tools agree on it.\n\n`ai-qa doctor` runs all of them, checks file integrity against the manifest, and\nverifies that the Node and Python config readers agree — because if they drift,\nthe gates read a different config than the CLI wrote.\n\nTwo things doctor will not do: call a runtime present when it is absent (a\nmissing Playwright on a repo with no web surface reads *\"Playwright not\ninstalled (not needed: no web surface)\"*, not a green tick), and count an\nedited gate as proof. A gate certifies itself with its own `--selftest`, so a\ngate script whose hash has drifted from what we shipped is reported\n**UNPROVEN** rather than green — amber, not red, because editing an installed\ngate is allowed; it just stops being evidence.\n\n## The mind of the tester\n\nGates catch what can be checked by a machine. What they cannot catch is a\nverification that satisfied every rule and still tested a route instead of a\nproduct: typed the perfect value once, never pressed anything twice, never came\nback after lunch, never read the ticket for the word \"should\". That is the\ndifference between a tester and a script, and it is written down in\n`docs/qa/method/` so the lane reads it at the moment it matters:\n\n| File | Opened when | What it changes |\n|---|---|---|\n| `requirement-smells.md` | Before a single expected value is written | The words in a ticket that hide a decision — \"should\", \"quickly\", \"the user\", \"like the other screen\", \"no change to existing behaviour\" — and the questions to ask the requirement owner **before** testing. A ticket that is smells all the way down gets `BLOCKED (not testable as written)`, honestly. |\n| `user-mindset.md` | Before cases are designed | Four people to borrow — the first-timer, the daily operator, the interrupted one, the one on a bad connection — the moves real people make that scripts never do, and the four questions after every action: *did it work, where am I, can I undo it, did I lose anything?* Every case names its `PERSONA:` and carries one real-user move. |\n| `hostile-inputs.md` | While writing the boundary case | The values ordinary people produce every week, by field type: the pasted trailing space, `1.000`, `O'Brien`, 29 February, 31 Jan + 1 month, a currency with no decimals, someone else's id. Pick two; never sweep. |\n| `heuristics.md` | For the exploratory case, and whenever the spec is silent | HICCUPPS consistency oracles — what the product is *inconsistent with* when nothing is written: its own other screen, its last release, its own tooltip, the law. SFDIPOT coverage, Zero-One-Many, interruptions, follow-the-data, the tours, RCRCRC. One heuristic per pack, named as `HEURISTIC:`, different each time. |\n| `checklists.md` | For the whole-screen case | The reflex checks a seasoned tester does without thinking, by feature shape — forms, lists, search, roles, money, lifecycles, dates, notifications, exports, delete, small screens. \"Still behaves\" becomes a list of specific looks. |\n| `security-probes.md` | When the ticket touches auth, sessions, roles, money, personal data, or uploads | The input an attacker sends *on purpose*, grounded in OWASP WSTG — weak lockout, user enumeration by message **and** timing, session and cookie flaws, IDOR, injection — with a floor of outcomes no spec permits (a bypass, an executed payload, a leaked secret, a session that outlives logout). |\n| `ui-fidelity.md` | For the whole-screen case, and any ticket with a design source | Measure the surface instead of admiring it: computed style vs the design token (never vs the code), fonts proven to load, and the measurable **WCAG 2.2 AA** subset — contrast, focus, keyboard, labels, target size — as a written oracle even when the ticket is silent. |\n| `red-flags.md` | When you hear yourself think \"obviously…\" | The excuses, and now the biases behind them — confirmation, anchoring, automation, sunk cost, the pesticide paradox, expert blindness — each paired with the part of the lane built to give it less room. |\n| `case-writing.md` | While writing each case record | The fifteen-second test: cover everything but TITLE, RESULT, EXPECTED and ACTUAL, and a stranger still knows what happened. A title that is a sentence about behaviour (*An order of exactly 499,999 gets no discount*), one action per step with the exact value typed, expected as an observable fact with its citation, actual in the same shape. Before-and-after tables for every field. |\n| `report-writing.md` | Before the first word of the report | The five lines everyone reads — the verdict word, then **Verdict / What it means / Next step**, under sixty words, pasteable into a chat. Table rows labelled by case title, never by number. Findings as four-sentence stories that say who it hurts. A jargon-to-plain table, a length budget, and the rule that \"What I could not check\" is present even when it says *Nothing*. |\n\nThree things this adds to the record, none of them a new gate:\n\n- **`OBSERVATIONS:`** on a case, and an *Observations* section in the report —\n  what was seen but not judged: the badge that did not update, the two-second\n  pause, the label two panels away that now names the wrong thing. No severity,\n  no change to the verdict. The thing a tester noticed and did not write down is\n  the ticket somebody files next week; `/regress` harvests the ones that recur.\n- **A consistency finding** where there is no spec — *\"inconsistent with its own\n  detail screen; decision requested from the product owner\"* — with\n  `ORIGIN: SPEC`. Still not a defect against a spec, still labelled as such, but\n  no longer \"I can say nothing\".\n- **The exploratory slot** — when the budget allows a fifth case, one of them\n  looks where nobody thought to look, and records what it tried even when it\n  found nothing. Four confirming cases and no exploring one has spent the whole\n  budget on what someone already thought of.\n\nNone of it softens the oracle rule. A persona says *how* to arrive and *what to\nlook at*; a heuristic says *where* to look. What is correct is still written\ndown somewhere, or it is still unknown — and the report still says which.\n\nOne of these is enforced, because it has been the entire content of too many\nreal case records: **an `EXPECTED:` or `ACTUAL:` that is only a judgement\nword** — *works as expected*, *correctly*, *failed*, *OK* — is refused by the\nevidence gate and reported by the spreadsheet. Present is not the same as\nwritten. If the only thing you can put after `EXPECTED:` is \"works\", you do not\nyet know what the product is supposed to show.\n\n## Rules the lane will not bend\n\n- **Every tool is told, whether or not a slash command is typed.** Installing\n  writes the same short section into the file each agent reads by itself —\n  `CLAUDE.md`, `.cursor/rules/aiqa-always.mdc` (`alwaysApply: true`),\n  `.windsurfrules`, `AGENTS.md`, `.github/copilot-instructions.md`. The most\n  common way a QA lane gets bypassed is not defiance, it is an agent that never\n  heard of it. `ai-qa doctor` reds if a tool is missing its section and ambers\n  if the section is older than the installed version.\n- **The spec is the oracle** — not the ticket prose, not the code. Spec silent →\n  the case is BLOCKED and escalated, never guessed.\n- **Every expected value cites the document it came from**, and the gate opens\n  it. `REQUIREMENT: docs/specs/orders.md 3.2 R1` — the document first, the\n  section after. A bare `3.2` is not a citation, a path that does not exist is\n  the *appearance* of evidence, and a document `oracle.specs` never declared is\n  an oracle chosen after the result was known. `FLOOR <rule>` is the honest\n  third option when nothing is written and the outcome is wrong anyway.\n- **A verdict needs a run that happened.** Not a status code, not a previous\n  session, not the developer's demo.\n- **Test data comes through the product**, under a write gate, marked `ZZTEST`,\n  and cleaned up. No reverse flow → nothing is written and the case blocks.\n- **The database is read-only.** It verifies writes; it never makes them.\n- **Entry is a click path.** A typed address hides a missing menu item, a wrong\n  permission, and an unreachable row at once.\n- **No product code is ever changed.** The moment QA edits the code, nobody is\n  checking it.\n\nEvery one of these has a matching entry in\n[`core/doctrine/red-flags.md`](core/doctrine/red-flags.md) — the excuse, and the\ngate that catches it.\n\n## Environments — local · dev · stg · prod\n\nA verdict is only meaningful on the environment that produced it — a bug found\non staging is not evidence about production — so *where* is a first-class,\ngated coordinate:\n\n```yaml\nenvironments:\n  default: local\n  local:\n    url: ''            # empty = app.url\n    writes: allowed\n  stg:\n    url: 'https://stg.example.com'\n    writes: allowed\n  prod:\n    url: 'https://www.example.com'\n    writes: forbidden  # the default for anything named prod\n```\n\n- **One resolver.** Every gate reads the active environment through the same\n  file (`lib/ctx.py`): the `env=` argument to `/qa`, else `$AIQA_ENV`, else\n  `default:`. `app_check.sh --env stg` · `api_check.mjs --env stg` ·\n  `AIQA_ENV=stg` for a whole session — the tools cannot disagree about where a\n  run happened.\n- **The report says where.** `ENVIRONMENT: <name — url>` is a gated header line\n  in `REPORT.md`: `evd_check.py` refuses a report without it, and the\n  spreadsheet prints it on the Summary sheet (or `NOT DECLARED`, listed as a\n  gap). The bring-up proof carries the same name: `APP: UP … · env: stg`.\n- **An undeclared name blocks.** `AIQA_ENV=stg` with no `stg:` block resolves to\n  no url at all — the run is BLOCKED, never a quiet fall-back to localhost\n  while the report says staging.\n- **prod is read-only by default.** On `writes: forbidden` — and any environment\n  named prod/production is, unless its block explicitly says otherwise — no\n  test data is created, the write gate never opens, and any case that would\n  change state is BLOCKED, not attempted; read-only journeys still run. Only\n  the literal `writes: allowed` opens the gate, so a typo fails closed.\n- **Per-environment `api_base` and `db_url_env`** override the global ones, so\n  a stg verification reads stg's database — not your laptop's wearing a\n  staging name.\n\n## The working language — en · vi\n\n`project.language` is not a translation step at the end — it is the language\nthe lane **works in**. Set once at `init` (or edit the config), and:\n\n- The `▶` narration, the questions `/onboard` puts to the team, the chat\n  summaries, the report, `/triage` bug reports, the dossier and ticket comments\n  are all written in it from the first word — never drafted in English and\n  translated after, which is how reports end up stilted.\n- The spreadsheet's headers and legend follow it automatically (`--lang`\n  overrides per export).\n- **What stays English is the machine-read contract**, and the rendered\n  workflows carry the exact list so no run re-decides it: field keys\n  (`RESULT:`, `EXPECTED:` …), report header keys (`COMMIT:` / `VERIFIED-AT:` /\n  `ENVIRONMENT:` / `ORACLE:`), the verdict word, the four severity words,\n  `ORIGIN`/`KIND` values, `TC_<n>_snake_case` folder names, and gate lines\n  (`APP: UP`, `DB: OK`). *\"Verdict\"* can be *\"Kết luận\"*; *\"PASS\"* on the first\n  line cannot.\n- Screen labels are quoted exactly as the product displays them: on a\n  Vietnamese product the step says `press \"Lưu\"`, never `press \"Save\"` — the\n  reader will look for the button that actually exists. The shape of a correct\n  line: `EXPECTED: \"Tổng cộng\" hiển thị 450.000 ₫ (spec §3.2)` — English key,\n  Vietnamese value, label verbatim.\n\n## Surfaces\n\nChosen at init; each activates its own gates and its own branch of the workflows,\nand an unchosen surface is not installed.\n\n- **web** — headed browser at a human pace (`app.pace`: a beat between actions,\n  key-by-key typing, a settle before each shutter), journey scripts kept as\n  re-runnable evidence, boxed screenshots\n- **api** — request and response recorded as files; the body is checked, not just the status\n- **database** — read-only verification, migrations proven on a clean database, tests proven able to red. The connection string comes from the env var named by `database.url_env`; `postgres://`, `mysql://` and sqlite are supported, and a sqlite path may be relative (`sqlite://data/shop.db`) or absolute (`sqlite:///var/db/shop.db`, `sqlite:////var/db/shop.db`, or a bare `/var/db/shop.db`)\n- **mobile** — device/emulator gating and evidence rules. *Honest scope: it gates the environment and the evidence; your project's Appium or Maestro setup does the driving.*\n\n## Agent tools\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/connorpham/ai-qa-assets/main/architecture.png\" alt=\"One tool-neutral core rendered by thin adapters into Claude Code, Cursor, Windsurf, Codex, and GitHub Copilot\" width=\"880\">\n</p>\n\nOne method, rendered into whatever each tool discovers natively:\n**Claude Code** (`.claude/skills/`), **Cursor** (`.cursor/rules/`),\n**Windsurf** (`.windsurf/workflows/`), **Codex** (`.codex/prompts/` + AGENTS.md),\n**GitHub Copilot** (`.github/prompts/`).\n\nTools without subagents still owe the challenger pass — they run it in a fresh\nchat. The requirement does not soften; only the mechanism changes.\n\n## Updating safely\n\n`ai-qa update` re-renders everything, then compares each file against the hash\nrecorded when it was written. Unchanged is ours to replace. **Drifted belongs to\nyou** — it is reported and left alone. Your config is never reverted.\n\n`--dry-run` genuinely writes nothing; the e2e suite asserts it by hashing the\nwhole tree before and after.\n\n## Layout\n\n```\nbin/ai-qa.mjs        scan · init · doctor · update\nsrc/cli/             the CLI; scan.mjs holds the readiness rubric\nsrc/ui/server.mjs    the browser wizard (local, single-use, no dependencies)\ncore/workflows/      onboard · qa · triage · regress   (tool-neutral)\ncore/doctrine/       the QA method: roles (the index), severity, evidence, test design,\n                     red flags — the tester's mind: user-mindset, heuristics,\n                     hostile-inputs, requirement-smells, checklists — the adversary\n                     and the eye: security-probes (OWASP WSTG), ui-fidelity (WCAG 2.2)\n                     — and the tester's pen: case-writing, report-writing\ncore/scripts/        the gates, each with a --selftest\ncore/templates/      the dossier and registries a human owns after install\nadapters/            one thin renderer per agent tool\nprofiles/            web · api · mobile · database\n```\n\n## Requirements\n\nNode ≥ 20 · Python 3 (3.9+) for the gates · Pillow for image annotation ·\nPlaywright for browser runs. The last two are installed on demand and report a\nloud BLOCKED with the install command rather than degrading quietly.\n\n## Tests\n\n```bash\nnpm test        # 476 conformance checks + 124 end-to-end checks\n```\n\nThe e2e suite installs into a scratch repository and then tries to break each\npromise: that `--dry-run` writes nothing, that `update` protects a file you\nedited, that `doctor` goes red when a gate goes missing, that the file count\n`init` prints matches what the repository actually gained, and that a repo with\nnothing detectable records empty values instead of guessing.\n\nConformance covers the contracts *between* the pieces, which is where the\nquiet failures live: the Node and Python config parsers agreeing, every\nadapter's workflow being discoverable by its tool — and an evidence pack built\nonly from what the lane's own recorder writes being accepted by the lane's own\ngate. That last one was broken while both sides passed their own selftest.\n\n## Licence\n\nMIT\n","readmeFilename":"README.md"}