* docs(guidance): repo-wide agent-guidance audit — fix drift, prune 21.5k lines, consolidate tests/ onto AGENTS.md convention
Full-layer audit of the agent-guidance system (root contracts, .claude rules/
skills/commands, family and crate AGENTS.md, CONTRACT specs, tests guidance,
docs/internal), verified reference-by-reference against HEAD.
- Fix stale/ghost references: UserSandboxProcessPort, ProductSurfaceError,
LlmError::ContextLengthExceeded, INVERTED_PORT_IMPLEMENTORS, split channel
traits (ChannelIngress/ChannelReply/ChannelDelivery), memory-native's
never-implemented EmbeddingProvider seam, wrong layer/crate/module counts.
- Convert unpinned prose numbers to regeneration commands or pinning-test
citations across root, family, and crate guidance (drift-proofing).
- tests/: rename CLAUDE.md -> AGENTS.md with CLAUDE.md symlinks (crates/
convention), extend scripts/ci/check-guidance.py discovery to tests/,
delete stale e2e scenario tables, dedupe tier taxonomy against
.claude/rules/testing.md.
- Commands/skills: delete six dead v1 commands (add-tool, review-pr,
review-crate, fix-issue, respond-pr, add-sse-event) and the v1-teaching
architecture-video skill; convert ironclaw-reborn-skill-maintainer into
the auto-loading rule .claude/rules/guidance-maintenance.md; fix clippy
-D warnings and portable date in surviving commands; triggers-only
frontmatter; add automations section to reborn-feature.
- Rules: rename gateway-events.md -> events.md; revive
scripts/check-type-duplicates.py (glob matched zero types since the
family reorg); index all 15 rules in root AGENTS.md for Codex parity.
- docs/internal: delete 70 superseded plans/specs/design docs (~21.5k
lines, each re-verified unreferenced); fix misleading v1-migration
status lines; rewrite the contracts index as a recipe; restore two docs
that proved live-referenced.
- Trim composition CONTRACT.md route-mirror sections (invariants kept).
Verified: check-guidance.py (384 files, 0 grandfathered),
docs_publication_boundary.py, cargo test -p ironclaw_architecture_tests,
scripts/ci test-plan suite (87/87) — all green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(guidance): address PR #7797 review comments
Verified each of the ~40 bot/reviewer findings against the tree; applied the
valid mechanical fixes, rebutted the rest with evidence (see PR comment).
- Test planner: add renamed tests/ guidance aliases to IGNORED_GUIDANCE_PATHS
(reproduced the fail-closed abort on this PR's own changed-file list) and
extend the planner test to all six guidance paths.
- check-guidance: add self-tests proving tests/-tree discovery and alias
enforcement (48 tests, was 46); new self-test file for
check-type-duplicates.py (4 tests).
- Portability: replace GNU-only date -d in triage commands with a python3
one-liner (works on macOS BSD and Linux).
- Count/claim accuracy: product_contracts manager-port prose 4 -> 3 (matches
INVERTED_PORT_IMPLEMENTORS), kernel grep -cF for literal #[test] (was regex
char class, 2 vs 23), rg -o|wc -l for a true total in architecture.md,
Rust-scoped LlmProvider count (catches 5 generic impls), measured
1/73-crate dual-backend claim in pr-shepherd, executable wc -l in
assistant guidance, AST/pytest recipes for the e2e test-count figures.
- Content: deslop co-author line no longer hardcodes an address; ship.md
surfaces Postgres-skip counts; risk-label guidance documents the crates/**
labeler blind spot; e2e authoring recipe leads with reborn_v2_* fixtures;
stale CLAUDE.md line citation replaced with a stable anchor; ✎ provenance
notes for two deleted-plan citations; unified-channel-model status text
reconciled with an explicit ChannelDelivery-only exception note.
Gates: check-guidance (384 files, 0 grandfathered), docs boundary, planner
tests 87/87, check-guidance self-test 48/48, type-dup self-test 4/4 - green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(ci): classify scripts/test-check-type-duplicates.py in the PR test planner
The self-test added in 77bc8f8c5 was never registered in
PR_STATIC_CONTROL_PATHS, so the planner's fail-closed unmapped-path arm
aborted 'Detect Reborn test scope' and cascaded into the whole Reborn
matrix skipping. Classified like its subject (deliberately CI-unwired
local dev tool, per the existing entry's rationale) and pinned in the
static-control planner test alongside it.
Verified: planner tests OK; planner run against this PR's full
changed-file list now returns mode=selected with the path owned by
static checks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(docs): drop parity-QA path reference from tests/integration/AGENTS.md
The guidance dedup in this PR added a literal
tests/support/reborn_parity_qa reference to tests/integration guidance,
which scripts/ci/check-test-suite-boundaries.sh correctly flags: the
one-way dependency guard covers docs too, and origin/main's version of
this file carried no such reference. Fix the content, not the check -
the tier comparison is reworded to describe the RebornBinaryE2EHarness
seam difference without naming the parity/QA tree.
Verified: check-test-suite-boundaries.sh OK; check-guidance OK; the
full 'Detect Reborn test scope' job reproduced locally end-to-end.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci: run the type-duplicates self-test in Code Style; exercise the production report path
Address the two follow-up review findings on 77bc8f8c5/9ed30caf8:
- Wire scripts/test-check-type-duplicates.py into code_style.yml's
Static-check self-tests step (next to test-check-guidance.py) so the
regression test is actually enforced; update the planner's
PR_STATIC_CONTROL_PATHS comment accordingly (classification unchanged
- Code Style is the static lane).
- Strengthen the semantic-duplicate self-test to also drive the
production main() report path and assert on its printed candidate
output, instead of only re-computing similarity locally. Strictly
stronger; the other three tests are untouched.
Verified: self-test 4/4, planner tests 87/87, workflow-contract
self-test 94/94, planner simulation over both changed paths classifies
cleanly (no unmapped-path abort).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci: trigger fast-checks on type-duplicates script changes; symbol recipe for the tier reference
- code_style.yml's has_code filter covered scripts/ci/ but not the bare
scripts/ type-duplicates pair, so a diff touching only the new
self-test skipped the fast-checks job that runs it (the previous
commit's 'runs unconditionally' claim was wrong - corrected the
planner comment too). Added the two files to the filter following the
check_no_panics precedent and pinned them as in_scope probes in the
ws12 workflow-contracts routing test.
- tests/integration/AGENTS.md tier reference is now re-verifiable via a
single-hit symbol recipe (rg 'struct RebornBinaryE2EHarness') instead
of a path citation, which the test-suite boundary guard forbids from
this subtree.
Verified: ws12 workflow contracts 94/94, planner tests 87/87,
check-test-suite-boundaries OK, check-guidance OK.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Henry Park <16583448+henrypark133@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
29 KiB
Ironclaw TDD Playbook for Engineers
This playbook explains which tests to write, where they belong, and how to develop a feature or bug fix test-first without adding unnecessary process.
It is written for engineers who are new to Ironclaw. Repository and crate-local
guidance remains authoritative when it is more specific. Start with
.claude/rules/testing.md; for cross-layer Reborn tests, also read
tests/integration/AGENTS.md.
Core rule
Start with what the user should experience, write a test for that behavior, and implement the smallest amount of code needed to make the test pass.
Do not write every kind of test for every change. Select tests based on the behavior and its risks.
The four test types
1. Unit or contract test
A unit or contract test proves one local rule or one crate-owned public contract.
Use it for:
- pure logic and validation
- typed errors and state transitions
- isolated bug fixes
- public API or policy tables owned by one crate
Examples include rejecting an invalid cron expression or denying an operation when the caller lacks permission.
Run the owning crate's tests:
cargo test -p OWNING_CRATE
2. Hermetic feature test
A hermetic feature test proves a complete Ironclaw behavior without calling a real model or external service. The result should be repeatable and require no API keys.
The Reborn integration harness scripts the vendor model response while keeping the real product workflow, scheduler, agent loop, LLM decorator chain, permissions, capabilities, and persistence in the path.
Use this for most new or changed production-wired Reborn behavior.
cargo test --test reborn_integration_SCENARIO
3. Surface test
A surface test proves behavior through the external surface affected by the change. Choose only the relevant kind:
- Recorded model fixture: the model must choose a particular tool or send particular arguments.
- Browser E2E: the user can see or interact with the behavior in WebUI.
- Backend or runtime integration: the behavior depends on PostgreSQL, libSQL, Docker, WASM, MCP, or another runtime.
Many local changes do not need a surface test.
4. Live canary
A live canary runs a small scenario against a real model or provider. It finds model drift, provider API changes, credential failures, and unexpected prompt behavior.
Live canaries are supplemental. They must not be the only test for a feature, and every pull request should not have to wait for one.
How to choose a test
Ask these questions:
- Can the user see or click the changed behavior in WebUI? Add a browser E2E test.
- Does the model need to choose a particular tool or arguments? Add a recorded fixture and replay test.
- Does the behavior cross Ironclaw components or perform a side effect? Add a hermetic Reborn integration test.
- Is the change only local logic? Add a unit or contract test.
- Does success depend on the current behavior of a real model or provider? Add or run a live canary after deterministic tests pass.
A change can match more than one question. For example, a WebUI approval feature may need both a hermetic integration test and a browser test.
Step-by-step workflow
Step 1: Describe the user behavior
Write one short Given/When/Then example. Describe an observable result, not an internal function call.
Given a user who must approve file writes, when Ironclaw tries to write a file, then the turn waits for approval, the file does not exist before approval, and the file exists after approval.
Step 2: Identify the risks
Mark the risks that apply:
- model behavior
- browser behavior
- side effect
- persistence
- security or permissions
- external provider
- cross-component behavior
These risks determine which test types are needed.
Step 3: Choose the highest-level deterministic test
For most production-wired Reborn behavior, start with a hermetic Reborn integration test. A live canary can be the first scenario designed, but it should not be the first or only automated test.
Step 4: Make the test fail
Run the test before implementing the feature. Confirm it fails because the behavior is missing, not because the test contains a typo or broken setup.
For a bug fix, the test must reproduce the original bug.
Step 5: Write the smallest fix
Implement only enough code to make the test pass. Avoid unrelated refactoring and speculative features.
Step 6: Add important edge cases
Add smaller tests when they improve diagnosis or protect an important rule:
- invalid input
- missing permission
- cancellation
- duplicate requests
- partial failure
- wrong user or tenant
- persistence after restart
Do not repeat the same happy path at every test level.
Step 7: Run tests from fast to slow
Run:
- unit and contract tests
- hermetic Reborn integration tests
- recorded fixture replay, browser E2E, or backend/runtime integration
- live canary
This keeps the development loop fast while preserving outside-in coverage.
How local tests map to CI
The test types above describe what evidence to add. CI lanes describe when that evidence runs. Keep those decisions separate: a browser test does not become a live canary because it runs nightly, and a deterministic provider journey remains hermetic even when the deep lane repeats it in more orders.
| Lane | Purpose | What engineers should expect |
|---|---|---|
| Pull request feedback | Fast, scoped signal on the proposed change | Relevant deterministic subsets, evidence gates, and changed-code checks may run before review. |
| Merge queue | Production gate on the merged result | Queue-covered deterministic checks run in the same shape required before main; path scope is computed by workflow jobs rather than trigger filters. |
Push to main |
Confirm the queue result and publish shared evidence | Repeats queue-covered checks, runs the documented post-merge-only Windows, benchmark, and legacy snapshot checks, warms caches, and publishes reports. |
| Deep scheduled | Exercise expensive breadth | Higher property-test counts, reversed and isolated journey order, mutation audits, browser shards, stress, soak, and live drift run outside the ordinary merge gate. |
| Release artifact | Verify what will actually ship | Smoke the exact built archive or binary rather than treating a development build as release evidence. |
The authoritative workflow contract, required-check names, and scheduling
details live in .github/workflows/README.md.
Its "Known accepted gaps" section names deterministic and informational checks
that are deliberately not merge-gating. Re-derive the current lanes before
changing CI:
rg -n "pull_request:|merge_group:|push:|schedule:|workflow_dispatch:" \
.github/workflows
A green pull request does not imply that scheduled, live, backend, or release tiers ran. Name the tiers actually exercised in the pull request test card.
Where tests belong
| Behavior | Location | Notes |
|---|---|---|
| Private helper or pure local rule | crates/<owning_crate>/src/ in #[cfg(test)] mod tests |
Keep the test next to the implementation. |
| Public crate contract | crates/<owning_crate>/tests/<behavior>_contract.rs |
Test through the crate's public API. |
| Whole Reborn turn or cross-component behavior | tests/integration/ |
Use the scripted-model harness and assert at a meaningful seam. |
| Model tool choice or request shape | tests/fixtures/llm_traces/reborn_qa/ and tests/reborn_qa_recorded_behavior.rs |
Commit only scrubbed fixtures. |
| WebUI behavior | tests/e2e/scenarios/test_<behavior>.py |
Use the Reborn v2 fixtures for WebChat v2. |
| Database or runtime behavior | Owning crate's feature-gated integration suite, or an existing root integration suite | Cover supported production backends. |
| Real model or provider drift | scripts/reborn_webui_v2_live_qa/ |
Reuse the current live-QA lane when possible. |
Codebase examples
Unit or crate contract
Example:
crates/product/ironclaw_webui/tests/webui_v2_descriptors_contract.rs
This contract locks the declared WebChat v2 route surface, including method, path, authentication, body limit, rate limit, CORS, audit class, and allowed effect path.
The test follows this shape:
#[test]
fn route_table_has_exactly_the_expected_routes() {
let routes = webui_v2_routes();
let expected = expected_table();
assert_eq!(
routes.len(),
expected.len(),
"expected {} WebChat v2 routes, found {}",
expected.len(),
routes.len()
);
}
Put a similar test inline when it covers a private helper. Put it under the
owning crate's tests/ directory when it protects a public contract or
caller-facing behavior.
cargo test -p ironclaw_webui
Hermetic Reborn integration
Simple example: tests/integration/greeting.rs
This proves that a synthetic inbound message travels through product workflow, scheduling, the agent loop, the real LLM decorator chain, and persisted thread history. Only the vendor model response is scripted.
let harness = RebornIntegrationHarness::test_default()
.script([RebornScriptedReply::text("Hello! How can I help?")])
.build()
.await
.expect("harness builds");
harness
.submit_turn("hi there")
.await
.expect("turn completes");
harness
.assert_reply_contains("Hello! How can I help?")
.await
.expect("reply finalized in thread history");
For a distinct scenario, add tests/integration/<scenario>.rs and register the
flat test binary in the root Cargo.toml as reborn_integration_<scenario>.
If a scenario shares expensive setup with an existing group, add it to the
group directory and include the module from that group's main.rs instead of
creating another harness.
cargo test --test reborn_integration_greeting
Caller-path side effect
Example:
tests/integration/group_approvals/scenario_gate_then_approve.rs
This drives the real approval and resume path and asserts that the approved
file write actually persisted. It does not stop at Completed status or a mock
call count.
let (run_id, gate_ref) = h
.submit_turn_until_blocked("write the approval file")
.await?;
h.approve_gate(run_id, &gate_ref).await?;
h.wait_for_status(run_id, TurnStatus::Completed).await?;
h.assert_workspace_file_contains("approved.txt", "approved write")
.await?;
Use this caller-path shape whenever a helper controls persistence, egress, dispatch, approval, secrets, or another side effect.
cargo test --test reborn_group_approvals
Recorded model fixture
Examples:
- fixture:
tests/fixtures/llm_traces/reborn_qa/web_status_check.json - contract and replay:
tests/reborn_qa_recorded_behavior.rs
The contract proves that the recorded model response selected builtin.http
with the expected target:
let trace = load_qa_trace(WEB_STATUS_CHECK.fixture);
assert_tool_called_with(&trace, "builtin.http", &["api.github.com"]);
Add tool-choice and key-argument assertions to
tests/reborn_qa_recorded_behavior.rs. Add a replay assertion when the trace
should create or modify durable state. Live recorders stay ignored; contract
and replay tests run hermetically in CI.
scripts/ci/check-reborn-qa-fixtures.sh
cargo test --test reborn_qa_recorded_behavior
Browser E2E
Example:
tests/e2e/scenarios/test_reborn_webui_v2_smoke.py
This starts the standalone Reborn server and proves that an authenticated user reaches the chat shell while an anonymous user reaches the login screen.
async def test_reborn_v2_serves_shell_and_gates_auth(
reborn_v2_server, reborn_v2_browser
):
authed_ctx = await reborn_v2_browser.new_context(
viewport={"width": 1280, "height": 720}
)
authed_page = await authed_ctx.new_page()
await authed_page.goto(
f"{reborn_v2_server}/?token={REBORN_V2_AUTH_TOKEN}"
)
await expect(authed_page.locator(SEL_V2["chat_composer"])).to_be_visible()
For Reborn WebChat v2, use the reborn_v2_* fixtures and SEL_V2 selectors.
If the scenario must be part of the Reborn coverage gate, add its file or
pytest node ID to
tests/e2e/reborn_coverage_tests.txt.
The merged Reborn LCOV report enforces three complementary ratchets:
- the aggregate floor preserves the stronger 85.11% post-process-journal baseline (which supersedes the historical 80.81% floor);
- critical production crates have percentage and covered-line floors in
tests/integration/coverage-floor.toml; - added, instrumentable production code must meet the original committed 90%
changed-line floor. Changed-branch coverage is still required in LCOV and
reported for review, but has no universal percentage floor. The denominator
comes from the pull-request diff intersected with LLVM
DAandBRDArecords, not from a hand-maintained file list. Missing production coverage and changed files with no measured instrumented lines remain hard failures.
Changed-code exemptions live in
tests/integration/changed-coverage-exemptions.toml.
They must name exact line numbers and/or branch-line numbers plus an owner,
reason, issue, and future review date. Globs and whole-file exemptions are not
accepted. Test modules and test-support paths are excluded mechanically; a
production source file missing entirely from LCOV fails rather than
disappearing from the denominator. Added lines in renamed production files
remain in scope.
Branch LCOV exports use --skip-functions because changed-code gates consume
line and branch records only. They pin nightly-2025-11-01 (LLVM 21.1.3):
newer bundled LLVM versions have a confirmed
getInstantiationGroups branch-mapping crash
for async generic Rust code. The coverage-only compiler therefore passes
--ignore-rust-version and bootstraps only the three library features used by
current dependencies that stabilized after Rust 1.93: array_windows,
debug_closure_helpers, and slice_as_array. Normal build, clippy, and test
jobs remain the authoritative current-MSRV checks. Dedicated cache keys
prevent mixing objects from the two compilers. The branch-export self-test
fails if the safe toolchain, exact compatibility envelope, MSRV override, or
record filter is removed, if discovery is empty, or if an expected
workflow/script path goes stale. The blocking workflow also runs the changed
coverage and LCOV-merge sabotage suites before accepting the real report.
The standalone shipping ironclaw binary is separately built under
cargo llvm-cov, driven by the production-composed Python E2E manifest, and
terminated through its graceful shutdown path. The Code Coverage workflow
clears profiles emitted by the instrumented prebuild before starting pytest,
then fails if the E2E run produces no new profiles. It uploads its branch-aware
LCOV as the reborn-shipping-binary-e2e-coverage artifact before sending it to
Codecov.
cd tests/e2e
pytest scenarios/test_reborn_webui_v2_smoke.py
Database or runtime integration
Example:
crates/loop/ironclaw_hooks/tests/parity_matrix.rs
This is the right shape when multiple backends must implement the same behavioral contract. Production-facing persistence behavior should cover both libSQL and PostgreSQL unless the owning contract explicitly says otherwise.
Prefer the owning crate's tests/ directory for one storage or runtime
contract. Use an existing feature-gated crate suite when behavior crosses
composition layers.
Never silently return from a test because Docker or PostgreSQL is missing. Use documented feature gates or a loud, explicit opt-out.
cargo test -p <owning-crate> --features integration # e.g. -p ironclaw_hooks; the workspace-root `integration` feature is empty
Live canary
Examples:
- implementation:
scripts/reborn_webui_v2_live_qa/run_live_qa.py - workflow:
.github/workflows/live-canary.yml
The existing qa_3b_endpoint_status_live_chat case asks Ironclaw to check
whether near.ai returns HTTP 200 and verifies that the current status is
reported.
Add or extend the case under scripts/reborn_webui_v2_live_qa/. Reuse the
current reborn-webui-v2-live-qa lane when possible. Change the workflow only
when a case needs new shard, secret, schedule, or lane wiring.
Authorized maintainers can run one case from a pull request:
/canary cases=qa_3b_endpoint_status_live_chat
Keep mutations isolated and reversible, and scrub uploaded artifacts.
Worked example
Feature: a user asks Ironclaw to create a routine that checks a website every hour.
Possible coverage:
- Unit test: reject an invalid schedule.
- Hermetic integration: script the routine-creation tool call and verify the routine is persisted with the correct schedule.
- Recorded fixture: verify a real model chooses the routine tool with the expected URL and schedule.
- Live canary: ask the current production model to create the routine and verify it succeeds.
A browser test is unnecessary unless the feature changes how routines appear or behave in WebUI.
Pull request test card
The pull request template includes this test card. Complete every field before requesting review; do not remove the section.
### Test card
User behavior:
Risk areas:
- [ ] Model behavior
- [ ] Browser
- [ ] Side effect
- [ ] Persistence
- [ ] Security or permissions
- [ ] External provider
- [ ] Cross-component behavior
Tests added or updated:
- Unit or contract:
- Reborn integration:
- Recorded fixture:
- Browser E2E:
- Backend or runtime:
- Live canary:
What the tests prove:
Commands run:
For every unused field, write Not applicable: <reason>. If an expected test
layer is omitted, explain why in one sentence.
Before creating a new test file
- Search for an existing test that already drives the same caller or workflow.
- Read the owning crate's
AGENTS.md,CLAUDE.md,CONTRACT.md, orREADME.md. - Read
tests/integration/AGENTS.mdbefore changing the Reborn integration harness. - Write or update the test first and confirm it fails for the expected reason.
- Assert an observable outcome, not only
Completedstatus or a mock call count. - Run the narrowest test during development, then expand based on risk.
- Add
cargo test -p ironclaw_architecture_testswhen dependency or ownership edges change. - Use
bash scripts/reborn-e2e-rust.shwhen a Reborn contract or whole-path behavior changes.
Rules to remember
- Most production changes need a unit or contract test plus a hermetic feature test.
- Every reproducible bug fix needs a regression test.
- Test real outcomes: files written, records persisted, events emitted, requests captured, or permissions enforced.
- Test through the real caller when permissions or side effects are involved.
- Use recorded fixtures only when model behavior matters.
- Use browser tests only when browser behavior matters.
- Live canaries supplement deterministic tests; they never replace them.
- Extend an existing test when it already covers the same workflow.
Provider capability coverage is counted per outcome, not per capability
A capability with one passing happy-path case is not covered. The gate in
tests/e2e/scenarios/test_provider_capability_inventory.py counts
capability x outcome class and enforces two rules, both derived from the
shipped manifests rather than a hand-maintained list:
| Operation kind | Required evidence |
|---|---|
external_write |
A ProviderOperationCase with provider-side readback, an integration_evidence entry, or a journey_evidence entry naming the exact test and its readback assertion helper. |
| read | A ProviderOperationCase with outcome_class = "success" and one with outcome_class = "empty". |
The read/write split comes from each tool's effects in
crates/extensions/packages/*/manifest.toml
(external_write), so shipping a new tool classifies it automatically.
A harvested tool-call name is not evidence for a write. A recorded model
response naming slack__send_message proves the model chose the tool. It says
nothing about whether the provider committed the effect, so it cannot stand in
for a readback. That distinction is the whole point of the rule — before it
existed, eight write capabilities were classified tested on the strength of a
recorded tool-call name, and auditing them found two (google-sheets.write_values
and google-sheets.rename_sheet) whose only journey issues them alongside
append_values against one spreadsheet, so nothing isolates either.
outcome_class covers only the per-operation semantic outcomes. Status and
transport failures belong to the fault profiles below — do not duplicate them
here.
Anything not yet meeting the rules goes in coverage_backlog in
tests/e2e/fixtures/provider_capability_coverage.toml with an owner, reason,
issue, and review condition. It is a ratchet: the gate fails when a backlog
entry names a capability that has since been covered, so entries must be
deleted as work lands.
Provider fault profiles
Use tests/e2e/provider_fault_proxy.py when a provider operation must cross
the real Reborn extension and network path while Emulate retains authoritative
provider state. The proxy supplies reusable HTTP, malformed-response, timeout,
connection-reset, and lost-acknowledgement profiles. Its ledger records only
request metadata, body digests, and credential fingerprints.
Apply profiles by operation equivalence class instead of multiplying every provider operation by every failure. A representative read, idempotent write, and non-idempotent write must assert the model-visible result, proxy attempt count, whether the provider received the request, and direct provider readback. A lost-acknowledgement test must prove the provider committed while the runtime did not report success, and must prove that no blind duplicate request occurred.
Keep missing credentials, credential refresh, and account-scope behavior at their existing auth/runtime seams when a provider proxy cannot create the condition faithfully. Fault state must be reset independently from provider state after every case.
Whole-path journey inventory
Use tests/e2e/journey_types.py and tests/e2e/journey_cases.py to register
representative compositions across ingress, execution, provider state, and
delivery. A JourneyCase is evidence metadata, not a workflow DSL: execution
logic, provider setup, and readback remain in their owning test modules.
Each case names isolated provider worlds, ingress, execution lane, delivery target, observable assertions, and an exact Pytest or Cargo declaration. Provider journeys also bind their recorded trace, replay facts, and scheduled live evidence. Product journeys may bind exact delivery addresses and browser evidence when the cited test proves those claims. Do not populate an optional field from intent: name it only when the referenced test asserts it.
test_journey_coverage.py verifies that the evidence still exists and is
executable. It also derives inbound and outbound channel surfaces from shipped
first-party manifests, so adding a production channel without representative
journey evidence fails CI.
Prefer one representative whole-path case per supported ingress and delivery mechanism. Do not multiply every provider operation by every ingress or move provider-specific assertions into the generic registry.
Run the registry gate directly after adding or changing a journey:
cd tests/e2e
pytest scenarios/test_journey_coverage.py -q
Generated lifecycle and interaction coverage
tests/e2e/state_machine_coverage.py projects journeys, provider operations,
provider faults, and focused Reborn integration tests onto the lifecycle
dimensions that matter across boundaries. It is an evidence inventory, not a
second runtime state machine and not a Cartesian-product test generator.
Update it when production adds or changes a supported:
- ingress, authentication, policy, operation, provider-outcome, lifecycle, or delivery class;
- trigger, retry, cancellation, duplicate, restart, or concurrent-submit sequence;
- terminal-stability, at-most-once-effect, actor-isolation, truthful-uncertainty, or no-orphan-resource invariant;
- high-risk interaction between two of those dimensions.
Prefer projecting an existing JourneyCase, ProviderOperationCase, or
provider fault. Add a focused row only when no existing registry owns the
evidence, and cite the exact executable Pytest or Cargo declaration. Add a
required pair only for an interaction whose combined behavior carries more
risk than either dimension alone.
Re-derive the supported dimensions, sequences, invariants, and selected pairs, then run the fail-loud gate:
rg -n \
"SUPPORTED_DIMENSIONS|REQUIRED_EQUIVALENCE_PAIRS|SequenceClass|StateMachineInvariant" \
tests/e2e/state_machine_coverage.py
cd tests/e2e
pytest scenarios/test_state_machine_coverage.py -q
Promoting failures into regression tests
When a production, live-canary, or QA failure is reproducible, promote it to the lowest deterministic seam that proves the broken rule:
- State the user-visible failure and the expected outcome.
- Remove credentials, personal data, provider identifiers, and irrelevant transcript content before committing any fixture.
- Reproduce the failure with a unit/contract, Reborn integration, recorded fixture, provider operation, journey, or browser test.
- Confirm the regression test fails for the original reason.
- Apply the fix and confirm the same test passes.
- Keep a live canary only when real model or provider drift remains a distinct risk.
The commit hook and
regression-test-check.yml
require test changes for conventionally named fixes and selected high-risk
paths. [skip-regression-check] and the matching label are review-visible
exceptions for genuinely infeasible cases, not substitutes for a reproducible
test. Explain the missing deterministic seam and compensating evidence in the
pull request test card.
Mutation audits test the assertions
Coverage proves that code ran; it does not prove that a test would detect the wrong result. Use a mutation audit when a critical invariant, escaped defect, or suspiciously broad coverage needs assertion-strength evidence.
Start with one file or package, triage every viable survivor, and verify a
real-gap fix against both unmodified and sabotaged code:
./scripts/mutation-audit.sh -p OWNING_CRATE path/to/production.rs
./scripts/mutation-verify-fix.sh -p OWNING_CRATE \
'copy the exact mutant string from the triage queue'
Do not optimize a workspace mutation score. Equivalent mutants and unclear
product contracts are explicit outcomes, and broad mutation work belongs in
the scheduled frontier. Follow
docs/internal/mutation-audit.md for environment
isolation, verdicts, acceptance criteria, and the fail-loud self-test.
Product-surface coverage report
The Reborn E2E lane publishes product-surface-coverage-<sha> as JSON and
Markdown. tests/e2e/product_surface_coverage.py joins the production-derived
capability inventory, ProviderOperationCase, JourneyCase, representative
fault cases, and the existing owned backlog. Do not add a second hand-maintained
capability or journey list to reporting code.
The five evidence axes are contract, journey, faults, browser, and
live. Empty optional cells are reported honestly. Missing production
classifications or a tested capability with no executable evidence fail the
lane; owned gaps, waivers, and live-only rows remain prominent but do not
silently become passing evidence. A harvested live-QA fixture is not current
live evidence unless a stable live result artifact binds back to its typed row.
Scheduled live cells name the exact workflow, job, case id, and result artifact
and remain scheduled until a consumer inspects that result.
Generate the same bird's-eye view locally instead of maintaining a separate capability or journey spreadsheet:
cd tests/e2e
python product_surface_coverage.py \
--json ../../artifacts/product-surface-coverage/matrix.json \
--markdown ../../artifacts/product-surface-coverage/matrix.md
Open artifacts/product-surface-coverage/matrix.md for the human-readable
matrix. In GitHub Actions, download the
product-surface-coverage-<source-commit> artifact from the Reborn E2E run.
External dashboards or Notion pages may summarize or link this report, but the
typed registries and generated matrix remain authoritative.