Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion scripts/sync-acr-contracts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,45 @@ const ARTIFACT_ROOT = path.join(ROOT, "src/contracts");
// WIDENING ONLY, and load-bearing: acr main mints this claim, so without the
// bump a response carrying it is rejected whole as `acr_contract_violation`
// instead of rendering.
export const SOURCE_COMMIT = "9e35fe9405d70879ed41600f3e4f484aea6411b2";
// 9e35fe94 -> 7ae3719f: PROVISIONAL pin, re-pin to the merge sha before this
// PR merges. Verified over the full tree
// (`git diff 9e35fe9405d70879ed41600f3e4f484aea6411b2
// 7ae3719fc2b214d60d51cc7d6144dc0a11318f05 -- contracts/`): four files
// change, and two of them are part of this repo's vendored surface.
// - `context_fabric_common.v1`'s `CoverageDetail.code` closed enum gains
// an 18th value, `kind_census_truncated`: a discovered cohort's kind-
// scoped census (the term-free fetch of a declared member kind) was cut
// at its own row bound. Three new additive properties ride with it,
// required together and only for this code: `kind` (the subject kind
// the census was for), `declared` (the census figure observed -- a
// floor) and `served` (how many of that kind made the answer). No
// other property, `$def`, or required-list change in this or the
// answer-projection schema.
// - `context_fabric_answer_projection.v1` carries the identical addition
// (this repo's own vendored copy of the coverage-detail shape).
// - `mcp_investigate_question_response.v1` and
// `mcp_investigation_result_response.v1` also carry the identical
// addition but are not part of this repo's vendored surface -- the
// same exclusion as every bump above.
// - `kind` is closed to the same 15-member subject-kind enum
// `SubjectRef.kind` already publishes (was an unenumerated string in
// the first cut of this pin) -- safe to tighten pre-merge since the
// field carries no production traffic yet.
// `context_fabric_investigation_request.v1`, `error.v1`, and every pinned
// example are byte-identical to the prior pin.
//
// WIDENING ONLY, and load-bearing once acr deploys this sha: the row is
// additive, so every previously-valid response still validates; a response
// carrying the new code is what an un-bumped ask-dev leg would reject as
// acr_contract_violation.
// 7ae3719f -> 72c9e3ea: `git diff 7ae3719fc2b214d60d51cc7d6144dc0a11318f05
// 72c9e3ea21b2e10e1537c502bd13d2be5e446664 -- contracts/ internal/mcp/schemas/`
// is empty: no schema, example, or manifest content changed. Every vendored
// file stays byte-identical to the prior pin; only this pointer moves.
// 72c9e3ea -> a57b599b: the acr PR merged (squash). Final pin -- every
// vendored file byte-identical to the pre-merge pin (squash preserves the
// tree; verified per-file blob shas against the merged sha on acr main).
export const SOURCE_COMMIT = "a57b599b88a28c170444e786d701af980911db9d";

const PRETTIER_OPTIONS = Object.freeze({
parser: "typescript",
Expand Down
154 changes: 149 additions & 5 deletions src/components/CoveragePanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1015,30 +1015,34 @@ describe("CoveragePanel — a detail's code changes nothing about how it renders
"requirement_read_not_planned",
"read_population_unverified",
"fact_read_origin_state",
"kind_census_truncated",
]);
});

/**
* Exactly ONE code gets rendering of its own: `fact_read_origin_state`,
* which also produces a per-kind read-state row. Every other code renders
* Exactly TWO codes get rendering of their own: `fact_read_origin_state`
* (a per-kind read-state row) and `kind_census_truncated` (a per-kind
* census row, its own "Kind census" section). Every other code renders
* identically for identical text.
*
* What this pins is the exception SET, so a code quietly GAINING its own
* rendering fails here. It cannot tell that a future code OUGHT to have its
* own — one with no arm is indistinguishable from one that needs none. The
* vocabulary cell above is what stops a new member arriving unnoticed.
*/
it("exactly one code renders anything beyond the shared reason rendering", () => {
it("exactly two codes render anything beyond the shared reason rendering", () => {
const baseline = renderWithCode("fact_provider_reported");
const different = CODES.filter(
(code) => JSON.stringify(renderWithCode(code)) !== JSON.stringify(baseline),
);
expect(different).toEqual(["fact_read_origin_state"]);
expect(different).toEqual(["fact_read_origin_state", "kind_census_truncated"]);
});

it("every other code renders identically for identical text", () => {
const baseline = renderWithCode("fact_provider_reported");
for (const code of CODES.filter((c) => c !== "fact_read_origin_state")) {
for (const code of CODES.filter(
(c) => c !== "fact_read_origin_state" && c !== "kind_census_truncated",
)) {
expect(renderWithCode(code)).toEqual(baseline);
}
});
Expand All @@ -1059,6 +1063,146 @@ describe("CoveragePanel — a detail's code changes nothing about how it renders
});
});

/**
* kind_census_truncated: a discovered cohort's kind-scoped census was cut,
* with kind/declared/served — additive fields present exactly on this code.
* The whole input domain (present, absent, and the boundary served===declared
* shape) is exercised through the real component render.
*/
describe("CoveragePanel — kind_census_truncated (D47)", () => {
function coverageWithKindCensusDetail(
detail: Partial<Record<"kind" | "declared" | "served" | "phrasing", unknown>>,
) {
return {
sources: [{ source: "context-fabric:graph", state: "available" }],
partial: true,
degraded_reasons: [],
details: [
{
detail_id: "cov-census-01",
source: "context-fabric:graph",
code: "kind_census_truncated",
degrading: true,
label: "At least 2000 team found; 25 included",
raw: "kind_census_truncated:team:2000:25",
...detail,
},
],
} as unknown as Parameters<typeof CoveragePanel>[0]["coverage"];
}

it("shows the kind, the declared/served numbers, and the engine label under its own 'Kind census' heading", () => {
const { container } = render(
<CoveragePanel
coverage={coverageWithKindCensusDetail({
kind: "team",
declared: 2000,
served: 25,
})}
/>,
);
const panel = container.querySelector('[data-testid="coverage-panel"]')!;
const visible = visibleText(panel);
expect(visible).toContain("Kind census");
expect(visible).toContain("team");
expect(visible).toContain("At least 2000 team found; 25 included");
expect(visible).toContain("declared 2000 (floor) · served 25");
});

it("prefers the synthesis phrasing over the label when both are present, keeping the numbers line separate", () => {
const { container } = render(
<CoveragePanel
coverage={coverageWithKindCensusDetail({
kind: "team",
declared: 2000,
served: 25,
phrasing: "There are far more teams than the census could enumerate.",
})}
/>,
);
const visible = visibleText(container.querySelector('[data-testid="coverage-panel"]')!);
expect(visible).toContain("There are far more teams than the census could enumerate.");
expect(visible).toContain("At least 2000 team found; 25 included");
expect(visible).toContain("declared 2000 (floor) · served 25");
});

it("omits the kind label and the numbers line when kind/declared/served are absent (legacy-shaped detail)", () => {
const { container } = render(<CoveragePanel coverage={coverageWithKindCensusDetail({})} />);
const visible = visibleText(container.querySelector('[data-testid="coverage-panel"]')!);
expect(visible).toContain("Kind census");
expect(visible).toContain("At least 2000 team found; 25 included");
expect(visible).not.toContain("declared");
expect(visible).not.toContain("served");
});

it("renders the boundary shape served === declared (an uncut census reported some other way) without dividing by anything odd", () => {
const { container } = render(
<CoveragePanel
coverage={coverageWithKindCensusDetail({ kind: "team", declared: 5, served: 5 })}
/>,
);
const visible = visibleText(container.querySelector('[data-testid="coverage-panel"]')!);
expect(visible).toContain("declared 5 (floor) · served 5");
});

it("renders served === 0 (no cohort of this kind was assembled) as a real number, not a fallback", () => {
const { container } = render(
<CoveragePanel
coverage={coverageWithKindCensusDetail({ kind: "team", declared: 2000, served: 0 })}
/>,
);
const visible = visibleText(container.querySelector('[data-testid="coverage-panel"]')!);
expect(visible).toContain("declared 2000 (floor) · served 0");
});

it("does not double-render the row in the generic 'Degraded reasons' list", () => {
const { container } = render(
<CoveragePanel
coverage={coverageWithKindCensusDetail({
kind: "team",
declared: 2000,
served: 25,
})}
/>,
);
const panel = container.querySelector('[data-testid="coverage-panel"]')!;
expect(panel.textContent ?? "").not.toContain("Degraded reasons");
});

it("still shows a generic degraded reason alongside a kind_census_truncated row, without merging the two", () => {
const coverage = {
sources: [{ source: "context-fabric:graph", state: "available" }],
partial: true,
degraded_reasons: [],
details: [
{
detail_id: "cov-census-01",
source: "context-fabric:graph",
code: "kind_census_truncated",
degrading: true,
label: "At least 2000 team found; 25 included",
kind: "team",
declared: 2000,
served: 25,
},
{
detail_id: "cov-fact-01",
source: "canonical_fact:status",
code: "fact_read_failed",
degrading: true,
label: "Status facts could not be read",
},
],
} as unknown as Parameters<typeof CoveragePanel>[0]["coverage"];
const { container } = render(<CoveragePanel coverage={coverage} />);
const visible = visibleText(container.querySelector('[data-testid="coverage-panel"]')!);
expect(visible).toContain("Kind census");
expect(visible).toContain("Degraded reasons");
expect(visible).toContain("At least 2000 team found; 25 included");
expect(visible).toContain("Status facts could not be read");
});
});

/**
* The per-kind read-state rows, against acr's own golden example for the shape.
* That document is the case the rows exist for: each row reads `available` while
Expand Down
58 changes: 56 additions & 2 deletions src/components/CoveragePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Badge } from "@/components/Badge";
import { Details } from "@/components/Details";
import type { Coverage, CoverageDetail } from "@/lib/contracts";
import {
GENERIC_DEGRADED_REASON_SENTENCE,
degradedReasonDisplay,
degradingDetails as degradingDetailsOf,
uncoveredLegacyReasons,
Expand Down Expand Up @@ -74,6 +75,22 @@ export function CoveragePanel({ coverage }: CoveragePanelProps) {
// when byte-identical to a degrading detail's rendered line or its own `raw`.
const degradingDetails: readonly CoverageDetail[] = degradingDetailsOf(coverage);
const legacyDegradedReasons = uncoveredLegacyReasons(coverage);
// A kind_census_truncated row names WHICH kind's census was cut and BOTH
// numbers: declared (the census figure observed -- a floor) and served
// (how many of that kind made this answer). It is degrading (a real
// coverage loss), so it stays in `degradingDetails` for every consumer
// that counts degradation — but it gets its OWN section here rather than
// the generic "Degraded reasons" list, the same reason `fact_read_origin_state`
// does above: displaying its structured fields (not a derived sentence —
// the numbers are the contract's own `kind`/`declared`/`served`, shown
// verbatim) says more than folding it into one more prose line would.
const kindCensusTruncated = (coverage.details ?? []).filter(
(detail) => detail.code === "kind_census_truncated",
);
const kindCensusTruncatedIds = new Set(kindCensusTruncated.map((detail) => detail.detail_id));
const genericDegradingDetails = degradingDetails.filter(
(detail) => !kindCensusTruncatedIds.has(detail.detail_id),
);
// A read-origin-state row reports the state of the population ONE read
// actually reached, per origin kind. It is not a degraded reason -- it can
// be non-degrading -- and it is not the source fold above it either: a row
Expand Down Expand Up @@ -244,7 +261,44 @@ export function CoveragePanel({ coverage }: CoveragePanelProps) {
</ul>
</>
)}
{degradingDetails.length === 0 && legacyDegradedReasons.length === 0 ? null : (
{kindCensusTruncated.length === 0 ? null : (
<>
{/* Below the per-kind read states, above the generic
degraded-reasons list: a cut kind census is its own
cause, with its own numbers, not a fold over sources. */}
<h3 className="panel__title" style={{ marginTop: 14 }}>
Kind census
</h3>
<ul className="stack stack--tight">
{kindCensusTruncated.map((detail) => {
const kind = nonBlank(detail.kind);
const phrasing = nonBlank(detail.phrasing);
const facts = nonBlank(detail.label);
return (
<li className="record" key={detail.detail_id}>
<p className="record__body">
{kind === undefined ? null : (
<strong>{humanizeTerm(kind)}: </strong>
)}
{facts ?? GENERIC_DEGRADED_REASON_SENTENCE}
</p>
{phrasing === undefined || phrasing === facts ? null : (
<p className="record__meta">{phrasing}</p>
)}
{detail.declared === undefined ||
detail.served === undefined ? null : (
<p className="record__meta">
declared {detail.declared} (floor) · served{" "}
{detail.served}
</p>
)}
</li>
);
})}
</ul>
</>
)}
{genericDegradingDetails.length === 0 && legacyDegradedReasons.length === 0 ? null : (
<>
{/* ONE heading for the union: both lists are the same
disclosure, and a response carrying both shapes must not
Expand All @@ -253,7 +307,7 @@ export function CoveragePanel({ coverage }: CoveragePanelProps) {
Degraded reasons
</h3>
<ul className="stack stack--tight">
{degradingDetails.map((detail) => {
{genericDegradingDetails.map((detail) => {
// CHAOS-4690: synthesis-phrased sentence when the
// model chose to phrase it, else the deterministic
// Label floor; then the raw text, and only then the
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/generated/error.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable */
/**
* GENERATED by scripts/sync-acr-contracts.mjs. DO NOT EDIT.
* Source: full-chaos/dev-health-acr @ 9e35fe9405d70879ed41600f3e4f484aea6411b2
* Source: full-chaos/dev-health-acr @ a57b599b88a28c170444e786d701af980911db9d
* Regenerate with `pnpm acr:contracts:generate --source <acr worktree>`.
*/

Expand Down
2 changes: 1 addition & 1 deletion src/contracts/generated/investigation-request.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable */
/**
* GENERATED by scripts/sync-acr-contracts.mjs. DO NOT EDIT.
* Source: full-chaos/dev-health-acr @ 9e35fe9405d70879ed41600f3e4f484aea6411b2
* Source: full-chaos/dev-health-acr @ a57b599b88a28c170444e786d701af980911db9d
* Regenerate with `pnpm acr:contracts:generate --source <acr worktree>`.
*/

Expand Down
23 changes: 21 additions & 2 deletions src/contracts/generated/investigation-result.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable */
/**
* GENERATED by scripts/sync-acr-contracts.mjs. DO NOT EDIT.
* Source: full-chaos/dev-health-acr @ 9e35fe9405d70879ed41600f3e4f484aea6411b2
* Source: full-chaos/dev-health-acr @ a57b599b88a28c170444e786d701af980911db9d
* Regenerate with `pnpm acr:contracts:generate --source <acr worktree>`.
*/

Expand Down Expand Up @@ -4400,7 +4400,8 @@ export interface CoverageDetail {
| "population_truncated"
| "requirement_read_not_planned"
| "read_population_unverified"
| "fact_read_origin_state";
| "fact_read_origin_state"
| "kind_census_truncated";
degrading: boolean;
fact_kind?: string;
source_state?: string;
Expand All @@ -4421,6 +4422,24 @@ export interface CoverageDetail {
label: string;
phrasing?: string;
raw?: string;
kind?:
| "organization"
| "team"
| "project"
| "repository"
| "work_item"
| "pull_request"
| "deployment"
| "incident"
| "document"
| "decision"
| "episode"
| "metric"
| "pull_request_review"
| "ci_pipeline_run"
| "work_item_ref";
declared?: number;
served?: number;
}
export interface VersionSet {
service_version: string;
Expand Down
4 changes: 2 additions & 2 deletions src/contracts/manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"source_commit": "9e35fe9405d70879ed41600f3e4f484aea6411b2",
"source_commit": "a57b599b88a28c170444e786d701af980911db9d",
"source_repository": "full-chaos/dev-health-acr",
"files": [
{
Expand Down Expand Up @@ -28,7 +28,7 @@
},
{
"path": "schemas/context_fabric_common.v1.schema.json",
"sha256": "707acb7ee25e1dbd252847740e3383ec4b204b77e8f0276e33f6911693afcfb8"
"sha256": "574d09fc77d4ad28641e2d7beafe6125a18738929baadb9b9be8c78dbfda8494"
},
{
"path": "schemas/context_fabric_investigation_request.v1.schema.json",
Expand Down
Loading