Skip to content

Record the provenance producer triple on proposals and expose a board-authorized metadata endpoint - #2494

Merged
Chris0Jeky merged 2 commits into
mainfrom
issue-1987/provenance-triple
Sep 4, 2026
Merged

Record the provenance producer triple on proposals and expose a board-authorized metadata endpoint#2494
Chris0Jeky merged 2 commits into
mainfrom
issue-1987/provenance-triple

Conversation

@Chris0Jeky

Copy link
Copy Markdown
Owner

Summary

Backend half of #1987: record the provenance triple (provider / model / promptVersion) on the proposal itself and expose it through a proposal-scoped, board-authorized endpoint, so the Paper Review provenance footnote and drawer can render server-recorded values instead of nothing.

Before this change the triple existed only on the capture payload. ProposalProvenance stored CorrelationId / ModelId / TotalTokens and nothing else, and GET /automation/proposals/{id}/provenance returned row DTOs only — so PR #2310's frontend path could only reach producer metadata through the owner-only capture-detail endpoint, and Chat-origin proposals and non-owner reviewers got nothing at all.

  • ProposalProvenance gains nullable Provider and PromptVersion (64 chars each, blank normalized to null, over-long rejected at the domain boundary).
  • Migration 20260904030926_AddProposalProvenanceProducerTriple: two additive nullable TEXT columns on ProposalProvenances. No rebuild, no backfill, no data change for existing rows.
  • CreateProposalDto gains ProvenanceProvider / ProvenancePromptVersion as [JsonIgnore] init-only properties — the same trusted-input pattern as TrustedConfidence, so a client cannot label its own proposal as produced by a provider. Only CaptureTriageService sets them, from the producer that actually ran (dispatched provider, or the deterministic extractor on fallback).
  • AutomationProposalService.BuildCreationProvenance stamps them server-side.
  • New ProposalProvenanceMetadataDto + IProvenanceQueryService.GetProvenanceMetadataAsync + GET /automation/proposals/{id}/provenance/metadata, sharing the exact board read authorization and 404 parity of the sibling {id}/provenance route.
  • UPGRADING.md Unreleased entry: BREAKING: none.

The contract fails closed. Provider is null whenever no producer was recorded — legacy rows, and Chat/Manual origins whose ModelId carries only an origin sentinel (chat-tools, manual, queue). Model and PromptVersion are reported only alongside a recorded provider, so an origin sentinel can never be rendered as a model name. A proposal with nothing recorded returns 200 with all-null fields, not an error: "not recorded" is an answer, and the surface renders it as no producer claim at all (#1963 — no claim beats a false claim).

No frontend changes in this PR. The one-line source swap in usePaperReviewSelectors.ts plus its API client and specs is deliberately deferred to a follow-up branch (issue-1987/provenance-frontend) to avoid colliding with the in-flight Review race-state work (#2455, #2457, #2458, #2460, #2461, #2464), which owns that file. That follow-up closes #1987 and #1284 AC4; this PR only makes it possible.

Relationship to #2315

The new endpoint addresses two of the four states tracked there:

  • State 3 (collaborator authorization) — addressed. Producer metadata now comes from a proposal-scoped, board-authorized projection that exposes no capture contents, so a collaborator reviewing another owner's proposal can see what produced it without gaining capture-detail access. A focused test proves the projection never touches the transcript repository.
  • State 4 (spurious capture-detail requests for generic Queue proposals) — addressed at the contract level: producer metadata no longer requires a capture-detail lookup at all. Removing the actual request is part of the frontend follow-up.
  • State 1 (bounded refresh after late stamping) and state 2 (distinguishing lookup failure from genuine absence) stay tracked on [Backend][Frontend][Review] Make proposal provenance metadata lifecycle- and authorization-safe #2315 — they are lifecycle machinery entangled with the Review race-state work and are not touched here.

Test plan

Base becd85fd4; head 5a86e16f3 (branch issue-1987/provenance-triple).

Command Result
dotnet build backend/Taskdeck.sln -c Release succeeded, 0 errors
dotnet ef migrations has-pending-model-changes --startup-project ../Taskdeck.Api/Taskdeck.Api.csproj "No changes have been made to the model since the last migration."
dotnet test backend/tests/Taskdeck.Domain.Tests/Taskdeck.Domain.Tests.csproj -c Release -m:1 --filter "FullyQualifiedName~ProposalProvenanceTests" 20 passed, 0 failed
dotnet test backend/tests/Taskdeck.Application.Tests/Taskdeck.Application.Tests.csproj -c Release -m:1 --filter "FullyQualifiedName~CaptureTriage|FullyQualifiedName~Provenance|FullyQualifiedName~AutomationProposalService" 428 passed, 0 failed
dotnet test backend/tests/Taskdeck.Api.Tests/Taskdeck.Api.Tests.csproj -c Release -m:1 --filter "FullyQualifiedName~GetProposalProvenanceMetadata" 5 passed, 0 failed
dotnet test backend/tests/Taskdeck.Api.Tests/Taskdeck.Api.Tests.csproj -c Release -m:1 --filter "FullyQualifiedName~AutomationProposalsApiTests" 56 passed, 0 failed
dotnet test backend/tests/Taskdeck.Api.Tests/Taskdeck.Api.Tests.csproj -c Release -m:1 --filter "FullyQualifiedName~MigrationBootstrap" 22 passed, 0 failed
dotnet test backend/tests/Taskdeck.Architecture.Tests/Taskdeck.Architecture.Tests.csproj -c Release -m:1 28 passed, 1 skipped, 0 failed
node scripts/check-docs-governance.mjs passed
git diff --check origin/main...HEAD clean

New coverage: blank/over-long/trimmed producer values at the domain boundary; deterministic and live-provider stamping through CreateProposalAsync; Chat-origin leaving the triple unrecorded; origin-sentinel suppression in the projection; owner 200, board-collaborator 200, stranger status equal to the sibling {id}/provenance status with no metadata leaked in the body, and unauthenticated rejection.

Documentation

  • UPGRADING.md — Unreleased entry for the migration (BREAKING: none), per docs/platform/EF_MIGRATION_WORKFLOW.md.
  • docs/STATUS.md — intentionally untouched (coordinator-owned); shipped user-visible reality changes with the frontend follow-up, not with this contract.
  • docs/IMPLEMENTATION_MASTERPLAN.md — no sequencing change.
  • ADR — none: this implements the already-decided review-first provenance model rather than choosing between approaches.

Risks

  • Migration. Two additive nullable columns; Down drops them. Existing rows are untouched and read back as "not recorded". The pre-migration snapshot runs as usual and the copy cost is the normal per-schema-change one.
  • Authorization. The endpoint reuses AuthorizeProposalAsync(requireWriteAccess: false) verbatim and returns no capture, transcript, or card content — only three short recorded strings. Stranger parity with the sibling route is asserted in a test.
  • Client trust. The new CreateProposalDto inputs are [JsonIgnore], so no HTTP caller can set them; the only writer is capture triage. Provenance remains server-stamped.
  • Behavior. No existing endpoint, DTO shape, or response changes. The frontend still renders exactly what it renders today until the follow-up lands.

Refs #1987
Refs #1284
Refs #2315

…enance

Adds nullable Provider/PromptVersion columns, stamps them from trusted capture-triage inputs, and exposes a proposal-scoped, board-authorized metadata endpoint so the Review provenance footnote can render server-recorded values. Refs #1987
…oint authorization

Domain normalization/length rules, deterministic and live-provider stamping, origin-sentinel suppression, and owner/collaborator/stranger parity on the metadata endpoint. Refs #1987
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Review gate (Codex credits exhausted, SC-9): one fresh-context reviewer with an authz/trust/migration lens found the new endpoint byte-identical in gating to {id}/provenance (owner/collaborator 200, stranger parity incl. body-leak assertion, missing id 404), the [JsonIgnore] props unreachable from the single [FromBody] site (System.Text.Json only, no Newtonsoft in src), the migration purely additive nullable, and layering clean. Verdict SHIP. MEDIUM (pre-migration LLM rows with a real ModelId but null Provider now render silent) plus two LOWs (only capture triage stamps the triple; BOM on four test files) tracked in #2499. STATUS.md entry lands in the coordinator's block PR. Merging once CI at this head is green and main's in-progress run completes.

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

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Backend][Frontend] Wire the provenance triple (provider/model/promptVersion) into the deep-review payload so #1963's honest footnote can render

1 participant