Stage 10: persist efficiency profile as stage artifact
Stage 10 was previously implicit — the per-discipline override sliders in
Stage 11 fed straight into team-shape calculations without leaving an
audit trail. This adds:
- POST /opportunities/{id}/efficiency-profile — saves the active
scenario / blanket pct / discipline overrides / tools applied / notes
as a stage_artifact (type='efficiency_profile').
- GET /opportunities/{id}/efficiency-profile — returns the most recent.
The payload shape is loose by design ({scenario, blanket_pct,
discipline_overrides, tools_applied, notes}) so the Stage 11 UI can
evolve without a migration. The artifact is the audit trail; the live
calculation still runs on the query params passed to GET /team-shape.
Smoke-tested against opp #2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7c5ab130ed
commit
2eb0422e40
1 changed files with 45 additions and 0 deletions
|
|
@ -519,6 +519,51 @@ async def post_support_docs(opportunity_id: int, db: AsyncSession = Depends(get_
|
|||
return await _run_simple_agent(db, opportunity_id, run_support_docs, 13, "support_docs")
|
||||
|
||||
|
||||
@router.post("/{opportunity_id}/efficiency-profile")
|
||||
async def save_efficiency_profile(
|
||||
opportunity_id: int,
|
||||
payload: dict,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Stage 10 — persist the efficiency profile (scenario + per-discipline
|
||||
overrides + tools applied + notes) as a stage_artifact so the
|
||||
team-shape inputs are audited alongside the rest of the pipeline.
|
||||
|
||||
Payload is intentionally loose: {scenario, blanket_pct,
|
||||
discipline_overrides: {discipline: pct}, tools_applied: [str], notes}.
|
||||
"""
|
||||
opp = await _get_opp(db, opportunity_id)
|
||||
artifact = StageArtifact(
|
||||
opportunity_id=opp.id,
|
||||
stage_number=10,
|
||||
artifact_type="efficiency_profile",
|
||||
content_json=payload,
|
||||
)
|
||||
db.add(artifact)
|
||||
await db.commit()
|
||||
await db.refresh(artifact)
|
||||
return {"artifact_id": artifact.id, "saved_at": artifact.created_at, "content": payload}
|
||||
|
||||
|
||||
@router.get("/{opportunity_id}/efficiency-profile")
|
||||
async def get_efficiency_profile(opportunity_id: int, db: AsyncSession = Depends(get_db)):
|
||||
await _get_opp(db, opportunity_id)
|
||||
result = await db.execute(
|
||||
select(StageArtifact)
|
||||
.where(
|
||||
StageArtifact.opportunity_id == opportunity_id,
|
||||
StageArtifact.stage_number == 10,
|
||||
StageArtifact.artifact_type == "efficiency_profile",
|
||||
)
|
||||
.order_by(StageArtifact.created_at.desc())
|
||||
.limit(1)
|
||||
)
|
||||
artifact = result.scalar_one_or_none()
|
||||
if artifact is None:
|
||||
return None
|
||||
return {"artifact_id": artifact.id, "saved_at": artifact.created_at, "content": artifact.content_json}
|
||||
|
||||
|
||||
@router.get("/{opportunity_id}/qualification", response_model=QualificationScorecardOut | None)
|
||||
async def get_qualification(opportunity_id: int, db: AsyncSession = Depends(get_db)):
|
||||
"""Return the most recent qualification scorecard, or null if none saved yet."""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue