Skip to content

Hide AI provider behind CoPlan::Ai facade (COPLAN-24)#120

Merged
HamptonMakes merged 1 commit into
hampton/coplan-24/ai-summariesfrom
hampton/coplan-24/ai-facade
Jun 1, 2026
Merged

Hide AI provider behind CoPlan::Ai facade (COPLAN-24)#120
HamptonMakes merged 1 commit into
hampton/coplan-24/ai-summariesfrom
hampton/coplan-24/ai-facade

Conversation

@HamptonMakes
Copy link
Copy Markdown
Collaborator

Stacked on top of #118.

Addresses Hampton's review comment on SummarizePlanJob: provider info shouldn't leak into a job that just wants "an AI". The job shouldn't have to know about OpenAi vs Anthropic.

What

New CoPlan::Ai facade that wraps AiProviders::OpenAi and exposes CoPlan::Ai::Error. Callers can discard_on CoPlan::Ai::Error without knowing the underlying provider.

- discard_on AiProviders::OpenAi::Error
+ discard_on CoPlan::Ai::Error

- AiProviders::OpenAi.call(system_prompt: ..., user_content: ...)
+ CoPlan::Ai.call(system_prompt: ..., user_content: ...)

What this is NOT

  • Not a config knob. Provider selection is hardcoded to OpenAi inside the facade for now. Adding a configuration.ai_provider setting is speculative — there's no second supported provider in production yet.
  • Not a refactor of AutomatedReviewJob. That job legitimately needs to dispatch between providers because each AutomatedPlanReviewer row carries its own ai_provider + ai_model. The facade is for the common case where a caller doesn't care.

Tests

  • 2 new facade specs (delegation + error wrapping)
  • All existing summarize-job specs updated to stub CoPlan::Ai instead of the provider
  • 885 / 885 green

🤖 Drafted with Amp.

Co-authored-by: Amp amp@ampcode.com

Addresses review feedback on PR #118: provider information was leaking
into SummarizePlanJob, which should be transparent about which provider
actually runs the prompt.

Adds CoPlan::Ai — a thin facade that wraps AiProviders::OpenAi today
and exposes CoPlan::Ai::Error so callers can discard_on without knowing
the underlying provider. SummarizePlanJob now calls CoPlan::Ai.call and
discards CoPlan::Ai::Error.

AutomatedReviewJob is intentionally left alone — its reviewers configure
their own provider+model per row, so it legitimately needs to dispatch
between providers. The facade is for the common case where a caller
just wants 'an AI'.

Amp-Thread-ID: https://ampcode.com/threads/T-019e84ca-1d09-777c-baf1-b1f380ec013c
Co-authored-by: Amp <amp@ampcode.com>
@HamptonMakes HamptonMakes merged commit a372429 into hampton/coplan-24/ai-summaries Jun 1, 2026
4 checks passed
@HamptonMakes HamptonMakes deleted the hampton/coplan-24/ai-facade branch June 1, 2026 20:51
HamptonMakes added a commit that referenced this pull request Jun 1, 2026
* Add AI-generated summaries for plans (COPLAN-24)

Every plan now gets a 1-2 sentence AI summary that regenerates whenever
the plan content changes.

Schema:
- coplan_plans gains summary (text), summary_generated_at (datetime),
  and summary_content_sha256 (string) columns. The sha column is the
  debounce key: jobs no-op when the plan's current content sha already
  matches it.

Job:
- SummarizePlanJob is enqueued from PlanVersion#after_create_commit with
  a 10s wait. Multiple jobs collapsing onto the same plan all sha-check
  and only one calls the AI provider.
- The persist step reloads under a row lock and re-verifies the sha so a
  slow job started against revision N cannot overwrite a fresher summary
  produced against revision N+1.
- Plan.find_by + early return so a deleted-mid-flight plan no-ops instead
  of crashing.

Prompt:
- engine/prompts/summarize.md — baked into the engine; uses the existing
  AiProviders::OpenAi hook. No new configuration knob.

Frontend rendering is out of scope here — handled in the cards workstream.

Amp-Thread-ID: https://ampcode.com/threads/T-019e84ca-1d09-777c-baf1-b1f380ec013c
Co-authored-by: Amp <amp@ampcode.com>

* Hide AI provider behind CoPlan::Ai facade (COPLAN-24) (#120)

Addresses review feedback on PR #118: provider information was leaking
into SummarizePlanJob, which should be transparent about which provider
actually runs the prompt.

Adds CoPlan::Ai — a thin facade that wraps AiProviders::OpenAi today
and exposes CoPlan::Ai::Error so callers can discard_on without knowing
the underlying provider. SummarizePlanJob now calls CoPlan::Ai.call and
discards CoPlan::Ai::Error.

AutomatedReviewJob is intentionally left alone — its reviewers configure
their own provider+model per row, so it legitimately needs to dispatch
between providers. The facade is for the common case where a caller
just wants 'an AI'.

Amp-Thread-ID: https://ampcode.com/threads/T-019e84ca-1d09-777c-baf1-b1f380ec013c

Co-authored-by: Amp <amp@ampcode.com>

* Atomically claim sha before AI call to prevent duplicate requests

Addresses Codex review on PR #118: two SolidQueue workers waking up
against the same plan-and-sha could both pass the pre-AI sha check
and both call OpenAI before either persisted, burning a duplicate
AI request per burst of edits.

Replace the with_lock pre-check with a single conditional UPDATE:

    UPDATE coplan_plans
       SET summary_content_sha256 = current_sha
     WHERE id = plan.id
       AND (summary_content_sha256 IS NULL
            OR summary_content_sha256 != current_sha)

The DB serializes the UPDATE, so exactly one worker wins the claim
per sha — the rest get 0 rows updated and no-op. No row locks held
during the AI call.

persist_summary is rewritten as a conditional update too: it only
writes summary/generated_at when our claimed sha is still current,
so a slow job that started against revision N can't stomp a fresher
summary produced against revision N+1.

Trade-off: if the AI call fails (CoPlan::Ai::Error → discarded), the
claim is not released. We won't retry until the next PlanVersion lands.
That's intentional — better than retry storms on a broken prompt.

Amp-Thread-ID: https://ampcode.com/threads/T-019e84ca-1d09-777c-baf1-b1f380ec013c
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant