/** * Copy contract for denied issue writes (open cross-task writes: failure UX). * * Cross-issue issue writes are default-open for standard-trust agents on issues * they can already read (see SPEC-implementation §9.3). The remaining walls are * rare — but a real incident burned a full detour discovering a workaround * behind an opaque 403, so every one of them must say three things: * * 1. which boundary fired, * 2. who *can* act, * 3. the sanctioned path forward. * * This module is the single source of truth for that copy, so the API error * body an agent reads and the notice a human sees in the UI are the same words. * It is the issue-write sibling of `responsible-user-denial.ts`: the two * responsible-user ceiling codes delegate to that module's copy so terminology * ("on behalf of {user}", never "impersonate") stays consistent. */ import { type ResponsibleUserDenialCode } from "./responsible-user-denial.js"; export declare const ISSUE_WRITE_DENIAL_CODES: readonly ["issue_write_not_visible", "issue_write_actor_class_excluded", "issue_write_responsible_user_ceiling", "issue_write_responsible_user_unavailable", "issue_write_assignee_run_lock", "cross_issue_influence_cap_exceeded", "cross_issue_influence_run_context_required", "issue_write_attribution_spoof_rejected"]; export type IssueWriteDenialCode = (typeof ISSUE_WRITE_DENIAL_CODES)[number]; /** * Why the write stopped, which drives icon + colour. `boundary` is an * authorization wall, `lock` is run-lifecycle machinery that will clear on its * own, `cap` is a rate backstop, and `attribution` is a rejected spoof. */ export type IssueWriteDenialTone = "boundary" | "lock" | "cap" | "attribution"; export interface IssueWriteDenialCopy { code: IssueWriteDenialCode; /** HTTP status the server pairs with this code. */ status: 403 | 409 | 422 | 429; tone: IssueWriteDenialTone; /** The boundary that fired, as a short noun phrase for a banner title. */ boundary: string; /** Short heading. */ title: string; /** What happened and why, in one or two sentences. */ description: string; /** Who is able to perform this write instead. */ whoCanAct: string; /** The supported way to get the work moving. */ sanctionedPath: string; } export interface IssueWriteDenialContext { /** Display name of the agent or user that attempted the write. */ actorLabel?: string | null; /** Display name of the responsible ("on behalf of") user for the attempt. */ responsibleUserName?: string | null; /** Display name of the target issue's current assignee. */ assigneeLabel?: string | null; /** Target issue identifier, e.g. `TASK-482`. */ issueIdentifier?: string | null; /** Per-run cross-issue influence cap. */ cap?: number | null; /** Attempt count that tripped the cap. */ count?: number | null; /** ISO timestamp at which log-only rollout becomes enforcement. */ enforceAt?: string | null; } export declare function isIssueWriteDenialCode(code: string | null | undefined): code is IssueWriteDenialCode; /** * Bridge the two responsible-user ceiling codes emitted by the authorization * layer into this module's code space, so a single UI notice covers every way * an issue write can be refused. */ export declare function issueWriteDenialCodeForResponsibleUserDenial(code: ResponsibleUserDenialCode): IssueWriteDenialCode; export declare function describeIssueWriteDenial(code: IssueWriteDenialCode, context?: IssueWriteDenialContext): IssueWriteDenialCopy; /** * Flatten a denial into the single `error` string an API client sees. * * Agents typically surface only `error`, so all three §6 obligations — boundary, * who can act, sanctioned path — have to survive the flattening. */ export declare function issueWriteDenialApiMessage(copy: IssueWriteDenialCopy): string; /** * Build the full `{ error, details }` body for a denied issue write. Keeping the * machine-readable `code` next to the prose lets the board UI render the same * copy without parsing sentences. */ export declare function issueWriteDenialResponse(code: IssueWriteDenialCode, context?: IssueWriteDenialContext): { status: IssueWriteDenialCopy["status"]; body: { error: string; details: { code: IssueWriteDenialCode; boundary: string; whoCanAct: string; sanctionedPath: string; } & Record; }; }; //# sourceMappingURL=issue-write-denial.d.ts.map