export declare const SHIPFLOW_CONTRACT: { readonly $comment: "Canonical ShipFlow contract (issue #179), a SIBLING of review-contract.json: the single source of truth for cross-surface ShipFlow constants that are NOT specific to the review packet — the workflow-type / execution-status / channel-type / plan-type taxonomies, the GitHub label palette + lifecycle label names, the hidden issue-lifecycle markers, and the message-readability word cap. These are shared by a BROADER audience than the two reviewers (the server webhook/issue handlers, the CLI issue/PR commands, and the dashboard), so they live beside — not inside — review-contract.json, whose scope stays exactly 'constants both reviewers share' and whose caps/lists/mirrors are left untouched. Mirrors (regenerate with `node scripts/sync-review-contract.mjs`; parity tests on all three sides fail on drift): apps/renaissshipflow-server/internal/shipflowcontract/shipflow-contract.json (go:embed, byte-identical), apps/renaissshipflow-cli/src/shipflow-contract-data.ts (generated), apps/renaissshipflow-dashboard/src/lib/shipflow-contract-data.ts (generated). Only the LITERALS are single-sourced — each consumer keeps its own MATCHING semantics (the server matches the escalation banner with a loose HasPrefix on markers.escalationBannerEmoji; the CLI matches with the stricter startsWith on markers.escalationBannerHeading)."; readonly version: 1; readonly workflowTypes: { readonly $comment: "The complete WorkflowType taxonomy. The Go domain.WorkflowType constants (domain/workflow.go — the dispatch source of truth, enumerated as domain.AllWorkflowTypes) are pinned to this list by a parity test, and the dashboard's WorkflowType union derives from it directly (typeof SHIPFLOW_CONTRACT.workflowTypes.values[number]). `uat_tests` is real (it has a Go runner + constant) and was missing from the dashboard union; `dependency_audit` was a phantom TS-only value with NO server constant or runner and has been removed (issue #179)."; readonly values: readonly ["issue_triage", "commit_impact", "patch_notes", "regression_tests", "uat_tests", "weekly_summary", "planning_summary", "pr_notification", "pr_review", "bug_report_triage", "test_runner", "codebase_qa"]; }; readonly executionStatuses: { readonly $comment: "The complete domain.ExecutionStatus taxonomy (execution.go — the execution-list statuses the API emits), single-sourced per issue #185 exactly as #179 did for workflowTypes. Go's domain constants (enumerated as domain.AllExecutionStatuses) are pinned to this list by a parity test, the dashboard's ExecutionStatus union derives from it (any value the API can emit that the union misses white-screens StatusBadge), and a spec-accuracy test pins every execution-status enum in openapi.yaml to it (the spec had verified drift: `cancelled` was missing from ExecutionLog.status). NOT to be confused with the CLI's per-run gate statuses (pending/in_progress/success/failure/skipped) — that is a different server enum, renamed GateStatus in the CLI so this domain name stays unambiguous."; readonly values: readonly ["queued", "dispatched", "running", "completed", "failed", "cancelled", "skipped"]; }; readonly channelTypes: { readonly $comment: "The complete domain.ChannelType taxonomy (notification.go — notification delivery methods), single-sourced per issue #185. Go's domain.AllChannelTypes is pinned to this list by a parity test, the dashboard and CLI ChannelType unions derive from it, and a spec-accuracy test pins every channelType enum in openapi.yaml to it (the spec had verified drift: `telegram` was live everywhere but missing from NotificationChannel/CreateChannelRequest)."; readonly values: readonly ["discord", "slack", "email", "webhook", "telegram", "whatsapp"]; }; readonly planTypes: { readonly $comment: "The complete domain.PlanType taxonomy (tenant.go — subscription tiers), single-sourced per issue #185. Go's domain.AllPlanTypes is pinned to this list by a parity test, the dashboard and CLI PlanType unions derive from it, and a spec-accuracy test requires every plan enum in openapi.yaml to be a subset of it (full-list where the field is a stored plan; deliberate subsets like the upgrade-target [pro, enterprise] stay subsets)."; readonly values: readonly ["free", "pro", "enterprise"]; }; readonly labels: { readonly $comment: "ShipFlow's GitHub label palette + the lifecycle label NAMES. `colors` maps a label name to its 6-hex color; ShipFlow owns these colors and BOTH the server's ensureLabels and the CLI's ghEnsureLabel source this ONE map (pre-contract they were byte-identical copies, so a color edit on one side flip-flopped the label). `prefixColors` colors open-ended label groups by name prefix. `names` are the lifecycle labels the code references by MEANING (claim / escalation / approval / reporter-review / verify-failed); every `names` value is also a `colors` key (parity-tested). `needsReporterApproval` is the INTAKE gate (issue #448): an issue opened by an account outside the code org carries it until a trusted maintainer approves, and `isActionableForPickup` refuses to claim a carrier — so the loop cannot build work nobody in the org has green-lit. It is deliberately NOT `needsReporterReview`: that label is the #441 MERGE gate with its own confirmation-token grammar, and one reply must never release two different decisions. `verifyFailed` is applied by the post-deploy verifier when a PR's verification manifest has a failing assertion (issue #207) — it hooks the reporter ping and the follow-up auto-revert. `autoHarvested` marks issues the cross-reviewer finding harvester files from valid findings OTHER PR reviewers (gemini-code-assist, chatgpt-codex-connector) raised and ShipFlow missed (part of #56). Repaint/matching semantics stay per-consumer — only the literals are shared."; readonly colors: { readonly bug: "d73a4a"; readonly enhancement: "a2eeef"; readonly feature: "a2eeef"; readonly task: "0052cc"; readonly "priority:low": "0e8a16"; readonly "priority:medium": "fbca04"; readonly "priority:high": "ff9f1c"; readonly "priority:critical": "e11d48"; readonly "severity:cosmetic": "c5def5"; readonly "severity:minor": "0e8a16"; readonly "severity:major": "ff9f1c"; readonly "severity:blocking": "e11d48"; readonly "\uD83E\uDD16 in-progress": "1d76db"; readonly "needs-reporter-review": "d4c5f9"; readonly "needs-reporter-approval": "c2a5f9"; readonly "needs-human": "d93f0b"; readonly "shipflow-approved": "0e8a16"; readonly "loop-proceed": "0e8a16"; readonly "auto-qa": "5319e7"; readonly "verify-failed": "b60205"; readonly "severity:critical": "b60205"; readonly "severity:high": "d93f0b"; readonly "severity:medium": "fbca04"; readonly "severity:low": "c2e0c6"; readonly "via-shipflow": "0e7490"; readonly "auto-harvested": "d876e3"; readonly "\u23F3 waiting-on": "fbca04"; }; readonly prefixColors: { readonly "category:": "5319e7"; readonly "area:": "c5def5"; readonly "epic:": "d4c5f9"; }; readonly names: { readonly inProgress: "🤖 in-progress"; readonly needsReporterReview: "needs-reporter-review"; readonly needsReporterApproval: "needs-reporter-approval"; readonly needsHuman: "needs-human"; readonly shipflowApproved: "shipflow-approved"; readonly verifyFailed: "verify-failed"; readonly viaShipflow: "via-shipflow"; readonly autoHarvested: "auto-harvested"; readonly waitingOn: "⏳ waiting-on"; }; }; readonly markers: { readonly $comment: "Hidden issue-lifecycle markers + the escalation-banner literals. `triaged` is stamped on every ShipFlow-created issue so the issues.opened webhook suppresses the redundant AI Issue Triage pass (server domain.IssueAutoTriagedMarker + CLI ghIssueCreate). `loop` marks loop-progress comments — matched by the server's needs-human auto-unblock, written by the loop per the skill contract. `interpretationNote` is the deliberate-reinterpretation flag a worker embeds in a PR body when it ships an off-brief reading of the ask (issue #190): the CLI intent gate (`pr automerge`/`pr ready`, via packet.hasInterpretationSignal) treats its presence as a first-class merge blocker so the human reporter confirms before it reaches production, and the server companion pings the reporter on the resulting needs-reporter-review label. `escalationBannerEmoji` (🚧) is what the server matches with a LOOSE HasPrefix (legacy comments depend on it). `escalationBannerHeading` is the stricter prefix the CLI matches with startsWith AND the opening of the CLI's rendered banner; it MUST start with escalationBannerEmoji (parity-tested), so a CLI-posted banner always satisfies the server's loose match. `verificationManifestHeading` is the section heading text a PR author uses to declare post-deploy verification assertions (issue #207); the server matches it tolerantly (case-insensitive, ignoring leading `#` and trailing punctuation) to extract the manifest, then posts its verdict comment stamped with `verificationComment`. `precedentContext` and `precedentApplied` are the decision-precedent-store markers (issue #210, slice 3). `precedentContext` is a HIDDEN OPEN-TOKEN the CLI appends to every `issue escalate` banner carrying the raw ask so the server's webhook capture can fingerprint the exact same text a later `precedents/match` lookup will — rendered as `` (the `cat=`/`q=` attributes are the CLI→server convention). `precedentApplied` is the OPEN-TOKEN on the auto-application disclosure comment (`🔁 Auto-resolved per your #N decision`), rendered as ``; the server's undo watcher matches it with a loose Contains and reads `pid=` to know which precedent a one-word `undo`/`no` reply reverses, and `commentIsLoopMachinery` learns it so a disclosure can never itself clear needs-human/needs-reporter-review. Both are OPEN tokens (no trailing `-->` in the literal) matched with Contains, like the escalation-banner emoji is matched with HasPrefix — the render closes the tag after the attributes. MATCHING SEMANTICS (issue #411 changed these deliberately — the note above used to read `Do NOT change these matching semantics`): `commentIsLoopMachinery` no longer denylists three specific markers, it matches `markerPrefix` — ANY `` (issue #637): `isApproved` / `classifyPR` / `mergeDecision` bind `shipflow-approved` to the reviewed head — a label without a matching SHA, or a missing/unreadable SHA, is not approved (fail closed). Quote-stripped and own-line-anchored. A clean rebase invalidates this slice; digest-equal keep-approval is a later product choice. `intentGateCleared` is the OPEN token on the server's attributable audit comment posted on EVERY `needs-reporter-review` removal, rendered as ``; the CLI's intent gate reads it as the clearance artifact instead of trusting the bare `unlabeled` timeline event (see the `intentGate` section) — and reads it ANCHORED (own line, `by= -->` shape) and ONLY from a bot/trusted-association author, because a bare Contains over every comment let anyone who can quote the literal disarm the gate permanently. `intentGateHint` stamps the server's one-time nudge posted when a human reply on a gated thread misses the release grammar; its presence is also how the nudge stays one-time (fail-stuck was previously invisible — the miss path only logged). `reworkFrom` is the OPEN-TOKEN a rework worker stamps on the comment it posts after acting on a reporter's CORRECTION of an intent-gated PR (issue #442), rendered as `` — the `id=` attribute convention mirrors `precedentApplied`'s `pid=`. The CLI's `reporterCorrectionOn` reads the id back so suppression is EXACT (that comment has been answered) rather than timestamp-ordered, and counts the markers to enforce the rework ceiling; the server needs no accessor because `markerPrefix` already makes any comment carrying it machinery. It is the anti-self-loop backstop: without it a loop comment on a gated PR reads as a fresh reporter correction and the loop reworks in response to itself. `escalateOnce` is the OPEN-TOKEN the CLI stamps INSIDE the escalation banner when `issue escalate` is given `--for-pr --once-reason `, rendered as `` — the `pr=`/`reason=` attribute convention mirrors `precedentContext`'s `cat=`/`q=`. It is the PERMANENT once-key for `inbox`'s `escalateOnce` row (issue #488): the key USED to be the parent issue's live `needs-human` label, but the server's UnblockNeedsHuman removes that label on any non-bot, non-machinery comment BY DESIGN, so the only once-key was erased the moment a human replied and the row re-escalated every tick forever. A comment marker cannot be erased by a reply, and unlike a label it CARRIES THE REASON — so the invariant it enforces is `at most one escalation per (PR, reason), EVER`: a new reason on the same PR earns exactly one more, and the PR is capped at one escalation per `ESCALATE_ONCE_REASONS` entry, forever. The CLI reads it back from the PARENT issue's comments under THREE filters, ALL required, because each of the first two was measured insufficient on its own: (1) only comments the CLI's own account authored (`viewerDidAuthor`); (2) only ANCHORED — alone on its line, starting at COLUMN 0, no leading whitespace — because a quoted marker is a claim, not evidence; those two are the intent-gate audit record's rule (#411). (3) only from comments that ARE an escalation banner (`isEscalationBanner`, the same `escalationBannerHeading` prefix `findLatestEscalationComment` selects on) — added in PR #489 round 4. Filters 1+2 alone accepted an anchored marker from ANY CLI-authored comment on the parent, and the CLI writes many comments that are not banners: `issue wait --reason ` posts one on that very issue with LLM-composed text interpolated raw, on the path loop-mode.md mandates. That reason forged a permanent key and silently spent the one escalation the (PR, reason) pair ever earns — issue #488's exact harm through the adjacent door. Scoping the READ was chosen over neutralizing yet another writer: rounds 2 and 3 each hardened one side of this boundary and left the other open, whereas a key that counts ONLY where the single writer of a key (`formatEscalationBody`) puts one means adding a new CLI comment can never create a new forgery surface. The harvester `extractEscalateOnceMarkers` is scoped by the same predicate one hop earlier, so an unscoped harvest cannot LAUNDER a forged key out of a non-banner comment and into a banner via the `--update` carry-forward. Neutralization below stays defence in depth, not the wall. The anchor tolerated leading indentation until PR #489 round 3; that tolerance bought nothing (the renderer always emits a marker at column 0) and cost a forgery route, since four leading spaces is exactly how markdown spells a code block. Trailing whitespace and CRLF ARE tolerated — opposite polarity: a real key that fails to match reads as never-filed and re-opens the storm, and trailing space cannot smuggle a marker in. NEUTRALIZATION (the other half, PR #489 round 3): every banner is a comment the CLI's OWN account authors, so ANY free-form string folded into one is a forgery vector — an own-line marker in it reads back as a filed key and permanently suppresses an escalation a human is waiting on. So EVERY operator- or server-supplied string reaching a banner has its `"; readonly loop: ""; readonly loopReview: ""; readonly interpretationNote: ""; readonly markerPrefix: ""; readonly precedentContext: ""; readonly reworkFrom: "