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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { bundleSignerPolicy, demosSigningIdentity } from "./bundleSignerPolicy.j
import { verifyListing } from "./listingVerification.js";
import type { DealRecord, RegisteredDeal } from "./types.js";

// PAYEE-BOUND COUPLING (issue #17 F2): site (c) of 3 (mirror of evidenceGraph.ts SEPARATORS).
// No `dacs-payee-bound-agreement:v1:` domain yet. When payee-bound support lands, add it here
// AND in evidenceGraph.ts, alongside shapeOk's discriminator and isNeutralCancellation's
// commit-kind. §8.5.1: SELECT the domain from the required discriminator — never strip-and-retry.
const SEPARATORS: Record<string, string> = {
"dacs-3-agreement": "dacs-agreement:v1:",
"dacs-4-evidence": "dacs-evidence:v1:",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ const priceTermOk = (value: unknown): boolean => {
return Boolean(price && /^(?:0|[1-9]\d*)(?:\.\d*[1-9])?$/.test(amount) && /[1-9]/.test(amount) &&
typeof price.currency === "string" && price.currency.length > 0 && price.currency.length <= 64);
};
// PAYEE-BOUND COUPLING (issue #17 F2): site (c) of 3. No `dacs-payee-bound-agreement:v1:`
// domain yet. When payee-bound support lands, add it here AND in bundlePolicy.ts SEPARATORS,
// alongside shapeOk's discriminator (above) and isNeutralCancellation's commit-kind
// (reputation.ts). §8.5.1: SELECT the domain from the required discriminator — never strip-and-retry.
const SEPARATORS: Record<Exclude<ArtifactKind, "attestation">, string> = {
agreement: "dacs-agreement:v1:", evidence: "dacs-evidence:v1:", "verify-result": "dacs-verifyresult:v1:",
composite: "dacs-composite:v1:", rating: "dacs-rating:v1:", listing: "dacs-listing:v1:", bundle: "dacs-bundle:v1:",
Expand Down Expand Up @@ -87,6 +91,12 @@ export function isCurrentRef(value: unknown): value is CurrentRef {
return Boolean(anchor && anchor.kind === "storage-program" && typeof anchor.locator === "string" && /^stor-[0-9a-f]{40}$/.test(anchor.locator) && /^[0-9a-f]{64}$/.test(normalizedHash(ref?.contentHash)));
}
function shapeOk(raw: Record<string, unknown>, kind: ArtifactKind): boolean {
// PAYEE-BOUND COUPLING (issue #17 F2): site (a) of 3. Today this accepts only
// `agreementVersion === "1"`, so a payee-bound agreement ref (#236) fails shape →
// bundle never refsVerified → excluded (fail-closed, CORE §11.1.2). When payee-bound
// support lands, this discriminator, isNeutralCancellation's commit-kind (reputation.ts
// ~L35), and the SEPARATORS domain (below ~L41 + bundlePolicy.ts ~L10) MUST move together
// — accept exactly-one of agreementVersion / payeeBoundAgreementVersion (§8.5.1), never both.
if (kind === "agreement") return raw.agreementVersion === "1" && typeof raw.jobId === "string" && rec(raw.listingRef) !== null && arr(raw.parties).length >= 2 && priceTermOk(rec(raw.terms)?.price);
if (kind === "evidence") return raw.evidenceVersion === "1" && typeof raw.jobId === "string" && typeof raw.phase === "string" && (raw.outcome === "success" || raw.outcome === "failure") && typeof raw.observedAt === "number" && (raw.amendmentRefs === undefined || Array.isArray(raw.amendmentRefs)) && (raw.attestationRef === undefined || isCurrentRef(raw.attestationRef));
if (kind === "verify-result") return raw.resultVersion === "1" && typeof raw.scheme === "string" && typeof raw.identifier === "string" && typeof raw.verifiedAt === "number" && ["pass", "fail", "indeterminate", "error"].includes(String(raw.decision)) && isCurrentRef(raw.attestation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export function isNeutralCancellation(
? listingTerms as Record<string, unknown> : undefined;
const phases = Array.isArray(phaseSummary) ? phaseSummary.filter((phase): phase is Record<string, unknown> =>
Boolean(phase && typeof phase === "object" && !Array.isArray(phase))) : [];
// PAYEE-BOUND COUPLING (issue #17 F2): site (b) of 3. Only "commit-agreement" is recognized
// as commit here. When payee-bound support lands, a session that committed via
// "commit-payee-bound-agreement" (#236) must also count as commitReached — otherwise a
// post-commit abort mis-scores as ST-10 reputation-neutral. Move this together with shapeOk's
// discriminator and the SEPARATORS domain in evidenceGraph.ts / bundlePolicy.ts.
const commitReached = phases.some((phase) => phase.kind === "commit-agreement" && phase.outcome === "ok");
return (sellerOutcome === "aborted-by-self" || sellerOutcome === "aborted-by-other") &&
marker?.claimedPolicy === "pre-commit" && terms?.cancellationPolicy === "pre-commit" && !commitReached;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
export const RAIL_LABELS: Record<string, string> = {
"pay-dem": "DEM",
"pay-x402": "USDC · x402",
"pay-evm-erc8183": "ERC-8183",
};
export const railLabel = (r: string) => RAIL_LABELS[r] ?? r.replace(/^pay-/, "");
/** "negotiate-fixed-price" → "fixed price"; acronym ids get proper casing. */
Expand Down