diff --git a/src/providers/matt-skills-v1/work-region.ts b/src/providers/matt-skills-v1/work-region.ts index 575c9ab..f3b4d91 100644 --- a/src/providers/matt-skills-v1/work-region.ts +++ b/src/providers/matt-skills-v1/work-region.ts @@ -223,6 +223,9 @@ const semanticEvidenceComplete = (object: MattProjectedObject): boolean => const trackerClosureState = (ticket: MattWayfinderTicket | MattDeliveryTicket): "open" | "closed" => ticket.trackerClosure.state; +const isClosedWontfixDelivery = (ticket: MattDeliveryTicket): boolean => + ticket.trackerClosure.state === "closed" && ticket.trackerClosure.disposition === "wontfix"; + const isTerminal = (object: MattProjectedObject): boolean => { switch (object.kind) { case "map": @@ -275,7 +278,9 @@ const anomaliesFor = (object: MattProjectedObject): readonly MattNativeWorkRegio ]; } if (object.kind === "delivery-ticket") { - return object.lifecycle.state !== "completed" && object.trackerClosure.state === "closed" + return object.lifecycle.state !== "completed" && + object.trackerClosure.state === "closed" && + !isClosedWontfixDelivery(object) ? [ { code: "matt.work-region.closure-without-completion", @@ -340,7 +345,7 @@ const frontierForDelivery = ( terminalReferences: ReadonlySet, trustworthy: boolean, ): MattNativeWorkRegionFrontier => { - if (ticket.lifecycle.state === "completed") return "resolved"; + if (ticket.lifecycle.state === "completed" || isClosedWontfixDelivery(ticket)) return "resolved"; if (blockers.some((blocker) => !terminalReferences.has(blocker))) return "blocked"; return ticket.lifecycle.state === "open" && trustworthy && semanticEvidenceComplete(ticket) ? "ready" @@ -393,6 +398,9 @@ const itemFor = ( frontier: frontierForDelivery(object, blockers, terminalReferences, trustworthy), blockers, trackerClosure: trackerClosureState(object), + ...(object.trackerClosure.state === "closed" + ? { nativeDisposition: object.trackerClosure.disposition } + : {}), ...(object.lifecycle.state === "completed" ? { completionEvidence: object.lifecycle.evidence } : {}), diff --git a/tests/github-provider.test.ts b/tests/github-provider.test.ts index 0aad793..cf36a56 100644 --- a/tests/github-provider.test.ts +++ b/tests/github-provider.test.ts @@ -820,6 +820,18 @@ Commit abc123 passed. lifecycle: { state: "completion-unavailable", reason: "source-contract-gap" }, trackerClosure: { state: "closed", disposition: "wontfix" }, }); + const region = buildMattNativeWorkRegion(result, [], { + state: "bound", + effortIds: ["effort:github-delivery"], + }); + expect(region.views[0]?.items).toEqual([]); + expect(region.views[1]?.items).toHaveLength(1); + expect(region.views[1]?.items[0]).toMatchObject({ + nativeLifecycle: "completion-unavailable", + nativeDisposition: "wontfix", + }); + expect(region.views[1]?.items[0]).not.toHaveProperty("completionEvidence"); + expect(region.diagnostics).toEqual([]); }); test("admits bound canonical GitHub subjects and rejects synthesized or unbound references", async () => { diff --git a/tests/matt-native-work-region.test.ts b/tests/matt-native-work-region.test.ts index e62d420..25ca738 100644 --- a/tests/matt-native-work-region.test.ts +++ b/tests/matt-native-work-region.test.ts @@ -504,6 +504,106 @@ test("covers closure and decision anomalies without promoting native lifecycle", ); }); +test.each([ + "complete", + "partial", +] as const)("resolves wontfix presentation with %s coverage without satisfying blockers", (coverage) => { + const { observation, selections } = fixture(); + if (observation.state !== "available") throw new Error("Expected an available observation."); + const delivery = observation.projection.deliveryTickets[0]; + const dependent = observation.projection.wayfinderTickets[1]; + if (delivery === undefined || dependent === undefined) throw new Error("Expected tickets."); + const region = buildMattNativeWorkRegion( + { + ...observation, + ...(coverage === "partial" + ? { + state: "partial" as const, + coverage: { + assessment: "incomplete" as const, + dimensions: [{ key: "scope", state: "gap" as const }], + }, + } + : {}), + projection: { + ...observation.projection, + deliveryTickets: [ + { + ...delivery, + lifecycle: { state: "completion-unavailable", reason: "source-contract-gap" }, + trackerClosure: { + state: "closed", + disposition: "wontfix", + closedAt: { availability: "unsupported" }, + }, + }, + ], + graph: { + ...observation.projection.graph, + blockedBy: [{ blocked: dependent.ref, blocker: delivery.ref, evidence: "matt-contract" }], + }, + }, + }, + selections, + { state: "bound", effortIds: ["effort:portal"] }, + ); + const mode = coverage === "complete" ? "exact" : "at-least"; + expect(region.views[0]?.count).toEqual({ mode, value: 3 }); + expect(region.views[1]?.count).toEqual({ mode, value: 1 }); + expect(region.views[1]?.items[0]).toMatchObject({ + reference: delivery.ref, + frontier: "resolved", + nativeDisposition: "wontfix", + nativeLifecycle: "completion-unavailable", + trackerClosure: "closed", + }); + expect(region.views[1]?.items[0]).not.toHaveProperty("completionEvidence"); + expect(region.views[0]?.items.find((item) => item.reference === dependent.ref)?.frontier).toBe( + "blocked", + ); + expect(region.views.flatMap((view) => view.items)).toHaveLength(4); + expect( + new Set(region.views.flatMap((view) => view.items.map((item) => item.reference))).size, + ).toBe(4); + expect(region.diagnostics).toEqual([]); +}); + +test.each([ + "unknown", + "completed", + "not-planned", +] as const)("keeps closed %s Delivery without completion evidence in Current Work", (disposition) => { + const { observation, selections } = fixture(); + if (observation.state !== "available") throw new Error("Expected an available observation."); + const region = buildMattNativeWorkRegion( + { + ...observation, + projection: { + ...observation.projection, + deliveryTickets: observation.projection.deliveryTickets.map((ticket) => ({ + ...ticket, + lifecycle: { state: "completion-unavailable", reason: "source-contract-gap" }, + trackerClosure: { + state: "closed", + disposition, + closedAt: { availability: "unsupported" }, + }, + })), + graph: { ...observation.projection.graph, blockedBy: [] }, + }, + }, + selections, + { state: "bound", effortIds: ["effort:portal"] }, + ); + expect(region.views[1]?.items).toEqual([]); + expect(region.views[0]?.items.find((item) => item.role === "delivery")?.frontier).toBe( + "uncertain", + ); + expect(region.diagnostics.map((diagnostic) => diagnostic.code)).toEqual([ + "matt.work-region.closure-without-completion", + ]); +}); + test("places provider subject diagnostics beside the affected native item in plain language", () => { const { observation, selections } = fixture(); if (observation.state !== "available" && observation.state !== "partial") { diff --git a/tests/matt-upstream-local-contract.test.ts b/tests/matt-upstream-local-contract.test.ts index 022ecf7..a65a281 100644 --- a/tests/matt-upstream-local-contract.test.ts +++ b/tests/matt-upstream-local-contract.test.ts @@ -334,6 +334,22 @@ Include atomic writes and CLI parsing. Exclude generic JSON canonicalization. state: "closed", disposition: "wontfix", }); + const region = buildMattNativeWorkRegion(targeted, [], { + state: "bound", + effortIds: ["effort:upstream"], + }); + expect(region.views[0]?.items).toEqual([]); + expect(region.views[1]?.items).toMatchObject([ + { + reference: ticketLocator, + frontier: "resolved", + nativeLifecycle: "completion-unavailable", + trackerClosure: "closed", + nativeDisposition: "wontfix", + }, + ]); + expect(region.views[1]?.items[0]).not.toHaveProperty("completionEvidence"); + expect(region.diagnostics).toEqual([]); } finally { await rm(root, { recursive: true, force: true }); } diff --git a/tests/planning-lineage-page.test.ts b/tests/planning-lineage-page.test.ts index 9d58f84..23ed912 100644 --- a/tests/planning-lineage-page.test.ts +++ b/tests/planning-lineage-page.test.ts @@ -3,7 +3,10 @@ import { readFile } from "node:fs/promises"; import { join } from "node:path"; import { createElement, createRef } from "react"; import { renderToStaticMarkup } from "react-dom/server"; -import { createProviderScopeObservation } from "../src/native-work-provider"; +import { + createProviderScopeObservation, + providerObservationIdentityFor, +} from "../src/native-work-provider"; import type { RequestedPlanningLineageFilteredView, RequestedPlanningLineageSubject, @@ -23,6 +26,7 @@ import { authoritySchema, effortSchema } from "../src/project-generation/schema" import { assetProjectionSchema } from "../src/project-generation/schema-asset"; import { createSourceRecord } from "../src/project-generation/source-records"; import { mattProviderSemanticSections } from "../src/providers/matt-skills-v1/projection"; +import { mattSkillsV1ProviderObservationSchema } from "../src/providers/matt-skills-v1/schema"; import type { SourceEventTime } from "../src/source-event-time"; import { createAttentionWithoutActiveWorkFixture, @@ -1220,6 +1224,70 @@ test("omits confirmed-empty role shells while preserving resolved Work", () => { expect(html).not.toContain("

Incoming

"); }); +test("renders rejected Delivery only in Resolved on Effort and native-scope pages", () => { + const snapshot = createProjectOverviewFixture(); + const portal = snapshot.providerObservations.find( + (observation) => observation.binding.nativeScope === ".scratch/portal", + ); + if (portal?.state !== "available") throw new Error("Expected an available observation."); + const { id: portalId, ...portalContent } = portal; + const rejectedContent = { + ...portalContent, + projection: { + ...portal.projection, + deliveryTickets: portal.projection.deliveryTickets.map((ticket) => ({ + ...ticket, + title: "Rejected Delivery", + lifecycle: { state: "completion-unavailable", reason: "source-contract-gap" }, + trackerClosure: { + state: "closed", + disposition: "wontfix", + closedAt: { availability: "unsupported" }, + }, + })), + }, + }; + const rejectedObservation = mattSkillsV1ProviderObservationSchema.parse({ + ...rejectedContent, + id: providerObservationIdentityFor(rejectedContent), + }); + const rejected = withLineage({ + ...snapshot, + providerObservations: snapshot.providerObservations.map((observation) => + observation.id === portalId ? rejectedObservation : observation, + ), + providerObservationSelections: snapshot.providerObservationSelections.map((selection) => + selection.observationId === portalId + ? { ...selection, observationId: rejectedObservation.id } + : selection, + ), + }); + for (const value of [ + { kind: "effort", id: "effort:portal" }, + { kind: "native-scope", id: ".scratch/portal" }, + ] as const) { + const html = render({ validity: "valid", value }, { snapshot: rejected }); + const currentStart = html.indexOf('
", currentStart)); + expect(current).not.toContain("Rejected Delivery"); + expect(html).not.toContain("The tracker is closed without provider-proven Delivery completion"); + if (value.kind === "effort") { + expect(current).toContain( + 'href="/projects/bearing/lineage/native-scope/.scratch%2Fportal#native-work-resolved">1', + ); + } else { + const resolvedStart = html.indexOf('
", resolvedStart)); + expect(resolved).toContain("Rejected Delivery"); + expect(resolved).toContain("
wontfix
"); + expect(resolved).toContain("completion-unavailable"); + expect(resolved).not.toContain('class="matt-work-evidence"'); + } + } +}); + test("keeps Roadmap and Gate Effort relations in their single semantic owners", () => { const roadmapHtml = render({ validity: "valid",