Skip to content

feat(noema-agent): add calendar conflict-check tool - #1486

Draft
seonghobae wants to merge 84 commits into
developfrom
claude/noema-contextualwisdomlab-commercialization-afow1j
Draft

feat(noema-agent): add calendar conflict-check tool#1486
seonghobae wants to merge 84 commits into
developfrom
claude/noema-contextualwisdomlab-commercialization-afow1j

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Current exact-head scheduler repair

Head 1709ebb8d79f55c688a141aa932fa00468bf836d, tree 5a39d4eef5a936969b14f649af21638df24a90f1, direct base 042b0c70531b229af3acbd0421a2f23098d848b3. Two ordinary commits preserve the full b32954dbf6066bc0d953887e8ca06820588f2c5f proposal. No force push, predecessor closure, source-file/workflow removal, model-routing change or deployment.

Actual PostgreSQL RED→GREEN covers a held connection lent to a pool reader after task commit, healthy-owner rollback recovery, aborted disconnected sweeps, uncertain acquisition/task cancellation, one-slot cleanup, real competing task inserts at bulk/savepoint boundaries, and mailbox deletion after the first workspace. The scheduler binds its existing session to one physical connection, confirms unlock, invalidates before session cleanup, and forces configuration revalidation. The shared task service refreshes expired mail after conflict rollback and no longer expunges already-detached savepoint inserts. Existing source-task identity and workspace boundaries remain intact. Independent read-only review findings were reproduced and repaired.

Final committed-head verification: 165 passed, 0 failed/errors/skips, 2.49 s, after uv sync --locked, fresh migration and repeat migration to 0022_noema_orchestrator_gateway; clean tree and head identity checked before/after. Scheduler 106/106 statements, 28/28 branches; escalation 153/153 statements, 54/54 branches; zero exclusions, 24/24 function/class/method docstrings. Twelve real-DB cases and twelve scripted unit-only edge cases are explicitly separated. Ruff passes and final runner output contains no Timeout/Fatal/Warn/Denied matches.

cd backend
uv sync --locked
uv run --locked python scripts/migrate_db.py
uv run --locked python scripts/migrate_db.py
uv run --locked coverage run --branch --source=services.reply_sla_scheduler,services.reply_sla_escalation_service -m pytest -q -W error -ra --tb=short tests/test_reply_sla_scheduler.py tests/test_reply_sla_scheduler_postgres.py tests/test_reply_sla_escalation_edges.py tests/test_tasks_api.py tests/test_reply_tracking.py tests/test_reply_tracking_service.py tests/test_db_session.py tests/test_alembic_migrations.py tests/test_bootstrap_db.py
uv run --locked coverage report -m

Use only an isolated PostgreSQL and generated test credentials. JUnit SHA-256 7ab943b0ceffcb26a72c802272cde48f410282c4cfe6e76bee5905725333a69f; coverage JSON SHA-256 207cd65f29b945c2f9f3d40f75bcb081f29da0c4e1744648edd7ebdea24a4c5d. Runtime source SHA-256: scheduler f2968660d77a69226e8acc95bdb92b3a4ed8115f564c8eed5dcdb9375d1930f7; service 7d8bbd424d79dae2cb84df4dd403415ecaa5dee69e973d5236412eaa3922f927.

Doctoring, Proposed ADR supplement, sequence and primary references preserve RED receipts and the failed intermediate test-only import rename; local 7ce6592 is not a GREEN receipt. The dev-only existing Naruon httpx2==2.5.0 pin removes Starlette's deprecated test-client fallback and obsolete ignore; production dependencies unchanged. ADR-0005 and index are corrected to Proposed, because the decision file is absent from protected develop. The existing AGENTS PR #1566 receives the reusable procedures; the Gap ledger stays with #1557.

Still Draft, unmerged, unreleased. Fresh enumeration found queued checks and skipped non-applicable jobs, no current-head reviews. These scoped results do not certify all other changes in this broad PR, repository-wide coverage, representative inbox accuracy, live signed HTTP/browser behavior, transaction-pooling proxies, exactly-once execution, realistic p95/capacity or default-branch security-alert closure. Existing API synthetic/metadata smoke is not migration or live-auth proof. Required checks/review, owner foundation/release adoption and protected merge remain open. Test-owned database/network cleanup was verified without touching other projects.

Preserved original proposal and historical evidence

Description

Noema is used as the org's review agent in ContextualWisdomLab/.github, but naruon needs its own, broader role for Noema rather than a copy of that reviewer role — it should be a genuinely general-purpose workspace assistant. Naruon already has a distinct noema-general-agent (backend/services/noema_agent.py) that reasons over mail, the content graph, and tasks. This PR widens that agent's own domain coverage.

It adds a check_calendar_conflict tool so Noema can judge whether a proposed meeting time double-books an existing commitment (PRD-02 in docs/product-technical-gap-baseline.md — "일정 이동과 RSVP/commitment 충돌을 놓치지 않는다"). Naruon already has a stateless, deterministic, well-tested conflict policy behind POST /api/calendar/conflicts/evaluate (services/calendar_conflict_policy.py::evaluate_calendar_conflicts — status-weighted confirmed > tentative > desired, RFC 5545 STATUS:CANCELLED handling). This PR reuses that exact function as a Noema tool instead of inventing a second conflict policy, so Noema's judgment and the customer-facing API can never diverge.

Naruon does not persist provider calendar events server-side (customer systems stay the source of truth), so the tool never fetches a provider calendar itself — it evaluates whatever commitments the caller (the LLM, after reading mail/task evidence earlier in the same run) already supplies. A malformed existing row is skipped rather than raised, so one bad entry can't block judgment on the rest; the proposed commitment itself is validated strictly and reported as a typed error (reusing CalendarPolicyValidationError's existing error codes).

Also documents, in registered_agents.json, that naruon's Noema (noema-general-agent) is a distinct deployment from the central .github CI review-bot Noema.

2026-09-02 architecture correction (owner-reported): this PR originally routed the agent's LLM calls through the tenant's own directly-configured LLM provider (resolve_runtime_llm_providerAsyncOpenAI with the tenant's base_url/api_key). The owner correctly flagged that this makes naruon a second LLM-provider-routing authority, which conflicts with naruon's ownership boundary: production LLM routing belongs exclusively to ContextualWisdomLab/contextual-orchestrator; naruon owns the Noema tools/authorization/context, not a second routing authority.

Fixed test-first: a RED test proved run_noema_agent reached resolve_runtime_llm_provider/direct AsyncOpenAI construction against the pre-fix code. The fix adds backend/services/orchestrator_gateway.py (resolve_orchestrator_gateway / OrchestratorGateway), which resolves a new, tenant-scoped, SSRF-validated tenant_configs.noema_orchestrator_base_url / noema_orchestrator_token pair (Alembic 0022_noema_orchestrator_gateway, mirroring the existing in-production batch_orchestrator_base_url/batch_orchestrator_token pattern from migration 0012) and always sends a fixed contextual-orchestrator model alias — naruon never picks or fails over across upstream models itself. When the gateway is unconfigured or invalid, noema_agent.py now returns a structured status="unavailable" / error_code="orchestrator_gateway_unavailable" result rather than ever falling back to a direct provider. This tenant-scoped credential stays distinct from .github's CI review credential (naruon workspace data never crosses that path), which is a security-scoping choice for this module, not a claim about the two deployments' overall relationship — see the next correction.

2026-09-02 second correction (owner-reported, superseding the "two separate agents" framing above and in the pre-existing PR text): the "naruon's Noema and .github's review-bot Noema are two separate agents that share only a name" framing (both the sentence in this description and this PR's docs/adr/0006-noema-bounded-context-separation.md-adjacent reasoning) is wrong. Per docs/CWL-MASTER-CONTEXT.md (ContextualWisdomLab/.github), Noema is one shared agent runtime (Pydantic-AI/Codex-Python) consumed by naruon, the .github CI review agent, and wardnet's AI SOC quarantine sandbox — this was the intended design from the start, confirmed directly by the owner, not a naming coincidence. docs/adr/0006-noema-bounded-context-separation.md (naruon#1527) has been updated in place to mark its "therefore keep them permanently separate" conclusion superseded-on-arrival; the code-level investigation of what each deployment currently does remains accurate, and the actual shared-runtime design is a separate, not-yet-settled follow-up. The calendar-conflict tool work in this PR, and the LLM-routing fix above, are unaffected by this correction.

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update (CHANGELOG.md)

Checklist

  • I have performed a self-review of my own code
  • I have added tests that prove my feature works (backend/tests/test_noema_agent.py: 22 passed, 0 skipped, including the real pydantic-ai/AsyncOpenAI build path exercised locally with pydantic-ai-slim[openai]==2.9.0 pinned in backend/requirements-agent.txt)
  • New and existing unit tests pass locally: PYTHONPATH=. python -m pytest backend/tests/test_noema_agent.py -q → 22 passed; full suite PYTHONWARNINGS=error DISABLE_BACKGROUND_WORKERS=1 python -m pytest -q → 1908 passed, 39 skipped; ruff check clean on changed files
  • Any dependent changes have been merged and published in downstream modules — n/a, no dependent changes

Fixes: none (product-gap increment, not a bug fix); tracks docs/product-technical-gap-baseline.md PRD-02 / G-06. Architecture correction responds to the owner's finding on this PR (naruon must not act as a second LLM-provider-routing authority); the second correction responds to the owner's follow-up on the "separate agents" framing (naruon#1527 / docs/CWL-MASTER-CONTEXT.md).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y6UJHYbfbGdHfYPjgbVhAr

Summary by CodeRabbit

  • New Features
    • Added workspace-scoped calendar conflict history, retrieval, and human corrections.
    • Added attachment reparse requests and automatic background reprocessing.
    • Added opaque attachment identifiers for reliable access.
  • Security & Reliability
    • Strengthened workspace isolation across email, attachments, search, tasks, and mail tools.
    • Excluded unparsed attachments from search results.
    • Calendar conflict checks now fail safely when authoritative evidence is unavailable.
    • Improved content-type quarantine handling and background worker reliability.
  • Documentation
    • Documented attachment quarantine behavior and calendar judgment workflows.

Noema is the org's review bot in .github but needs a role suited to
naruon's own workspace instead of a copy of that role. Its existing
naruon agent (services/noema_agent.py) already covers mail/content-graph/
tasks/writeback; add scheduling-conflict judgment (PRD-02) by reusing the
same deterministic, status-weighted policy the /api/calendar/conflicts
endpoint applies, so the agent's judgment never diverges from that API.
Naruon does not persist provider calendar events server-side, so the tool
evaluates whatever commitments the caller already knows about rather than
fetching a provider calendar itself; malformed existing-commitment rows are
skipped instead of raised.
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: efe45288-385b-4e64-9097-25e132119931

📥 Commits

Reviewing files that changed from the base of the PR and between b9b02dd and a4e0119.

📒 Files selected for processing (12)
  • .github/workflows/app-ci.yml
  • .github/workflows/bandit.yml
  • .github/workflows/dependency-review.yml
  • .github/workflows/docker-publish.yml
  • CHANGELOG.md
  • backend/api/data.py
  • backend/services/newsdom_worker.py
  • backend/tests/test_data_api.py
  • backend/tests/test_newsdom_worker.py
  • scripts/ci/pr_governance_gate.sh
  • scripts/ci/test_pr_governance_gate.sh
  • tests/test_stacked_pr_workflow_contract.py
💤 Files with no reviewable changes (4)
  • .github/workflows/docker-publish.yml
  • .github/workflows/app-ci.yml
  • .github/workflows/dependency-review.yml
  • .github/workflows/bandit.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The PR adds workspace-scoped email handling, attachment reparsing, persisted calendar conflict judgments and corrections, a fail-closed Noema conflict tool, and migration, worker, and CI workflow updates.

Changes

Workflow changes

Layer / File(s) Summary
Workspace-scoped email and imports
backend/db/models.py, backend/api/*.py, backend/services/*, backend/import_fixtures.py, backend/scripts/*
Email records, duplicate checks, imports, threading, search, tasks, and synchronization now propagate and enforce workspace_id. Workspace-scoped uniqueness and backfills are added.
Attachment quarantine and reparse
backend/services/attachment_parser.py, backend/api/data.py, backend/services/attachment_reparse_worker.py
Attachment classification distinguishes generic and ZIP-container types. The API records reparse intent. The worker decodes retained payloads and applies fresh parse results.
Calendar judgments and corrections
backend/services/calendar_conflict_policy.py, backend/services/calendar_conflict_judgment_service.py, backend/api/calendar_conflicts.py, backend/alembic/versions/*
Calendar decisions can be persisted, listed, retrieved, corrected, and audited with workspace scoping and typed validation errors.
Agent and worker integration
backend/services/noema_agent.py, backend/services/newsdom_worker.py, backend/main.py, registered_agents.json, task_agent_mapping.json
Noema scopes mail reads by workspace and returns review_required without authoritative provider evidence. Workers use dedicated PostgreSQL lease connections, fresh row fetches, and failure-safe cursors.
Validation and documentation
backend/tests/*, CHANGELOG.md, docs/adr/*, docs/doctoring/*
Tests cover migrations, workspace isolation, attachment parsing, judgment correction, worker behavior, PostgreSQL smoke paths, and CI governance behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Merge Risk: 🟡 Moderate · up to a4e01

This PR adds calendar conflict checking while also changing attachment reprocessing, workspace-scoped email/project-graph handling, fixture imports, and worker transactions. Open issues could mis-scope persisted data, suppress valid imports, bypass required configuration registration, or hold database snapshots during external calls, so merge should wait for fixes or explicit owner acceptance.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main Noema agent change: adding the calendar conflict-check tool. It is concise and directly related to the pull request.
✨ 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 claude/noema-contextualwisdomlab-commercialization-afow1j

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

PR governance metadata gate is not ready for b32954dbf6066bc0d953887e8ca06820588f2c5f:

  • Draft PR: merge automation is paused.
  • Required check strix is CANCELLED on the current head.
  • Required check coverage-source-tree is CANCELLED on the current head.

Copy link
Copy Markdown
Contributor Author

Status on this PR's two failing required checks — neither traces to this PR's diff:

opencode-review: fails because there is no APPROVED/CHANGES_REQUESTED review from opencode-agent at the current head yet (##[error]No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head...). This is the gate correctly failing closed while it waits for the separate async OpenCode dispatch to post a current-head verdict — documented, expected behavior for a freshly-pushed head, not a defect. No action needed; it clears once the dispatch posts, and the central queue scanner retries it.

noema-review: real failure, but in the central vendored contextual-orchestrator review sidecar (ContextualWisdomLab/.github's trusted base), not in this PR's code. New evidence for the already-tracked "orchestrator/free pool" gap (see ContextualWisdomLab/.github docs/product-technical-gap-baseline.md, 2026-08-30 entries): the sidecar correctly vendored the current pin (30c6d71680e659f25a0a433d4726ad0d437f9757, the post-#919 User-Agent/403 fix), but this run hit two further provider-side issues — a ZDR-catalog prefetch got request_failed status=413 code=request_too_large and fell back to a live OpenRouter feed (non-fatal), then bytez model discovery returned http_status_500 during sidecar startup, and the sidecar exited before /healthz. This is the same structural free-pool-exhaustion class already open centrally (OpenRouter's genuinely-free models stay evidence_only by design; nvidia_nim/nvidia_nim_sub/openai/bytez have historically contributed ~0 routable free models), now compounded by bytez itself erroring. Nothing in services/noema_agent.py or this PR's tests is implicated — full local suite passes (1808 passed, 32 skipped) and the new tool's tests pass in isolation (21 passed).

Not fixing the central sidecar/free-pool gap from this PR — it's org-wide infra tracked in the central repo, not scoped to naruon. I've recorded this run's new evidence there. Keeping this PR watched; will re-check once required checks re-run.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

One more failing check, distinct mechanism from the noema-review bytez failure above: strix (using the provider-diverse orchestrator/auto pool, not the free pool) actually got the sidecar healthyhealthz and provider-route preflight confirmed after 34s — but ~30s later a follow-up gateway request to the local sidecar timed out: curl: (28) Operation timed out after 30002 milliseconds with 0 bytes receivedgateway preflight request could not reach the local sidecar. So this isn't the free-pool-exhaustion class at all; it's the sidecar becoming unresponsive to a real request after successfully starting. Also not caused by this PR's diff (same central sidecar). Not root-caused yet — flagging it as a second, separate central-infra failure mode rather than assuming it shares a cause with noema-review's. Tracking both centrally; will follow up once I have a diagnosis for this one too.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Follow-up on the noema-review failure above: investigated properly (the user flagged that a single provider erroring should never fail-close review CI org-wide) and it turned out my earlier read was wrong on two points — Bytez's HTTP 500 never mattered either way (Bytez can never populate orchestrator/free regardless of status), and the actual failure was a live completion-warm-up-probe rejection, not the "no eligible models" path. Real fix (bounded retry on the discovery side, plus visibility for the warm-up-probe path) is in ContextualWisdomLab/contextual-orchestrator#923 and ContextualWisdomLab/.github#1438; corrected write-up is in the gap baseline. Nothing further needed on this PR for that issue — still watching for this PR's own required Checks to clear.


Generated by Claude Code

@seonghobae
seonghobae marked this pull request as ready for review August 30, 2026 11:28
devin-ai-integration[bot]

This comment was marked as resolved.

Devin review on #1486: existing[:_MAX_EXISTING_COMMITMENTS] silently
dropped every commitment past the 500th instead of rejecting the
evaluation, so a real conflict past that boundary could produce a
false "available" decision. Return calendar_existing_batch_exceeded
instead, matching the REST endpoint's own bound.
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

@seonghobae I will review the current pull request changes.

⚠️ Action not completed

Review rate limited.

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.

Closes the missing "human correction" leg of the G-06 killer-workflow
pipeline (thread/sender ontology -> temporal commitment/conflict ->
human correction), whose temporal-conflict half PR #1486 already ships
via check_calendar_conflict. evaluate_calendar_conflicts's decision was
computed and handed back in-memory only, with nowhere for a human to
record disagreement -- there is now a real judgment record and audit
trail, mirroring the existing project_graph_corrections pattern.

- New tables (Alembic 0018, structured ops): calendar_conflict_judgments
  (a persisted decision, status_code proposed/confirmed/overridden/
  dismissed) and calendar_conflict_corrections (before/after JSON audit
  trail per correction, same shape as ProjectGraphCorrectionRecord).
- POST /api/calendar/conflicts/judgments evaluates the same policy as
  /evaluate (now shared via _resolve_commitments) and persists the
  result, optionally tagged with source_thread_id/source_message_id.
- GET /api/calendar/conflicts/judgments lists judgments scoped to the
  caller, optionally filtered by source_thread_id.
- POST /api/calendar/conflicts/judgments/{judgment_uid}/corrections
  records a human override/confirm/dismiss with a full before/after
  snapshot; 404s (not silent no-op) outside the caller's own scope.
- /evaluate's own stateless contract is unchanged.

Verified: targeted tests/test_calendar_conflict_judgment_service.py +
tests/test_calendar_conflict_judgment_api.py (8 passed); full backend
suite python -m pytest -q (1821 passed, 32 skipped, no new skips);
ruff check clean; alembic heads resolves to one head
(0018_calendar_conflict_judgments); test_alembic_migrations.py +
test_email_model_reconciliation.py + test_release_governance.py (58
passed).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y6UJHYbfbGdHfYPjgbVhAr
devin-ai-integration[bot]

This comment was marked as resolved.

github-code-quality[bot]

This comment was marked as resolved.

…on API

- Lock the target judgment row (SELECT ... FOR UPDATE) before snapshotting
  and mutating it in apply_correction, so concurrent corrections can no
  longer read the same prior state and race on the final decision/status.
- When a correction changes decision_code, replace reason_code and
  recommended_action together (corrected_by_human_review + the rationale),
  so a later read can never pair a corrected decision with the previous
  decision's now-stale reason/instruction. The original values are never
  lost -- they are exactly what the correction's before_json captures.
- Bound list_judgments to 200 rows so a long-lived account's history can't
  grow an unbounded query/response.
- Deduplicate MAX_EXISTING_COMMITMENTS: it lived separately in
  api/calendar_conflicts.py and services/noema_agent.py with only a comment
  tying them together. Moved it to services/calendar_conflict_policy.py
  (already a shared import for both) so the two enforcement points cannot
  silently drift apart.
- Fix the double import style of api.calendar_conflicts in
  tests/test_calendar_conflict_judgment_api.py (github-code-quality finding).

Not fixed: a live-PostgreSQL concurrency test proving the row lock under
real concurrent transactions -- this session has no PostgreSQL access, the
same limitation test_project_graph_api.py's own analogous correction tests
already carry (skip-when-unavailable). Left as a PR comment rather than a
fabricated test.

Verified: tests/test_calendar_conflict_judgment_service.py +
tests/test_calendar_conflict_judgment_api.py + tests/test_noema_agent.py +
tests/test_calendar_conflict_api.py + tests/test_calendar_conflict_policy.py
(65 passed); full backend suite python -m pytest -q (1825 passed, 32
skipped, no new skips); ruff check clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y6UJHYbfbGdHfYPjgbVhAr
devin-ai-integration[bot]

This comment was marked as resolved.

…ings

- [security, highest severity] Scope calendar_conflict_judgments/
  corrections by workspace_id, not just user_id+organization_id.
  AuthContext.workspace_id is an independent session-token claim (only
  the test stub derives it from user_id/org for convenience), so the
  same user_id+organization_id can legitimately operate under different
  workspace_id values -- the prior scoping let a caller list/correct
  another workspace's judgments. Follows the same workspace_id scoping
  already established by the project_graph module. Alembic 0018 is
  amended in place (not a new migration) since it has never been
  applied to any database -- this whole feature is still an open,
  unmerged PR.
- Add GET /api/calendar/conflicts/judgments/{judgment_uid}: a judgment
  older than list_judgments' 200-row bound is otherwise unreachable by
  any caller who already knows its uid. Full pagination remains future
  work; single-item retrieval is the small, proportionate fix for "how
  do I get back to a specific judgment."
- Stop storing a correction's rationale as recommended_action. Added
  calendar_conflict_policy.default_recommended_action() as the single
  source of truth for decision_code -> recommended_action (also now
  used by evaluate_calendar_conflicts itself, removing the previous
  inline-string duplication); apply_correction restates recommended_action
  from it instead of the rationale, which explains why a human overrode
  a decision but is not itself forward-looking scheduling guidance.
- Add validate_correction_coherence(): a correction's status_code and
  decision_code must agree (override requires a replacement decision;
  confirm/dismiss must not change one). Enforced both in the API request
  model (fast, specific 422) and in apply_correction (non-HTTP callers).
- Deduplicate the ICS parser's hardcoded 500-commitment bound into the
  same shared MAX_EXISTING_COMMITMENTS constant already used by the API
  and the Noema tool.
- Noema's check_calendar_conflict now reports skipped_existing_count so
  a caller can tell a clean "available" from one computed after silently
  dropping malformed evidence.

Verified: full backend suite python -m pytest -q (1835 passed, 32
skipped -- one unrelated process-group-timing test flaked only under
full-suite concurrency and passed in isolation, confirmed unrelated to
this diff); ruff check clean; alembic heads resolves to one head.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y6UJHYbfbGdHfYPjgbVhAr
devin-ai-integration[bot]

This comment was marked as resolved.

… doc

- apply_correction only replaces reason_code/recommended_action when
  decision_code actually differs from the judgment's current value. An
  override that repeats the current decision is otherwise a no-op on
  the decision itself and must not erase an original, still-accurate
  reason/action for no real change.
- docs/doctoring/status-weighted-calendar-conflicts.md still described
  the calendar-conflict feature as introducing no database objects.
  Updated its shipped boundary, rollback sequence (downgrading 0018 is
  destructive once real corrections exist), and verification-evidence
  section to cover the judgment/correction persistence slice; the
  stateless /evaluate endpoint's own boundary is unchanged.

Verified: new regression test for the no-op-override case; full
backend suite python -m pytest -q (1836 passed, 32 skipped); ruff
check clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y6UJHYbfbGdHfYPjgbVhAr
devin-ai-integration[bot]

This comment was marked as resolved.

claude added 2 commits August 30, 2026 13:48
Devin review on #1486: my own previous doc update said
validate_correction_coherence() rejects a no-op override (one that
repeats the judgment's current decision), but the actual contract is
the opposite -- that case passes coherence validation and is accepted;
apply_correction just skips the reason/action replacement for it.
Reworded to state both facts without conflating them.
…_uid + reparse-intent (G-15)

Sniffs magic bytes (PDF/PNG/JPEG/GIF/ZIP) against the declared/resolved
content_type in services/attachment_parser.py; a mismatch is quarantined
(content_type_mismatch_quarantined) instead of silently parsed or
misclassified, retaining the raw bytes for later re-evaluation. Adds
Attachment.attachment_uid (Alembic 0019, backfilled) so a quarantined
attachment is individually addressable, and POST
/api/data/attachments/{attachment_uid}/reparse-intent to record a
re-evaluation request, matching the existing hwp-conversion-intent /
pdf-dom-recognition-intent pattern (intent recorded, no synchronous
re-parse). Design rationale in docs/adr/0005-attachment-content-type-quarantine.md.

Verification: full backend suite 1842 passed/33 skipped (8 new tests),
ruff clean, alembic heads resolves to a single 0019_attachment_uid head.
devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

…able oversized reparse, error_code convention

Addresses three verified Devin/CodeRabbit findings on naruon#1486:

- DOCX/XLSX/PPTX (and other ZIP-based container formats: ODF, EPUB, JAR)
  were quarantined as content-type mismatches purely because they sniff
  as application/zip, which is what those formats correctly are.
  _is_genuine_content_type_mismatch now excludes a ZIP sniff whose
  declared type is itself a known ZIP-container family (matched by MIME
  substring); a ZIP sniffed under any other declared type is still
  quarantined.
- An oversized mismatched attachment retained no bytes but still got
  content_type_mismatch_quarantined, so the reparse-intent API would
  accept a request for a row with nothing left to act on. It now gets
  the existing parse_size_limit_exceeded status instead, matching every
  other oversized attachment already in this parser.
- apply_correction's status_code/decision_code validation raised
  text-only ValueError; replaced with CalendarConflictUnsupportedValueError
  (mirrors CalendarPolicyValidationError's error_code attribute pattern)
  and mapped by type at the API layer, matching this repo's
  error_code convention. Currently unreachable via the HTTP route
  (Literal-typed request fields already reject bad values), kept as
  defense-in-depth for non-HTTP callers.

Documented, not silently patched: _get_scoped_attachment's lack of
workspace_id scoping is real but pre-existing -- Email itself has never
had a workspace_id column, unlike every workspace-scoped entity added
since. Properly closing it needs an Email migration plus updating every
existing email/attachment query, well outside this PR; recorded in
docs/adr/0005-attachment-content-type-quarantine.md's Consequences as a
tracked follow-up.

Verification: full backend suite 1845 passed/33 skipped, ruff clean.

Copy link
Copy Markdown
Contributor Author

The required strix check failed on this head (dcc9fcd0). Job log: Strix run failed for model 'orchestrator/free' after 5404s (exit code 124) — the scanner itself timed out (~90 min) waiting on the contextual-orchestrator gateway backend, and the workflow's own gate correctly classifies that as STRIX_PROVIDER_UNAVAILABLE (provider/backend unavailable), not a scan finding against this PR's diff.

This is not this PR's failure: it's the same "healthz passes then the real completion request hangs with 0 bytes" gateway-hang signature already tracked as a distinct, open issue in ContextualWisdomLab/.github#1438's PR description, seen there on that PR's own noema-review/strix runs too. No fix exists yet for the underlying gateway hang (it's tracked separately, not something to speculatively patch from here). Since the job died on a backend timeout rather than any test/scan body running against this diff, re-ran the failed job once (rerun_failed_jobs on run 33318142487) to see if it clears on retry; will keep watching until this check is green.


Generated by Claude Code

…ding (G-15)

ADR-0005 deliberately deferred the worker that actually re-evaluates a
quarantined attachment after reparse-intent, mirroring the NewsDOM PDF
worker's own separate-follow-up precedent. This ships that worker.

AttachmentReparseWorker (services/attachment_reparse_worker.py) mirrors
NewsdomRecognitionWorker's shape (jittered loop, PostgreSQL advisory-lock
lease, starvation-free cursor, per-item error isolation), wired into
main.py's lifespan alongside it. Every sweep re-decodes a reparse_pending
attachment's retained bytes and replays parse_email_attachment against
them and the attachment's original declared content_type -- deliberately
not the sniffed type, so the worker carries no bespoke "which type do I
trust" logic and automatically benefits from any future classification
fix (as the already-shipped OOXML carve-out would have, had it existed
first). A row that's no longer a genuine mismatch escapes quarantine into
its ordinary classification; a genuine one lands back in quarantine.

Added decode_quarantined_attachment_payload (a non-PDF-specific sibling
of decode_deferred_attachment_payload) since a quarantined attachment's
sniffed type can be any of the magic-byte families this parser
recognizes, not just PDF. A retained payload that isn't valid base64
moves to a new terminal reparse_payload_invalid status instead of being
swept forever.

Updated ADR-0005 (Consequences + a Revisions entry) and its README index
row, both of which said "reparse_pending has no consumer worker yet."

Verification: 19 new tests (worker: process-level outcomes, sweep
pagination/wrap/isolation, lease honor, start/stop, loop behavior;
parser: the new decoder's round-trip and rejection paths). Full suite
1864 passed/33 skipped (was 1845), ruff clean.
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Fixed the "older requests disappear behind cursors" finding (attachment_reparse_worker.py:346-352). Pushed in b8bccf7c.

Verified this is a live, real path, not just theoretical: POST /attachments/{uid}/reparse-intent and POST /documents/{id}/pdf-dom-recognition-intent (both backend/api/data.py) each explicitly re-mark an arbitrary existing row pending, regardless of its age or position relative to any worker's cursor. A row whose id (or, for documents, (created_at, document_id)) is already behind the forward cursor, reverted this way, was never seen as unresolved by the worker — so it's not in the retry set — and the cursor+retry-id design alone could never rediscover it, matching your description exactly. I didn't find a live trigger for the equivalent case on the NewsDOM attachment sweep specifically (no attachment-level recognition-intent endpoint exists today), but the same design flaw applies there too, so fixed all three sweep methods (this file's, plus NewsdomRecognitionWorker's attachment and document sweeps) symmetrically per your suggested approach.

Fix: every FULL_RESCAN_EVERY_N_SWEEPS-th (20) sweep forces a full rescan by resetting the cursor to None. This is always safe regardless of cadence, since the parse_status/document_status filter already excludes every row that's actually resolved — a full rescan only ever widens the candidate set, it never re-admits a genuinely-resolved row.

Fixing this surfaced a latent bug the change would otherwise have triggered: the query builders restricted results to only retry_ids whenever the cursor was None and retry_ids was non-empty, instead of treating "no cursor" as "no restriction at all" — that combination never arose naturally before (retry_ids starts empty, and only the cursor starts None, on the very first sweep), but the new periodic reset now creates exactly that combination. Fixed by nesting the retry_ids condition inside the "cursor is not None" branch.

Verified test-first in both workers: reproduced each row staying invisible across many sweeps against the pre-fix code, then confirmed it gets rediscovered at exactly the 20th sweep after the fix.

PYTHONPATH=. python -m pytest tests/test_newsdom_worker.py tests/test_attachment_reparse_worker.py -q → 34 passed, 23 passed; full suite PYTHONWARNINGS=error DISABLE_BACKGROUND_WORKERS=1 python -m pytest -q → 1916 passed, 39 skipped; ruff check clean.


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Responding to this round's three Devin Review notes on head b8bccf7c:

"Worker retry paths remain reachable" (newsdom_worker.py:507-602) and "Terminal reparse retains source bytes" (attachment_reparse_worker.py:79-86) — both are confirmatory analysis notes, not findings (no bug flagged, no action requested). No change needed.

"Baseline migration depends on mutable code" (backend/alembic/versions/0001_initial_control_plane.py:10-20) — this one's a real, valid architectural concern (a fresh install running migration 0001 today gets whatever db.models.Base.metadata.create_all() currently produces, not a frozen snapshot, so it can silently diverge from what a long-lived database — 0001 run long ago, then every subsequent incremental migration in order — actually has). But it's out of scope for this PR: this pattern predates this session's work (it's from b9b02dd0, an earlier pass on this same branch, "fix(db): stop 0001's fresh-install migration from crashing" — i.e. a deliberate prior fix for a different problem), this PR doesn't touch that file, and making it properly reproducible (either freezing 0001 to hand-written DDL, or adding a fresh-install-vs-incremental-migration drift test) is a nontrivial, separate change I'd rather not fold into a PR about the Noema calendar-conflict tool and LLM-gateway routing. I attempted to queue it as a follow-up task but the tool timed out twice — flagging it here instead so it isn't lost; happy to pick it up in a dedicated PR if you'd like.


Generated by Claude Code

…-commercialization-afow1j' into claude/attachment-reparse-content-graph-index

# Conflicts:
#	CHANGELOG.md
Merging origin/claude/noema-contextualwisdomlab-commercialization-afow1j
(the cursor/retry-set starvation fix, which added _LiveReparsePendingSession
in tests/test_attachment_reparse_worker.py) into this PR's branch (which
added the session.refresh(attachment, attribute_names=[...]) call in
_sweep_attachments, needed to eager-load relationships before
apply_reparsed_result appends content-graph rows through them) surfaced an
integration gap neither branch could have caught alone: the merged
production code now calls session.refresh() on every sweep, but
_LiveReparsePendingSession (used by two multi-sweep scheduling tests) never
implemented it, since it predates that call.

Add a no-op refresh(), matching the sibling _SequenceSession fake's
pattern. Confirmed via RED (test_sweep_does_not_starve_rows_behind_many_failing_rows
and test_sweep_rediscovers_a_row_reverted_to_pending_behind_the_cursor
both failed with AttributeError before this fix) -> GREEN (full backend
suite: 1920 passed, 43 skipped; ruff clean). One unrelated timing-sensitive
test (test_main_kills_original_process_group_on_timeout) failed once under
full-suite load and passed in isolation and on a second full-suite run --
a pre-existing flake, not caused by this merge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M4GKADWJyd8NToEAK5SH6Q

Copy link
Copy Markdown
Contributor Author

Status update on the governance-gate blocker:

  • "3 unresolved current review thread(s)": all three identified and resolved (two were Devin's own confirmatory Info: notes on the starvation/reparse fixes needing no change; the third, the baseline-migration-depends-on-mutable-code note, got a scope-rationale reply — real observation, but a pre-existing repo-wide pattern unrelated to this PR's actual change, not fixed here).
  • strix CANCELLED: confirmed this is the organization-wide Actions queue saturation currently affecting every required check on this PR (Semgrep, scorecard, osv-scan, dependency-review, trivy-fs, frontend, backend, security, noema-review, CodeQL — all still queued since 03:05 UTC), not a defect in this PR's code. .github's own docs/doctoring/org-queue-sweep-rate-limit-investigation-20260902.md (just merged) independently diagnoses the same-day secondary-rate-limit/queue-saturation incident as concurrent multi-session GitHub API load, unrelated to any one workflow or PR. Attempted rerun-failed-jobs on the cancelled strix job; GitHub rejected it with 403 This workflow is already running (other jobs in the same run are still queued) — nothing further to trigger from this side until the run's own jobs clear.

No code changes needed on this PR beyond what's already pushed (b8bccf7c). Will keep watching for the queue to drain.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Update: the organization-wide Actions queue saturation has drained — nearly every check on this PR just went green (CodeQL, Bandit, Semgrep, scorecard, osv-scan, trivy-fs, dependency-review, frontend/backend/security, all image validations). Two failures remain, both diagnosed as infra-caused, not defects in this PR's own diff:

  • noema-review — failed, not this PR's. Job logs show the job's environment was set up at 11:54:36Z (including minting a short-lived cwl-noema-review[bot] GitHub App installation token), but the actual gh publish-verdict command didn't execute until 13:08:34Z — 74 minutes later, because the job sat queued behind the same organization-wide backlog this PR's strix check hit earlier. GitHub App installation tokens are valid for ~1 hour, so by the time the job actually ran, its own token had expired: gh: Bad credentials (HTTP 401). Nothing in this PR's changed files touches Noema's review workflow or token minting. Re-ran the failed job (rerun-failed-jobs on run 33585697673) now that the queue has cleared — it should mint a fresh token and run to completion this time.
  • strix — cancelled, not this PR's. Sat queued on a floating runner for the same reason from 03:05 UTC before GitHub auto-cancelled it. Also re-ran (rerun-failed-jobs on run 33585697667) now that runners are actually available.

Will keep watching for both to complete.


Generated by Claude Code

…graph (#1501)

Indexes a successfully-reparsed attachment's recognized content into the content graph, matching the existing first-import indexing path. Devin Review clean on exact head d7e5d2d; all 4 required checks (Bandit, Application CI, Dependency Review, Docker build) green after the org-wide Actions runner-starvation backlog cleared. Full backend suite 1920 passed/43 skipped, ruff clean.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 new potential issue.

Devin Review

Comment thread backend/alembic/versions/0011_email_read_state.py

Copy link
Copy Markdown
Contributor Author

Fresh dependency-gate check: the earlier correction away from direct tenant-provider routing is directionally right, but this lane still cannot be treated as production-ready under the current immutable-owner rule.

Current exact head is ff8807afd71cf32877b8acf10d90d750619d41ed. The PR now introduces a Naruon-local OrchestratorGateway, tenant gateway URL/token fields, and a fixed contextual-orchestrator model alias. Fresh owner evidence still shows ContextualWisdomLab/contextual-orchestrator with zero GitHub releases, so neither that alias nor the local client/schema semantics can yet be proven as an immutable released CO contract.

Current acceptance is Naruon #1540 / contextual-orchestrator #1023: preserve Naruon's valid calendar-conflict tool/domain work and fail-closed behavior, but do not make Naruon a second owner of CO API/model/pool semantics. After CO publishes an immutable API/client/schema/runtime identity with exact version/digest/source provenance and auth/failure contracts, this lane should consume that released surface through an ACL, remove/reconcile locally copied gateway semantics, and rerun its exact-head Python 3.14/security/coverage/review evidence. If the dependency is absent or incompatible, Noema remains unavailable/review-required rather than using a direct-provider fallback.

This is an owner-path note only; no write was made to the externally owned branch.

Copy link
Copy Markdown
Contributor Author

Acknowledged and verified against #1540/#1023 (both real, both tracking the same underlying "immature core" concern applied consistently across keyverse#132/TEPP#480/this PR).

Checked this PR's OrchestratorGateway against #1540's "Required Naruon behavior" checklist: it never clones/imports contextual-orchestrator source, never receives or forwards provider credentials, and already fails closed (status="unavailable", error_code="orchestrator_gateway_unavailable") rather than falling back to a direct provider when the gateway is unconfigured or invalid — so item 6 (the hard requirement for this PR's scope) is already met. The gap #1540 correctly identifies — pinning contextual-orchestrator as a bare model alias rather than through an immutable, version/digest-verified release — is real and not yet closeable here, since contextual-orchestrator has zero releases to pin against; premature version-pinning against a mutable head would be worse, not better.

No code change to this PR right now: the remediation path (consume the released API/client/schema via an ACL once CO publishes it, rerun exact-head evidence) is correctly gated on CO's own release, tracked in #1023, not something this PR can do today. Leaving #1540/#1023 as the durable tracking record rather than duplicating it here.


_Generated by Claude Code


Generated by Claude Code

seonghobae pushed a commit to ContextualWisdomLab/.github that referenced this pull request Sep 2, 2026
…ref fix, new security gap G-24)

- Qualify two newly-added bare `naruon#1486` references to
  `ContextualWisdomLab/naruon#1486` per CWL-MASTER-CONTEXT.md's
  cross-repo reference convention (same-repo `.github#1659` already
  complied and was left as-is).
- Record G-24: the owner's verbatim Keyverse Direct Grant/ROPC mandate
  hands raw credentials to product-owned login forms, a real,
  previously-untracked security trade-off. Not fixed by rewording the
  owner's explicit directive text -- recorded as a gap directing a
  future Keyverse-integration ADR to make an explicit decision.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y6UJHYbfbGdHfYPjgbVhAr
Comment thread backend/alembic/versions/0011_email_read_state.py Fixed
Comment thread backend/alembic/versions/0011_email_read_state.py Fixed
Comment thread backend/alembic/versions/0011_email_read_state.py Fixed
Comment thread backend/alembic/versions/0011_email_read_state.py Fixed
@seonghobae
seonghobae marked this pull request as draft September 3, 2026 07:15

Copy link
Copy Markdown
Contributor Author

Lifecycle repair on exact head ff8807afd71cf32877b8acf10d90d750619d41ed: converted this PR back to Draft after re-validating the current architecture against the product owner boundaries.

The calendar-conflict domain slice itself is not being rejected. The blocker is the LLM/runtime integration mixed into this 85-file branch:

  • backend/services/orchestrator_gateway.py creates a Naruon-owned contextual-orchestrator transport/credential abstraction, including tenant base_url/token resolution and ORCHESTRATOR_MODEL_ALIAS = "contextual-orchestrator".
  • backend/services/noema_agent.py still constructs AsyncOpenAI + Pydantic-AI Agent inside Naruon and binds that local runtime to the gateway. That makes Naruon a runtime/client implementation owner rather than a consumer of the canonical Noema runtime-agent capability and contextual-orchestrator released client/schema.
  • Fresh owner reads show zero GitHub Releases in both ContextualWisdomLab/contextual-orchestrator and ContextualWisdomLab/noema. A protected-main implementation or locally invented model alias is not an immutable consumer contract.
  • The branch also introduces Naruon-local noema_orchestrator_base_url / noema_orchestrator_token persistence before the canonical consumer credential/schema contract exists.

Repair direction: keep Naruon-owned calendar policy/tools, authorization, workspace context, and UI/domain truth here; move reusable Noema runtime/client capability to ContextualWisdomLab/noema, and LLM routing/credential/provider/model authority to ContextualWisdomLab/contextual-orchestrator. After those owners publish immutable versioned contracts, consume them through a narrow Naruon ACL/version pin and regenerate exact-head tests/security/review evidence. Do not make this Ready by treating the local OrchestratorGateway as the final contract, by hard-coding another alias, or by transferring protected-main/unreleased owner evidence.

…tate DDL

Semgrep OSS's sqlalchemy-execute-raw-query and formatted-sql-query rules flag
both op.execute() calls in this migration as raw-SQL injection risk. Both are
false positives: _UPGRADE_SQL/_DOWNGRADE_SQL interpolate only the fixed
module-level literal _IS_READ_PROVENANCE_MARKER (already documented in the
module docstring and covered by the existing `# nosec B608` comments) --
never external input or an identifier. Semgrep's pattern only recognizes
"op.execute(f-string)" and can't see the interpolated value is a constant.

No behavior change. Unblocks naruon#1486 (4 open review threads and 2 required
check failures were exactly these 4 Semgrep findings on the same 2 lines).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@seonghobae

seonghobae commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Repair delivered; protected validation remains pending

The prior runtime-reproduction handoff below is historical. The repair is now normally pushed to this existing owner PR at 1709ebb8d79f55c688a141aa932fa00468bf836d, tree 5a39d4eef5a936969b14f649af21638df24a90f1, preserving the valid delta from b32954dbf6066bc0d953887e8ca06820588f2c5f.

The exact-head doctoring record records the RED failures, choices, root repairs, reproduction command, and limitations. One physical connection now survives work commits/rollbacks; unlock must be confirmed, interrupted ownership invalidates the connection, and shared escalation conflict handling refreshes expired source objects without redundantly expunging failed inserts. Configuration is re-read at workspace boundaries.

The unchanged exact head passed 165 tests (0 failures/errors/skips, 2.49s), including 12 actual migrated PostgreSQL cases. The two changed runtime modules have 259/259 statements, 82/82 branches, zero exclusions, 24/24 definition docstrings. JUnit SHA-256: 7ab943b0ceffcb26a72c802272cde48f410282c4cfe6e76bee5905725333a69f; coverage SHA-256: 207cd65f29b945c2f9f3d40f75bcb081f29da0c4e1744648edd7ebdea24a4c5d.

The intermediate test-import error at 7ce6592eadfd6130ba66ef348e5f0050a1411458 is retained as failed history and corrected by forward commit 1709ebb; no GREEN result is attributed to that intermediate head. The test-client collection failure was repaired with the existing owner development dependency contract, not a warning suppression.

This PR remains Draft, open, unmerged, and unreleased. Current-head backend checks and Noema admission were queued at the recorded snapshot. Neither this comment nor local tests replace checks, independent exact-head review, or protected merge. #1317's separate import lease repair is not claimed complete. The public-mail replay does not establish realistic inbox accuracy, live authentication, or latency acceptance.

Original handoff at b32954d — preserved historical evidence

Root-repair handoff at b32954dbf6066bc0d953887e8ca06820588f2c5f; scheduler runtime reproduction remains pending.

_ sync lease flow, lines134–146 acquires and releases a session advisory lock through an ordinary engine-bound session. The called escalation service commits bulk/fallback work and rolls back on integrity conflict before scheduler unlock. Keeping that Python session does not preserve the physical backend across these boundaries.

#1469 reproduced lease leakage after both commit and rollback with an unrelated pooled reader and an independent PostgreSQL replica. For this owner, reproduce with actual migrated escalation writes, retain one physical lease holder across the entire owner/workspace sweep, require confirmed unlock, invalidate uncertainty before session-close rollback, and abort connection loss instead of continuing unleased. Preserve this PR's workspace iteration and other valid deltas.

Cover same-pool contention, independent-replica reacquisition, supported pool capacity, actual task cancellation and cleanup ordering, connection loss, and owner/workspace recovery after healthy rollback. Existing tests/test_reply_sla_scheduler.py fakes and the manual API smoke in tests/test_tasks_api.py do not prove scheduler contention behavior.

Worker-specific RED/GREEN and alternatives are investigation evidence, not copied source or scheduler GREEN. No scheduler source was changed by #1469; this is not approval, protected merge, or an exactly-once external-execution claim.

seonghobae and others added 2 commits September 5, 2026 22:30
Retain all existing PR1486 delta. Bind the sweep session to one physical connection, invalidate before cleanup, recover expired rows and savepoint conflicts, and revalidate mailbox configuration between workspaces. Preserve competing task identity and source workspace scope.

Actual migrated PostgreSQL RED/GREEN covers pool lending, rollback, disconnect, cancellation, concurrent task creation and configuration deletion. Reviewed candidate:165 tests pass; two runtime modules259statements/82branches100%;24/24 docstrings. Exact-head verification follows. Reuse dev-only httpx2 pin and remove obsolete warning ignore; no source/workflow deletion or release claim.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Correct the test-only mechanical rename of db.models and record the failed intermediate collection. The 12 focused edge tests and Ruff pass again; a full exact-head migrated PostgreSQL run is still required before push. No history rewrite.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
await release_lease(replica_session)
cleanup_allowed.set()
with pytest.raises(asyncio.CancelledError):
await sweep_task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

4 participants