Skip to content

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

Merged
leynos merged 120 commits into
mainfrom
4-3-2-no-qa-generation-runs-and-tei-p5-retrieval
Aug 22, 2026
Merged

No-QA generation runs and TEI-P5 retrieval (4.3.2)#141
leynos merged 120 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

Summary by Sourcery

Deliver the no-QA source-to-script REST slice with durable generation runs, validated TEI-P5 persistence, authenticated polling, and content-negotiated retrieval.

New Features:

  • Add REST APIs for creating, polling, and listing events for authenticated no-QA generation runs.
  • Generate draft scripts from ingestion sources and bound presenter profiles, persist validated TEI-P5 revisions with skipped-QA provenance, and retrieve them as JSON or downloadable XML.
  • Add bounded in-process generation execution with lifecycle events, classified failures, cost recording, source limits, shutdown handling, and observability.

Bug Fixes:

  • Prevent duplicate episode and source projections during repeated or concurrent ingestion materialization.
  • Preserve idempotent response metadata and scope resources and idempotency keys to authenticated principals.
  • Prevent concurrent TEI overwrites and terminal generation-run mutations through optimistic and conditional persistence.

Enhancements:

  • Add durable SQL persistence for generation runs, append-only events, leases, TEI revision metadata, and ingestion-job ownership.
  • Require production bearer-token authorization and document generation configuration, manual lease recovery, and the no-QA workflow.

Build:

  • Add Alembic migrations for generation-run persistence, TEI revisioning, and ingestion-job ownership.

CI:

  • Harden workflow artifact-server compatibility detection and expand automated validation coverage.

Documentation:

  • Document the no-QA generation and TEI retrieval architecture, API workflow, runtime settings, operational limitations, and completed roadmap item.

Tests:

  • Add unit, integration, property, persistence, lifecycle, authorization, tracing, source-limit, and Vidai Mock behavioural coverage for the generation slice.

@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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 332bd230-ef8f-4944-912e-19640c10ff37

📥 Commits

Reviewing files that changed from the base of the PR and between 8ba2357 and 68ea6f3.

📒 Files selected for processing (11)
  • docs/episodic-podcast-generation-system-design.md
  • docs/execplans/4-3-2-no-qa-generation-runs-and-tei-p5-retrieval.md
  • episodic/api/app.py
  • episodic/api/dependencies.py
  • episodic/api/resources/generation_runs.py
  • episodic/canonical/entity_protocols.py
  • episodic/canonical/generation_persistence.py
  • episodic/canonical/generation_persistence_projection.py
  • episodic/canonical/storage/ingestion_job_repositories.py
  • tests/test_generation_persistence_failures.py
  • tests/test_runtime_metrics_wiring.py
🔗 Linked repositories identified

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

  • leynos/df12-python-lints (auto-detected)
  • 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

Included review availability: 4 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.


Summary

Implement durable no-QA generation runs and TEI-P5 retrieval.

  • Add idempotent run creation, polling, event pagination, ownership checks, lease recovery, bounded admission, metrics, tracing, and shutdown handling.
  • Persist revisioned draft TEI with skipped-QA provenance, content hashes, optimistic concurrency, and run linkage.
  • Generate drafts from ready ingestion jobs, bounded sources, uploads, and presenter profiles.
  • Add JSON and application/tei+xml retrieval with content negotiation, ETags, conditional responses, and attachment metadata.
  • Add bearer-token authorisation and principal-scoped idempotency.
  • Refactor lifecycle updates around GenerationRunStatusUpdate.
  • Add migrations, SQLAlchemy stores, runtime configuration, observability ports, and cost recording.
  • Add unit, integration, property-based, BDD, persistence, concurrency, and lifecycle tests.

Document the implementation in ADR-017 and the completed ExecPlan. Update the developer, user, system design, repository layout, contents, and roadmap documentation.

Walkthrough

Add a no-QA generation-run slice. Define durable run and event storage, draft generation, TEI revision persistence, authenticated REST endpoints, ownership rules, runtime configuration, tracing and metrics, and broad test and document updates.

Changes

No-QA generation slice

Layer / File(s) Summary
Contracts, schema, and persistence
alembic/versions/*, episodic/canonical/..., episodic/cost/*, episodic/orchestration/*
Add generation-run tables, TEI revision fields, owner fields, quality and QA types, optimistic TEI updates, generation persistence services, SQLAlchemy stores, event logging, and cost-recorder contracts.
Draft execution and API surface
episodic/generation/*, episodic/api/resources/*, episodic/api/app.py, episodic/api/runtime*.py, episodic/api/source_idempotency.py, episodic/observability.py
Add the LLM draft generator, in-process launcher, source limits, authenticated generation-run routes, event pagination, TEI retrieval with content negotiation and ETags, ownership checks, observability, and runtime shutdown wiring.
Validation and records
tests/**, docs/**, pyproject.toml, typos*.toml
Add storage, launcher, persistence, API, runtime, authorisation, property-based, and BDD coverage. Update ADRs, guides, roadmap, design documents, and project configuration.

Suggested labels: Roadmap

Poem

A draft run starts with a song,
TEI and events move along.
Owners pass the bearer gate,
Stored revisions keep their state.
Tests trace each success and fall,
Shutdown closes resources all.

Merge Risk: 🟡 Moderate · up to 68ea6

This PR adds durable no-QA generation runs and TEI retrieval, allowing clients to start runs and consume generated documents. Merge readiness is currently moderate because the public run contract rejects values it declares as defaulted and required quality gates are still failing; shutdown failure paths can also leave resources undisposed until these issues are fixed or explicitly accepted.


Caution

Pre-merge checks failed

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

  • Ignore

❌ Failed checks (1 error, 4 warnings)

Check name Status Explanation Resolution
Unit Architecture ❌ Error New InProcessGenerationRunLauncher mixes task admission, database queries, object-store I/O, LLM generation, TEI persistence, cost writes, events, logging, metrics and tracing. Split scheduling, input hydration, generation, lifecycle persistence, cost recording and telemetry into narrow services or ports; keep the launcher as a coordinator.
Developer Documentation ⚠️ Warning Fail this check: the PR adds TracerPort, SpanHandle, NoopTracer and StructuredLogTracer, but the developer guide still documents only two observability ports and omits these abstractions. Document the tracing port, span lifecycle, safe attribute allow-list, production and no-op adapters, and test adapter in the Observability section of docs/developers-guide.md.
Domain Architecture ⚠️ Warning The PR adds direct tei_rapporteur serialization calls to canonical/generation_persistence.py and exposes sqlalchemy.exc.IntegrityError plus get_for_update in canonical ports. Move TEI construction behind a domain-facing port and adapter, replace get_for_update with a domain operation, and translate storage errors before they cross the port.
Performance And Resource Use ⚠️ Warning _claim() calls unbounded resolve_bindings, which loads all bindings, revisions and documents; profile projection and dc.asdict retain and copy JSON without count or byte bounds. Bound profile count and content before loading, query only host and guest bindings with a bounded page, and build prompts without deep copies; add a large-profile regression test.
Concurrency And State ⚠️ Warning materialise_episode_from_ingestion reads sources before locking the job, while attach_source_to_ingestion_job can commit a source concurrently; a ready job can therefore materialise an incomple... Lock or snapshot the source set at the readiness boundary, reject attachments after readiness, or re-read and verify the source set after acquiring the job lock; add a coordinated concurrent test.
✅ Passed checks (15 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the implemented no-QA generation and TEI-P5 retrieval work and includes the referenced roadmap item 4.3.2.
Description check ✅ Passed The description directly explains the implemented APIs, persistence, generation workflow, retrieval behaviour, validation, and test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 90.09% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 676 functions across 97 files. (1 skipped: 1 unsupported.)
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 The PR adds 178 substantive test functions, SQL and in-memory property/locking tests, failure-path assertions, and seven collected REST BDD scenarios; changed snapshots contain concrete TEI and dom...
User-Facing Documentation ✅ Passed The user's guide documents the new no-QA workflow, routes, required payload and headers, polling/event pagination, source and output limits, TEI formats, ETags, authentication, ownership, and migra...
Module-Level Documentation ✅ Passed Keep the documentation. Static inspection found module-level docstrings in all 521 Python modules, including all 109 changed modules; new modules describe their purpose and component boundaries.
Testing (Unit And Behavioural) ✅ Passed Added unit tests cover validation, failures, limits and invariants; SQL tests cover persistence and concurrency; BDD tests drive the ASGI API, database and live Vidai Mock process.
Testing (Property / Proof) ✅ Passed The PR introduces substantive Hypothesis tests for state-machine, pagination, idempotency, claim, event-sequencing, and optimistic TEI invariants across generated inputs and concurrent states.
Testing (Compile-Time / Ui) ✅ Passed The PR is Python-only, so Rust/TypeScript compile-time testing does not apply. Syrupy snapshots cover deterministic TEI and generation-run metadata, with fixed inputs and semantic assertions.
Observability ✅ Passed Accept this check: route and launcher metrics cover throughput, latency, errors and queue depth with bounded labels; traces span API, storage and async execution, while logs record claim and lifecy...
Security And Privacy ✅ Passed Diff evidence shows production bearer auth, principal ownership checks, validated object keys, bounded source inputs, secret-redacted config, and allow-listed telemetry; test credentials are clearl...
Architectural Complexity And Maintainability ✅ Passed Accept this change: ADR-017 and the ExecPlan define immediate launcher, generator, persistence, and observability seams; static analysis found no new import cycles or dependencies.
Rust Compiler Lint Integrity ✅ Passed The PR diff contains zero Rust or Cargo files, and HEAD contains no Rust source, Cargo manifest, or Rust lint suppressions; the Rust compiler-lint check is therefore inapplicable.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 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.

@lodyai
lodyai Bot force-pushed the 4-3-2-no-qa-generation-runs-and-tei-p5-retrieval branch from 94b189d to e551b3a Compare July 21, 2026 22:04
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review July 22, 2026 19:57

@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, your pull request is larger than the review limit of 150000 diff characters

chatgpt-codex-connector[bot]

This comment was marked as resolved.

codescene-access[bot]

This comment was marked as outdated.

Repository owner deleted a comment from coderabbitai Bot Jul 22, 2026
@leynos

leynos commented Jul 22, 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.

Overall Code Complexity

episodic/canonical/adapters/generation_runs.py:

What lead to degradation?

This module has a mean cyclomatic complexity of 4.18 across 11 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 coderabbitai Bot added the Roadmap label Jul 22, 2026
@leynos

leynos commented Aug 22, 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 in a code block that I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

episodic/canonical/storage/generation_runs.py

Comment on lines +303 to +319

    async def list_events(
        self,
        run_id: uuid.UUID,
        *,
        after_seq: EventSeq | None = None,
        limit: int = 100,
        offset: int = 0,
    ) -> tuple[GenerationEvent, ...]:
        """List events for a run after an optional sequence cursor."""
        if limit < 0 or offset < 0:
            msg = "limit and offset must be non-negative."
            raise ValueError(msg)
        if after_seq is not None and offset != 0:
            msg = "after_seq and offset cannot be combined."
            raise ValueError(msg)
        if await self._get_record(run_id) is None:
            raise RunNotFound(run_id)

❌ New issue: Complex Method
SqlAlchemyGenerationRunStore.list_events has a cyclomatic complexity of 9, threshold = 9

@leynos

leynos commented Aug 22, 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 in a code block that 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

        *,
        after_seq: EventSeq | None = None,
        limit: int = 100,
        offset: int = 0,

❌ New issue: Complex Method
InMemoryGenerationRunStore.list_events has a cyclomatic complexity of 10, threshold = 9

@leynos

leynos commented Aug 22, 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 in a code block that I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

tests/test_source_intake_api.py

Comment on lines +54 to +58

    dependencies = build_api_dependencies(
        session_factory,
        authorization=HeaderPrincipalAuthorization(),
        object_store=object_store,
    )

❌ New issue: Large Method
test_source_intake_upload_job_and_attach_flow has 71 lines, threshold = 70

@leynos

leynos commented Aug 22, 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 in a code block that I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

tests/test_source_intake_api_contract.py

Comment on file

"""Contract tests for source-intake REST error paths and read endpoints."""

import contextlib

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: test_attach_upload_reports_missing_upload,test_attach_upload_reports_not_ready_upload

@coderabbitai

This comment was marked as resolved.

@leynos

leynos commented Aug 22, 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 in a code block that I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

tests/test_source_intake_api_contract_support.py

Comment on lines +91 to +113

async def _create_pending_upload(
    session_factory: async_sessionmaker[AsyncSession],
) -> uuid.UUID:
    """Persist one pending upload for not-ready attach tests."""
    now = dt.datetime.now(dt.UTC)
    upload = Upload(
        id=uuid.uuid4(),
        owner_principal_id="principal-a",
        content_type="text/plain",
        declared_size=1,
        actual_size=None,
        declared_sha256=None,
        content_hash=None,
        storage_key=f"uploads/{uuid.uuid4()}",
        state=UploadState.PENDING,
        metadata={},
        created_at=now,
        updated_at=now,
    )
    async with SqlAlchemyUnitOfWork(session_factory) as uow:
        await uow.uploads.add(upload)
        await uow.commit()
    return upload.id

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: _create_pending_upload,_create_ready_upload

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@leynos

leynos commented Aug 22, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following now been resolved?

episodic/api/runtime.py (1)

254-257: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Close the LLM client when launcher shutdown fails.
Put llm_port.aclose() in a finally block. Line 256 can raise before Line

257 executes. This leaves provider-client connections open while shutdown

continues with the remaining hooks.

Proposed fix
async def shutdown_generation() -> None:
             """Stop generation work before closing its provider client."""
-            await launcher.shutdown()
-            await llm_port.aclose()
+            try:
+                await launcher.shutdown()
+            finally:
+                await llm_port.aclose()
🤖 Detailed instructions

Use a wyvern agent team to verify each finding against the current code and only fix it if needed. Fix any documentation issues using a scribe agent team. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Use a scrutineer agent for execution of tests and summarizing the results.

In @episodic/api/runtime.py around lines 254 - 257, Update shutdown_generation
so llm_port.aclose() runs in a finally block even when launcher.shutdown()
raises, while preserving the existing shutdown order and async behavior.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Current revision =="
git rev-parse HEAD

echo
echo "== Runtime outline =="
ast-grep outline episodic/api/runtime.py --match 'shutdown_generation|create_app_from_env' --view expanded || true

echo
echo "== Shutdown implementation =="
rg -n -A45 -B15 \
  'async def shutdown_generation\b|launcher\.shutdown\(\)|llm_port\.aclose\(\)' \
  episodic/api/runtime.py

echo
echo "== Shutdown lifecycle tests and references =="
rg -n -A90 -B12 \
  'shutdown_generation|launcher.*shutdown|aclose\(|shuts_down_generation|shutdown.*fail|RuntimeError' \
  tests episodic/api -g '*.py'

Length of output: 195307


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Current revision =="
git rev-parse HEAD

echo
echo "== shutdown_generation source =="
rg -n -A24 -B8 'async def shutdown_generation\b' episodic/api/runtime.py

echo
echo "== All launcher/LLM shutdown ordering in runtime =="
rg -n -C4 'launcher\.shutdown\(\)|llm_port\.aclose\(\)|shutdown_hooks' episodic/api/runtime.py

echo
echo "== Runtime wiring test body =="
rg -n -A100 -B8 \
  '^async def test_create_app_from_env_wires_configured_llm_launcher\b' \
  tests/test_env_runtime_wiring.py

echo
echo "== Explicit regression tests for launcher failure plus LLM closure =="
rg -n -C5 \
  'launcher shutdown failed|shutdown.*raises|aclose.*called|close.*launcher|Failing.*Launcher|_Failing.*Launcher' \
  tests/test_env_runtime_wiring.py tests/test_lifespan_hooks.py

Length of output: 8086


@leynos

episodic/api/runtime.py — create_app_from_env.<locals>.shutdown_generation

Treat this finding as resolved at revision da8c3b93cceefe68a64b38769d67799d6deb532b.

Lines 263-268 now use try/finally:

try:
    await launcher.shutdown()
finally:
    await llm_port.aclose()

llm_port.aclose() now runs if launcher.shutdown() raises. The shutdown order remains correct: the launcher shutdown executes first, then the LLM client closes, then the separate database shutdown hook runs through shutdown_hooks.

The current runtime-wiring test covers the normal shutdown path. It does not simulate a launcher.shutdown() failure and assert llm_port.aclose() execution. Add that focused regression test separately if you require direct test proof of the failure path.

You are interacting with an AI system.

Add bounded API request metrics, durable scheduling-failure handling, and
deterministic ingestion-job reservation timestamps. Refactor generation-event
pagination and source-intake test fixtures while extending the associated
contract, terminal-claim, property, and API failure coverage.

Document the public generation-run and runtime contracts and keep the
reflection-based Falcon middleware visible to the dead-code gate.
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 22, 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[bot]

This comment was marked as resolved.

Commit placeholder episode reservation only after source projection and
verification succeed, so a missing upload cannot leave a reusable partial
episode. Inject the API route metrics clock through `ApiDependencies` and
correct launch-failure, persistence-port, and ADR contracts.
codescene-access[bot]

This comment was marked as outdated.

@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 Aug 22, 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.

@leynos

leynos commented Aug 22, 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 in a code block that I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

Code Duplication

tests/test_source_intake_api_contract_support.py:

What lead to degradation?

The module contains 2 functions with similar structure: _create_pending_upload,_create_ready_upload

Why does this problem occur?

Duplicated code often leads to code that's harder to change since the same logical change has to be done in multiple functions. More duplication gives lower code health.

How to fix it?

A certain degree of duplicated code might be acceptable. The problems start when it is the same behavior that is duplicated across the functions in the module, ie. a violation of the Don't Repeat Yourself (DRY) principle. DRY violations lead to code that is changed together in predictable patterns, which is both expensive and risky. DRY violations can be identified using CodeScene's X-Ray analysis to detect clusters of change coupled functions with high code similarity. Read More
Once you have identified the similarities across functions, look to extract and encapsulate the concept that varies into its own function(s). These shared abstractions can then be re-used, which minimizes the amount of duplication and simplifies change.

@leynos

leynos commented Aug 22, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following now been resolved?

episodic/api/resources/generation_runs.py (1)

63-89: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Document the public resource API completely.
Replace the summary-only docstrings on GenerationRunsResource and on_post

with NumPy-style documentation. Define the request inputs, successful response,

error responses, ownership rule, and idempotency requirement.
As per coding guidelines, “Public functions, classes, and methods must have

comprehensive NumPy-style docstrings.” As per path instructions, “Docstrings

must follow the numpy style guide ... for all public interfaces.”

🤖 Detailed instructions

Use a wyvern agent team to verify each finding against the current code and only fix it if needed. Fix any documentation issues using a scribe agent team. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Use a scrutineer agent for execution of tests and summarizing the results.

In @episodic/api/resources/generation_runs.py around lines 63 - 89, Expand the
NumPy-style docstrings for GenerationRunsResource and its public on_post method.
Document the resource’s purpose and ownership rule, and for on_post describe
ingestion_job_id, request and response behavior, successful status/output,
possible error responses, and the idempotency requirement; retain the existing
implementation unchanged.

Sources: Coding guidelines, Path instructions

episodic/canonical/entity_protocols.py (1)

118-126: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Document set_target_episode as a complete persistence contract.
Add NumPy-style Parameters, Returns, and transaction-behaviour documentation

to the port and its SQLAlchemy implementation. State the updated_at ownership

and the zero-row behaviour for an unknown job_id.

  • episodic/canonical/entity_protocols.py#L118-L126: define the caller-facing

contract for job_id, episode_id, updated_at, and transaction ownership.

  • episodic/canonical/storage/ingestion_job_repositories.py#L51-L64: match the

port documentation and state that the SQL UPDATE does not validate rowcount.
As per coding guidelines, “Document public APIs comprehensively.” As per path

instructions, “Docstrings must follow the numpy style guide.”

🤖 Detailed instructions

Use a wyvern agent team to verify each finding against the current code and only fix it if needed. Fix any documentation issues using a scribe agent team. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Use a scrutineer agent for execution of tests and summarizing the results.

In @episodic/canonical/entity_protocols.py around lines 118 - 126, Document
set_target_episode in episodic/canonical/entity_protocols.py lines 118-126 with
NumPy-style Parameters and Returns sections covering job_id, episode_id,
updated_at, and transaction ownership, including that callers provide updated_at
and own transaction boundaries and that unknown job_id updates affect zero rows
without raising. Mirror the same contract in
episodic/canonical/storage/ingestion_job_repositories.py lines 51-64, explicitly
noting that the SQL UPDATE does not validate rowcount.

Sources: Coding guidelines, Path instructions

episodic/canonical/entity_protocols.py (1)

182-183: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Name the concrete persistence exception.
Replace Exception with the specific exception that add_projection propagates

for an unrecognized persistence failure. The generic type prevents callers from

implementing a reliable failure boundary.

#!/bin/bash
set -euo pipefail

ast-grep outline episodic/canonical/storage/repositories.py \
  --items all --type method --match 'add_projection' --view expanded
rg -n -C 8 'async def add_projection|IntegrityError|SourceDocumentProjectionResult' \
  episodic/canonical/storage/repositories.py
🤖 Detailed instructions

Use a wyvern agent team to verify each finding against the current code and only fix it if needed. Fix any documentation issues using a scribe agent team. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Use a scrutineer agent for execution of tests and summarizing the results.

In @episodic/canonical/entity_protocols.py around lines 182 - 183, Update the
exception documentation for add_projection to name the concrete persistence
exception propagated for unrecognized failures instead of the generic Exception
type, using the exception raised by add_projection as the authoritative symbol.

Source: Coding guidelines

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== CURRENT REVISION =="
git rev-parse HEAD

echo
echo "== TARGET MODULE OUTLINE =="
ast-grep outline tests/test_source_intake_api_contract_support.py --view expanded || true

echo
echo "== UPLOAD FIXTURE HELPERS =="
rg -n -A90 -B12 \
  '^(async )?def (_create_pending_upload|_create_ready_upload|_create_upload)\b' \
  tests/test_source_intake_api_contract_support.py

echo
echo "== HELPER CALL SITES =="
rg -n -C3 \
  '\b(_create_pending_upload|_create_ready_upload|_create_upload)\s*\(' \
  tests -g '*.py'

echo
echo "== PR-RELATIVE DIFF FOR TARGET =="
base="$(git merge-base HEAD origin/main 2>/dev/null || true)"
if [ -n "$base" ]; then
  git diff --unified=12 "$base" HEAD -- tests/test_source_intake_api_contract_support.py
else
  echo "No merge base with origin/main is available."
fi

echo
echo "== STRUCTURAL UPLOAD CONSTRUCTION COUNT =="
ast-grep run \
  --pattern 'Upload($$$)' \
  --lang python \
  tests/test_source_intake_api_contract_support.py || true

Length of output: 11185


@leynos

tests/test_source_intake_api_contract_support.py — _create_pending_upload, _create_ready_upload, _create_upload

Suppress this diagnostic. The finding is resolved in the current codegraph at revision 68ea6f3f3ad492ace653158e061f575769c9c027.

_create_pending_upload and _create_ready_upload now preserve only state-specific fixture intent. Both wrappers delegate to _create_upload. _create_upload owns the single Upload(...) construction and the single SqlAlchemyUnitOfWork add-and-commit sequence. _UploadFixtureState carries the varying state, actual size, and content hash values.

Do not apply another refactor. Further extraction would either merge distinct pending and ready fixture contracts or add indirection with no duplicated persistence logic to remove.

Validated this diagnostic against the current PR codegraph at revision `68ea6f3f3ad492ace653158e061f575769c9c027`.

The duplicated upload-fixture implementation no longer exists. `tests/test_source_intake_api_contract_support.py` contains one shared `_create_upload` helper. That helper owns `Upload` construction, unit-of-work persistence, commit, and ID return. `_create_pending_upload` and `_create_ready_upload` are thin semantic wrappers. They only supply their distinct fixture values.

Suppress this diagnostic because further refactoring would not remove duplicated behaviour. It would either hide the pending and ready fixture contracts behind a less specific API or add unnecessary abstraction. Keep the wrappers because their names express the required test state at each call site.

You are interacting with an AI system.

@leynos
leynos merged commit 5af0638 into main Aug 22, 2026
7 checks passed
@leynos
leynos deleted the 4-3-2-no-qa-generation-runs-and-tei-p5-retrieval branch August 22, 2026 22:25
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Current revision =="
git rev-parse HEAD

echo
echo "== Target outlines =="
ast-grep outline episodic/api/resources/generation_runs.py \
  --match 'GenerationRunsResource' --view expanded
ast-grep outline episodic/canonical/entity_protocols.py \
  --match 'IngestionJobRepository|SourceDocumentRepository' --view expanded
ast-grep outline episodic/canonical/storage/ingestion_job_repositories.py \
  --match 'SqlAlchemyIngestionJobRepository' --view expanded
ast-grep outline episodic/canonical/storage/repositories.py \
  --match 'SqlAlchemySourceDocumentRepository' --view expanded

echo
echo "== GenerationRunsResource and on_post =="
rg -n -A230 -B12 \
  '^class GenerationRunsResource\b|^    async def on_post\b' \
  episodic/api/resources/generation_runs.py

echo
echo "== Port contracts =="
rg -n -A100 -B12 \
  '^class IngestionJobRepository\b|^    async def set_target_episode\b|^class SourceDocumentRepository\b|^    async def add_projection\b' \
  episodic/canonical/entity_protocols.py

echo
echo "== SQLAlchemy ingestion-job contract =="
rg -n -A80 -B12 \
  '^class SqlAlchemyIngestionJobRepository\b|^    async def set_target_episode\b' \
  episodic/canonical/storage/ingestion_job_repositories.py

echo
echo "== SQLAlchemy projection exception path =="
rg -n -A130 -B20 \
  '^class SqlAlchemySourceDocumentRepository\b|^    async def add_projection\b|IntegrityError|is_source_document_duplicate_integrity_error' \
  episodic/canonical/storage/repositories.py \
  episodic/canonical/storage/integrity_helpers.py

echo
echo "== Relevant imports =="
sed -n '1,80p' episodic/canonical/entity_protocols.py
sed -n '1,70p' episodic/canonical/storage/repositories.py

Length of output: 68298


@leynos

episodic/api/resources/generation_runs.py — GenerationRunsResource, GenerationRunsResource.on_post
This finding is not fully resolved at revision 68ea6f3f3ad492ace653158e061f575769c9c027.

GenerationRunsResource now documents the purpose and ownership rule. on_post now has Parameters, Raises, and Notes sections. However, the class docstring does not use a NumPy-style section, and on_post has no Returns section. The docstring also does not explicitly state that a successful request returns HTTP 202 Accepted, a serialized generation run, Location, and Retry-After.

episodic/canonical/entity_protocols.py — IngestionJobRepository.set_target_episode
episodic/canonical/storage/ingestion_job_repositories.py — SqlAlchemyIngestionJobRepository.set_target_episode
This finding is not fully resolved.

Both docstrings now document job_id, episode_id, updated_at, transaction ownership, and the zero-row behaviour for an unknown job_id. The SQLAlchemy adapter also states that it does not validate the update rowcount. Both docstrings still lack the requested NumPy-style Returns section. Add Returns with None and state that the operation completes without a return value.

episodic/canonical/entity_protocols.py — SourceDocumentRepository.add_projection
This finding is resolved.

The port now names sqlalchemy.exc.IntegrityError in its Raises section. SqlAlchemySourceDocumentRepository.add_projection confirms the contract: it translates only the recognised deterministic-ID race to DUPLICATE; it propagates other IntegrityError instances unchanged.

  • Complete the two incomplete public documentation contracts.
  • Keep the changes documentation-only.
  • Do not modify runtime behaviour or API signatures.

  • 📌 Create a pull request with these changes

You are interacting with an AI system.

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.

4 participants