fix(web): label current Ticket approval from requirement attestation
T-716 MR seq3 CURRENT_REQUIREMENT_APPROVAL_USES_INTEGRATION_PROOF: derive the requirement approval label from review_after_rescope rather than approved_current_subject. Preserve separate persisted integration/source approval display. Strengthen the Web regression for approved integration with stale current requirements and recovery by exact current attestation; require the Ticket route to render that tested status projection. Validation: 28 focused Deno decoder/status/resource/route-reuse tests; deno task check (0 errors/warnings); root cargo check; cargo fmt --all -- --check; git diff --check HEAD. Rust contracts unchanged from validated source14fea520.
This commit is contained in:
@@ -7,6 +7,7 @@ import type { TicketMergeRequestSummary } from "#lib/generated/ticket-api.ts";
|
|||||||
import { fixtureDetail } from "./home/dashboard.test-fixtures.ts";
|
import { fixtureDetail } from "./home/dashboard.test-fixtures.ts";
|
||||||
import { parseTicketDetail } from "./api/ticket-browser.ts";
|
import { parseTicketDetail } from "./api/ticket-browser.ts";
|
||||||
import {
|
import {
|
||||||
|
currentRequirementApprovalStatus,
|
||||||
sourceReviewFreshness,
|
sourceReviewFreshness,
|
||||||
summarySourceReviewStatus,
|
summarySourceReviewStatus,
|
||||||
} from "./merge-request-status.ts";
|
} from "./merge-request-status.ts";
|
||||||
@@ -91,6 +92,13 @@ Deno.test("done Ticket can retain immutable approval while current requirement e
|
|||||||
state: "done",
|
state: "done",
|
||||||
evidence: {
|
evidence: {
|
||||||
...fixtureDetail().evidence,
|
...fixtureDetail().evidence,
|
||||||
|
has_merge_request: true,
|
||||||
|
has_current_subject_ref: true,
|
||||||
|
has_commit: true,
|
||||||
|
has_review_request: true,
|
||||||
|
review_status: "pending",
|
||||||
|
approved_current_subject: true,
|
||||||
|
review_after_rescope: false,
|
||||||
missing: ["review_after_rescope"],
|
missing: ["review_after_rescope"],
|
||||||
},
|
},
|
||||||
merge_requests: [{
|
merge_requests: [{
|
||||||
@@ -101,6 +109,26 @@ Deno.test("done Ticket can retain immutable approval while current requirement e
|
|||||||
});
|
});
|
||||||
assertEquals(ticket.state, "done");
|
assertEquals(ticket.state, "done");
|
||||||
assertEquals(ticket.evidence.complete_for_integration, false);
|
assertEquals(ticket.evidence.complete_for_integration, false);
|
||||||
|
assertEquals(
|
||||||
|
currentRequirementApprovalStatus(ticket.evidence),
|
||||||
|
"not established",
|
||||||
|
);
|
||||||
|
const recovered = parseTicketDetail({
|
||||||
|
...ticket,
|
||||||
|
evidence: {
|
||||||
|
...ticket.evidence,
|
||||||
|
review_after_rescope: true,
|
||||||
|
review_status: "approved",
|
||||||
|
complete_for_integration: true,
|
||||||
|
missing: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assertEquals(recovered.evidence.approved_current_subject, true);
|
||||||
|
assertEquals(
|
||||||
|
currentRequirementApprovalStatus(recovered.evidence),
|
||||||
|
"approved",
|
||||||
|
);
|
||||||
|
assertEquals(recovered.state, "done");
|
||||||
assertEquals(
|
assertEquals(
|
||||||
summarySourceReviewStatus(ticket.merge_requests[0]!),
|
summarySourceReviewStatus(ticket.merge_requests[0]!),
|
||||||
"Integration approval recorded for immutable merged source source-1.",
|
"Integration approval recorded for immutable merged source source-1.",
|
||||||
@@ -118,7 +146,7 @@ Deno.test("Ticket route separates current requirement evidence from immutable in
|
|||||||
const required of [
|
const required of [
|
||||||
"Current requirement evidence",
|
"Current requirement evidence",
|
||||||
"ticket.evidence.complete_for_integration",
|
"ticket.evidence.complete_for_integration",
|
||||||
"ticket.evidence.approved_current_subject",
|
"Current requirement approval:</strong> {currentRequirementApprovalStatus(ticket.evidence)}",
|
||||||
"ticket.evidence.missing",
|
"ticket.evidence.missing",
|
||||||
"summarySourceReviewStatus(mergeRequest)",
|
"summarySourceReviewStatus(mergeRequest)",
|
||||||
"Immutable integration evidence",
|
"Immutable integration evidence",
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import type { TicketMergeRequestSummary } from "#lib/generated/ticket-api.ts";
|
import type {
|
||||||
|
TicketEvidenceSummary,
|
||||||
|
TicketMergeRequestSummary,
|
||||||
|
} from "#lib/generated/ticket-api.ts";
|
||||||
import type {
|
import type {
|
||||||
MergeRequestDetail,
|
MergeRequestDetail,
|
||||||
MergeRequestThreadEvent,
|
MergeRequestThreadEvent,
|
||||||
@@ -89,6 +92,14 @@ export function sourceReviewFreshness(
|
|||||||
return `Fresh source review required: no effective verdict exists for ${source}.`;
|
return `Fresh source review required: no effective verdict exists for ${source}.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Source/integration approval can survive rescope; only this exact revision
|
||||||
|
// and linked-result attestation approves the current Ticket requirements.
|
||||||
|
export function currentRequirementApprovalStatus(
|
||||||
|
evidence: TicketEvidenceSummary,
|
||||||
|
): string {
|
||||||
|
return evidence.review_after_rescope ? "approved" : "not established";
|
||||||
|
}
|
||||||
|
|
||||||
export function summarySourceReviewStatus(
|
export function summarySourceReviewStatus(
|
||||||
mergeRequest: TicketMergeRequestSummary,
|
mergeRequest: TicketMergeRequestSummary,
|
||||||
): string {
|
): string {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
parseTicketRoleAssignmentMutationResponse,
|
parseTicketRoleAssignmentMutationResponse,
|
||||||
} from "#lib/workspace/api/ticket-browser.ts";
|
} from "#lib/workspace/api/ticket-browser.ts";
|
||||||
import { mergeRequestPagePath } from "#lib/workspace/api/merge-requests.ts";
|
import { mergeRequestPagePath } from "#lib/workspace/api/merge-requests.ts";
|
||||||
import { summarySourceReviewStatus } from "#lib/workspace/merge-request-status.ts";
|
import { currentRequirementApprovalStatus, summarySourceReviewStatus } from "#lib/workspace/merge-request-status.ts";
|
||||||
import {
|
import {
|
||||||
relationLabel,
|
relationLabel,
|
||||||
TICKET_STATES,
|
TICKET_STATES,
|
||||||
@@ -686,7 +686,7 @@
|
|||||||
<section class="ticket-control-card">
|
<section class="ticket-control-card">
|
||||||
<header><h2>Current requirement evidence</h2></header>
|
<header><h2>Current requirement evidence</h2></header>
|
||||||
<p><strong>Current requirements:</strong> {ticket.evidence.complete_for_integration ? "satisfied for integration" : "not satisfied for integration"}.</p>
|
<p><strong>Current requirements:</strong> {ticket.evidence.complete_for_integration ? "satisfied for integration" : "not satisfied for integration"}.</p>
|
||||||
<p><strong>Current requirement approval:</strong> {ticket.evidence.approved_current_subject ? "approved" : "not established"}.</p>
|
<p><strong>Current requirement approval:</strong> {currentRequirementApprovalStatus(ticket.evidence)}.</p>
|
||||||
{#if ticket.evidence.missing.length > 0}
|
{#if ticket.evidence.missing.length > 0}
|
||||||
<ul>
|
<ul>
|
||||||
{#each ticket.evidence.missing as missing}<li>{missing}</li>{/each}
|
{#each ticket.evidence.missing as missing}<li>{missing}</li>{/each}
|
||||||
|
|||||||
Reference in New Issue
Block a user