Skip to content

WorkWingman — Delivery Board

Meet the team

Our build crew is a family of AI teammates — we call them by name. The name is the teammate; the "Runs on" column is the AI tool they work through. So when a task or review says Cedric did it, that's the teammate — the underlying tool is in the table.

Teammate Runs on What they usually do
Clahadore Jones-Gaffney Claude (Anthropic) Planning, coordinating the work, reviewing, and the hard calls
Cedric "Cody" Jones-Gaffney Codex / ChatGPT (OpenAI) Writing code and giving a second-opinion review
Jenny Jones-Gaffney Gemini (Google) Writing code, research, and review
Gronktayvius "Gronk" Jones-Gaffney Grok (xAI) Tough adversarial review — trying to break things before users can

All four share the surname Jones-Gaffney because they're family. Led by Andrew (Andrew David Jones-Gaffney).

Live interactive board: https://claude.ai/code/artifact/27a44c1c-d629-4067-995e-a02fc41444a9 — the kanban view (In Progress · In Review · Done). This file and that artifact are kept in lockstep: both move on every status transition.

Live project board: the active sprint plus every delivered epic, reconstructed from the full commit history. Cards move on every STATUS TRANSITION (started / in review / done) — not just at commit time — so In Progress and In Review reflect work as it happens; Done rows carry the commit that landed them. (274 commits, Jul 3 → Jul 8, 2026 so far.)

Ground rules the whole board inherits: every commit passes a multi-model review gate (Codex + Gemini, later + Semgrep/gitleaks/ZAP; Grok seated Jul 9 night — from then on review chips name Grok explicitly whenever it sat, including degraded runs; chips tagged pre-Grok seat are gates that ran before Grok joined); the automation never clicks Submit, never types stored passwords, and never creates accounts; nothing is pushed or sent anywhere without an explicit decision.

Posture right now: 3,095 backend tests passing (full suite, on master after the BYOK council-review pass) · full frontend spec suite green · Semgrep 0 findings · gitleaks clean over full history · ZAP baseline 0 FAIL / 66 PASS · Greenhouse + Workday + iCIMS background apply-drive live · provider-agnostic BYOK AI harnesses (LLM · research/RAG · audio) live · tester-ready NSIS installer shipped · CI on the self-hosted warm pool (GitHub cloud minutes exhausted for the window).


Active sprint

Status Key Ticket Epic Notes
⚪ Backlog WW-94 · WING-86 Triage the wider class of unguarded Playwright waiting calls in Automation/ — the WW-91/WING-14 tail Quality A triage task, NOT a defect list — do not mass-convert. WW-91 fixed 9 unbounded waiting calls guarded by catch (PlaywrightException) only. It was scoped by searching inside those catch blocks, and a council seat (Grok) found the blind spot: calls with no guard at all are invisible to that search — Paycom/Paycor's legend/heading reads were fixed reactively inside WW-91. This is the rest of that class. A heuristic scan flags ~78 candidate calls across ~14 engines, but that is an over-count by design: many are wrapped by a try further out, and — the point — many SHOULD fail the run and must be left alone (if GotoAsync can't reach the apply page, or the one resume SetInputFilesAsync fails, throwing is correct; swallowing hides a broken apply behind a cheerful "done"). The work is classification, per-site, not a regex: best-effort probe → route through LocatorProbes Try*/IsMiss keeping the default budget (never a tighter explicit timeout — manufactures load-flake, the c77df35 trap); load-bearing → leave, document why. The count itself is untrustworthy — WW-91's wobbled 24→2→7→6→9 across four scripts before a per-site read settled it, so enumerate with an analyzer or careful read, not the ~12-line window. WW-86 family: c77df359a35144 → WW-91 → this. Confirmed present; not a regression; not blocking — shipped fixes already cover the observed flakes.
🔵 In Review WW-92 Amazon.Tests's one browser test takes the shared browser — the last manual LaunchAsync in a test project Quality WW-93 promoted SharedBrowser to tools/WorkWingman.TestSupport and adopted it in Meta.Tests (11 browsers → 1). AmazonLabTests.cs was the only remaining test file calling Chromium.LaunchAsync itself — but note exactly one test in it launches a browser (Engine_answers_each_radio_question…), not the per-test storm the row title first assumed. Honest value, measured before building: SharedBrowser bounds browsers to one per assembly, and Amazon.Tests is its own process with a single browser test, so it was already at one browser — adopting SharedBrowser changes the count 1 → 1. This is NOT a perf fix; that story ended with Meta. The value is the invariant: no test project launches its own browser anymore, so a second browser test added to Amazon.Tests later shares instead of spawning a second Chromium, and "browsers ≤ assemblies" holds by construction rather than by luck. Preserved the test's graceful skip when Chromium is absent (SharedBrowser's install-fallback throws; the test catches and returns, unchanged intent). Corrections carried from this row's first draft (they'd have justified work that isn't needed): "all 13 labs launch a browser per test" was false (only Meta and Amazon ever did; Google/Icims/Microsoft launch none — 13 tests in 894ms proves it), and the PaycorApplyEngineTests failure blamed on lab load does not reproduce post-9a35144 (measured pre-rebase; the victim side was fixed independently). Separately still open and now WT-ae5b's (WW-86 follow-up): once no test launches its own browser, tests/WorkWingman.Tests/xunit.runner.json's maxParallelThreads: 0.25x may be pure cost — re-measure without it and delete if same-or-faster, since WW-86 found the cap made the suite faster, which is the tell it was throttling browser launches, not CPU.
⚪ Backlog WW-89 · WING-13 Frontend gate hole #2 — a vitest worker that fails to spawn exits 1 with every test green Quality The same masked-regression symptom WW-88 just fixed, arriving by a different route. Under memory pressure the threads pool fails to start a worker ([vitest-pool]: Failed to start threads worker for test files .../app.spec.tsTimeout waiting for worker to respond); that file's tests are silently dropped from collection rather than reported failed, and vitest exits 1 with Errors 1 error while every test that ran passed. The healthy run is 43 files / 851 tests; the degraded run is 42 / 849, and nothing in the summary says two tests vanished — the denominator just shrinks. Worse than WW-88 in one respect: all-green output plus exit 1 invites "the exit code is just noise", which is exactly how a real regression walks through. Reproduced 2026-07-16 on andygreatroompc (29.37 GB): at 1.17 GB free / 89.5% commit it failed twice consecutively; same tree at ~4 GB free ran 43/851/exit 0 three times. Trigger is starvation at worker-spawn, not the code under test. Do NOT "fix" by raising the worker timeout — that trades a fast visible failure for a slow one and still drops the tests. Real options: (a) make dropped collection fatal and legible (name the file that never ran); (b) assert the expected file/test count in CI so silent shrinkage can't pass, independent of exit code; (c) evaluate pool: 'forks' vs threads (maxWorkers=1 is already set and still starves); (d) a RAM-floor preflight mirroring the council skill's local-seat canary — refuse to report a verdict the environment can't support. Same family as WW-86's load-rotating backend flakiness. Diagnosed from the log, not the symptom (WW-50/WW-86 lesson): first suspected as a rebase regression, ruled out — the nine commits it rebased past touch only .cs/.csproj.
⚪ Backlog WW-90 · WING-12 OfferDetail.delete() fails silently — no catchError, no toast, unlike its sibling methods Insights offer-detail.ts:172-176this.api.deleteOffer(o.id).subscribe(() => this.router.navigate(['/offers'])). Two gaps on one line: the subscribe(next) form handles only next, so a failed delete (network drop, backend 500) becomes an unhandled RxJS error and the user sees nothing — offer still on screen, no explanation, no retry; and the router.navigate promise is discarded, so a future guard or route rename surfaces as an unhandled rejection. This is the outlier in its own file: recordDecision() at line 160 already does it right (catchErrortoasts.show('Couldn't save your decision.', 'Try again in a moment.')of(null)). ToastService is already injected, so the fix carries no new dependency. Latent gate hazard: there is no delete-failure test today; adding one with throwError against the current code would produce an unhandled rejection and return the suite to exit 1 — the exact WW-88 class of failure. AC: failed delete shows a toast and stays put; success still navigates; spec gains a throwError test asserting the toast fires and navigate is NOT called; npm test still exits 0. Found by the Gemini and Grok seats during the WW-88 council (both rated it out of scope for a test-only commit; Codex and the local seat did not raise it); verified by hand against the source.
⚪ Backlog WW-73 · WING-10 Full-scope mutation measurement done right (WW-34 follow-up, council-ordered sequencing) Security (a) Fix VsTest socket instability: per-module/per-project shards, bounded concurrency, raised timeouts, and fix timeout-vs-killed scoring (exclude or bucket timeouts) before any full-scope number is trusted; (b) only after one clean full run: add the non-blocking scheduled broad run; (c) then ratchet per-module break thresholds against that real baseline; (d) after a week of stable scoped-gate runs, tighten scoped break 90 → ~95–97 (97.64% ceiling leaves ~7.6pt slack today). Historical failed full-scope runs are NOT score baselines. Council verdict 7/11 (llm-council/reports/2026-07-11_004747_*).
🟡 Needs Andrew WW-63e · WING-62 Fresh-desktop NVDA reconfirm + qualitative human NVDA/Narrator listen — the one irreducibly-human accessibility check Quality Everything automatable shipped (structure gate + apply-flow surfaces, 4da1144). NVDA's automation build degraded after many launches tonight; a fresh session re-confirms the spoken output, then a person judges whether the experience is actually good.
⚪ Backlog WW-65 · WING-64 Electron→Tauri shell migration — post-pilot, trigger-gated Architecture Council verdict 7/9 (3:1, logged in llm-council/reports/COUNCIL-LOG.md): stay on Electron; Pake rejected outright. Reopen ONLY on a trigger (tester size/RAM complaints or macOS demand), post-pilot: spike → a11y WebView2 parity hard gate → updater bridge release → opt-in ring cutover. Track A owns; GREEN-tier only; three council gates before any port work.
⚪ Backlog WW-75 · WING-73 Offer Review & Decision — accept/negotiate/wait support (council-specced) Insights Full-council design 7/11 (Claude/Codex/Gemini/Grok fable tier + 3 house seats; unanimous ranking Grok>Claude>Codex>Gemini; llm-council/reports/2026-07-11_130158_*; spec = docs/technical/offer-review-design.md). Three levers accept/negotiate/wait, never a ranking oracle. 6 axes: real comp (reuse JobImpact/COL/tax/equity chain) > timing/opp-cost > hard constraints (GATES not weighted axes) > growth delta > stability > fit. Kerr bans (binding): composite 0–100 score, point pipeline %, equity point-dollars, negotiation-success %, culture-fit %, company health score, Accept-as-CTA. Output = ordinal bands Strong/Mixed/Weak/Unknown first-class + per-axis narrative + scenario cards + decision journal. Intake ≤8 questions lifetime via new OfferGapAnalyzer. Pipeline EV = qualitative bands from published stage ranges (per-user calibration dishonest <~15 outcomes/stage); static verified extension templates, never auto-send. Stability panel shallow (layoff/WARN, revenue-trend enum, RSU vol haircut band) — starts Alpha Vantage, FMP unwired, no phase blocks on it. No personality test: O*NET work-values 6-item rank + retro culture signal → one Fit-axis line. New Offer entity (comp encrypted) + OfferService/OfferEvaluationService/PipelineOutlookService/StabilityContextService; equity → per-offer grants; tax estimator Single-only gap scheduled. MVP = Offer CRUD + deadline tracker + comp card + banded axes + scenario stubs + templates + journal.
⚪ Backlog WW-78 · WING-75 [SPIKE] USAJOBS API surface + auth/rate-limit recon Sources Research-only (output = decision doc, no prod code). GTM goal: prove "USAJOBS as a pipeline" is real + cheap before the pitch promises it. Enumerate all 3 REST APIs — Search (auth: API-key request form + User-Agent + Authorization-Key headers; paging 250 default / 500 via ResultsPerPage), Historic JOA (NO auth, bulk), Announcement Text — + unauth codelists. Deliverable: capability matrix + go/no-go on JSON-only ingestion (no Playwright). Unblocks WW-79..83. Spec §Phase-2 research spikes in docs/technical/veteran-vertical-design.md.
⚪ Backlog WW-79 · WING-76 [SPIKE] Hiring-path & veterans-preference codelist mapping Sources Research-only. GTM goal: the veteran-preference surfacing that differentiates our channel. Pull /codelist/hiringpaths + /codelist/militarystatuscodes; map vet-preference paths (vet, 30%-disabled, disabled, mil-spouse, VEOA/VRA) → neutral posting-chip taxonomy. Kerr gate: chips = factual posting metadata, NEVER sort keys/scores/eligibility oracles. Deliverable: mapping table + chip spec. Parallel w/ WW-80.
⚪ Backlog WW-80 · WING-77 [SPIKE] Occupational-series ↔ MOS crosswalk join Sources Research-only. GTM goal: closes the core loop — a veteran's MOS → real federal jobs. Join USAJOBS /codelist/occupationalseries to the DoD/O*NET MOC crosswalk (WW-76 P1) → MOS → federal series → Search-keyword query builder. Deliverable: join feasibility + coverage/gaps report (which MOSs map cleanly, which don't). Parallel w/ WW-79.
⚪ Backlog WW-81 · WING-78 [SPIKE] Search (BYO-key) vs Historic JOA (bulk) ingestion strategy Sources Research-only. GTM goal: distribution friction — how fast a veteran gets value on install. Compare per-user BYO Search key (rate limit, minutes-to-issue onboarding friction) vs Historic JOA bulk pull (no auth, our-side ingest, freshness lag). Deliverable: architecture decision (which powers discovery vs refresh) + onboarding-friction estimate. Depends on WW-78 rate-limit findings.
⚪ Backlog WW-82 · WING-79 [SPIKE] Federal apply-path reality (login.gov, DD-214/SF-15, federal résumé) Sources Research-only. GTM goal: honest end-to-end path, not just discovery — the pitch's credibility. What a USAJOBS application actually requires: login.gov auth (WW never touches — deep-link out only), required docs (DD-214, SF-15), federal-résumé conventions. Deliverable: apply-prep checklist spec + federal-résumé template scope. Independent.
⚪ Backlog WW-83 · WING-80 [SPIKE] SAM.gov opportunities API recon (P3 precursor) Sources Research-only. GTM goal: the contractor pipeline for veterans going independent. Endpoints, key tiers (~10 req/day basic), set-aside/NAICS filters, UEI/SAM entity-registration reality for an individual contractor. Deliverable: P3 feasibility + daily-pull + cache design; flags whether individual-contractor bidding is realistic for our users. P3 on-ramp.
⚪ Backlog WW-76 · WING-74 Veteran vertical: MOS→civilian translation + USAJOBS pipeline + SAM.gov contractor bids (3 phases) Sources Spec = docs/technical/veteran-vertical-design.md (Kerr-checked at requirements altitude 2026-07-11; converts the YC veteran-channel claim from decorative to load-bearing per pitch-council verdict llm-council/reports/2026-07-11_135155_*). P1: bundled DMDC/ONET MOC crosswalk (static corpus, offline, WW-45 pattern) → skills/occupations enter ONLY as provenance-tagged proposals through the WW-39 confirm→apply rows — never auto-applied; confirmed-only data feeds fit + drafters; FitLevel enum UNCHANGED. P2: UsaJobsSource — free BYO key (vault), JSON API pipeline peer to LinkedIn; hiring-path/vet-preference chips = factual metadata NEVER sort keys (WW-74 rule); federal-résumé template mode (explicit user choice, honesty guard untouched); WW never clicks Submit, never touches login.gov. P3: SamOpportunitiesSource — contract opportunities for individual contractors; eligibility (set-aside/NAICS) = GATES not scores; Kerr bans:* win-likelihood %, composite bid score, federal-fit %, bids-submitted as hero metric (WW-68 headline stands); 10 req/day basic tier → daily scheduled pull + cache. Framing rule: service to veterans, not extraction (binding on copy/metrics/UI).
🔵 In Review WW-74 · WING-72 LinkedIn repost tracking — neutral "listing history" context (council-gated minimal build) Insights Built on branch wt/WT-55ee/WW-74 (e622315), NOT merged/pushed. Recon found the design pre-existed further than the spec assumed: LinkedInPostingSignal + extractor already wired at scrape time (LinkedInJobsScraper.cs:405) — the real gap was cross-listing linkage (a repost mints a NEW LinkedIn job id/URL → lands as an orphan queue row). Added: ListingHistoryEntry (append-only Sightings[]), ListingIdentity (SHA256 canonicalKey = company+title+locationBucket, exact-only auto-link, ≥0.85 same-company similarity = possible-match flag never merged), ListingHistoryService (best-effort resync hook, derived days at read), token-gated GET api/jobs/{id}/listing-history mirroring WW-59, neutral listing-history-card. Code-review gate: Codex (member+chair) + Gemini 3.1 Pro cross-reviewed; both independently caught a High bug — history recorded against the transient scrape GUID → 404 for already-saved jobs — fixed by remapping to the persisted queue-row id; +query-param strip (CanonicalUrl), clock-skew clamp, URL-encode. Gemini's cache finding skipped (matches LocalJsonStore full-load pattern). Grok not run (54k prompt > grok -p arg cap); house seats void (MCP can't read file-ref). Backend 64 targeted (incl. 4 new regression) + frontend 33 specs green; both builds clean. Remaining before Done: master merge (multi-session FF-push) + live in-app browser verify of the card. Council spec: build minimal, unanimous 4/4 (llm-council/reports/2026-07-11_001107_*). Detect reposts from OUR scrape history, never LinkedIn's fragile "Reposted" DOM label (closed shadow DOM on /jobs/view/; capture raw list-card label text opportunistically only). Identity: canonicalKey = hash(normalize(company) + normalize(title) + locationBucket); auto-link ONLY company-exact + title near-exact, anything fuzzier = "possible match" flag, never silent merge (multi-req employers false-merge otherwise); append-only listingIds[] under one canonical job. LocalJsonStore fields: canonicalKey, listingIds[], firstSeenAt, lastSeenAt, firstPostedAt, lastRepostedAt, repostCount, rawPostedText, companyNorm, titleNorm; total-days-open + days-since-repost DERIVED at read time, not stored. UI = one neutral "Listing history (from your scrapes)" context line; Kerr guardrails: never a score, sort key, red icon, or ghost-job label (proxy A = avoid reposted listings vs goal B = apply to right jobs — punishes legit evergreen/gov/big-tech reqs); copy: "re-listed; confirm still open — not a reason to skip"; optional pairing: long market-time → tactic suggestion (direct InMail over ATS). Explicitly OUT: urgency/desperation scores, ghost-job classifier, auto-deprioritize rules, opt-out-default filters.
🟢 Done (local) WW-68 · WING-67 Metrics tab + Keys/Plans Angular UI TROI Landed local master 7f70c8c (not pushed). /metrics "Your AI efficiency" section after Value Metrics content, real-wired to token-gated plan/capacity/usage endpoints; empty MEASURED/MODELED/routing/outcome panes stay honest until WW-69/WW-71. Backend 3,290/3,290 green; frontend 777/777 green; live browser verify passed/metrics hit api/llm/plans + usage-summary + capacity (all 200), honest empty state rendered, 0 console errors. Gemini review findings applied. Awaiting Andrew push.
⚪ Backlog WW-69 · WING-68 Model router + RoutingRulesGrid TROI Task classes → models by fit + remaining budget, governor-aware: parse/extract/classify → local Ollama else cheapest; resume prose/interview answers → strongest key. User-visible + overridable. Savings claims per Token-ROI §5: MEASURED only with priced same-taskClass baseline.
⚪ Backlog WW-70 · WING-69 WW caveman: taskClass compression boundary TROI Compression allowed for internal pipeline classes {parse, extract, classify, dedupe, score, summarize-internal}; artifact-producing classes (resume/coverLetter/thankYou/interviewAnswer/learningPlan) structurally reject compression — user-facing artifacts always full quality. Savings MODELED unless A/B-measured.
⚪ Backlog WW-71 · WING-70 DurableOutcomeLinker TROI Links ledger spend → outcomes: submitted+kept-7d headline, artifact-adopted (explicit user keep/export/send + min meaningful diff), regenerations-per-artifact anti-frugality guardrail; interviews = context line only. Feeds Metrics tab + Value Metrics event log.
⚪ Backlog WW-72 · WING-71 WW council: multi-BYOK deliberation for high-stakes artifacts TROI Resume finals, key interview answers, high-stakes cover letters (learning plans/projects on request). Never auto-convened; disabled at YELLOW+. Consent modal w/ est. $, window-impact %, per-seat drop, max-spend cap, [Single model instead]. Tracking: $/adopted-council-artifact (adoption MEASURED, uplift MODELED).

Board status: the repo is pushed and CI green — the first push proved 11/11 jobs on a real GitHub runner (f02e64a, closes WW-33); as of b606a93 the workflows now target the self-hosted warm pool (home-ci runner group) because the GitHub-hosted cloud minutes are exhausted for the billing window. Every buildable Forms/ATS/Ops ticket is done; WW-32 is closed (real CVS Workday tenant surfaced + fixed a genuine phone/postal selector bug); the first push's three reds are fixed (WW-50 + WW-51). The tester-ready installer shipped (WW-55..58: bundled Chromium fixes the LinkedIn "local engine" failure, opt-in auto-updater, brand identity, tagline hero — installed and wizard-verified on Andrew's machine). Since then a provider-agnostic BYOK AI layer landed — the AIH epic below: one LLM harness (Claude/Codex/Gemini/Grok/Meta/Ollama), harness-agnostic grounded research + local RAG, BYOK audio overviews, and the Claude-CLI tailored-document drafters — the drafting path behind a verified zero-tools CLI lockdown and generated documents behind an anti-fabrication honesty guard — plus the company-health apply-time signal (WW-59). In progress now: WW-34's mutation ratchet is the last active build item — the apply vertical measures 100% of killable mutants; a full-scope run is measuring the rest before setting the break threshold. The accessibility ladder is fully automated — enforcing static axe gate (244c3d1, WW-63), live-backend axe + keyboard pass over real data (b10e146, WW-63b), and a real-NVDA screen-reader rig that asserts actual spoken output (afa0459, WW-63c) — leaving only WW-63d, the qualitative human NVDA/Narrator walkthrough. WW-62 is merged (4b71ee7 — token isolation + single-instance lock, council-gated to convergence). The BYOK layer then passed its full multi-model council review (AIH-7, ce6e40a): 11 verified findings fixed — research/ask/ingest + harness-list endpoints token-gated, correct Windows argv escaping, RAG ingest race + chunker skip fixed, cancellation honored, audio range-streaming — with the suite grown to 3,095 green.

Done this sprint

| Key | Ticket | Commit | | WW-95 | Deleted the 0.25x xunit parallelism cap — the tail of the WW-86 family. The cap was misdiagnosed at birth. WW-86 capped WorkWingman.Tests at maxParallelThreads: 0.25x and the suite went greener and faster; "faster from a cap" is the tell I missed — a thread cap relieving CPU contention does not speed a suite up, so it was never a CPU cap. It was rate-limiting the 137 per-test Chromium launches, treating the flake symptom while the real cause (a browser/memory storm) stayed. Once SharedBrowser (WW-93/WW-92) bound each assembly to one browser — zero Chromium.LaunchAsync left in any test project — the storm was gone and the cap became measurable in isolation for the first time. Measured, interleaved, same built tree (toggled only xunit.runner.json in the output dir, --no-build, identical binaries): capped ~119s vs uncapped ~74s steady-state, 3654/3654 green both, uncapped green even in the round that ran during a fleet spike at 1.6 GB free — the exact memory pressure the cap was blamed for. So it protected nothing; it throttled 3654 tests to 5 threads on 22 cores against browsers that no longer launch. Suite now runs at xunit's default width; CI-path re-run with coverage 3654/3654, 1m5s. Codex review caught a real migration gap (single-seat gate, RED governor): deleting the source file leaves the stale bin/xunit.runner.json on an incremental build, and xUnit reads the cap from there — a pulled-in dev would stay silently capped while the docs say uncapped (the same silent-wrong-config class as the whole WW-86/87 line). Added a BeforeTargets=Build delete of $(OutDir)xunit.runner.json, verified by planting a stale capped file and watching the build remove it; safe to drop once every checkout has rebuilt. This deletion is the better epitaph for WW-86 than the cap was — the compensation comes out because the cause got fixed. | 5d86a91 | | WW-91 · WING-14 | The tail of the WW-86 Playwright-flake family: 9 unbounded waiting calls across 7 apply engines were guarded by catch (PlaywrightException) only. Playwright for .NET raises PlaywrightException for a detached/unactionable element but throws System.TimeoutException (which does NOT derive from it) on a blown timeout, so a 30s-default expiry escaped and killed the whole apply run where the code plainly meant to shrug and try the next selector — the exact bug c77df35 fixed for the explicit-timeout sites, at the sites carrying no explicit budget. Two shapes: single-call sites take LocatorProbes' no-arg Try* overloads (keeping Playwright's default budget — imposing a tighter one would manufacture new load-flakiness, the trap c77df35 documented); block-level guards (Icims/Workday option readers, Greenhouse combo fallback) wrap several calls and mean "…or report none", so LocatorProbes.IsMiss was made internal and they write catch (Exception ex) when (LocatorProbes.IsMiss(ex)) — the exception pair defined exactly once. Scope measured per-site, not swept: IsVisibleAsync/CountAsync return immediately (can't time out) and Dayforce already caught both types, so both were left alone. Council earned this one: a seat (Grok) found the gap the audit could not see — it searched for unbounded calls inside catch blocks, so Paycom/Paycor's ReadPrescreenLabelAsync, which read legend/heading in NO try/catch at all, was structurally invisible; fixed here, all three reads now fall through on a miss. Three other seat findings dropped as false against git show HEAD: two claimed Workday's add.ClickAsync() had no prior guard (it had catch (PlaywrightException) { unmatched.Add(sk); }), one claimed Adp's break moved (always unconditional). Chairman: 0 surviving findings, and correctly rejected the original AC as an overclaim — this proves only the sites it touched. The rest of the class (unbounded waiting calls with NO guard, ~78 candidates, many of which SHOULD fail a run) is triaged as WING-86, not swept. Verified: build Release 0 errors; WorkWingman.Tests 3649/3649 green ×2. | 56474b4 | | WW-93 | The four orphan ScraperLab test projects join WorkWingman.slnx, and a test makes it stick. The bug: tests/WorkWingman.ScraperLab.{Google,Icims,Meta,Microsoft}.Tests (143 tests) were on disk but not in the solution, so dotnet build/test WorkWingman.slnx never touched them — which is how Google.Tests sat with 20 compile errors while the build said "0 Error(s)" (WW-87 found it by reading the diff). This was a1868e7 incompletely applied, not policy. That commit already fixed this exact bug for 23 projects, calling the exclusion a bug — "nothing in CI compiled or ran them" — but matched with a tools/ glob, so the four under tests/ were never seen. The "lab-exclusion rule" of b277aa4/ef48346 exists in no document, only those two commit messages, and cannot mean "lab tests stay out": a1868e7 re-admitted nine lab .Tests projects, and docs/ATS-LABS-TODO.md has said "Add both the lab and a .Tests project to WorkWingman.slnx" the whole time. b277aa4 DID remove Microsoft.Tests deliberately (peer WT-ae5b's catch, against my initial "accidental" framing) — but it removed the engine with it, and a1868e7 re-admitted the engine alone, so master implemented neither decision. tests/…Microsoft.Tests/stryker-config.json still declares "solution": "WorkWingman.slnx" — its own config assumed the membership b277aa4 took away. Council 7 seats / 5 lineages, unanimous: "exclusion is sediment, not policy"; the load-bearing argument is that every case for excluding labs is about running them, not compiling them — so compilation is exhaustive, execution selective. Landed: (1) the 4 projects in the .slnx (34 on disk = 34 in solution, exactly); (2) SharedBrowser promoted tests/WorkWingman.Tests/tools/WorkWingman.TestSupport/ and adopted in Meta.Tests, which launched a driver+Chromium per test (xUnit builds one instance per test method) — assemblies are what parallelise, so a helper only one assembly can reach caps only that one. Isolation preserved exactly: IBrowser.NewPageAsync() made a new context per call, so the helper takes a fresh context per page and tracks it — page1/page2 are still independent. Playwright added to TestSupport is free: all 17 consumers already pin 1.61.0, and its "dependency-free" rule was about PROJECT refs (keeping Core/Infrastructure out of E2E), not NuGet. Meta 6m15s → 16s. (3) [assembly: AssemblyTrait("Category", "Lab")] on all 13 lab test projects → dotnet test WorkWingman.slnx --filter Category!=Lab (.slnf rejected by council: a second membership list that drifts); (4) SolutionMembershipTests — globs csproj, parses the slnx, asserts set equality both ways. It lives in WorkWingman.Tests because that is the ONLY project CI executes: a script can be deleted or never reached, and prose already failed twice. (5) ci.yml names the solution instead of inferring it. Council round 2 (Codex/Gemini/Grok/local qwen3:8b; house seats skipped) caught 3 real defects, all fixed: the guard could pass on two empty sets (a guard against silent no-ops that could itself silently no-op — Grok); the csproj walk enumerated node_modules/.git before discarding them (Gemini, 506ms→282ms); one throwing context dispose stranded the rest. Two seats invented the same SharedBrowser leak — a false positive: git rendered the move as a rename, so the method body (which already has the try/catch) was not in their diff. Agreement between seats reading the same incomplete artifact is not corroboration. Verified by running, not asserting: guard green 5/5 and red 2/5 twice when a project is deleted from the slnx; --filter Category!=Lab → "No test matches"; full solution 3887 non-E2E passed / 0 failed (WorkWingman.Tests 3654/3654, 14m25s→1m32s), all 13 labs green. E2E's 7 reds are the documented missing npm run build, unchanged and untouched by this diff. Corrections I shipped against myself: "the omission was accidental" (partly wrong), "this change makes the suite flakier" (measured pre-rebase; 9a35144 fixed the victim side — it does not reproduce), "all 13 labs launch a browser per test" and "27 lab test projects" (false: only Meta and Amazon launch; there are 13). Each would have justified work that isn't needed. Follow-up WW-92 | 6f75eb9 | | --- | --- | --- | | WW-88 · WING-85 | npm test in frontend/ exited 1 with all 851 tests passing — a green run and a red run were indistinguishable by exit code, so the suite could not gate anything. Cause: offer-detail.spec.ts mocked ActivatedRoute but let the real Router reach the component; OfferDetail.delete() calls router.navigate(['/offers']), which rejects NG04002: Cannot match any routes against an empty TestBed route table, and vitest counts the unhandled rejection as an error. The spec's own comment called that rejection "harmless (no test impact)" — it was wrong, and the wrongness is the whole bug: the rejection had no test impact and total gate impact. Fixed by stubbing Router with { navigate } rather than provideRouter([]), which shadows the mocked :id param (component then loads id '' instead of 'offer-1' — the trap the original comment correctly documented). Confirmed pre-existing and unrelated to WW-73 by stashing all local changes and reproducing NG04002 on the clean base. Council 4/4 CLI seats (Codex/Gemini/Grok/local qwen3:8b; house seats skipped by request): unanimous that stubbing beats a routed harness here, 3/4 independently confirmed the new navigate assertion is sound under synchronous of(void 0); the local seat dissented and was overruled as wrong on RxJS semantics (claimed the sync observable causes a race). Applied the one shared finding — setup() now returns { fixture, navigate } instead of mutating a describe-scope let. Validation: unverified, not passednpm test was never run green on the exact landed tree. frontend/ is byte-identical to a tree that ran 43 files / 851 tests / exit 0 three times, and all nine commits it rebased past touch only .cs/.csproj that vitest never reads — strong inference, but inference. The two rerun attempts died to the box at 1.1 GB free (→ WW-89, filed). | 9381771 | | WW-87 · WING-84 | LoopbackListener follow-ups from the WW-86 council. Dropped the requestedPort hint — only ever a first guess the helper re-picks past, so 29 call sites were handing it a probed port it would have found itself; deleting it took the last 5 duplicated FreePort() probes and 8 int port ctor params with it. out param → tuple (Start() -> (HttpListener, string BaseUrl)) — Gemini and Grok both preferred it, and the argument isn't style: after a collision the bound port is NOT the one asked for, so an ignored out-param binds one port and advertises another. With no port at construction time there is no honest provisional url, so Shape-B fixtures expose BaseUrl via a throwing getter and the two BaseUrl_is_loopback_only tests now Start() first — asserting the ACTUALLY-bound url instead of a ctor-composed string, which is strictly stronger. The retry is finally tested: it was unreachable on purpose (the public picker only returns FREE ports), so an internal Start(Func<int> nextPort, int maxAttempts) seam + InternalsVisibleTo lets a test squat a port and feed it back as every pick — exhaustion, attempt-limit, re-pick and picker-throws now covered, on the exact path that shipped broken in c373db9 and survived 11 green runs. Plus a _disposed guard on FakePaycorSite, and Oracle's --port flag removed (nothing consumed basePort any more, so it silently did nothing) with its stale learnings-doc bullet rewritten. Council: Codex caught that tests/WorkWingman.ScraperLab.Google.Tests is NOT in WorkWingman.slnx — the ctor change broke it with 20 compile errors while dotnet build WorkWingman.slnx said "0 Error(s)"; fixed here, orphan-projects blind spot filed separately. Grok caught, for the second round running, that this change's own new test contained the WW-79 race (probe a port, release it, assert that exact port) — rewritten. Also from Grok: listener leaked if the picker threw (fixed + tested), maxAttempts <= 0 silently bound once (now rejected). Verified: 4/4 slnx runs green at 3645/3645, zero non-E2E failures, + all four out-of-solution projects run by hand (39/13/79/12) | 9e1335b | | WW-86 · WING-83 | dotnet test WorkWingman.slnx nondeterministically red — 6/4/2 failures over three runs on an unmodified tree, 11 distinct tests, all green in isolation. Diagnosed from the logs, not the names (WW-50's lesson): the four tests named in the report were never the cause — they already isolate via TempStore's per-instance GUID temp dir. The failing set rotated with machine load: as background load drained across four runs, failures fell 6→4→2→0 with no code change. Three real causes. (1) Oversubscription — apply-engine tests each launch their own headless Chromium; at xunit's default width (× ~10 projects under the .slnx) they saturate the box and wall-clock waits expire. xunit.runner.json caps at 0.25x cores, which made the suite faster (2m26s→~1m50s) — the oversubscription was costing throughput. (2) Real production data loss in LocalJsonStoreWithRetryAsync caught only IOException, but Windows reports a File.Move colliding with a reader's open handle as UnauthorizedAccessException, which derives from SystemException (compiler proves it: CS0184 "never of the provided type"). The retry could never catch the platform's most common contention error; best-effort callers (RoutingDecisionLog, AiUsageLedger) then swallowed it and dropped rows. Reproduced idle in 525ms as Expected 25/Actual 24. Also guarded the Directory.GetFiles sweep so litter collection can't fail a save. (3) Port TOCTOU in the fake sites: GetFreePort() releases its probe before HttpListener.Start() binds. Fixed in two passes — Grok caught that the first sweep missed the copies drifted to a different helper name (FreePort(), port picked at the call site, so the window spans construction). The second commit then found the first one's retry could not retry at all: HttpListener.Start() closes itself when the bind fails, so looping on the same instance throws ObjectDisposedException on attempt 2. Eleven green suite runs and a 4-seat council all missed it — the race is rare enough the retry path never once executed, and green runs only prove the code that ran. Only a test that forces the collision finds it. The correct retry needs a fresh listener per attempt, which a caller can't produce inside its own catch — so the helper had to own creation, making the dedupe the fix rather than the cleanup: new dependency-free tools/WorkWingman.TestSupport/LoopbackListener (jittered backoff, returns the url actually bound), all 21 fixtures migrated, ~23 duplicated helpers deleted, net −154 lines. The duplication wasn't hypothetical debt — it's exactly why the first sweep missed files. Also killed FakePaycorSite's null! (→ throwing getter) and MsLearnCatalogSourceTests' blind port guess. Note: these two commits carry a fix(WW-79): prefix in error — WW-79 is the USAJOBS veterans-preference spike; the ticket number was invented mid-session and the mislabel was found only after both had been pushed. History left intact (no force-push per the isolation policy); this row is the reconciliation. | c373db9 + 0cd6895 | | WW-85 · WING-82 | ApiToken's token-file write retried only IOException while guarding a cross-process race Windows reports as UnauthorizedAccessException — same root-cause class as WW-86's LocalJsonStore retry, spun out when it was spotted in passing. Worse failure mode than a dropped telemetry row: ApiToken is constructed at app launch, so an escaping exception fails startup | e4521d5 | | WW-84 · WING-81 | Equity "Grant Details" + all of Financial History rendered form fields/pills/hint-text unstyled — the shared .field/.pills/.pill/.q/.q-label/.footnote/.grid2 primitives live per-feature in ~20 component scss files but were missing from these two, so they diverged from the rest of the app. Promoted the canonical primitives to global styles.scss as a fallback (Angular emulated encapsulation keeps the 20 local copies higher-specificity, so no other page changes; only the gap pages inherit). Council review (Codex + Gemini + Grok seats; local/house seats skipped — trivial additive CSS): scoped the pill fallback to .pills .pill so it doesn't leak button chrome onto bare status badges (offer-detail's <span class="pill gate-pill">); rejected namespacing/:hover/mobile-collapse — goal is parity with the existing unprefixed primitives, not new divergence. Also fixes offer-detail's own previously-unstyled pill group | 717c0d8 | | WW-67 · WING-66 | Per-key plan tiers + WW usage governor: plan settings in settings JSON (never the vault — key material untouched), per key alias/provider/declaredTier/userMonthlyWWCap/reservePercent/model allowlist; PlanObservation capture of anthropic-ratelimit-*/x-ratelimit-*/429 retry-after (exact header allowlist + value cap, RFC HTTP-date Retry-After parsed) via a second AsyncLocal ambient — GenerateAsync contract unchanged; tier inference vs declared with mismatch SURFACED never overwritten, confidence enum declared/header-confirmed/inferred/unknown; editable MODELED capacity table (embedded defaults + source URLs); UsageGovernor per key per window — WW cap = cap×(1−reserve), GREEN<60 / YELLOW 60–80 (enrichment+suggestions pause) / RED 80–95 (core apply only) / BLACKOUT ≥95 or active hard-429 (all paused; opt-in failover; UserOverride with cost-warning flag), wired into the WW-66 metering door so blocks actually block (GovernorBlocked ledger rows); token-gated /api/llm/plans endpoints. Deferred: explicit user-initiated signal → WW-68 UI plumb; fire-and-forget ledger staleness accepted (WW-66 precedent, bounded by 15s TTL + hard-429 cache invalidation). Built via Codex offload (c98ebf9), review gate: Claude ceiling + Codex + Gemini — 8 findings fixed, 2 justified-deferred (f05be5d); Grok + house seats not run. Suite 3,286/3,286 green | c8bbe96 (c98ebf9 + f05be5d) | | WW-34 · WING-8 | Mutation coverage ratchet for the apply vertical — CI gate wired and enforcing: mutation.yml backend job now runs stryker-ww34-scoped.json (4 apply-vertical core files, validated 97.64% = 100% of killable mutants — 289 killed / 0 timeout / 2 survived-equivalent w/ in-source proofs / 5 NoCoverage) with break: 90 (ratchet ON), job renamed mutation-gate (apply-vertical scoped, break 90) so green never reads as full-codebase coverage; triggers = weekly + dispatch + master pushes/PRs touching the mutated core or its tests (test-only bypass closed). Broad ~22-module run REMOVED until VsTest socket fix — two full-scope collapses scored timeouts as killed (inflated, discarded); follow-up = WW-73. Survivor-killer tests landed earlier at 2165909. Council verdict 7/11 unanimous option-1 (Claude+Grok+Gemini seats; Codex/local seats not run; house seats concur). Review gate: Codex (test-path bypass) + Gemini (concurrency cross-cancel, persistent-runner tool install) — all applied; Grok seat not run | e24a1c3 (+ 2165909) | | WW-66 · WING-65 | BYOK AI-usage metering — sole-door content-free ledger: MeteringLlmHarness decorator wraps EVERY registered harness (runtime all-metered proof — no harness escapes the door; hostname string-grep kept as defense-in-depth); AiUsageEvent structurally cannot hold content (no prompt/response fields — PII excluded by type); AiCallContext AsyncLocal ambient reports real provider tokens without changing the frozen GenerateAsync signature (null = not measured, never a fake zero); append-only ai-usage-{yyyy-MM} shards, fire-and-forget ledger writes off the caller's critical path, thrown harness calls still metered as Failed then rethrown. Meter-before-optimize satisfied — unblocks WW-67..72. Council-gated (Gemini seat; Codex sandbox-blind, Claude chaired; Grok seat not run); Kerr check: runtime all-metered proof closes the bypass vector | b28bf7a (feat 6fd624f) | | WW-64 · WING-63 | DraftHonestyGuard fabrication-detection expansion: the fd2d8d1-deferred bypasses closed — narrative-verb orgs ("joined Google", "attended MIT"), ex-Org shorthand, affiliation-prefix orgs ("graduate of Yale"), all case-insensitive with alumni/alumnae plurals; "with"-bound titles gated behind an explicit "as <Title> with" lead-in (bare "with" swallows collaboration prose); commas kept in title tokens ("VP, Engineering" binds whole); dotted degrees (M.B.A.) recognized by both the level check and the school-anchored tuple check (deg terminator \b(?![A-Za-z])\.?\b fails after a terminal dot); ex-Role/latin-finance exclusions (ex-CEO, ex-Officio, ex-Dividend). Postfix "<Org> graduate" deliberately excluded (collides with "Computer Science graduate"). Council gate to convergence, 4 rounds (Codex chair + Codex/Gemini/local seats; Grok seat wired into the council mid-gate, degraded this run; house-PC seats down). Tests 37→62 adversarial · full suite 3,120/3,120 green | 8d51f0d | | WW-63d · WING-61 | Full-route + apply-flow accessibility: a deterministic a11y-tree structure gate over all 22 routes (one h1, <main>, heading order, every focusable control has an accessible name, no positive tabindex) now enforced in CI, extended to the two dynamic apply-flow states (judgement / account-wall) via read-only fabricated-run mocks. Fixed a real bug: the mid-run account-wall pause ("create an account to continue") had no screen-reader announcement — now a labelled navigable region + a persistent aria-live region announced on the state transition (host only, never the vault credential), all controls named. NVDA fidelity suite extended (representative-route h1, account-wall region, judgement question). Codex+Gemini council (pre-Grok seat): all findings applied (firewall read-only hole, reliable-announce live region, detector accuracy, PII redaction). Structure 0/22 + dynamic states · axe 0/22 · frontend 757/757 | 4da1144 | | WW-63c · WING-60 | Real-NVDA screen-reader rig: drives actual NVDA via Guidepup, asserts SPOKEN OUTPUT as text over 5 flows (skip-link-first-at-top, nav names + role, form-label announcements, tagline no-spam, sensitive-dialog announce/Escape). Strictly READ-ONLY over real data (per-request token injection, non-GET + WS + SW blocked, propose-from-linkedin stubbed + allowlisted, PII-safe report). Local-only, never CI. Caught + fixed a real skip-link exposure bug (clip-path→clip:rect). Two-track docs + 2 Mermaid diagrams. Codex+Gemini council (pre-Grok seat): all findings applied; verified 4 pass/1 skip + strict-mode negative check | afa0459 | | WW-63b · WING-59 | Live-backend a11y pass: axe over REAL data (25 routes incl. param variants from real ids, 0 violations; job-detail target-size was the one data-full find, fixed) + keyboard-behavior tests (skip-link ✓, strict-mode option for env-gated ones) behind a hard READ-ONLY firewall (context-scoped, WS/SW blocked, mutation attempt = red test, artifacts off, PII-safe report). Codex+Gemini council (pre-Grok seat): all 8 findings applied | b10e146 | | WW-62 · WING-57 | ApiToken test isolation + Electron single-instance lock (root cause of the demo-morning 401 hang): WORKWINGMAN_TOKEN_PATH override so test suites/Stryker never clobber the live app's token; test-assembly [ModuleInitializer] temp path with ProcessExit cleanup; requestSingleInstanceLock(); Electron strips the override from the spawned API env (preload-path contract). Full council gate (Codex chairman + Codex/Gemini/local qwen3:8b/gaming-PC qwen3-coder:30b seats; pre-Grok seat): 2 findings applied, 1 justified-skipped, 8 dropped; 3,095/3,095 green | 4b71ee7 (53dddaf + 649e2db) | | WW-63 · WING-58 | WCAG 2.2 AA package: ARIA/table/combobox/tab semantics across ~40 templates, native <dialog> modals (guarded showModal), skip-link + .sr-only + global reduced-motion kill-switch, contrast token floors both themes (semantic ladder kept), eslint a11y rules, and an enforcing axe-core CI gate (22 routes, 0 violations; critical/serious or unauditable = fail). Codex+Gemini council (pre-Grok seat): 5 findings applied, 2 deferred to WW-63b, 2 refuted | 244c3d1 | | WW-61 · WING-9 | CI rerouted to the self-hosted warm pool: GitHub-hosted cloud minutes exhausted for the billing window, so ci.yml + mutation.yml now target the home-ci runner group (org warm pool) instead of ubuntu-latest/windows-latest | b606a93 | | WW-60 · WING-56 | Claude-CLI zero-tools hardening: verified against the real CLI (claude.exe 2.1.201, cmd.exe /c, stdin) that --allowedTools "" is only an allow-list and left Read/Bash live (read a planted secret back); switched all three CLI drafters to the documented --tools "" (blocks tools, still returns text). Added PromptSafety — fences untrusted JD/resume/interview text, neutralizes forged fence markers, forbids URL/image exfil markup — and pinned SystemProcessRunner WorkingDirectory against a claude.cmd CWD hijack. Codex+Gemini council to convergence (pre-Grok seat) | 580478f + fd2d8d1 | | WW-59 · WING-55 | Company-health apply-time signal: backward-looking read (layoff history + news + price momentum) surfaced as a job-detail card at apply time — informational, never advice | eb9dad5 + c38b1e9 | | WW-58 · WING-54 | Brand hero (Angular): reusable <app-brand-hero> on Onboarding — wide lockup + the four canonical taglines (elevator-pitch doc) rotating in a fade/slide carousel over a drifting amber/teal aura; theme-adaptive, responsive, honors prefers-reduced-motion; verified live in both themes | 3b91612 | | WW-57 · WING-53 | Brand identity pass: "Ascending Wing" placeholder mark (3-model convergence — Claude + Codex + Gemini briefs), generated icon.ico/favicon.ico/adaptive favicon.svg (make-brand.ps1), amber promoted to first-class --brand token, logo tile flips ink↔cream by theme so it never sinks into the background; sidebar lockup + tagline | 3b91612 + 5255d53 | | WW-56 · WING-52 | Opt-in auto-updater (electron-updater on NSIS, council-endorsed): Settings → Updates card with three modes — Manual (never phones home) / Notify (default) / Automatic — progress bar, restart-and-install; window.workWingmanUpdates IPC bridge; dev builds degrade to unsupported, browser degrades to "managed by the desktop app"; generic feed inert until latest.yml + exe are hosted | 3b91612 | | WW-55 · WING-51 | Installer completes the "one program" promise: Playwright Chromium bundled into the NSIS installer (PLAYWRIGHT_BROWSERS_PATH + extraResources + reproducible build-installer.ps1) — fixes the LinkedIn vault login "Could not reach the local engine" (browser was missing on clean machines, mid-request download timed out); branded assisted wizard (sidebar/header art, beta EULA, no-UAC per-user); /healthz readiness probe with X-WorkWingman-Api marker; 7za shim for winCodeSign symlinks without Dev Mode | 3b91612 + 379911e + 622421b | | WW-33 · WING-23 | Security CI proven on a real GitHub runner: after the first push, all four Security jobs (Semgrep SAST, gitleaks secrets, ZAP DAST baseline, dependency audit) pass green on ubuntu-latest — not just locally via Docker. Full run: 11/11 jobs green | f02e64a (run) | | WW-51 · WING-47 | First-push CI corrections (green locally ≠ green on a Win+Linux matrix): (1) WW-50 wrongly added a Linux sudo rm apt-cleanup to the windows-latest backend install step — reverted, apt-hardening kept only on the ubuntu churn+E2E jobs; (2) ConnectionsService vault badge used Path.GetFileName on a Windows path → returned whole on the Linux runner, normalized \/; (3) references.md missing KPCLib + System.Security.Cryptography.ProtectedData, exposed once the Docs job got past two-track to the references gate. Codex caught the windows-vs-linux job | f02e64a | | WW-54 · WING-50 | Docs link-integrity gate: a relative-link checker across all of docs/ (Gate 2b — catches the kind of ../plain../business-plain break the WW-50 move risked) + orphan gate widened to docs/reference + docs/business-plain; also repointed stale doc→source links left by the Phase-2 stub removal (cb297f4) | f52fb9f + 2f12987 | | WW-53 · WING-49 | iCIMS real-tenant gaps (Cotiviti): fill the structured address (AddressCity/AddressZip/AddressState/AddressCountry) and label-driven custom questions (<label for>-resolved, for opaque rcf<n> ids); country filled from Contact.Country, not a literal | e7118b3 + a51efef | | WW-52 · WING-48 | Greenhouse real-tenant gap (PerfectServe): fill the required Location (City) + Country comboboxes (type-then-pick-from-list), which the engine previously left for the user | a760dd0 | | WW-50 · WING-46 | First-build CI green: diagnosed each first-push red from its log, not its name. Churn wasn't a flaky test — playwright --with-deps hit the runner's intermittently-unsigned packages.microsoft.com apt repo; drop that source before all three installs (Chromium libs come from Ubuntu's own repos). Electron wasn't a boot hang — .brand-name now wraps the REAL/SANDBOX pill, so exact toHaveText('Work Wingman') retried to timeout; assert on a new env-independent data-testid="brand-product". Docs was real — moved misfiled records → docs/reference/, plain business companions → new docs/business-plain/ (council: move, don't game). Full council (Codex + qwen3:8b + gpt-oss:120b + qwen3-coder:30b) concurred all three. | 93d8155 | | WW-32 · WING-22 | Real-tenant Workday fix (CVS Health): the signed-in "My Information" form revealed the data-automation-id sits on the field CONTAINER, inner <input> is bare — so our input-first contact selectors made phone + postal-code fail outright and the rest match only by luck. Contact chains now lead with [data-automation-id='formField-…'] input:not([type='hidden']) + phone/postal name fallbacks. Validation doc | 761aae9 | | WW-49 · WING-45 | Bug-hunt fixes: address suggestion adopted WHOLESALE (+ Nominatim now requires a street Line1) so a partial pick can't blank a typed ZIP/state; skill picker over-fetches 30 then caps to 12 AFTER removing already-selected so the dropdown can't empty. Also: real-tenant Workday validation doc (WW-32) | (this) | | WW-48 · WING-44 | Bug fix (found mid-run): résumé PASTE stamped a bogus SourceResumeFile="pasted résumé" — hid the WW-41 Generate fallback AND gave engines a path File.Exists rejects. Now applyResume never fakes a file; provenance moved to a cosmetic label; Generate stays offered so paste-only users get a real attachable file | 4a40aa1 | | WW-42 · WING-35 | First-push prep: docs/reference/first-push-checklist.md — verified pre-push hygiene (no artifacts/secrets/large blobs; gitignore/gitattributes/gitleaks/Stryker/SECURITY.md all present; clean tree) + what the first push triggers (full ci.yml security suite + mutation.yml) + post-push verification = WW-33 | (this commit) | | WW-44b · WING-39 | Skill-graph picker (Angular): onboarding skills step gets a real editor — chips + debounced corpus search + a "related to what you added" one-tap strip (excludes already-selected); live-verified (react→related minus already-picked Angular) | 7980fe8 | | WW-44a · WING-38 | Related-skills graph: SkillGraph (~20 overlapping clusters over the WW-45 corpus → shared-family adjacency, no-self/dedup/case-insensitive; corpus-integrity test) + RelatedSkills (normalize→graph) + /api/vocab/skills/related | f06b63b | | WW-45 · WING-40 | Canonical ATS reference vocab: AtsReferenceVocab (curated skills/certs/schools seeds + complete education-levels) + AtsReferenceVocabService (prefix/substring Suggest for pickers, symbol-aware Normalize via OptionMatching, null below threshold) + /api/vocab/*; seed sizes queryable (no silent cap) | 9ecffd2 | | WW-46 · WING-41 | Address autofill: CensusAddressSearchService (US Census geocoder — resolves real residential addresses Nominatim misses) + CompositeAddressSearchService (authority-first, Census→Nominatim) + fast-fail Autocomplete pipeline (4s/no-retry) on both; frontend/endpoint unchanged; live-verified vs the real Census API | a814fb8 | | WW-43b · WING-37 | LinkedIn import UI (Angular): "Import from your connected LinkedIn" button on onboarding feeds the SAME confirm→apply rows as résumé paste; proposal-source flag keeps LinkedIn imports from stamping SourceResumeFile; best-effort toasts; button verified live | 63527ab | | WW-43a · WING-36 | LinkedIn profile import: LinkedInProfileLdParser (pure, schema.org Person ld+json) + LinkedInProfileProposalBuilder (→ proposals on the WW-39 field-path convention) + LinkedInProfileImporter (own profile, read-only, never-throws, login-wall→gap) + /propose-from-linkedin | bb3b1cc | | WW-47c · WING-43 | Field-validation UI (Angular): review-step banner lists present-but-malformed fields (email/phone/URL/date) via /validate, advisory (never blocks save); @let-clean, clears-before-fetch, takeUntilDestroyed; verified live | 829d583 | | WW-47a/b · WING-42 | Canonical validation: FieldValidators (+date/safe-text/by-format), ProfileValidator, /validate endpoint; never-type-invalid guard in all 3 engine fill paths (Greenhouse/iCIMS/Workday, secrets exempt) → skips malformed + logs "left for you" | 043c6e1 | | WW-41b · WING-34 | No-résumé start (Angular): review-step "Generate from my answers" card (offer / ready+preview / already-on-file); persists intake → generate-resume → points SourceResumeFile at the file; race-guarded vs the review auto-save, takeUntilDestroyed; verified live (file on disk, card flips) | 0b7d81c | | WW-41a · WING-33 | No-résumé start backend: ResumeComposer (pure IntakeProfile→plain-text résumé, invents nothing) + ResumeGenerationService (writes generated-resumes/base-resume.txt in a contained dir → sets SourceResumeFile → persists) + /generate-resume endpoint; engine attach works unchanged (File.Exists-only). Inverse twin of WW-39 | 863e622 | | WW-40b · WING-32 | Profile Gaps panel (Angular): /gaps route + nav item; Recommended/Optional sections, apply-time pill, error-reverting dismiss, empty state; verified live (10 gaps, dismiss 10→9) | 7c19732 | | WW-40a · WING-31 | Profile Gaps backend: ProfileGapAnalyzer (completeness, blank-row + partial-address aware) + ProfileGapsService (apply-time "left for you" harvest, deduped, serialized, dismissable) + /gaps endpoints; sensitive backup domain | 1b1be61 | | WW-39c · WING-30 | Resume-parse confirm UI (Angular): paste-résumé card on onboarding step 1 → confidence-checked proposal rows (edit inline) → Apply maps into the typed profile (whitelisted, index-capped); background /enrich merges education/work history; stale-callback token; verified live | f4f54e1 | | WW-39b · WING-29 | Resume-parse Claude-CLI enrichment: mockable IProcessRunner (hard timeout, tree-kill, no truncation/orphan) + ClaudeResumeExtractor (best-effort, gated, never-throws) filling education/work-history; split /enrich endpoint off the critical path | 2676dae | | WW-39a · WING-28 | Resume-parse backbone: deterministic ResumePreParser (email/phone/name/links/skills → proposals, never saved) + canonical FieldValidators + POST /api/profile/propose-from-resume; 10 review rounds | a24c754 | | WW-38 · WING-27 | "How did you hear about us?" + referral across all engines: honesty order (referral → stated channel → LinkedIn default), referrer name/email follow-ups; shared ApplicationSource | ae0953d | | WW-37 · WING-26 | Skills + language picker fill across all engines: symbol-aware matching (C#≠C++≠C), descriptor tolerance, Greenhouse skills-vs-spoken routing, iCIMS hidden-checkbox fallback, Workday prompt-style skills | 59176c6 | | WW-36 · WING-25 | Credential translation across all engines: levels, majors (aliases), expected graduation, cert sections | bc71432 + de9e006 | | WW-35 · WING-24 | Profile + intake for real forms: credential kinds, certs, language proficiency, referrals | 3a10ec5 | | WW-31 · WING-21 | iCIMS promoted to production — third ATS engine (iframe-aware, coordinated wall, stops at EEO) | b7a549a | | WW-29 · WING-20 | Workday: fill every work-history block (repeater targeting + dates + current-role skew) | d8f690e | | WW-25 · WING-16 | Workday multi-phase resumable drive (account wall + degree judgement) | 2d65168 | | WW-25b · WING-17 | Coordinate the account wall · Workday background drive ON | da58b0e | | WW-28a · WING-18 | Semgrep SAST hard gate in CI + both first-scan findings fixed | f6891f0 | | WW-28b · WING-19 | Security workflow: Semgrep MCP, gitleaks, ZAP DAST, SECURITY.md | 2bf147d |


AI spend — retroactive backfill (Token-ROI, 2026-07-10)

Read this first: every figure below is estimated — reconstructed after the fact by joining session logs to ticketed commits by time window (token-roi backfill, commit c0a3eec). Dollars are equivalent API prices, not cash (work ran on plan subscriptions). Phases show [unsplit] because historical board transitions weren't logged — per the Token-ROI v1.1 rule we refuse to fabricate phase splits. Spend that couldn't be attributed without guessing is listed as unattributed, never pro-rated. Live tickets (WW-66+) will carry exact, phase-split figures once metering lands.

Ticket AI spend (est.) Turns Phases
CON-7 ~$292 1 [unsplit]
CON-1 ~$224 16 [unsplit]
WW-55 ~$183 7 [unsplit]
APL-9 ~$165 1 [unsplit]
DOC-1 ~$119 1 [unsplit]
AIH-3 ~$112 1 [unsplit]
WW-52 ~$109 5 [unsplit]
APL-3 ~$97 1 [unsplit]
WW-66 (design sitting) ~$93 1 [unsplit]
TST-5 ~$84 1 [unsplit]
WW-34 ~$83 2 [unsplit]
FND-6 ~$76 1 [unsplit]
APL-10 ~$75 1 [unsplit]
CON-11 ~$67 4 [unsplit]
TST-1 ~$57 1 [unsplit]

Unattributed (visible by design): repos-root sessions ~$16,709 (1,143 turns — most work ran at the shared repos root on master, too ambiguous to assign without guessing); WorkWingman-cwd residue ~$499; all other repos itemized in backfill2.json. Grand total across all history: ~$20,607 equivalent-API (1,314 session files). Per the Kerr table: a rising unattributed share counts against the measuring system, not the tickets.

Delivered epics

AIH · Provider-agnostic AI (BYOK) — Jul 7–8

Story: technical · plain

Every AI capability moved behind one bring-your-own-key seam so the user picks the provider and holds the key. The Claude-CLI drafting path adds a verified zero-tools lockdown (--tools "") plus PromptSafety fencing, generated documents pass an anti-fabrication honesty guard, and the other BYOK paths (LLM / research-RAG / audio) fence untrusted input through the same PromptSafety seam — so untrusted job/resume text can never turn a model into an exfiltration or fabrication vector.

Key Ticket Commit
AIH-1 Tailored documents: Claude-CLI resume rewrite + cover letter + thank-you notes + .docx writer, with DraftHonestyGuard rejecting any draft that invents an employer/school/degree/year 0925e62
AIH-2 Claude-CLI security hardening: --tools "" zero-tools lockdown (verified --allowedTools "" was ineffective), PromptSafety untrusted-data fencing + marker neutralization + no-exfil-markup, CWD-hijack fix; honesty-guard bypasses closed 580478f + fd2d8d1
AIH-3 Provider-agnostic BYOK LLM harness (Claude/Codex/Gemini/Grok/Meta/Ollama) + LlmController + connections wiring; the drafters/extractors rewired onto it 86c5509 + 8644c81
AIH-4 Harness-agnostic grounded research + local RAG: LocalRAG (Ollama embeddings, cosine retrieval, citations), honest-failure on missing embedder, StudyController research endpoints 3793429 + 1c329bd
AIH-5 BYOK audio overviews: provider-agnostic TTS + AI music + grounded scripts (AudioOverviewController), untrusted script text fenced a7e6f68 + bd04086
AIH-6 Integration merge to master + Codex integration-gate fixes on the BYOK merge 9cd8f9c + eb07b3c
AIH-7 Full council review of the BYOK merge (Codex + Gemini seats + Codex chairman; house-PC seats unreachable; pre-Grok seat): 11 verified findings fixed — [RequireLocalToken] on research/ingest + research/ask (+ length cap, unknown-harness 400) and /api/llm/harnesses (account-email leak), correct CommandLineToArgvW argv escaping (+ round-trip tests), RAG ingest SemaphoreSlim, chunker skip fix, cancellation rethrow, audio range-streaming, Stdin/Timeout override parsing, probe via ArgumentList, HMAC disposal. 3,095/3,095 green ce6e40a

BIZ · Business & strategy — Jul 6

Story: technical · plain

A multi-model council (Codex/gpt-5.5 + Gemini + gaming/streaming-PC open councils, Claude chairing) decided the monetization sequence — free local core → paid convenience → institutional seats → consent-gated staffing → verification → partnerships last — then dedicated research agents deepened it into a full business doc track.

Key Ticket Status
BIZ-1 Council monetization verdict + decision record ✅ Done (council-verdict)
BIZ-2 Monetization strategy, roadmap, GTM & positioning (detailed + plain) ✅ Done
BIZ-3 Financial model — pricing/costs/ROI/break-even (narrative + live .xlsx) ✅ Done
BIZ-4 Pitch deck + elevator pitch + executive one-pager (.md + .pptx) ✅ Done
BIZ-5 Legal/compliance & risk analysis (ToS/CFAA/FCRA/privacy/security, cited) ✅ Done
BIZ-6 llm-council.exe — persist partial answers before chairman step ✅ Done
BIZ-7 Adaptive Solutions partner pitch deck — council-reviewed, revised, rendered to .pptx; de-slop pass retitled slide 7 (.pptx regen pending PowerPoint close) ✅ Done (50c634e + ef542df)
BIZ-8 Competitive analysis: 10-vendor sweep (OSS + commercial auto-apply) → competitive-analysis.md + plain twin; conclusions folded into GTM positioning; Black-owned identity positioning across business docs; managed-AI tier clarified opt-in/BYOK-default; July-8 making-of chapter (both tracks). Council-gated (Codex + Gemini, pre-Grok seat; 19 findings applied) ✅ Done (a3eb109 + 32e9d6f)

APL · The apply-automation vertical — Jul 5–6

Story: technical · plain

"Start Run" went from checklist theater to a real, resumable, coordinated browser drive.

Key Ticket Commit
APL-1 IApplyAutomationEngine contract + AtsKind→engine router 29a468a
APL-2 Production Greenhouse engine drives the fake site end-to-end 50f3a1e
APL-3 Background apply-run driver + advisor seam — Start Run drives for real a28be22
APL-4 Capture Ats.ApplyUrl from the page (externalApply unwrap + SSRF host guard) e914cc1
APL-5 Atomic pause/resume + defensive URL decode (full-council review) e0ba560
APL-6 Resumable-drive infrastructure — park at a pause holding the live page 804c9c5
APL-7 Greenhouse resumable custom-question answering (never guess) cfd0855
APL-8 Auth test gaps filled (GoogleOAuth / GitHub device flow / ClaudeCli) 9c3cc5c
APL-9 Workday multi-phase resumable drive 2d65168
APL-10 Account-wall coordination + background drive ON da58b0e
APL-11 Fill every work-history block (repeater targeting + dates) d8f690e
APL-12 iCIMS production engine — iframe topologies, coordinated account wall, EEO-default stop b7a549a

SEC · Security & scanning — Jul 4–6

Story: technical · plain

CVE gates first, then the full SAST / secrets / DAST stack.

Key Ticket Commit
SEC-1 Vulnerable-dependency CI gate + Dependabot 55b277c
SEC-2 Sensitive-endpoint gating consolidated on RequireLocalToken 7feb614
SEC-3 Semgrep SAST hard gate (675 files; both findings fixed) f6891f0
SEC-4 Semgrep MCP + gitleaks history gate + ZAP DAST + no-store API + SECURITY.md 2bf147d

CON · Connections, vault & real data — Jul 5

Story: technical · plain

Demo boilerplate out — real KeePass vault, working sign-ins, real scraped jobs.

Key Ticket Commit
CON-1 Structured intake + Google OAuth (PKCE loopback) + honest save UX 98a8cb2
CON-2 Profile links + portfolio repos on Connections c520927
CON-3 Real .kdbx vault + working GitHub/Claude/LinkedIn sign-ins 3d26ae0
CON-4 Apple Calendar via .ics email + dual real/sandbox environments c5daed1
CON-5 Medium study suggestions via per-tag RSS 2337c1e
CON-6 Tier-0 Google (no sign-in) + LinkedIn connect fixes + env pill 4d2f248
CON-7 Queue stops serving demo boilerplate bd4c0e2
CON-8 LinkedIn saved-jobs scraper rebuilt (JSON-LD first) + platform research dbfbedf
CON-9 Real study-platform deep links + MS Learn Catalog API 506760d
CON-10 ATS detection extended to 12 families d18e5d7
CON-11 Analytics engine seam (funnel/trend/salary) 17e1b5f
CON-12 Full merge audit — found the apply vertical unwired; became the APL epic 005b86d

INS · Job insights & interview prep — Jul 4 (overnight run 2)

Story: technical · plain

~28 feature branches fanned out concurrently, merged through the review gate, then 7 integration seams wired so nothing stayed orphaned.

Key Ticket Commit
INS-1 Financial profile + job-impact comparison 52a068b
INS-2 Lifestyle costs (HUD housing equivalence + commute projection) 054ef5d
INS-3 Family costs (USDA groceries + NCES/SEDA schools) c24c587
INS-4 Cost-of-living + local income-tax estimator fdc2ddf
INS-5 Comp signals (H-1B LCA + BLS OEWS + posted ranges) 25d1f94
INS-6 Equity-grant valuation (informational only, never advice) 8bdf07d
INS-7 News → realized-price event study (backward-looking) 034b2a7
INS-8 Company research briefing (GitHub + GDELT + Wikidata) 0530a9d
INS-9 Benefits insight (Form 5500 + HRC CEI) 3897ec8
INS-10 Assessment-platform detection + practice deep links 33ed317
INS-11 Learning paths (role-matched practice catalog) 05ef630
INS-12 Demographic resources (opt-in, local-only) 5337668
INS-13 Prep studio (company LeetCode libraries + phone flashcards) 897774c
INS-14 Interview prep bank 6dff92d
INS-15 NotebookLM opt-in prep export ef8f598
INS-16 BYO-key onboarding (vault-stored, masked) 27d6e77
INS-17 Interviewer research (read-only, no auto-connect) 0e502db
INS-18 Self-assessment suite (local IPIP Big Five) c4e3c66
INS-19 Interview retro loop dc17ab6
INS-20 Interview coaching edf3511
INS-21 ATS account-wall handling (vault mint + KeePassXC handoff) 6fa3436
INS-22 Data backup (Excel round-trip + opt-in Drive sync) 993e77e
INS-23 Study media + visuals (real results only; BYO-key illustrations) c82df83
INS-24 Integration: 7 seams wired + job-detail page + anti-hallucination verifier 7373f15

LAB · ScraperLab — Jul 4 (overnight run 1)

Story: technical · plain

Fifteen offline ATS learning labs: every ATS practiced against a local fake site before touching a real form. Selector fallback chains and judgement pauses were learned here.

Key Ticket Commit
LAB-1 Workday lab — selector fallback chains hardened (visible-scope trick) 6dfebcb
LAB-2 Greenhouse · Lever · iCIMS labs (iCIMS iframe-aware) bf15b7b
LAB-3 Oracle/Taleo · UKG · SuccessFactors labs 1243c59
LAB-4 Paycom · Paycor · Dayforce · ADP labs 1997e76
LAB-5 Microsoft · Amazon · Google · Meta labs (advisor-ready pauses) b2559be
LAB-6 Answer taxonomy + AnswerResolver + DRAFT-ONLY outreach 61cd682
LAB-7 Job signals (repost detection + legal layoff history) d38310f
LAB-8 PageAgent research → hybrid pivot (Playwright drives, AI advises) 4115c88
LAB-9 Frontend resilience interceptor (GET-only retry, error→toast) 875eee9
LAB-10 20-branch merge + integration fixes 9fd3155

TST · Testing foundation — Jul 3–4

Story: technical · plain

24 tests → 2,648: mocked coverage on both sides, E2E journeys, mutation testing.

Key Ticket Commit
TST-1 Backend mocked coverage (NSubstitute + Bogus, Flurl HttpTest) 6d7b356
TST-2 Frontend specs for every screen + service (faker builders) 0d7a3e5
TST-3 Playwright.NET UI journeys (Page Objects, own CI job) 1f6839d
TST-4 Electron desktop-shell E2E c49a459
TST-5 Stryker.NET backend mutation + weekly workflow cdc916e
TST-6 Frontend StrykerJS via standalone Analog vitest 96bf02e

DOC · Two-track documentation & diagrams — Jul 3–6

Story: technical · plain

Technical + plain versions of everything, CI-gated so they can't drift.

Key Ticket Commit
DOC-1 Two-track learning docs + Mermaid diagrams 9037713
DOC-2 C4 diagrams (D2) + rendered SVGs + drift-check + self-heal CI 01959ae
DOC-3 Docs gates: two-track pairing, no orphans, src-changes-carry-docs 713c31d
DOC-4 Making-of journal (living build story, both tracks) 981a246
DOC-5 Merge-audit + security-audit write-ups bcaa48c

FND · Foundation — Jul 3

Story: technical · plain

Design import → working Electron + Angular + .NET skeleton with CI and a dependency doctor.

Key Ticket Commit
FND-1 AutoApply prototype design import 7d35e4b
FND-2 .NET backend skeleton (Core, Flurl+Polly, Playwright, local-first services) 2135bce
FND-3 Angular frontend (9 screens) + Electron shell 8a77132
FND-4 CI: Azure Pipelines → GitHub Actions 647d865
FND-5 Full dependency + framework refresh (Angular 22, Electron 43) 99a3626
FND-6 Dependency doctor (detect on launch, per-item approval, token-gated) 75ccfe9

Defect & review-bounce history

Every ticket goes through the review gate before its commit lands, and the gate regularly sends work back. This ledger keeps that history: what bounced, who caught it, and what was found only after code landed. Review models are advisors, not oracles — so the ledger also records claims the gate REJECTED with evidence, because a rejected false positive is a decision worth remembering too.

Review bounces — caught before the commit landed

A "bounce" = a reviewer finding that sent the ticket back for another pass before its gate converged. Chronic themes the gate kept enforcing: honest progress reporting (never count a field that didn't land), Stop means stop (no mutation after a Stop, across every await), and intent ≠ reality (verify what the page actually did).

Bug Ticket Found by Finding Outcome
BUG-1 FND (CI) Codex CORS config blocked the packaged Electron origin; --no-build broke fresh-checkout dev start Fixed 0ec7519
BUG-2 FND-6 Codex Dependency doctor's install endpoint was unauthenticated; Playwright installer assumed a PATH CLI; KeePassXC undetectable off-PATH Fixed 329477f
BUG-3 FND-6 Codex Electron preload read the API token from the wrong path (LOCALAPPDATA mismatch) Fixed 1c3f2eb
BUG-4 TST-5 Codex Two mutants "killed" by over-suppression instead of real assertions (reseed-id stability, failed-serialization atomicity) Un-disabled + covered 922d640
BUG-5 LAB-10 build break Triple-duplicate SelectorAttempt record collision surfaced at the 20-branch merge Fixed 9fd3155
BUG-6 LAB-10 / INS-24 Codex Integration-diff P2s on both overnight merges Fixed a164e03, 798de59
BUG-7 APL-3 Codex Run published to ActiveRuns before its first log entry — a concurrent poller could serialize mid-add Fixed afbbd22
BUG-8 APL-5 full council Pause/resume not atomic vs a concurrent Stop; apply-URL decode could throw on malformed input Fixed e0ba560
BUG-9 APL-9 Codex ×8 rounds Workday drive bounced repeatedly: background launch unsafe pre-coordination (P1); only first work-history entry filled; saved degree answers not consulted before parking; Stop not honored mid-fill or across resolve awaits; EEO advance unverified; run left AwaitingJudgement after resume; resumed degree choice unvalidated; FieldsFilled counted profile values not DOM writes All fixed in 2d65168
BUG-10 APL-9 Gemini Stop check raced the selector-resolve await; fill-count inflated when halted mid-contact Fixed 2d65168
BUG-11 APL-10 Codex ×5 rounds Coordination bounced: null-from-coordinator treated as "signed in" (would fill the app on a login page); stopped runs resurrectable mid-check (P1 — coordinator ran on the wrong cancellation token); drives left zombie-Running; blocked runs mislabeled "final review"; coordinated prompt lost the real sign-in URL All fixed in da58b0e
BUG-12 SEC-3 Gemini Electron waitForApi could hang forever — Node fetch has no timeout, bypassing the 30s deadline Fixed f6891f0
BUG-13 SEC-4 Codex Gitleaks CI job would abort on runners (root container vs runner-owned checkout → git "dubious ownership"); allowlist was whole-file (could hide future real leaks); electron dep tree Dependabot'd but not audit-gated All fixed in 2bf147d
BUG-14 APL-11 Codex ×7 rounds Repeater work bounced repeatedly: naive loop would overwrite block 0 (shared automation-ids); added blocks left required dates blank; current role never ticked "currently work here"; blank end date wrongly treated as "current"; fake-site clone kept the checkbox checked (caught by a failing test); hidden end-date sections skewed later blocks' visible indices; skew tracked intent not reality; fallback-tenant counts read 0/0; Stop raced the checkbox tick All fixed in d8f690e
BUG-15 APL-11 Gemini Real Workday hides the end-date section asynchronously (React render cycle) — the immediate visible-count read would always race it; pre-pause saved-degree reuse ignored click failure; hardcoded log blamed EEO for education failures Fixed in d8f690e

| BUG-16 | APL-12 | Codex ×9 rounds | iCIMS promotion bounced repeatedly: email-probe raised false account walls (password field is the real gate signal); async-injected iframes mis-read as inline; contact email skipped on no-gate tenants; stopped runs resurrectable at the wall and the judgement raise (P1); unrecognized resume pages stranded the drive; advancing past EEO would silently keep iCIMS's PRESELECTED protected-status defaults (P1) — the engine now stops there; unresolved judgements no longer advanced past (same latent gap found + fixed in Workday); paste-resume textarea could shadow a hidden file input | All fixed in b7a549a | | BUG-17 | APL-12 | Gemini | Instant CountAsync frame probes raced JS hydration (bounded waits now); stop-guard gaps at resume attach + degree selects | Fixed in b7a549a |

| BUG-18 | WW-36 | Codex ×9 rounds | Translation layer bounced: judgement parks didn't stop LATER education fills (both engines); the translator's "Bachelor's default" overstated partial profiles (P1-class honesty); "graduation year" vs "expected graduation date" label precision ×3 (year-only fields nearly got yyyy-MM); cert-name selector fallbacks could hit checkboxes/date fields; unconditional cert-section Next clicked on from review on no-cert tenants; missing stop re-checks + quiet probes | All fixed in de9e006 | | BUG-19 | WW-36 | Gemini | New public matcher entry points NRE'd on null degree text (profiles legitimately omit it); Combine("aa","a","a") set-logic would fabricate associate bridges from stray "a" tokens; noisy noExpire probe | Fixed in bc71432/de9e006 |

Found after the fact — landed code, caught later

Bug Surface Found by Finding Outcome
LIVE-1 Apply vertical merge audit The headline: StartRunAsync seeded a checklist and logged "Run started" without ever opening a browser — the whole vertical was scaffolded but never connected across the overnight merges Audit 005b86d → became the APL epic
LIVE-2 Job queue live use Queue served demo boilerplate as if it were real scraped jobs; resync didn't fail fast without LinkedIn Fixed bd4c0e2
LIVE-3 Connections live use LinkedIn connect flow + scraper URLs broken in real use Fixed 4d2f248
LIVE-4 Intake live use Saving intake returned 400 (validator demanded implicit-flow fields that PKCE doesn't use) Fixed in the Google OAuth work 98a8cb2
LIVE-5 Connections live use Token-file race between API startup and Electron preload Fixed c520927
LIVE-6 Interview retro live use Recording company feedback wiped unsaved retro fields Fixed 875ade6
LIVE-7 StudyVisualStore Semgrep (first full scan) SaveAsync path-combined an unsanitized file name — a path-traversal hazard one caller away from real (all existing callers mint Guid names) Hardened f6891f0
LIVE-8 API caching ZAP baseline API responses (personal data) were cacheable — flagged on the live loopback API Cache-Control: no-store on everything 2bf147d
LIVE-9 Git history gitleaks (first full-history scan) 2 hits in 131 commits — both reviewed false positives (enum identifiers; a literal YOUR_LAUNCH_TOKEN docs placeholder) Narrow line-targeted allowlist in .gitleaks.toml 2bf147d
LIVE-10 Semgrep MCP live smoke test The planned uvx semgrep-mcp / ghcr.io/semgrep/mcp forms were deprecated upstream — the old image serves only a deprecation notice Registered the current semgrep mcp form 2bf147d
LIVE-11 Address autofill live use The address filler couldn't autofill a real user address — not every address resolves through the current fill path Tracked → WW-46 (postal validation/autocomplete source)

Rejected findings — claims the gate refuted with evidence

Recorded because "reviewer said X, evidence said otherwise" is a decision with precedent value. House rule: Codex is the tiebreaker; every rejection needs a concrete refutation, never a shrug.

Claim By Refutation
Git CRLF warnings in diff text are "compilation errors" (twice) Gemini Build succeeded + full suite green both times
Headers.CacheControl "doesn't compile" Gemini Strongly-typed IHeaderDictionary property since .NET 6; built, tested, and observed live via curl
gitleaks git is "an invalid command" Gemini The exact invocation had just scanned 131 commits successfully, twice
GitHub auto-links bare commit SHAs in .md files Gemini Auto-linking works in issues/PRs, not rendered markdown — board uses full commit URLs (Codex tiebreak)
Degree-dropdown click result ignored Gemini Code already captured and branched on the result (misread)
LocateSubmit would block 30s on the EEO wall Gemini ResolveAsync uses a 1.2s per-selector timeout, not Playwright's default
Coordinated prompt's SignupUrl fix "is lost before save" Gemini No database — the UI polls the same live object; the mutation is visible on the next poll
CountContactFields allocation "hot path" Gemini Called once per run — not a hot path (superseded anyway by the DOM-write counter)
visible=true chained locator "searches descendants, resolver always null" (P1, asserted twice) Codex Refuted by passing E2E — a leaf <input type='submit'> resolves and fills read back; pattern normalized to the explicit >> form for readability
Coordinated account wall "deadlocks — engine never sets AwaitingAccount" Gemini The SERVICE's CheckAccountWallAsync sets PendingAccount + status itself (seam ownership); proven by the Workday coordination tests
Angular templates "can't compile object spread — job-detail fails to build" (P1) Codex ng build succeeded, 27/27 job-detail specs pass, live preview rendered the exact card — Angular 22 supports it
"iCIMS level/expected fills index Education[0] unguarded" Gemini The fills already sit inside the Education.Count > 0 block — diff-context misread
Referrer WorkEmail "can be null → crash / CS8619" on iCIMS+Workday+Greenhouse (WW-38) Gemini WorkEmail is a non-nullable string (=""); build emitted zero CS8619; FillPresentAsync skips empty. No null reaches a fill
Referrer fills "lack an IsHalted guard between name and email" (WW-38) Gemini Both engines' FillPresentAsync/FillAsync self-guard IsHalted on entry AND across the resolve await — a Stop between fills is already honored

Scorecard so far: Codex — high precision, two refuted claims (the visible=true descendant assertion and the template-spread compile claim — both stale-knowledge, both disproven by passing builds/tests), caught five P1s (unauthenticated install endpoint, stopped-run resurrection, iCIMS EEO preselected defaults, judgement stop race, month-input DateOnly 400s). Gemini — real catches the others missed (async-hide race, fetch-with-no-timeout, cross-OS path normalization, hydration-racing frame probes) but ~9 refuted claims, mostly from reasoning about a stack it assumed rather than the one in front of it. The scanners each earned a LIVE row on their first run. That spread is the argument for the council: no single reviewer has the whole picture.


Gate evolution across the project: Codex solo → Codex + Gemini → full multi-model council → + Semgrep / gitleaks / ZAP → + Grok seat (Jul 9; chips name Grok whenever it sat, first runs degraded). See SECURITY.md for the current stack and technical/making-of.md for the narrative version of this board.