Conversation
- Created `qa-asesoria.md` for providing QA advisory support during sprints, focusing on specific queries related to manual testing. - Introduced `qa-informe-resultados.md` to generate and update test result reports from Azure DevOps execution data, summarizing key metrics and findings. - Developed `qa-plan.md` to generate detailed test plans for sprints, including test case traceability and prioritization based on project requirements. - Established `qa-framework.rules.md` to outline agent behavior, formatting rules, and Azure DevOps integration guidelines for the QA framework.
Both qa-framework.instructions.md and qa-framework.rules.md referenced integrations.ado.enabled, a path that never existed in qa-framework.config.json. The real schema uses integrations.azureDevOps.enabled, matching what init.js bootstraps and what scripts/lib/claude-agents.js reads.
…tomation qa-spec-generation/SKILL.md pointed at references/spec-file-formats.md, but that file only exists under qa-module-analysis/references/ - its own header declares it shared by both skills. The relative path was never corrected for the second consumer, so Stage 2 could not find the template it is told to use. The empty qa-spec-generation/references/ directory is removed with it. qa-automation/SKILL.md referenced references/pom-template.md, which did not exist. Created from the page-object pattern in real project use: an abstract per-module CRUD base plus thin per-submodule classes, with the accent-insensitive label matching that pattern needs against Spanish-language UIs.
The Origin field allowed only UI-OBSERVED, PENDING-CODE and BLOCKED-PERMISSIONS. A common real case fits none of them: the element was observed and confirmed to exist, but its behavior could not be exercised end to end in the session - a disabled control, or a precondition that was never reached. Without a fourth value agents either overclaim with UI-OBSERVED, which implies the behavior was verified, or misuse PENDING-CODE, which implies the feature is absent and pollutes gap tracking downstream. In one session roughly 70 percent of a submodule's generated TCs were mistagged before an independent review caught it. Added to all five places the vocabulary appears: the definition in qa-module-analysis, both enum tables, and the two sites that branch on it.
The four agent templates are Spanish-language content that shipped entirely accent-stripped: 144, 190, 88 and 52 Spanish function-word markers respectively, with 0 to 2 accented characters between them. They are published to npm and copied into every consuming project. This is a functional defect, not a style preference. Page objects locate fields by their visible label, so a spec written as Codigo never matches a UI that renders Codigo with its accent. In one project that cost 24 of 39 failing smoke tests and two wrong mitigations - raising a grid timeout, then forcing HTTP/1.1 - because the selector failure presents as a timeout rather than as a text mismatch. Orthography only: line counts unchanged, no BOM, no forbidden characters, and identifiers, paths, placeholders and code spans left untouched.
The template hardcoded DEF-{{NNN}} while defectIdPattern in the config carried
DEF-{NUM}, neither of which matches how projects actually name defects - real files
use a project prefix, e.g. DEF-SIS-014. Hardcoding one project's prefix upstream is
the wrong fix; the pattern now takes the prefix as a placeholder so each project
supplies its own.
Stage 5 had no tsc --noEmit step and no instruction to run a newly written spec even once before marking the submodule complete. The only execution-adjacent step was the pre-inspection script, which runs before any test code exists. A real ReferenceError - a variable referenced but never declared, left over from a half-applied edit - survived into a spec and surfaced only when the full module suite ran later. A static check costs seconds and catches that class of error before any Playwright cycle is spent on it. Both gates run before the completion checklist. The smoke run targets execution-time errors only; a full green suite remains Stage 5b's job.
The skill pointed Step 1 at the official runner but said nothing about what to do when a standalone diagnostic script disagreed with it, and nothing stopped a standalone result from closing a defect on its own. That gap produced a real close-and-reopen cycle: a polling diagnostic script saw a toast 4 of 4 times while the official suite saw none, 6 of 6 across two independent runs. The defect had already been closed on the standalone evidence. Standalone output is now diagnostic input, never a verdict - stated in Step 1, in the Category I assertion-polarity rule, and as a row in Key Rules.
Nothing in the pipeline required checking whether a defect had already been filed before creating a new one. Four defects were filed in a single session without that check; a later manual pass found one of them sharing its exact symptom with three already-closed tracker items - unresolved whether that is a thrice-closed regression or a distinct submodule carrying the same defect. Either way it should have been caught at filing time, not months later. The gate is written against the project's known-issues record generically rather than one project's file layout, and distinguishes the two outcomes that matter: an open match means reference it instead of refiling, a closed match means file it as a regression. A defect closed three times and refiled as new each time reads as three unrelated bugs. The defect template gains the section the gate requires, so the search is recorded in the artifact rather than living only in the session that ran it.
Spanish content must carry its accents, but nothing enforced it for .md files, and compliance depended on the executor honoring the rule. In one session a sub-agent wrote six accent-stripped Spanish files and then reported explicitly that it complied; the defect surfaced only because someone ran the count by hand. The hard part is classification, not detection. A naive rule fails every English artifact the framework ships, and counting Spanish marker words in raw text does not separate them either - an English document about a Spanish-language project quotes enough Spanish paths and identifiers to cross any absolute threshold that still catches short Spanish files. So this measures the density of Spanish function words in prose only, after stripping fenced code, inline code, link targets, tags, placeholders and paths. Against this package's own corpus the separation is two orders of magnitude: English lands at 0.000-0.001, Spanish templates at 0.125-0.165, threshold at 0.07. A test asserts the gap stays wide so a future edit cannot erode it silently. Exposed as npm run check-accents; --staged suits a pre-commit hook.
Every stage that writes Markdown ended on a compliance claim rather than evidence. The difference is not academic: asked whether its output carried correct accents, a sub-agent answered yes and was wrong; asked for the count instead, the same agent produced the real per-file numbers and corrected itself. The shared reference gives the three commands that close a stage - accent count, forbidden characters, BOM - and states the language boundary, so English artifacts are not asked for accents they should not have. Hooked into the closing checklist of all four skills that generate documentation. This is the cheap half of enforcing the orthography rule. The other half is the gate in scripts/check-spanish-accents.js, which this reference points at.
Both scaffolds typed the QA password with locator().fill(password). Playwright records fill() argument values in the trace, and the automation scaffold ships trace and video set to 'retain-on-failure', so any failed CI run persisted an artifact containing the plaintext password. The integration scaffold has no capture configured today but carried the same unsafe call, so it breaks the moment a project turns tracing on. Both call sites now set the value through page.evaluate(), which is the mechanism already documented in this package as "Pattern 7: Password Injection (Trace Safety)" in skills/qa-automation/references/patterns.md - the scaffolds simply were not using their own pattern. The selector and password are passed in as evaluate arguments rather than re-read from process.env inside the scaffolds. A bare input.value assignment does not fire input events, and the Blazor/Radzen-style apps these scaffolds target bind on them, so the handler dispatches bubbling input and change events after setting the value. evaluate() also has no auto-waiting, so an explicit visible waitFor replaces the wait that locator().fill() performed implicitly, keeping the automation scaffold's 3-attempt retry loop and the integration scaffold's waitFor sequence behaving as before. Email fill() calls are left untouched: emails are not secret and keeping them in the trace helps debugging. No trace or video settings were changed.
Follow-up to the global-setup fix. Two further call sites still typed the QA password with locator().fill(password): fixtures/base.ts - TTL-aware session refresh (reauth) fixtures/auth.ts - per-role loginAs helper These carry more exposure than the global-setup sites, not less. global-setup runs once per run, whereas reauth runs for every test whose session exceeded the TTL and loginAs runs for every test that logs in as a role. With trace and video set to 'retain-on-failure', each of those executions could persist an artifact containing the plaintext password. Both sites now use the same evaluate()-based injection as global-setup, matching "Pattern 7: Password Injection (Trace Safety)" in skills/qa-automation/references/patterns.md: selector and password passed in as evaluate arguments, a null-check throw when the selector matches nothing, and bubbling input and change events dispatched after the assignment because a bare .value write does not notify the Blazor/Radzen style frameworks these scaffolds target. An explicit visible waitFor replaces the auto-wait that locator().fill() provided. Surrounding behavior is unchanged: the existing post-login waits keep their own timeouts (30s in base.ts, 15s in auth.ts), and the email fill() calls are left in place since emails are not secret and aid debugging. No trace or video settings were changed. There are now no remaining fill(password) call sites in templates/.
init.js wrote per-submodule spec templates to qa/{module}/{submodule}/, but
the authoritative spec home is qa/01-specifications/{module}/{submodule}/.
Evidence for the authoritative layout:
- all 8 skills in skills/ declare it
- docs/folder-structure-guide.md, docs/architecture.md and
docs/usage-with-agent.md document it
- qa-framework.config.json specPath is
"qa/01-specifications/module-{{module-name}}"
- upgrade.js treats 01-specifications/ as the spec home
- the three live consuming projects (QA_Sispro_Exportadora,
QA_PortalProductores, QA_PortalProveedores) keep their real specs there
and have zero top-level module directories
init.js already created qa/01-specifications/ as a top-level folder, so the
old behavior left that folder permanently empty while scattering specs as
sibling directories beside the numbered ones.
The existing init tests ran with no config, so the module loop iterated zero
times and no spec path was ever asserted. Adds a test that runs init with a
real module/submodule config and asserts the 6 spec files land under
01-specifications/, that nothing is created at qa/{module}/, and that the
e2e stub lands at e2e/tests/{module}/. It fails against the old path join.
The --strict automation-spec check looked for
qa/07-automation/e2e/{module}/{submodule}.spec.ts, a path nothing creates.
init.js writes the stub to e2e/tests/{module}/, and upgrade.js documents a
v1.6.0 migration that moved e2e/{module}/ -> e2e/tests/{module}/. validate.js
was never updated for that migration, so --strict emitted a permanently false
warning for every submodule; all three live consuming projects use e2e/tests/.
The old test asserted the buggy e2e/{module}/ path as expected output, and its
fixture hand-built qa/suppliers/create/ to match validate.js's code rather than
any documented contract. The fixture now uses the authoritative
qa/01-specifications/module-suppliers/submodule-create/ layout and the warning
assertion pins the e2e/tests/ segment, with a negative assertion guarding
against a regression to the pre-v1.6.0 layout.
Moving the fixture to the real layout exposes a separate, out-of-scope defect:
validate.js's scan treats every top-level qa/ directory as a module, so it now
reads 01-specifications/ as the module and demands the 6 spec files one level
too high. A correctly scaffolded project therefore fails validation. The test
covering that path documents the current behavior explicitly instead of
padding the fixture with module-level spec files no real project has. Fixing
the scan is deliberately left to its own change.
Check 2 walked all of qa/, treating every top-level directory as a module
and every directory inside it as a submodule. Since specs live at
qa/01-specifications/{module}/{submodule}/, that scan was off by one level:
it read 01-specifications/ as the module and module-* as the submodule, then
demanded the 6 spec files one level above where they exist.
Rooting the scan at qa/01-specifications/ fixes two failure families at once.
The traversal now starts at the correct depth, and sibling folders such as
02-test-plans/, 03-test-cases/, 04-test-data/ and memory/ are never visited,
so their subdirectories can no longer be mistaken for submodules.
SKIP_DIRS is removed rather than extended. It was a denylist that had to
enumerate every non-module folder by hand and had already fallen behind by
four entries; once the scan is rooted correctly it can never match anything,
so extending it would preserve a maintenance burden that no longer has a
purpose. Module directories are accepted without a name-prefix filter,
because docs/folder-structure-guide.md documents shared/ as a legitimate
non-module directory at this level.
Errors now report the full qa/01-specifications/... path.
Tests: the spec-layout test that pinned the off-by-one now asserts exit 0,
and three tests are added - an init-to-validate round trip, a populated
sibling-folder case, and a shared/ directory case. All six spec-scan tests
fail without this change.
Both scaffolds and both auth fixtures typed the password with locator().fill(), whose argument values Playwright records in traces. With trace and video set to 'retain-on-failure', a failed CI run persisted an artifact containing the plaintext password. All four call sites now inject through page.evaluate() - the mechanism this package already documented as Pattern 7 in qa-automation/references/patterns.md and did not use in its own scaffolds.
Three components disagreed on where specs live. init.js scaffolded them to
qa/{module}/{sub}/, validate.js scanned every top-level qa/ directory as a
module, and the 8 skills, 3 docs, the config's specPath and upgrade.js all
declared qa/01-specifications/{module}/{sub}/. All three live consuming projects
use the latter exclusively, with zero top-level module directories.
init.js now scaffolds there; validate.js roots its scan there, which corrects an
off-by-one depth and removes the need for SKIP_DIRS; and --strict looks for
automation specs under e2e/tests/, where the v1.6.0 migration moved them.
Before this, validate exited 1 on a clean install and reported 150 errors across
the three live projects, every one of them a scan artifact. The round-trip test
that would have caught all of it is added here.
The generators embed em-dashes and arrows in template literals and console output that propagate into every consuming project: qa/memory/INDEX.md and qa/03-test-cases/README.md are written with an em-dash in their heading. Replaces all 30 occurrences across cli.js, generate.js, init.js, upgrade.js and validate.js with the ASCII equivalents required by the character-safety rule in .github/copilot-instructions.md. Characters only - no wording changes, no reflowed lines, column-aligned comment blocks keep their alignment, and emoji are untouched since they are outside the forbidden set.
Enforces the BLOCKING character-safety rule from .github/copilot-instructions.md that the package previously stated but never checked: em-dash, en-dash, ellipsis, smart quotes and arrows are flagged, Latin Extended and emoji are not. Scans .md, .js and .ts - unlike check-spanish-accents.js, which is .md-only - because the generators under scripts/ are the occurrences that propagate into consuming projects. Reports file:line:column with the code point and its ASCII replacement. Exit 0 clean, 1 on violations, 2 on usage error; --staged and --report mirror the accent checker's CLI. The suite pins scripts/ and test/ as clean, which is the regression guard for the generators. templates/ and skills/ still carry 486 occurrences and are a deliberate follow-up, so they are not asserted.
copilot-instructions.md declares a BLOCKING rule against em-dash, en-dash, ellipsis, smart quotes and arrows, and the package violated it in its own generators - init.js and upgrade.js wrote em-dashes straight into every consuming project's qa/memory/INDEX.md and 03-test-cases README. The framework was propagating the defect it forbids. All 30 occurrences in scripts/ are now ASCII, and check-forbidden-chars.js gates .md, .js and .ts against the whole set while leaving Latin Extended alone, so Spanish accents are unaffected. A test pins scripts/ and test/ clean. The prose in templates/, skills/, docs/ and meta/ still carries the legacy occurrences; those are a separate cleanup now that the gate exists to verify it.
Decision-tree and flow diagrams used U+2192, U+2190, U+2194, U+2193 and U+21FF, which the character-safety rule forbids. Right/left/bidirectional arrows map to ->, <- and <->. The down arrow U+2193 has no ASCII spelling, so it becomes "v": it is one column wide like the original, which preserves the diagrams' existing alignment, and it reads as flow-down in a left-aligned decision tree. output-verification.md is a special case: its rg pattern is a character class enumerating the forbidden characters themselves, so substituting ASCII would destroy the pattern. It now uses $'\uXXXX' escapes, which bash expands to the same code points while keeping the file ASCII-clean.
Em-dashes, en-dashes, remaining arrows, smart quotes and an ellipsis are replaced with the ASCII equivalents required by the character-safety rule in .github/copilot-instructions.md. These files are copied into consuming projects, so the characters propagated into user specs and test code. Substitution is context-sensitive rather than literal: - padded prose dashes become " - " - numeric and label ranges (50-85, P0-P3) keep a tight hyphen so the meaning and the column width are unchanged - standalone dash table cells become a bare "-" - the horizontal rule in agent-next-steps.md becomes the same number of ASCII hyphens, preserving its 68-column width Wording, structure and line counts are unchanged, and Spanish accented characters are preserved exactly.
…ters Applies the same context-sensitive substitution to the remaining prose: padded dashes become " - ", ranges keep a tight hyphen, and the last arrows become -> and <-. Line counts, wording and Spanish accents are unchanged.
791 occurrences across skills/, templates/, docs/, meta/, examples/ and integrations/, closing the debt the character gate exposed. scripts/ and test/ were cleaned when the gate landed. Not a blind substitution: numeric ranges (50-85 TCs, P0-P3) keep a tight hyphen rather than becoming ' - ', which would read as subtraction and widen table columns; standalone table cells take a bare '-'; and the decision trees in classification-protocol.md use 'v' for flow-down, which holds one column like the character it replaces and does not collide with Markdown's table delimiter. One edit is semantic rather than character-level: output-verification.md documents a command whose regex enumerates these very characters, so replacing them would have made it match hyphens. It now uses \u escapes, which bash expands to the same code points while leaving the file clean. Spanish accents verified unchanged: 449 in templates/agents, 566 repo-wide.
…ration notes Closes the last of the prose debt outside CHANGELOG.md. The numeric range 22957-23034 takes a tight hyphen rather than ' - ', which would read as subtraction. CHANGELOG.md keeps its 87 occurrences deliberately: it is a historical record, and rewriting past entries to satisfy a rule adopted later would falsify the log rather than fix anything.
The gate covered .md, .js and .ts, so sync-ado-titles.ps1 was never scanned - and it carried an em-dash and an arrow in two console messages. PowerShell scripts under integrations/ are copied into consuming projects like any other template, so the same propagation argument applies. Adding .ps1 to the extension list closes the blind spot.
…figs Both playwright.config.ts files called dotenv.config(), which injects the entire .env into process.env. In a real project that file holds every QA account password plus ADO tokens - in one, 82 lines of them - while these configs need a handful of non-secret values. Any dependency, crash dump or child process inherited the rest for no benefit. They now read an explicit key list with dotenv.parse(), which returns a plain object and never touches process.env, and only fill keys the environment has not already set so shell and CI variables still win. The integration scaffold's list includes QA_USER_PASSWORD because its credentials check reads it; the value is only tested for presence. The three remaining dotenv.config() calls are in global-setup and the session fixture, which genuinely need credentials to log in.
The dispatcher calls heartbeat, not the executor. Written down because the decision was made in conversation and the N-lane work it governs is not started yet - a later session would otherwise rediscover the fork from scratch, or ship the staleness sweep without an answer and reproduce the 2026-08-22 incident it came from.
A lane is four coupled values - account env prefix, storageState, MCP namespace and runner project - none of which can be inferred from the others, so all four come from config. Capabilities are modelled per-lane rather than as a property of the lane set. A project can have six accounts but only three MCP servers, and handing a lane to a consumer needing tools that lane lacks is a real failure, not a hypothetical. A project with no parallelLanes block, or with enabled:false, resolves the single implicit default lane and behaves exactly as before.
Ports the reference implementation, replacing its hardcoded six-entry LANE_INFO with the configured lane table. Keeps the behaviors the reference earned in production use: - acquire (random free lane, queues) and reserve (exact lane, no queue, throws when busy) stay two verbs. Collapsing them is how a CI job lands on a random lane. - The composite staleness predicate: stale only when the hold is past the threshold AND no recent heartbeat proves the holder alive. - QA_LANE_LOCKS_DIR with shell > .env > per-checkout precedence. A fixed per-checkout .locks/ gives separate worktrees zero contention while every command still reports success. - The stderr line naming the resolved lock dir and its source. The underlying failure is silent, so this is part of the fix. - mkdir mutex, FIFO queue capped at the lane count, stale-release logging. Reads the .env key with dotenv.parse(), never config(), so a script whose only job is choosing a directory never gains ambient access to the QA account passwords in the same file. Falls back to a narrow single-key parser when dotenv is absent rather than silently ignoring .env.
A lock nothing consults is decoration. global-setup performs the only login, so it is the last point at which a run about to drive an account it does not hold can be stopped. - assertColdSetupIsIntentional throws before any login when neither QA_LANE_ONLY nor QA_COLD_SETUP=1 is set, since a cold setup re-logs in every lane and invalidates other running sessions. - assertLaneIsLocked verifies outside CI that the lane is actually held. - assertProjectsMatchLane catches a --project whose storageState points at a different lane than the one reserved. Plain CommonJS so the logic is testable with node --test without adding ts-node. global-setup never acquires or reserves a lane itself; reserving is the caller's job, and a global-setup that reserved its own lane would make every accidental run look legitimate.
Replaces the commented-out chromium-user1/chromium-user2 block, which sat
outside the projects array and could not have worked as written, with
projects generated from parallelLanes.lanes.
playwright.config.ts derives every storageState through lane-lock rather
than hardcoding one per project. A parallel hardcoded mapping caused real
evidence corruption once: a run recorded under the wrong account because
the project was pinned to one lane while QA_LANE_ONLY named another.
global-setup's resolveAuthUsers returned Set<'1'|'2'> and picked accounts
with storageState.includes('user-2'). That substring match is what breaks
first past two lanes - 'user-2' matches 'user-20', and no branch existed
for a third lane at all. It now resolves lanes by exact match against the
lane table and reads credentials from each lane's declared account prefix.
init.js scaffolds the lane scripts. A project with no parallelLanes config
still gets exactly one 'chromium' project on .auth/user-default.json.
Shipping the lock without a rule requiring its use repeats an established mistake: the capability exists but nothing makes an agent reach for it. States the requirement only - acquire before dispatching, release when done, dispatcher keeps the lane alive, queue or reject at the cap, never force past it. Agent-agnostic, so no Claude Code primitives appear here; each agent's adaptation prescribes its own mechanism.
Both .env.example files carried a UTF-8 BOM and three em-dashes. The gate never saw them because it scanned .md, .js, .ts and .ps1 only - the same blind-spot class as the PowerShell script fixed earlier. A BOM in a .env is not cosmetic: some parsers read the first key as \ufeffQA_BASE_URL and silently fail to find it, and these files ship to every consuming project as the template developers copy to .env. Adding '.example' to the extension list closes the blind spot.
Lanes exist to work around a constraint of the applications under test: one account cannot hold two sessions, so a second login silently invalidates the first. The scaffold supported exactly two, through a commented-out project block and a resolveAuthUsers that picked accounts by matching 'user-2' inside a storageState string - the part that breaks first past two lanes. A lane is now a config entry declaring account, storageState, MCP namespace, runner project and its own capabilities. Capabilities are per-lane rather than a property of the set, so a project with six accounts and three MCP servers cannot hand an MCP consumer a CLI-only lane - a failure that happened in practice. lane-lock.js ships with the behaviors its reference implementation earned the hard way: acquire and reserve kept distinct, a composite staleness predicate that a live heartbeat overrides, and a relocatable QA_LANE_LOCKS_DIR. That last one matters most: with a fixed per-checkout .locks/, parallel worktrees get zero contention while every command reports success. Verified both directions - two checkouts sharing a locks dir contend correctly, and without it both reserve the same lane, reproducing the bug. storageState is derived from the lane table alone, never a parallel copy; a duplicated mapping previously recorded a run under the wrong account. Projects without parallelLanes behave exactly as before: init and validate exit 0, and the resolver reports one lane on the default account.
The test script used test/**/*.test.js, which needs globstar to match files at the root of test/ - and the CI runner's shell does not enable it. Every test file in this package sits directly in test/, with no subdirectories, so the pattern expanded to nothing and CI failed with 'Could not find'. The defect predates this branch: it arrived with the commit that added the runner, which is the current head of dev, so no PR had exercised it yet. It passed locally because Git Bash on Windows expands the pattern differently. Verified under plain sh, which is what the runner uses: 118 tests, 0 failures.
The previous fix swapped the pattern but kept it quoted, so the shell passed 'test/*.test.js' to node as a literal path and it failed the same way. The quoting was the actual defect, not the globstar - node received a string it tried to resolve as a file. Dropping the quotes lets the shell expand the glob to the eight test files before node sees them. Chosen over a bare 'node --test', which relies on automatic test discovery whose behavior differs across the Node 18 and 20 versions this CI runs and which I cannot verify from Node 22 here. Verified under plain sh: the glob expands to 8 files, 118 tests, 0 failures.
The matrix tested Node 18 and 20, both out of maintenance - 18 since April 2025, 20 since April 2026 - so CI was verifying two unsupported runtimes and none of the supported ones. Adding 24 also closes a gap: publish.yml releases on Node 24, which no job tested. The package was published on a runtime CI never exercised. engines moves from >=18 to >=20 so the declared floor matches what is actually verified. Users on 18 will now get an npm warning, which is the point - that version was never being tested.
The validate step ran against this repository's own qa/ directory, which is gitignored - it is the framework's live instance for this repo's QA work, not package content. The runner checks out without it, so validate found nothing and failed with four missing-folder errors. Like the test glob, this predates the branch: the step came from dev and had never passed, because no PR had exercised the workflow. Scaffolding a throwaway project and validating that exercises the contract that actually matters - what init.js generates must pass validate.js. That round trip is what three spec-path defects slipped through for want of a test, so it earns its place in CI. Both plain and --strict run. Verified locally under sh with GITHUB_WORKSPACE set: init 0, validate 0, --strict 0.
Closes the backlog filed in `FRAMEWORK-FIXES-qa-framework{,-v2,-v3,-v4,-v5}.md` - five documents written while applying this framework to real projects, each pairing a real pain point with a proposed upstream fix.
keber
marked this pull request as ready for review
September 9, 2026 02:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes to
maineverything merged intodevin PR #1. Same diff, no additionalcommits: 42 commits, 96 files, +5539/-820.
This closes the backlog filed across five
FRAMEWORK-FIXES-qa-framework*.mddocuments, each written while applying this framework to real projects. Three of
the defects were shipping into consuming projects.
Defects that propagated. All four login call sites typed the QA password with
locator().fill(), whose argument Playwright records - withretain-on-failure,a failed CI run persisted the plaintext password in its artifact. The generators
wrote em-dashes into every project's files, violating the encoding rule this
package itself declares BLOCKING. Both
.env.examplefiles carried a BOM, whichmakes some parsers miss the first key.
Spec paths converged.
init.jsscaffolded specs wherevalidate.jsdid notlook and the skills did not declare.
initfollowed byvalidateexited 1 on aclean install and reported 150 errors across three live projects - every one a
scan artifact. Both now exit 0.
Parallel lanes generalized. From two hardcoded accounts to N driven by config,
with per-lane capabilities and a lock whose staleness predicate a live heartbeat
overrides. Lanes exist because the applications under test invalidate a session
when the same account logs in twice.
Gates added.
tsc --noEmitand a smoke run before Stage 5 completes; theofficial suite as the only source of truth for defect status; a known-issues
lookup before filing; language-aware accent and character checkers.
CI now runs. Both steps were broken in
devand had never passed - the testglob never expanded on the runner, and
validatechecked a gitignored directory.It now validates a freshly scaffolded project, which is the contract that matters.
118 tests passing, up from 23.
CHANGELOG.mdkeeps its forbidden characters deliberately - rewriting ahistorical log to satisfy a rule adopted later would falsify it.