Skip to content

No-QA generation runs and TEI-P5 retrieval (4.3.2) - #141

Open
leynos wants to merge 36 commits into
mainfrom
4-3-2-no-qa-generation-runs-and-tei-p5-retrieval
Open

No-QA generation runs and TEI-P5 retrieval (4.3.2)#141
leynos wants to merge 36 commits into
mainfrom
4-3-2-no-qa-generation-runs-and-tei-p5-retrieval

Conversation

@leynos

@leynos leynos commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary

This implementation completes roadmap task 4.3.2 — No-QA generation runs
and TEI-P5 retrieval
, the second half of the source-to-script REST vertical
slice defined in ADR 009.

The design and build phases are complete. The living ExecPlan records all
milestone decisions, discoveries, progress, and validation evidence:

docs/execplans/4-3-2-no-qa-generation-runs-and-tei-p5-retrieval.md

Implemented behaviour

An integration client can now:

  1. Create an idempotent run with quality_mode=draft_without_qa, a rationale,
    and actor metadata.
  2. Poll durable run state and the append-only event log until terminal.
  3. Generate from ingestion sources plus bound host and guest profiles.
  4. Persist validated, revisioned canonical TEI with skipped-QA provenance.
  5. Retrieve a JSON TEI envelope or download application/tei+xml with an ETag
    and attachment metadata.

Identical idempotent replays preserve the run id, Location, and
Retry-After; changed bodies conflict. Provider and TEI failures become
classified terminal run state.

Architecture

  • GenerationRunLauncher isolates scheduling from durable execution state; the
    first adapter runs in-process with bounded concurrency and shutdown draining.
  • Generation runs, events, claims, leases, error categories, and TEI revisions
    persist through SQLAlchemy adapters and Alembic migrations.
  • DraftScriptGenerator isolates the single-pass LLM draft policy from the
    launcher and its future roadmap 4.4.1 successor.
  • ADR 016 records execution, episode materialization, optimistic persistence,
    recovery hooks, HTTP status choices, and content negotiation.

Validation

  • make check-fmt
  • make typecheck
  • make lint — Pylint 10.00/10
  • make check-migrations
  • make test — 1,076 passed, 1 skipped
  • make markdownlint
  • make nixie
  • Vidai Mock behavioural slice — 7 passed with vidaimock 0.1.3
  • CodeRabbit milestone reviews — zero findings after each reviewed milestone

References

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary

Implements roadmap task 4.3.2: no-QA source-to-script REST workflows with durable, idempotent generation runs and TEI-P5 retrieval.

Highlights

  • Added durable generation-run and append-only event persistence, migrations, lifecycle transitions, execution claims, recovery handling and bounded in-process concurrency.
  • Added draft script generation from ingestion sources and presenter profiles, with validated TEI-P5 output, SHA-256 content hashes, revision tracking and skipped-QA provenance.
  • Added REST endpoints for creating, polling and inspecting generation runs, including idempotency replay, Location, Retry-After and structured error responses.
  • Added TEI retrieval as JSON or application/tei+xml, including content negotiation, ETags and attachment metadata.
  • Added optimistic TEI revision updates and cost-recording integration.
  • Consolidated lifecycle status updates through GenerationRunStatusUpdate and centralised mutable-run validation/logging.
  • Updated runtime wiring, exports, documentation and roadmap status.

Documentation

Testing

Added unit, persistence, API, launcher and Vidai Mock behavioural coverage, including seven end-to-end scenarios. Validation reports 1,076 passing tests, one skipped test and seven passing Vidai Mock scenarios.

Walkthrough

Adds durable generation-run and event persistence, optimistic TEI revisioning, validated no-QA LLM draft generation, asynchronous launcher orchestration, cost recording, REST retrieval, and acceptance-test coverage.

Changes

No-QA generation slice

Layer / File(s) Summary
End-to-end generation contracts and persistence
alembic/versions/*, episodic/canonical/*, episodic/canonical/storage/*
Adds QA and TEI revision metadata, generation-run/event schemas, guarded repositories, idempotency handling, ordered events, and unit-of-work wiring.
Draft generation and launcher execution
episodic/generation/*, episodic/canonical/generation_persistence.py, episodic/api/runtime.py
Validates LLM JSON, emits TEI-P5, materialises episodes, persists drafts, claims runs, records events and costs, classifies failures, and manages shutdown.
REST endpoints and response replay
episodic/api/app.py, episodic/api/resources/*, episodic/api/serializers.py, episodic/api/source_idempotency.py
Registers generation-run and TEI routes, handles polling and content negotiation, serialises run/event/TEI data, and replays Location and Retry-After.
Adapter refactoring and validation
episodic/canonical/adapters/*, tests/*
Moves checkpoint transitions into a mixin, centralises mutable-run checks, updates status contracts, and adds storage, launcher, API, and end-to-end tests.
Design and operational documentation
docs/*, charts/episodic/README.md, pyproject.toml, typos.toml
Documents ADR-016 and the completed roadmap slice, updates repository guidance, and adjusts architecture and spelling configuration.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GenerationRunsResource
  participant InProcessGenerationRunLauncher
  participant SqlAlchemyGenerationRunStore
  participant LLMDraftScriptGenerator
  participant SqlAlchemyEpisodeRepository
  Client->>GenerationRunsResource: POST draft_without_qa request
  GenerationRunsResource->>SqlAlchemyGenerationRunStore: create pending run
  GenerationRunsResource->>InProcessGenerationRunLauncher: launch run
  InProcessGenerationRunLauncher->>SqlAlchemyGenerationRunStore: claim pending run
  InProcessGenerationRunLauncher->>LLMDraftScriptGenerator: generate draft
  InProcessGenerationRunLauncher->>SqlAlchemyEpisodeRepository: persist TEI with revision precondition
  InProcessGenerationRunLauncher->>SqlAlchemyGenerationRunStore: append lifecycle events and update status
  Client->>GenerationRunsResource: poll run and fetch TEI
Loading

Suggested labels: Roadmap

Poem

Runs wake softly, events align,
Drafted words become TEI fine.
Revisions guard each careful page,
Cost and status mark the stage.
A launcher hums, then shuts down bright.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 4 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Unit Architecture ❌ Error Generation-run POST still hard-codes dt.datetime.now(dt.UTC) and uuid.uuid7() in the resource, so a command path depends on ambient time/randomness instead of injected seams. Inject a clock and run-id factory (or move timestamps/IDs into a service boundary) and thread them through _create_run and _mark_launch_failed.
Testing (Property / Proof) ⚠️ Warning Only the generation-run store got Hypothesis coverage; the new TEI revision, persistence, and launcher transition invariants are still example-based. Add Hypothesis property tests, or CrossHair proofs, for concurrent claim, idempotent replay, revision-guarded TEI updates, and event/page ordering.
Observability ⚠️ Warning Launcher adds bounded metrics and some logs, but no tracing spans exist, and the core claim transition lacks success/failure logging. Add spans at the API→UoW and launcher task boundaries, and log claim_run_for_execution outcomes with run_id and status/error category.
Performance And Resource Use ⚠️ Warning InProcessGenerationRunLauncher spawns an unbounded task per launch, and materialise_episode_from_ingestion holds a DB lock while paging and inserting every source. Bound launches with queue/backpressure, and shorten the ingestion-job lock to claim the target episode id before paged source processing; add a load test.
Concurrency And State ⚠️ Warning SQL claim is only tested sequentially; the durable adapter lacks a race test, and EpisodeRevisionConflictError still falls into the launcher’s generic 'unexpected' path. Add a two-session claim-race test for the SQL store, classify episode revision/not-found conflicts in launcher failures, and shutdown before drain in test teardown.
Developer Documentation ❓ Inconclusive placeholder Need evidence.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the PR scope and includes the roadmap item reference 4.3.2.
Description check ✅ Passed The description clearly describes the no-QA generation-run and TEI-P5 retrieval changes.
Docstring Coverage ✅ Passed Docstring coverage is 89.29% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Testing (Overall) ✅ Passed PASS: The new suite exercises real API, storage, launcher, TEI, and BDD flows with concrete state, header, event, content, idempotency, and concurrency checks.
User-Facing Documentation ✅ Passed docs/users-guide.md adds a dedicated no-QA draft section covering creation, polling, TEI retrieval, idempotency, and QA bypass for end users.
Module-Level Documentation ✅ Passed Every touched module begins with a top-level docstring, including the new resources, storage, generation and test-support modules.
Testing (Unit And Behavioural) ✅ Passed PASS: Unit tests cover invariants and error paths; integration/BDD tests hit the Falcon app via ASGITransport and Vidai Mock, exercising real REST and persistence boundaries.
Testing (Compile-Time / Ui) ✅ Passed PASS: No Rust/TypeScript compile-time surface exists; the PR includes focused snapshot tests for stable TEI XML and domain repr/error messages with semantic assertions.
Domain Architecture ✅ Passed Core domain modules only use stdlib and domain imports; SQLAlchemy, Falcon, object stores, and HTTP headers stay in adapter/service layers.
Security And Privacy ✅ Passed Auth middleware still wraps all /v1 routes, inputs are parsed/validated, object-store keys are confined, and no real secrets or raw provider/TEI data leak into logs or docs.
Architectural Complexity And Maintainability ✅ Passed PASS: The new launcher, TEI update and cost-recording ports are explicit, locally owned seams with immediate consumers, clear ADR-documented reuse paths, and no cycle-prone registry layer.
Rust Compiler Lint Integrity ✅ Passed No Rust sources or Cargo files changed in this PR, so the lint-integrity check is not applicable.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 4-3-2-no-qa-generation-runs-and-tei-p5-retrieval

Comment @coderabbitai help to get the list of available commands.

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the 4-3-2-no-qa-generation-runs-and-tei-p5-retrieval branch from c5422af to 0d648a9 Compare June 15, 2026 20:18
@lodyai lodyai Bot changed the title (4.3.2) No-QA generation runs and TEI-P5 retrieval (4.3.2) No-QA generation runs and TEI-P5 retrieval execplan Jun 15, 2026
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos changed the title (4.3.2) No-QA generation runs and TEI-P5 retrieval execplan No-QA generation runs and TEI-P5 retrieval execplan (4.3.2) Jun 15, 2026
@leynos leynos changed the title No-QA generation runs and TEI-P5 retrieval execplan (4.3.2) Plan: No-QA generation runs and TEI-P5 retrieval (4.3.2) Jun 15, 2026
@lodyai lodyai Bot changed the title Plan: No-QA generation runs and TEI-P5 retrieval (4.3.2) No-QA generation runs and TEI-P5 retrieval (4.3.2) Jun 24, 2026
codescene-delta-analysis[bot]

This comment was marked as resolved.

@leynos

leynos commented Jun 25, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response.

Excess Number of Function Arguments

tests/test_generation_run_port_contract.py: NoopGenerationRunPort.update_run_status has 6 arguments, max arguments = 4
episodic/canonical/adapters/generation_runs.py: InMemoryGenerationRunStore.update_run_status has 6 arguments, max arguments = 4
episodic/canonical/generation_run_ports.py: GenerationRunRepository.update_run_status has 6 arguments, max arguments = 4
episodic/canonical/storage/generation_runs.py: SqlAlchemyGenerationRunStore.update_run_status has 6 arguments, max arguments = 4

Why does this problem occur?

Functions with many arguments indicate either a) low cohesion where the function has too many responsibilities, or b) a missing abstraction that encapsulates those arguments.
The threshold for the Python language is 4 function arguments.

How to fix it?

Start by investigating the responsibilities of the function. Make sure it doesn't do too many things, in which case it should be split into smaller and more cohesive functions. Consider the refactoring INTRODUCE PARAMETER OBJECT to encapsulate arguments that refer to the same logical concept.

Overall Code Complexity

episodic/canonical/adapters/generation_runs.py:

What lead to degradation?

This module has a mean cyclomatic complexity of 4.10 across 10 functions. The mean complexity threshold is 4

Why does this problem occur?

Overall Code Complexity is measured by the mean cyclomatic complexity across all functions in the file. The lower the number, the better.
Cyclomatic complexity is a function level metric that measures the number of logical branches (if-else, loops, etc.). Cyclomatic complexity is a rough complexity measure, but useful as a way of estimating the minimum number of unit tests you would need. As such, prefer functions with low cyclomatic complexity (2-3 branches).

How to fix it?

You address the overall cyclomatic complexity by a) modularizing the code, and b) abstract away the complexity. Let's look at some examples:
Modularizing the Code: Do an X-Ray and inspect the local hotspots. Are there any complex conditional expressions? If yes, then do a DECOMPOSE CONDITIONAL refactoring. Extract the conditional logic into a separate function and put a good name on that function. This clarifies the intent and makes the original function easier to read. Repeat until all complex conditional expressions have been simplified.

@coderabbitai

This comment was marked as resolved.

@lodyai
lodyai Bot force-pushed the 4-3-2-no-qa-generation-runs-and-tei-p5-retrieval branch from 34d6f58 to a3d64bb Compare June 25, 2026 22:59
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Jun 26, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph.

If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

episodic/canonical/adapters/generation_runs.py

Comment on file

from episodic.canonical.domain import (
    Checkpoint,
    CheckpointResponse,

❌ New issue: Overall Code Complexity
This module has a mean cyclomatic complexity of 4.18 across 11 functions. The mean complexity threshold is 4

@coderabbitai

This comment was marked as resolved.

leynos and others added 11 commits July 21, 2026 23:35
Draft the execution plan for roadmap task 4.3.2, the second half of the
source-to-script REST vertical slice (ADR 009). The plan delivers
`draft_without_qa` generation-run creation, REST polling of the run and
its event log, persistence of the generated script into the canonical
episode TEI with QA marked skipped, and `GET /v1/episodes/{id}/tei` as a
JSON envelope or `application/tei+xml` download.

Key confirmed decisions: in-process async execution behind a launcher
port with durable run/event records (Celery deferred per ADR 007);
episode materialised from the ingestion job; a minimal real
DraftScriptGenerator behind a port (full QA-bypass graph left to 4.4.1).

The plan was stress-tested by a Logisphere community-of-experts design
review and revised accordingly: detached-task unit-of-work isolation,
durable stuck-run hooks and conditional state transitions, idempotency
replay carrying Location/Retry-After, deterministic TEI ids and clock
injection for stable snapshots, explicit cost-recorder wiring, and a
must-run-once Vidai Mock acceptance gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mark the 4.3.2 ExecPlan as in progress, record the branch and PR metadata findings, and update the concrete worktree path. Include Markdown formatter output required by the documentation gate.
Capture the source-to-script REST slice as strict-xfail BDD scenarios. Record the red scaffold evidence and deterministic M0 gate results in the living ExecPlan.
Mark the red behavioural scaffold milestone complete after deterministic gates and CodeRabbit review completed with zero findings.
Introduce `QualityMode` and `QaStatus` for the draft-without-QA
vertical slice, and validate that generation runs record skipped QA
status with a non-empty bypass rationale.

Update generation-run fixtures, snapshots, and architecture-prefix
configuration so the new domain module is covered by the existing gates.
Record the M1 red, green, and deterministic-gate evidence in the
ExecPlan.
Mark the no-QA generation-run metadata milestone complete in the
ExecPlan and record the CodeRabbit rate-limit retry plus zero-finding
review result.
Introduce SQLAlchemy generation-run and event-log tables plus the
matching repository adapter and unit-of-work wiring. The adapter preserves
idempotent run creation, uses an explicit guarded claim operation for
pending-to-running execution, and serializes event sequence allocation by
locking the parent run row.

Split the in-memory checkpoint methods into a focused mixin so the
existing test adapter stays within the project file-size limit while the
run/event surface grows.
Mark the durable generation-run persistence milestone complete after the
full deterministic gates and CodeRabbit review completed without findings.
Add optimistic episode TEI update metadata, storage mapping, and SQL migration support for no-QA generation persistence.

Split the SQLAlchemy episode repository and TEI update tests so the changed modules stay focused while preserving existing episode storage behaviour.
Mark the episode TEI revisioning milestone complete after deterministic gates and a zero-finding CodeRabbit review.
Add a single-pass draft script generator that emits validated TEI-P5 through tei_rapporteur and records deterministic content hashes.

Add application persistence services that materialise episodes from intake sources, project provenance source documents, and write generated no-QA TEI with optimistic episode revisioning.
leynos added 2 commits July 22, 2026 23:12
Resolve series-level host and guest profile revisions when claiming a run and
project them into the draft request. Exercise real presenter document,
revision, and binding setup in the end-to-end behavioural slice.
Record the launcher, persistence, recovery, and content-negotiation decisions
in ADR 016. Update system, user, maintainer, and repository guidance to match
the completed implementation and close roadmap item 4.3.2.
codescene-access[bot]

This comment was marked as outdated.

Move scenario teardown into the support context so asynchronous resources and
the Vidai Mock process retain one lifecycle owner while the step module stays
within the repository's 400-line policy.
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

Load upload-backed source text before draft generation, and retain resolved
presenter context in launcher requests.

Serialize episode materialization through the ingestion job and persist its
target episode so retries converge. Lock generation-run rows before status
mutation so competing terminal transitions preserve terminal immutability.
codescene-access[bot]

This comment was marked as outdated.

Remove superseded progress and retrospective statements from the ExecPlan.
Record the completed correctness review, final gate totals, and confirmed
roadmap completion as the current implementation state.
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

Replace the stale in-progress header and intermediate audit language with the
completed implementation state. Preserve red-state transcripts as historical
evidence and record the final behavioural and correctness-review results.
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

leynos added 3 commits July 22, 2026 23:54
Replace the second-person success wording in the 4.3.2 ExecPlan with
impersonal language, add a brief review-follow-up note for the verified
live fixes and stale findings, and correct the generation-run route
placeholder in the user guide.

The route now matches the implemented  endpoint.
Document the in-memory checkpoint mixin and its public methods with
NumPy-style purpose, usage, return, and error details. Also add the
missing `EpisodeRecord` attribute entries for the TEI revision and
generation-quality fields.
Rephrase the review-follow-up entry to stay concise and impersonal
while preserving the live-fix/stale-findings status.
codescene-access[bot]

This comment was marked as outdated.

leynos added 3 commits July 23, 2026 00:09
Wire the configured LLM launcher, enforce generation eligibility and TEI
invariants, preserve cancellation semantics, and strengthen adapter tests.
Document the current review-follow-up state at commit `42757a3`,
including the valid fixes, stale findings skipped, deliberate
non-changes, and the 2026-07-23 gate evidence.
Append the final CodeRabbit review record to the latest review-follow-up
entry, noting the f8609b4 run on 2026-07-23 and its terminal
review_completed result with zero findings.
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

leynos added 2 commits July 23, 2026 00:38
Extract cohesive validation and test helpers while preserving runtime and
domain behaviour, and add diagnostic context to launcher assertions.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Failed
Enforce advisory code health rules (1 file with Overall Code Complexity)

Our agent can fix these. Install it.

Gates Passed
5 Quality Gates Passed

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
generation_runs.py 1 advisory rule no change Suppress

See analysis details in CodeScene

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No quality gates enabled for this code.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 25

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
episodic/canonical/storage/repositories.py (1)

171-217: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the stale SqlAlchemyIngestionJobRepository copy. Keep the implementation in episodic/canonical/storage/ingestion_job_repositories.py; episodic/canonical/storage/uow.py and episodic/canonical/storage/__init__.py already import that module, so the duplicate in episodic/canonical/storage/repositories.py#L171-L217 only creates drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@episodic/canonical/storage/repositories.py` around lines 171 - 217, Remove
the duplicate SqlAlchemyIngestionJobRepository implementation from
episodic/canonical/storage/repositories.py lines 171-217. Retain the canonical
implementation in episodic/canonical/storage/ingestion_job_repositories.py lines
26-65; no direct change is needed there, and existing imports in uow.py and
__init__.py should continue referencing it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/contents.md`:
- Line 100: Wrap the ADR 016 links to remain within 80 columns while preserving
their titles and targets. Update docs/contents.md lines 100-100 and
docs/developers-guide.md lines 25-25, using reference-style links or another
equivalent wrapped format at both sites.

In `@docs/execplans/4-3-2-no-qa-generation-runs-and-tei-p5-retrieval.md`:
- Around line 1010-1024: Update the embedded feature narrative to use impersonal
wording instead of “As an integration client,” “I want,” and “So that I can.”
Also replace “my rationale” in the scenario outcome with neutral wording while
preserving the intended behavior and meaning.

In `@episodic/api/resources/generation_runs.py`:
- Line 3: Remove the `from __future__ import annotations` statement from the
module so annotations for `UowFactory`, `JsonPayload`, `GenerationRunLauncher`,
and `cabc.Callable` are evaluated normally under Python 3.14.

In `@episodic/api/runtime.py`:
- Around line 79-126: Replace the generic RuntimeError exceptions raised by
_required_setting and _llm_settings with ValueError or a dedicated
configuration-specific exception. Update the handler in _load_runtime_config to
catch that exact configuration error type while preserving the existing warning
and re-raise behavior; do not catch unrelated runtime faults.

In `@episodic/api/serializers.py`:
- Around line 247-259: Update serialize_tei_envelope to import and reference
QualityMode.DRAFT_WITHOUT_QA.value for quality_mode instead of hardcoding
"draft_without_qa". Keep the remaining envelope fields unchanged and align this
serializer with serialize_generation_run.

In `@episodic/canonical/adapters/generation_runs.py`:
- Around line 62-70: Consolidate mutation-failure logging by introducing a
frozen, slotted _RunMutationLogSpec near _require_mutable_run and updating that
method to accept the spec, log operation-specific missing and terminal events
with shared and extra fields, then raise the existing exceptions. Replace the
duplicated try/except handling in update_run_status, claim_run_for_execution,
and append_event with appropriate specs while preserving their current event
names and fields.

In `@episodic/canonical/domain.py`:
- Around line 348-352: Update _require_value so a None provenance value raises
ValueError instead of TypeError, preserving the existing message and validation
flow to remain consistent with the sibling domain-validation helpers.
- Around line 383-386: Update the EpisodeTeiUpdate dataclass decorator to
include slots=True, matching the existing structured-data dataclasses
GenerationRunStatusUpdate and _CheckpointTransitionSpec.

In `@episodic/canonical/generation_persistence.py`:
- Around line 84-121: Review materialise_episode_from_ingestion and
_get_ingestion_job_for_update to narrow the ingestion-job row lock: claim and
persist the target episode identity under the lock, then release it before the
per-source insertion work and remaining materialisation steps. Preserve existing
READY_FOR_GENERATION validation and duplicate-episode behavior while ensuring
concurrent retries cannot claim different episode IDs.
- Around line 46-52: Split the direct DraftScriptPersistenceError raises in the
generation persistence flow into concrete IngestionJobNotReadyError,
MissingAttachedSourcesError, and UploadNotFoundError subclasses, keeping them
under the shared base and naming them with the Error suffix. Give each exception
a structured attribute for its relevant identifier, update the “not ready,” “no
attached sources,” and “upload not found” call sites to raise the appropriate
subtype, and provide a dedicated subtype for the content-hash mismatch as well.
- Around line 295-302: Update _source_content_hash to use structural pattern
matching when extracting and validating source.metadata["content_hash"] instead
of the isinstance() guard, while preserving the existing non-empty string
behavior and fallback order.
- Around line 32-33: Update the Clock and UuidFactory type aliases to use the
Python 3.14 PEP 695 type statement instead of plain assignments, preserving
their existing callable signatures.

In `@episodic/canonical/storage/entity_mappers.py`:
- Around line 140-143: Update the entity mapping around the persisted
tei_content_hash assignment to always derive the hash by calling
sha256_text(episode.tei_xml), removing the fallback to episode.tei_content_hash.
Add a test using a deliberately stale supplied hash and verify the persisted
value matches the current TEI XML.

In `@episodic/canonical/storage/entity_models.py`:
- Around line 145-153: Update the episode entity model around qa_status and
last_generation_run_id to persist quality_mode and the skip-QA rationale as
nullable mapped fields, add the corresponding database migration, and expose
both fields through the episode mapper/API; alternatively, revise the documented
and API contract to resolve this provenance exclusively from the linked
generation run.

In `@episodic/canonical/storage/generation_runs.py`:
- Around line 239-276: Update claim_run_for_execution to call _log_event for
both outcomes: after successfully transitioning a pending run to running, and
when the conditional update loses the race and returns None. Include the run
identifier and appropriate claim outcome details, while preserving the existing
terminal and not-found exception behavior.
- Line 22: Update claim_run_for_execution to emit a _log_event after
successfully transitioning the run from pending to running. Include the claim
transition details consistently with the existing create_run, update_run_status,
and append_event lifecycle logs, using the imported _log_event helper.

In `@episodic/generation/launcher_support.py`:
- Around line 303-313: Add explicit entries to _FAILURE_CATEGORIES for
EpisodeRevisionConflictError and EpisodeNotFoundError, mapping them to a
dedicated episode-persistence conflict category and the appropriate
retry/terminal flag. Ensure persist_draft_script failures from
uow.episodes.update are classified as this first-class conflict rather than the
generic unexpected category.

In `@episodic/generation/launcher.py`:
- Line 365: Update the exception handler around RunAlreadyTerminal and
RunNotFound to use the parenthesized multi-exception form, preserving the
existing handler behavior.

In `@tests/canonical_storage/test_episode_tei_updates.py`:
- Around line 136-153: Type the session_factory parameter in all three affected
tests as the quoted async_sessionmaker[AsyncSession] type already supported by
the TYPE_CHECKING imports, and remove the redundant typ.cast assignments. Keep
the existing factory usage unchanged so type checking validates fixture
compatibility at each test call site.

In `@tests/canonical_storage/test_generation_runs.py`:
- Around line 258-292: Add a concurrent SQL-adapter test alongside
test_generation_run_store_claims_pending_run_once that creates one pending run,
opens separate sessions, and invokes claim_run_for_execution for the same run_id
concurrently via asyncio.gather(). Assert exactly one claim returns a result and
the other returns None, while preserving the successful claim’s running-state
assertions; follow the setup and first-writer-wins expectations from
test_claim_run_for_execution_is_first_writer_wins.

In `@tests/generation_run_launcher_support.py`:
- Line 3: Remove the redundant from __future__ import annotations from
tests/generation_run_launcher_support.py at lines 3-3 and
tests/steps/no_qa_generation_slice_support.py at lines 3-3; leave the
TYPE_CHECKING-guarded imports and remaining support-module logic unchanged.

In `@tests/steps/no_qa_generation_slice_support.py`:
- Line 38: Replace the module-level RunResult TypeVar with PEP 695 bracketed
generic syntax on the run function, matching the existing require[RequiredValue]
convention; remove the standalone TypeVar declaration and update run’s generic
annotation accordingly.

In `@tests/test_env_runtime_wiring.py`:
- Around line 104-109: Replace each bare assertion in the specified
integration-test sites with assert …, "message" diagnostics: in
tests/test_env_runtime_wiring.py lines 104-109 cover launcher and cost-recorder
wiring, and lines 160-165 cover captured dependencies and shutdown hooks; in
tests/test_episode_tei_api.py lines 63-83 cover response status, body, and
headers; in tests/test_generation_run_api.py lines 76-89 cover replay, polling,
and event pagination, and lines 128-132 cover conflict and validation responses.
Use concise messages identifying the violated HTTP or wiring contract at every
listed site.
- Around line 355-356: Update the UP037 suppressions on the request and response
annotations in the affected test helper to include an inline justification that
the quoted types remain type-only imports and must not become runtime
dependencies; keep the suppressions narrow and unchanged otherwise.

In `@tests/test_generation_run_port_contract.py`:
- Around line 336-341: In the test assertion block, move the count assertion
before the `claimed = next(...)` assignment so the uniqueness/claim-existence
check runs first. Keep the existing `next(...)` selection and subsequent status,
node, and timestamp assertions unchanged.

---

Outside diff comments:
In `@episodic/canonical/storage/repositories.py`:
- Around line 171-217: Remove the duplicate SqlAlchemyIngestionJobRepository
implementation from episodic/canonical/storage/repositories.py lines 171-217.
Retain the canonical implementation in
episodic/canonical/storage/ingestion_job_repositories.py lines 26-65; no direct
change is needed there, and existing imports in uow.py and __init__.py should
continue referencing it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9479d14a-6d07-478f-9dc6-30ee466a29ca

📥 Commits

Reviewing files that changed from the base of the PR and between 575ee91 and 74b8bd1.

📒 Files selected for processing (70)
  • alembic/versions/20260624_000010_add_generation_run_tables.py
  • alembic/versions/20260624_000011_add_episode_tei_revisioning.py
  • charts/episodic/README.md
  • docs/adr/adr-009-source-to-script-rest-vertical-slice.md
  • docs/adr/adr-016-no-qa-generation-run-execution-and-tei-persistence.md
  • docs/contents.md
  • docs/developers-guide.md
  • docs/episodic-podcast-generation-system-design.md
  • docs/execplans/4-3-2-no-qa-generation-runs-and-tei-p5-retrieval.md
  • docs/repository-layout.md
  • docs/roadmap.md
  • docs/users-guide.md
  • episodic/api/app.py
  • episodic/api/dependencies.py
  • episodic/api/resources/__init__.py
  • episodic/api/resources/episode_tei.py
  • episodic/api/resources/generation_runs.py
  • episodic/api/runtime.py
  • episodic/api/serializers.py
  • episodic/api/source_idempotency.py
  • episodic/canonical/__init__.py
  • episodic/canonical/adapters/generation_checkpoints.py
  • episodic/canonical/adapters/generation_runs.py
  • episodic/canonical/domain.py
  • episodic/canonical/entity_protocols.py
  • episodic/canonical/episode_errors.py
  • episodic/canonical/generation_persistence.py
  • episodic/canonical/generation_quality.py
  • episodic/canonical/generation_run_ports.py
  • episodic/canonical/hashing.py
  • episodic/canonical/storage/__init__.py
  • episodic/canonical/storage/entity_mappers.py
  • episodic/canonical/storage/entity_models.py
  • episodic/canonical/storage/episode_repository.py
  • episodic/canonical/storage/generation_run_models.py
  • episodic/canonical/storage/generation_runs.py
  • episodic/canonical/storage/ingestion_job_repositories.py
  • episodic/canonical/storage/models.py
  • episodic/canonical/storage/models_base.py
  • episodic/canonical/storage/repositories.py
  • episodic/canonical/storage/uow.py
  • episodic/canonical/unit_of_work_protocols.py
  • episodic/generation/__init__.py
  • episodic/generation/draft_script.py
  • episodic/generation/launcher.py
  • episodic/generation/launcher_support.py
  • pyproject.toml
  • tests/__snapshots__/test_draft_script_generation.ambr
  • tests/__snapshots__/test_generation_run_domain.ambr
  • tests/canonical_storage/test_episode_tei_updates.py
  • tests/canonical_storage/test_generation_runs.py
  • tests/features/no_qa_generation_slice.feature
  • tests/generation_run_launcher_support.py
  • tests/steps/generation_orchestration_vidaimock.py
  • tests/steps/no_qa_generation_slice_support.py
  • tests/steps/test_generation_run_lifecycle_steps.py
  • tests/steps/test_no_qa_generation_slice.py
  • tests/test_draft_script_generation.py
  • tests/test_env_runtime_wiring.py
  • tests/test_episode_tei_api.py
  • tests/test_generation_persistence.py
  • tests/test_generation_run_api.py
  • tests/test_generation_run_domain.py
  • tests/test_generation_run_launcher.py
  • tests/test_generation_run_port_contract.py
  • tests/test_generation_run_properties.py
  • tests/test_protocol_stubs.py
  • tests/test_workflow_test_utils.py
  • tests/workflow_test_utils.py
  • typos.toml
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/hecate (auto-detected)
  • leynos/femtologging (auto-detected)
  • leynos/tei-rapporteur (auto-detected)
  • leynos/falcon-correlate (auto-detected)
  • leynos/shared-actions (auto-detected) → reviewed against branch 4-3-2-no-qa-generation-runs-and-tei-p5-retrieval instead of the default branch

Comment thread docs/contents.md
- import-boundary enforcement model.
- [ADR 015: Upload and idempotency ports](adr/adr-015-upload-and-idempotency-ports.md)
- source-intake upload storage and idempotency port decisions.
- [ADR 016: No-QA generation execution and TEI persistence](adr/adr-016-no-qa-generation-run-execution-and-tei-persistence.md)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Wrap the added ADR links at 80 columns.

Use reference-style links or another wrapped form that preserves each ADR title
and target.

  • docs/contents.md#L100-L100: wrap the new ADR 016 index entry.
  • docs/developers-guide.md#L25-L25: wrap the new ADR 016 decision link.

Triage: [type:docstyle]

📍 Affects 2 files
  • docs/contents.md#L100-L100 (this comment)
  • docs/developers-guide.md#L25-L25
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/contents.md` at line 100, Wrap the ADR 016 links to remain within 80
columns while preserving their titles and targets. Update docs/contents.md lines
100-100 and docs/developers-guide.md lines 25-25, using reference-style links or
another equivalent wrapped format at both sites.

Sources: Coding guidelines, Path instructions

Comment on lines +1010 to +1024
As an integration client
I want to generate a draft script without QA and download the TEI
So that I can validate the source-to-script workflow over REST

Background:
Given a Vidai Mock inference server is running
And a series profile exists
And a host presenter profile and a guest presenter profile are bound

Scenario: Draft generation without QA produces a downloadable TEI-P5 script
Given an ingestion job with an attached source document
When I create a draft-without-qa generation run for the ingested episode
Then the run creation responds 202 Accepted with a Location header
And the response carries a Retry-After header
And the run is created with qa_status "skipped" and my rationale recorded

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove first-person wording from the embedded feature.

Replace the As an integration client / I want / So that I can narrative and
my rationale with impersonal wording. This is Markdown, not a .feature
file, so the BDD exception does not apply.

Triage: [type:docstyle]

As per path instructions, “Avoid 2nd person or 1st person pronouns ("I", "you", "we"), exceptions: README.md, BDD .feature files.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/execplans/4-3-2-no-qa-generation-runs-and-tei-p5-retrieval.md` around
lines 1010 - 1024, Update the embedded feature narrative to use impersonal
wording instead of “As an integration client,” “I want,” and “So that I can.”
Also replace “my rationale” in the scenario outcome with neutral wording while
preserving the intended behavior and meaning.

Source: Path instructions

@@ -0,0 +1,349 @@
"""Falcon resources for no-QA generation-run creation and polling."""

from __future__ import annotations

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== file ==\n'
cat -n episodic/api/resources/generation_runs.py

printf '\n== version/config search ==\n'
rg -n --hidden --glob '!**/.git/**' 'python_requires|requires-python|target-version|python_version|from __future__ import annotations|GenerationRunLauncher|UowFactory|JsonPayload|cabc.Callable' pyproject.toml .python-version setup.cfg setup.py tox.ini pytest.ini mypy.ini ruff.toml . 2>/dev/null | sed -n '1,220p'

Repository: leynos/episodic

Length of output: 35425


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cat -n episodic/api/resources/generation_runs.py | sed -n '1,200p'

Repository: leynos/episodic

Length of output: 8432


Remove from __future__ import annotations.
This module targets Python 3.14, so keep UowFactory, JsonPayload, GenerationRunLauncher, and cabc.Callable usable without deferred annotations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@episodic/api/resources/generation_runs.py` at line 3, Remove the `from
__future__ import annotations` statement from the module so annotations for
`UowFactory`, `JsonPayload`, `GenerationRunLauncher`, and `cabc.Callable` are
evaluated normally under Python 3.14.

Source: Coding guidelines

Comment thread episodic/api/runtime.py
Comment on lines +79 to +126
def _required_setting(
environment: cabc.Mapping[str, str],
name: str,
error_message: str,
) -> str:
"""Return a required, non-empty environment setting."""
value = environment.get(name, "").strip()
if not value:
raise RuntimeError(error_message)
return value


def _llm_settings(
environment: cabc.Mapping[str, str],
) -> tuple[str | None, str | None]:
"""Return the optional, paired OpenAI-compatible provider settings."""
base_url = environment.get("OPENAI_BASE_URL", "").strip() or None
api_key = environment.get("OPENAI_API_KEY", "").strip() or None
if (base_url is None) != (api_key is None):
msg = "OPENAI_BASE_URL and OPENAI_API_KEY must be configured together."
raise RuntimeError(msg)
return base_url, api_key


def _load_runtime_config(
environ: cabc.Mapping[str, str] | None = None,
) -> RuntimeConfig:
"""Read and validate runtime configuration from environment variables."""
environment = os.environ if environ is None else environ
database_url = environment.get("DATABASE_URL", "").strip()
if not database_url:
msg = "DATABASE_URL must be set before starting the HTTP service."
raise RuntimeError(msg)
object_store_root = environment.get("SOURCE_INTAKE_OBJECT_STORE_ROOT", "").strip()
if not object_store_root:
database_url = _required_setting(
environment,
"DATABASE_URL",
"DATABASE_URL must be set before starting the HTTP service.",
)
try:
object_store_root = _required_setting(
environment,
"SOURCE_INTAKE_OBJECT_STORE_ROOT",
"SOURCE_INTAKE_OBJECT_STORE_ROOT must be set before starting "
"the HTTP service.",
)
except RuntimeError:
log_warning(
logger,
"runtime_config_missing setting=%s",
"SOURCE_INTAKE_OBJECT_STORE_ROOT",
)
msg = (
"SOURCE_INTAKE_OBJECT_STORE_ROOT must be set before starting "
"the HTTP service."
raise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Raise a configuration-specific error.

Replace these generic RuntimeError paths with ValueError or a dedicated
configuration error, and catch that exact type at Lines 113-126. Preserve
runtime faults as distinct failures.

As per coding guidelines, “Raise specific built-in exceptions or domain-specific exceptions instead of generic Exception or catch-all RuntimeError”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@episodic/api/runtime.py` around lines 79 - 126, Replace the generic
RuntimeError exceptions raised by _required_setting and _llm_settings with
ValueError or a dedicated configuration-specific exception. Update the handler
in _load_runtime_config to catch that exact configuration error type while
preserving the existing warning and re-raise behavior; do not catch unrelated
runtime faults.

Source: Coding guidelines

Comment on lines +247 to +259
def serialize_tei_envelope(episode: CanonicalEpisode) -> dict[str, typ.Any]:
"""Serialize generated TEI metadata using the public field names."""
return {
"episode_id": str(episode.id),
"tei_header_id": str(episode.tei_header_id),
"tei_xml": episode.tei_xml,
"content_hash": episode.tei_content_hash,
"version": episode.tei_revision,
"last_generation_run_id": _optional_uuid_str(episode.last_generation_run_id),
"quality_mode": "draft_without_qa",
"qa_status": None if episode.qa_status is None else episode.qa_status.value,
"updated_at": episode.updated_at.isoformat(),
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reference QualityMode.DRAFT_WITHOUT_QA instead of hardcoding its wire value.

Stop duplicating the enum's string value here. serialize_generation_run correctly reads run.quality_mode.value; make serialize_tei_envelope do the same by importing the enum, so the two serializers cannot drift apart if the wire value ever changes.

♻️ Proposed fix
+from episodic.canonical.generation_quality import QualityMode
+
 def serialize_tei_envelope(episode: CanonicalEpisode) -> dict[str, typ.Any]:
     """Serialize generated TEI metadata using the public field names."""
     return {
         "episode_id": str(episode.id),
         "tei_header_id": str(episode.tei_header_id),
         "tei_xml": episode.tei_xml,
         "content_hash": episode.tei_content_hash,
         "version": episode.tei_revision,
         "last_generation_run_id": _optional_uuid_str(episode.last_generation_run_id),
-        "quality_mode": "draft_without_qa",
+        "quality_mode": QualityMode.DRAFT_WITHOUT_QA.value,
         "qa_status": None if episode.qa_status is None else episode.qa_status.value,
         "updated_at": episode.updated_at.isoformat(),
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def serialize_tei_envelope(episode: CanonicalEpisode) -> dict[str, typ.Any]:
"""Serialize generated TEI metadata using the public field names."""
return {
"episode_id": str(episode.id),
"tei_header_id": str(episode.tei_header_id),
"tei_xml": episode.tei_xml,
"content_hash": episode.tei_content_hash,
"version": episode.tei_revision,
"last_generation_run_id": _optional_uuid_str(episode.last_generation_run_id),
"quality_mode": "draft_without_qa",
"qa_status": None if episode.qa_status is None else episode.qa_status.value,
"updated_at": episode.updated_at.isoformat(),
}
from episodic.canonical.generation_quality import QualityMode
def serialize_tei_envelope(episode: CanonicalEpisode) -> dict[str, typ.Any]:
"""Serialize generated TEI metadata using the public field names."""
return {
"episode_id": str(episode.id),
"tei_header_id": str(episode.tei_header_id),
"tei_xml": episode.tei_xml,
"content_hash": episode.tei_content_hash,
"version": episode.tei_revision,
"last_generation_run_id": _optional_uuid_str(episode.last_generation_run_id),
"quality_mode": QualityMode.DRAFT_WITHOUT_QA.value,
"qa_status": None if episode.qa_status is None else episode.qa_status.value,
"updated_at": episode.updated_at.isoformat(),
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@episodic/api/serializers.py` around lines 247 - 259, Update
serialize_tei_envelope to import and reference
QualityMode.DRAFT_WITHOUT_QA.value for quality_mode instead of hardcoding
"draft_without_qa". Keep the remaining envelope fields unchanged and align this
serializer with serialize_generation_run.

@@ -0,0 +1,313 @@
"""Support fixtures for generation-run launcher tests."""

from __future__ import annotations

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop the redundant from __future__ import annotations from both new test-support modules. Neither file matches the tests/steps/test_*_steps.py retention exception, and the Python 3.14 baseline already defers annotation evaluation by default — exactly the reasoning already applied earlier in this PR to strip the same import from episodic/generation/launcher.py and episodic/generation/launcher_support.py.

  • tests/generation_run_launcher_support.py#L3-L3: delete the from __future__ import annotations line; TYPE_CHECKING-guarded SQLAlchemy imports remain safe since annotations are lazily evaluated.
  • tests/steps/no_qa_generation_slice_support.py#L3-L3: delete the from __future__ import annotations line for the same reason.
📍 Affects 2 files
  • tests/generation_run_launcher_support.py#L3-L3 (this comment)
  • tests/steps/no_qa_generation_slice_support.py#L3-L3
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/generation_run_launcher_support.py` at line 3, Remove the redundant
from __future__ import annotations from tests/generation_run_launcher_support.py
at lines 3-3 and tests/steps/no_qa_generation_slice_support.py at lines 3-3;
leave the TYPE_CHECKING-guarded imports and remaining support-module logic
unchanged.

Source: Coding guidelines

from episodic.api.dependencies import ApiDependencies


RunResult = typ.TypeVar("RunResult")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a bracketed generic instead of a module-level TypeVar.

require in this same file already uses PEP 695 bracket syntax (def require[RequiredValue](...)). Match that convention for run and drop the standalone TypeVar.

♻️ Proposed fix
-RunResult = typ.TypeVar("RunResult")
-
-
 `@dc.dataclass`(slots=True)
 class NoQaGenerationSliceContext:
     ...
-    def run(self, operation: cabc.Awaitable[RunResult]) -> RunResult:
+    def run[RunResult](self, operation: cabc.Awaitable[RunResult]) -> RunResult:
         """Run an asynchronous scenario operation on the shared event loop."""
         return self.runner.run(operation)

Also applies to: 68-70

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/steps/no_qa_generation_slice_support.py` at line 38, Replace the
module-level RunResult TypeVar with PEP 695 bracketed generic syntax on the run
function, matching the existing require[RequiredValue] convention; remove the
standalone TypeVar declaration and update run’s generic annotation accordingly.

Source: Coding guidelines

Comment on lines +104 to +109
assert isinstance(launcher, InProcessGenerationRunLauncher)
assert launcher.cost_recorder_factory is not None
async with SqlAlchemyUnitOfWork(factory) as uow:
recorder = launcher.cost_recorder_factory(uow)
assert isinstance(recorder, CostRecorder)
assert recorder.ledger is uow.cost_ledger

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add diagnostic messages to bare assertions.

Add concise failure messages consistently so failed integration assertions identify
the violated HTTP or wiring contract.

  • tests/test_env_runtime_wiring.py#L104-L109: add messages for launcher and
    cost-recorder assertions.
  • tests/test_env_runtime_wiring.py#L160-L165: add messages for captured
    dependency and shutdown-hook assertions.
  • tests/test_episode_tei_api.py#L63-L83: add messages for status, body, and
    response-header assertions.
  • tests/test_generation_run_api.py#L76-L89: add messages for replay, polling,
    and event-pagination assertions.
  • tests/test_generation_run_api.py#L128-L132: add messages for conflict and
    validation response assertions.

As per coding guidelines and path instructions, use assert …, "message" over bare assertions.

📍 Affects 3 files
  • tests/test_env_runtime_wiring.py#L104-L109 (this comment)
  • tests/test_env_runtime_wiring.py#L160-L165
  • tests/test_episode_tei_api.py#L63-L83
  • tests/test_generation_run_api.py#L76-L89
  • tests/test_generation_run_api.py#L128-L132
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_env_runtime_wiring.py` around lines 104 - 109, Replace each bare
assertion in the specified integration-test sites with assert …, "message"
diagnostics: in tests/test_env_runtime_wiring.py lines 104-109 cover launcher
and cost-recorder wiring, and lines 160-165 cover captured dependencies and
shutdown hooks; in tests/test_episode_tei_api.py lines 63-83 cover response
status, body, and headers; in tests/test_generation_run_api.py lines 76-89 cover
replay, polling, and event pagination, and lines 128-132 cover conflict and
validation responses. Use concise messages identifying the violated HTTP or
wiring contract at every listed site.

Sources: Coding guidelines, Path instructions

Comment on lines +355 to +356
request: "LLMRequest", # noqa: UP037
) -> "LLMResponse": # noqa: UP037

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Justify the lint suppressions inline.

Append a reason to each # noqa: UP037, explaining that the quoted types remain
type-only imports and must not become runtime dependencies.

As per path instructions, “Only narrow in-line disables (# noqa: XYZ) are permitted, must be accompanied by justification and used only as a last resort.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_env_runtime_wiring.py` around lines 355 - 356, Update the UP037
suppressions on the request and response annotations in the affected test helper
to include an inline justification that the quoted types remain type-only
imports and must not become runtime dependencies; keep the suppressions narrow
and unchanged otherwise.

Comment on lines +336 to +341
claimed = next(result for result in results if result is not None)

assert sum(result is not None for result in results) == 1
assert claimed.status is GenerationRunStatus.RUNNING
assert claimed.current_node == "draft"
assert claimed.started_at == NOW

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert before calling next() to avoid masking a real failure with StopIteration.

If both claims ever come back None, next() on the empty generator raises StopIteration before the informative sum(...) == 1 assertion runs. Reorder so the assertion fires first and gives a clear failure message.

🐛 Proposed fix
-        claimed = next(result for result in results if result is not None)
-
-        assert sum(result is not None for result in results) == 1
+        assert sum(result is not None for result in results) == 1
+        claimed = next(result for result in results if result is not None)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
claimed = next(result for result in results if result is not None)
assert sum(result is not None for result in results) == 1
assert claimed.status is GenerationRunStatus.RUNNING
assert claimed.current_node == "draft"
assert claimed.started_at == NOW
assert sum(result is not None for result in results) == 1
claimed = next(result for result in results if result is not None)
assert claimed.status is GenerationRunStatus.RUNNING
assert claimed.current_node == "draft"
assert claimed.started_at == NOW
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_generation_run_port_contract.py` around lines 336 - 341, In the
test assertion block, move the count assertion before the `claimed = next(...)`
assignment so the uniqueness/claim-existence check runs first. Keep the existing
`next(...)` selection and subsequent status, node, and timestamp assertions
unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants