Skip to content

release: v0.9.1 — auth scheme, doc-system router, lint adoption#53

Merged
kwschulz merged 5 commits into
mainfrom
test/dialog-integration
May 13, 2026
Merged

release: v0.9.1 — auth scheme, doc-system router, lint adoption#53
kwschulz merged 5 commits into
mainfrom
test/dialog-integration

Conversation

@kwschulz
Copy link
Copy Markdown
Contributor

@kwschulz kwschulz commented May 12, 2026

Release scope: v0.9.1

Bundles four streams of work into a single patch release. CHANGELOG entries have the full details — this PR description is a tour for reviewers.

1. Authorization scheme preservation — feat(sanitization):

Authorization header redaction now preserves the RFC 7235 scheme token (Basic, Bearer, Digest, NTLM, Negotiate, OAuth) and only redacts the credential after the first whitespace: Authorization: Bearer AUTH_xxxxx. Unknown schemes fall through to full redaction so non-standard leading tokens can't escape. Implemented as a third header-classification bucket (headers.scheme_redact). Restores the ability of downstream pipelines (e.g. cable_modem_monitor's analyze_har) to classify auth strategy from a single authenticated request.

2. Dialog observability integration tests — test(capture):

Closes the test-coverage gap acknowledged when v0.9.0 shipped: the dialog observability code (PR #52, closes #46) had unit tests but was never verified against a real browser. Adds 8 integration tests that drive the production JS init script + page.expose_function bridge through a real Playwright headless Chromium session and assert the cross-context callback round-trip works. Tests import the production strings directly so any future drift in either side fails the suite.

Test Verifies
test_alert_records_accept alert() has only an accept path; bridge records action=accept
test_confirm_accept_records_accept Accepted confirm() records action=accept
test_confirm_dismiss_records_dismiss Dismissed confirm() records action=dismiss
test_prompt_accept_with_value_records_accept prompt() with value records action=accept
test_prompt_dismiss_records_dismiss Dismissed prompt() records action=dismiss
test_multiple_dialogs_in_sequence_all_recorded Three dialogs in sequence — all three recorded in order
test_duplicate_messages_each_recorded_separately Two confirm() calls with identical message produce two distinct resolutions (regression guard)
test_production_dialog_handlers_round_trip_end_to_end The EXACT production closures wired into a real Playwright session, asserts the match-by-(type, message) update produces the final resolved_by="browser_ui" + action=accept record

Marked @pytest.mark.integration + @skip_no_browser so the tests only run with pytest -m integration against an installed chromium.

3. Doc-system router restructure + lint adoption

CLAUDE.md becomes a thin router with a "Where Things Live" routing table; architecture / code-quality / testing / release principles move to their authoritative homes:

  • New docs/CODE_REVIEW.md houses code-quality and test-file standards plus a concrete Quality Gates table.
  • docs/ARCHITECTURE.md gains a Code Organization section (SoC, DRY, no-CLI-dep, additive-only).
  • docs/RELEASE.md gains a Version Numbering policy (pre-1.0 bump rules + CHANGELOG-section tiebreaker) and a Branching and Merging section (one PR per release, consolidate before pushing, cherry-pick vs rebase).
  • ADR-11 in docs/ARCHITECTURE_DECISIONS.md records the rationale.
  • 10 numbered-principle cross-refs in code/tests/scripts migrated to authoritative-doc pointers.

Markdownlint adopted alongside the existing mdformat hook:

  • New .markdownlint.jsonc / .markdownlint.json / .markdownlint-cli2.jsonc — every customization verified by isolation-testing, only MD013 customized (line-length 120 with table/code exemptions documented in the config).
  • markdownlint-cli2 v0.22.1 added to pre-commit.
  • mdformat hook now uses --wrap 120 + the mdformat-frontmatter plugin so YAML frontmatter in issue templates stops being collapsed to horizontal rules.
  • Full doc tree reflowed to 120-char lines (~2700 mechanical diff lines).
  • Surfaced and fixed during the lint adoption: 3 bare-URL violations, 2 trailing-colon headings, 1 missing-H1 in PR template.

Release-flow hardening:

  • .github/workflows/release.yml awk regex was extracting empty release notes for every prior release. Replaced with flag-toggle pattern; verified against the 0.9.1 CHANGELOG content.
  • .github/ISSUE_TEMPLATE/bug_report.md and feature_request.md restored to functioning YAML frontmatter.
  • scripts/release.py rule-3 reference corrected.

4. custom_patterns cross-cutting coverage audit — test(sanitization):

New 387-line parametrized matrix asserting custom_patterns takes effect at every public sanitization entry point × every reachable detection site. Executable form of the coverage claim around custom_patterns.

Verification

  • Default suite (-m "not integration"): 2064 passed, coverage 94.34% (gate 90%)
  • All pre-commit hooks green (incl. new markdownlint-cli2, mdformat with --wrap 120, mdformat-frontmatter, detect-secrets)
  • Fresh-context subagent smoke test on three trick questions (version bump, PR consolidation, coverage threshold) — all answered correctly from docs alone
  • scripts/release.py 0.9.1 --dry-run pre-flight validates (the on-main part of the gate runs post-merge)

Version bump

pyproject.toml and src/har_capture/__init__.py bumped 0.9.0 → 0.9.1. CHANGELOG.md dated 2026-05-13 with comparison link.

🤖 Generated with Claude Code

chore(release): v0.9.0 — mandatory --patterns + heuristic detectors + Chromium fix + lint cleanup
@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 98.47328% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/har_capture/capture/browser.py 92.59% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

kwschulz and others added 4 commits May 11, 2026 20:34
…ases

Codecov flagged 5 uncovered lines in the v0.9.0 dialog resolution
closure (browser.py:611, 615 partial, 629) — the defensive
isinstance() guard, one arm of the match condition, and the no-match
warning path. Integration tests cover them end-to-end but CI's
default `-m "not integration"` profile doesn't see those, so the
coverage gate didn't observe the gap.

Refactor: extract the pure match-and-update logic from the
`_on_dialog_resolved` closure inside `_run_browser_session` into a
module-level pure function `_apply_dialog_resolution`. The closure
stays thin — delegate, then log. Pure function is fixture-driven
unit-testable without going through Playwright mocks (CLAUDE.md
principle #12: test overrides are a code smell — restructure code,
don't paper over with mocks).

Add 12 fixture-driven test cases in tests/fixtures/test_browser.json
covering every branch:
- Match accept (single unresolved entry)
- Match dismiss (single unresolved entry)
- Skip already-resolved, pick unresolved LIFO
- Match by message when multiple unresolved
- Missing 'action' key defaults to 'unknown'
- No match (different message) returns None
- Empty dialogs list returns None
- Type mismatch returns None
- All-already-resolved returns None
- Non-dict outcome (string) returns None
- Non-dict outcome (None) returns None
- Non-dict outcome (list) returns None

Each case asserts the return value AND the in-place mutation (or
non-mutation) of the dialogs list. `expect_updated_index` pins which
entry got updated for multi-entry cases.

Full suite: 2014 passed, 26 deselected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ction

Add a third header-classification bucket `headers.scheme_redact` alongside
`full_redact` and `cookie_redact`. RFC 7235 `Scheme credentials` headers are
recognized by scheme token (Basic, Bearer, Digest, NTLM, Negotiate, OAuth) —
the scheme is preserved, only the credential after the first whitespace is
tag-redacted (`Authorization: Bearer AUTH_xxxxxxxx`). Unknown schemes or
values with no whitespace fall through to full redaction so non-standard
leading tokens can't escape.

Restores the ability of intake pipelines (e.g. cable_modem_monitor's
analyze_har) to classify auth strategy from a single authenticated request,
without needing a 401 + WWW-Authenticate exchange that often doesn't appear
when the browser sends cached credentials from request 1.

Also adds `AUTH_[a-f0-9]{8}` to allowlist.json `redaction_patterns` so
pre-commit validation's `is_redacted` matches the scheme-preserved form
(`Bearer AUTH_xxx`) via `re.search`.

Specs reconciled: SANITIZATION_SPEC, PATTERN_SPEC, VALIDATION_SPEC, patterns
README. CHANGELOG entry added under Unreleased.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Enumerates every public sanitization entry point x every reachable
detection site as a parametrized test matrix and asserts custom_patterns
takes effect at each pair. 30 rows: 4 sanitize_post_data body cases, 1
sanitize_html inline-script case, 8 sanitize_entry cases, 8 sanitize_har
cases, 8 sanitize_har_file cases (tmp_path-bound), plus 1 control test
proving the SENSITIVE marker is NOT redacted without custom_patterns
(guards against built-in patterns coincidentally catching the marker
and making every row pass falsely).

Background: 0.7.0 shipped a ContextVar-scoped custom_patterns override
that entered the scope only at two leaf entry points. Three detection
sites (sanitize_header_value, structured queryString params, URL query
params) ran from wrapper entry points BEFORE the leaves were called,
so custom_patterns silently no-op'd when callers used sanitize_entry /
sanitize_har / sanitize_har_file. 0.7.1 closed the specific gap.

This file is the executable form of the coverage claim "every public
entry point honors custom_patterns at every reachable detection site."
Adding a new detection site in source requires adding a matching row
here; adding a new public entry point likewise. A missing row is a
coverage claim without evidence — the exact failure mode that shipped
in 0.7.0.

No production code change; quality / regression-prevention only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundles the v0.9.1 release content: the Authorization-scheme
preservation feature (already cherry-picked), the custom_patterns
coverage audit (already cherry-picked), the dialog integration tests
(already on this branch from PR #52 follow-up), plus the doc-system
audit and lint hardening work this commit adds.

Doc-system router restructure
- CLAUDE.md slimmed to disciplines + Where Things Live routing table
- New docs/CODE_REVIEW.md houses code-quality and test-file standards
- docs/ARCHITECTURE.md gains Code Organization section (SoC, DRY,
  no-CLI-dep, additive-only)
- docs/RELEASE.md gains Version Numbering (pre-1.0 bump policy +
  CHANGELOG-section tiebreaker) and Branching and Merging sections
- New ADR-11 in docs/ARCHITECTURE_DECISIONS.md records the rationale

Cross-reference migration
- 10 code/test/script comments that cited numbered CLAUDE.md
  principles now point at the authoritative doc and section instead
- scripts/release.py rule-3 reference corrected (DRY was rule 2)
- Historical CHANGELOG entries untouched

Lint adoption
- New .markdownlint.jsonc / .json / -cli2.jsonc — every customization
  verified by isolation-test, only MD013 customized (line-length
  enforced at 120 with table/code exemptions documented in config)
- markdownlint-cli2 v0.22.1 added as pre-commit hook
- mdformat hook now passes --wrap 120 and includes mdformat-frontmatter
  plugin (required so .github/ISSUE_TEMPLATE/*.md frontmatter stops
  being collapsed to horizontal-rule underscores)
- Full doc tree reflowed to 120-char lines (~2700 mechanical diff lines)
- Surfaced and fixed: 3 bare-URL violations, 2 trailing-colon headings,
  1 missing-H1 in PR template, 1 detect-secrets pragma separated from
  its target line by the reflow

Release-flow fixes
- .github/workflows/release.yml awk regex extracted empty release notes
  for every prior release (single-range pattern where both endpoints
  matched the same line). Replaced with flag-toggle pattern; verified
  against the 0.9.1 CHANGELOG content
- .github/ISSUE_TEMPLATE/bug_report.md and feature_request.md restored
  to proper YAML frontmatter format

Verification
- 2064 tests passing, coverage 94.34%
- All pre-commit hooks green
- markdownlint 0 errors across 24 doc files
- Fresh-context subagent smoke test on three trick questions
  (version bump, PR consolidation, coverage threshold) — all
  answered correctly from docs alone

Version bump
- pyproject.toml + src/har_capture/__init__.py: 0.9.0 -> 0.9.1
- CHANGELOG.md dated 2026-05-13 with comparison link

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kwschulz kwschulz changed the title test(capture): integration tests for the dialog observability bridge release: v0.9.1 — auth scheme, doc-system router, lint adoption May 13, 2026
@kwschulz kwschulz merged commit 0e98ed4 into main May 13, 2026
6 checks passed
@kwschulz kwschulz deleted the test/dialog-integration branch May 13, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Interactive Dialog Handling for Modem Captures

1 participant