fix(native): retain actionable final replies and document links

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-04 20:27:53 -05:00
1 parent 3737f89626
commit be604fcbf8
15 files changed
+195 -12

No files matched your search

@@ -0,0 +1,36 @@
/** Bounded release-access fixture: an action alone is not a blocker explanation. */
export function explainsMissingReleaseAccess(text: string): boolean {
// The requested action must not supply the access fact that the explanation lacks.
const explanation = text.replaceAll("Grant deployment access", "");
const access = String.raw`(?:required\s+access|(?:required\s+)?(?:release|deployment)\s+access)`;
const absent = String.raw`(?:missing|unavailable|absent|not\s+(?:yet\s+)?(?:(?:been|being)\s+)?(?:granted|provided|available)|hasn['’]t\s+been\s+granted)`;
if (new RegExp(String.raw`\b${access}\s+(?:is|was|has\s+been)\s+(?:not\s+(?:missing|unavailable|absent)|(?:already\s+)?(?:available|granted|provided))\b`, "i").test(explanation)) return false;
return new RegExp(String.raw`\b${access}\b[^.!?\n]{0,100}\b${absent}\b`, "i").test(explanation)
|| new RegExp(String.raw`\b(?:missing|unavailable|lack(?:ing)?)\b[^.!?\n]{0,60}\b${access}\b`, "i").test(explanation)
|| /\b(?:deployment|release)\b[^.!?\n]{0,60}\b(?:blocked|cannot proceed|can't proceed)\b[^.!?\n]{0,60}\b(?:until|without)\b[^.!?\n]{0,60}\baccess\b/i.test(explanation);
}
export interface NativeDocumentLinkContext {
appOrigin: string;
issuePrefix: string;
issueIdentifier: string;
documents: readonly { key: string; latestRevisionId?: string | null; latestRevisionNumber?: number | null }[];
}
/** A citation must resolve to this task's one saved, revisioned document. */
export function linksSavedNativeDocument(text: string, context: NativeDocumentLinkContext | undefined): boolean {
if (!context?.appOrigin || !context.issuePrefix || !context.issueIdentifier || context.documents.length !== 1) return false;
const document = context.documents[0]!;
if (!document.key || !document.latestRevisionId || !Number.isSafeInteger(document.latestRevisionNumber) || Number(document.latestRevisionNumber) < 1) return false;
let origin: URL;
try { origin = new URL(context.appOrigin); } catch { return false; }
if (!["http:", "https:"].includes(origin.protocol) || origin.username || origin.password || origin.pathname !== "/" || origin.search || origin.hash) return false;
const route = `/${encodeURIComponent(context.issuePrefix)}/issues/${encodeURIComponent(context.issueIdentifier)}#document-${encodeURIComponent(document.key)}`;
const links = text.matchAll(/\[[^\]]+\]\((?:<([^>]+)>|([^\s)]+))(?:\s+["'][^"']*["'])?\)/g);
return [...links].some(link => {
try {
const url = new URL(link[1] ?? link[2]!, origin);
return url.origin === origin.origin && !url.username && !url.password && !url.search && `${url.pathname}${url.hash}` === route;
} catch { return false; }
});
}