diff --git a/app/(app)/create/CreateFlowLayoutClient.tsx b/app/(app)/create/CreateFlowLayoutClient.tsx
index 9230b57..0499c88 100644
--- a/app/(app)/create/CreateFlowLayoutClient.tsx
+++ b/app/(app)/create/CreateFlowLayoutClient.tsx
@@ -593,7 +593,7 @@ function CreateFlowLayoutContent({
key: "publish",
status: "danger" as const,
title:
- messages.create.reviewAndComplete.publish.finalizeBannerTitle,
+ messages.create.reviewAndComplete.publish.publishBannerTitle,
description: publishBannerMessage,
onClose: () => setPublishBannerMessage(null),
}
@@ -956,8 +956,8 @@ function CreateFlowLayoutContent({
{isFinalReviewLike
? isPublishing
? messages.create.reviewAndComplete.publish
- .finalizeButtonPublishing
- : footer.finalizeCommunityRule
+ .publishButtonPublishing
+ : footer.publishCommunityRule
: getDefaultFooterLabel(currentStep, footer)}
) : null
diff --git a/app/(app)/create/hooks/useCreateFlowFinalize.ts b/app/(app)/create/hooks/useCreateFlowFinalize.ts
index 849d4a0..52a007c 100644
--- a/app/(app)/create/hooks/useCreateFlowFinalize.ts
+++ b/app/(app)/create/hooks/useCreateFlowFinalize.ts
@@ -47,7 +47,7 @@ export function useCreateFlowFinalize({
/** Session gate return path (`?syncDraft=1`) — differs for `/create/edit-rule` vs `/create/final-review`. */
loginReturnPath: string;
/**
- * `undefined` while `/api/auth/session` is in flight — finalize is a no-op
+ * `undefined` while `/api/auth/session` is in flight — publish is a no-op
* until it resolves. `null` is a guest (anonymous publish).
*/
sessionUser: SessionUser | null | undefined;
diff --git a/app/(app)/create/types.ts b/app/(app)/create/types.ts
index 56f4798..8ac929e 100644
--- a/app/(app)/create/types.ts
+++ b/app/(app)/create/types.ts
@@ -222,7 +222,7 @@ export interface CreateFlowState {
*/
templateReviewEntryFromCreateFlow?: boolean;
/**
- * When set, **Finalize** and signed-in **Save & Exit** update this published
+ * When set, **Publish** and signed-in **Save & Exit** update this published
* rule (PATCH) instead of POSTing a new rule or only saving a draft.
*/
editingPublishedRuleId?: string;
diff --git a/app/(app)/create/utils/createFlowFooterLabels.ts b/app/(app)/create/utils/createFlowFooterLabels.ts
index be3a82a..2d045b7 100644
--- a/app/(app)/create/utils/createFlowFooterLabels.ts
+++ b/app/(app)/create/utils/createFlowFooterLabels.ts
@@ -10,8 +10,8 @@ type FooterMessages = typeof footerMessages;
* from this map fall back to `footer.next`.
*
* `final-review` is handled separately by the caller because its label
- * also depends on the in-flight publish flag (`finalizeButtonPublishing`
- * vs `finalizeCommunityRule`).
+ * also depends on the in-flight publish flag (`publishButtonPublishing`
+ * vs `publishCommunityRule`).
*/
const DEFAULT_FOOTER_LABEL_BY_STEP: ReadonlyMap<
CreateFlowStep,
diff --git a/app/(app)/create/utils/flowSteps.ts b/app/(app)/create/utils/flowSteps.ts
index 2251e1d..ac476d0 100644
--- a/app/(app)/create/utils/flowSteps.ts
+++ b/app/(app)/create/utils/flowSteps.ts
@@ -202,7 +202,7 @@ export const TEMPLATE_REVIEW_FROM_CREATE_FLOW_VALUE = "1" as const;
export const TEMPLATES_FACET_RECOMMEND_QUERY = "recommendTemplates" as const;
export const TEMPLATES_FACET_RECOMMEND_VALUE = "1" as const;
-/** `/create/completed?celebrate=1` — post-finalize toast; set only after **initial** POST publish, not PATCH updates. */
+/** `/create/completed?celebrate=1` — post-publish toast; set only after **initial** POST publish, not PATCH updates. */
export const CREATE_FLOW_COMPLETED_CELEBRATE_QUERY = "celebrate" as const;
export const CREATE_FLOW_COMPLETED_CELEBRATE_VALUE = "1" as const;
diff --git a/docs/create-flow.md b/docs/create-flow.md
index a5498bf..31e2880 100644
--- a/docs/create-flow.md
+++ b/docs/create-flow.md
@@ -105,8 +105,8 @@ Only one `?fromFlow=1` marker exists, on one hop (`/create/review` → `/templat
| Mode | Where progress lives | Save & Exit / publish |
| --- | --- | --- |
-| **Anonymous** | `localStorage` key **`create-flow-anonymous`** | **Save & Exit** (from `community-structure` onward, plus `edit-rule`) opens the save-progress magic-link modal; after verify, optional **PUT** `/api/drafts/me` when `NEXT_PUBLIC_ENABLE_BACKEND_SYNC=true` (see Tickets 4–5 in [guides/backend-linear-tickets.md](guides/backend-linear-tickets.md)). **Finalize** `POST`s `/api/rules` without a session (`userId` null) and sets httpOnly **`cr_rule_claim`**. Landing on **`/create/completed`** opens a **keep-this-rule** magic-link modal (no draft transfer); the top nav stays **Save & Exit** so they can reopen it (**Continue without saving** from that control leaves the flow). After Finalize, skip on the auto-opened modal stays on completed. **Edit** shows when a last-published rule is in session storage. The public URL works; the row is omitted from `GET /api/rules` until claimed. Signing in on the same browser attaches `userId` (profile, edit, invites). |
-| **Signed-in** | In-memory React state in **`CreateFlowContext`** | **Save & Exit** from the **`community-structure`** step onward (and `edit-rule`) may **PUT** `/api/drafts/me` when sync is on. **Completed** is **Exit**. **Finalize** stores the rule with **`userId`**. **Sign out** is on profile, not in the create top nav. |
+| **Anonymous** | `localStorage` key **`create-flow-anonymous`** | **Save & Exit** (from `community-structure` onward, plus `edit-rule`) opens the save-progress magic-link modal; after verify, optional **PUT** `/api/drafts/me` when `NEXT_PUBLIC_ENABLE_BACKEND_SYNC=true` (see Tickets 4–5 in [guides/backend-linear-tickets.md](guides/backend-linear-tickets.md)). **Publish** `POST`s `/api/rules` without a session (`userId` null) and sets httpOnly **`cr_rule_claim`**. Landing on **`/create/completed`** opens a **keep-this-rule** magic-link modal (no draft transfer); the top nav stays **Save & Exit** so they can reopen it (**Continue without saving** from that control leaves the flow). After Publish, skip on the auto-opened modal stays on completed. **Edit** shows when a last-published rule is in session storage. The public URL works; the row is omitted from `GET /api/rules` until claimed. Signing in on the same browser attaches `userId` (profile, edit, invites). |
+| **Signed-in** | In-memory React state in **`CreateFlowContext`** | **Save & Exit** from the **`community-structure`** step onward (and `edit-rule`) may **PUT** `/api/drafts/me` when sync is on. **Completed** is **Exit**. **Publish** stores the rule with **`userId`**. **Sign out** is on profile, not in the create top nav. |
Details and edge cases (conflict confirm, banners, `?syncDraft=1`) match **Ticket 4**, **Ticket 5**, and [`docs/guides/backend-roadmap.md`](guides/backend-roadmap.md) §12.
diff --git a/docs/guides/backend-linear-tickets.md b/docs/guides/backend-linear-tickets.md
index 61440c4..c982d18 100644
--- a/docs/guides/backend-linear-tickets.md
+++ b/docs/guides/backend-linear-tickets.md
@@ -218,19 +218,19 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
**Goal:** Completing the flow persists a **PublishedRule** via existing [publishRule](lib/create/api.ts).
-**Context:** [lib/create/api.ts](lib/create/api.ts) wraps `POST /api/rules` with Zod-validated body (Ticket 2). Finalize flows through [useCreateFlowFinalize](app/(app)/create/hooks/useCreateFlowFinalize.ts) from [CreateFlowLayoutClient](app/(app)/create/CreateFlowLayoutClient.tsx) (`final-review` → `publishRule` → `/create/completed`).
+**Context:** [lib/create/api.ts](lib/create/api.ts) wraps `POST /api/rules` with Zod-validated body (Ticket 2). Publish flows through [useCreateFlowFinalize](app/(app)/create/hooks/useCreateFlowFinalize.ts) from [CreateFlowLayoutClient](app/(app)/create/CreateFlowLayoutClient.tsx) (`final-review` → `publishRule` → `/create/completed`).
**Implementation (shipped):**
1. Map `CreateFlowState` → `title` / `summary` / `document` via [buildPublishPayload](lib/create/buildPublishPayload.ts) (and related builders).
-2. Call `publishRule` on explicit **Finalize** from `final-review` ([useCreateFlowFinalize](app/(app)/create/hooks/useCreateFlowFinalize.ts)).
+2. Call `publishRule` on explicit **Publish** from `final-review` ([useCreateFlowFinalize](app/(app)/create/hooks/useCreateFlowFinalize.ts)).
3. **401** → `openLogin` with return path (Ticket 3 / `AuthModalProvider`).
4. Success: navigate to `completed` with rule id in query string.
**Acceptance criteria:**
- [x] Published row appears in Postgres (`PublishedRule`) and `GET /api/rules` lists it.
-- [x] User sees clear success/failure (banner / flow state; see finalize hook).
+- [x] User sees clear success/failure (banner / flow state; see publish hook).
**Files:** [app/(app)/create/hooks/useCreateFlowFinalize.ts](app/(app)/create/hooks/useCreateFlowFinalize.ts), [CreateFlowLayoutClient.tsx](app/(app)/create/CreateFlowLayoutClient.tsx), [app/api/rules/route.ts](app/api/rules/route.ts), [lib/create/api.ts](lib/create/api.ts), [lib/create/buildPublishPayload.ts](lib/create/buildPublishPayload.ts).
diff --git a/lib/create/pendingKeepRuleLogin.ts b/lib/create/pendingKeepRuleLogin.ts
index 145461d..9ca652c 100644
--- a/lib/create/pendingKeepRuleLogin.ts
+++ b/lib/create/pendingKeepRuleLogin.ts
@@ -1,7 +1,7 @@
/**
- * Guest finalize → `/create/completed` keep-this-rule login. Stored in
+ * Guest publish → `/create/completed` keep-this-rule login. Stored in
* sessionStorage so the prompt survives create-layout remounts and is not
- * tied to the Finalize click (that click would otherwise dismiss the overlay).
+ * tied to the Publish click (that click would otherwise dismiss the overlay).
*/
export const CREATE_FLOW_PENDING_KEEP_RULE_LOGIN_KEY =
"createFlow.pendingKeepRuleLogin";
diff --git a/messages/en/create/footer.json b/messages/en/create/footer.json
index bb9573d..2f228e0 100644
--- a/messages/en/create/footer.json
+++ b/messages/en/create/footer.json
@@ -9,7 +9,7 @@
"confirmDetails": "Confirm details",
"confirmDescription": "Confirm description",
"confirmMembers": "Confirm members",
- "finalizeCommunityRule": "Finalize CommunityRule",
+ "publishCommunityRule": "Publish CommunityRule",
"confirmStakeholders": "Confirm Stakeholders",
"confirmCoreValues": "Confirm values",
"confirmCommunication": "Confirm",
diff --git a/messages/en/create/reviewAndComplete/finalReview.json b/messages/en/create/reviewAndComplete/finalReview.json
index 460378c..6a9deae 100644
--- a/messages/en/create/reviewAndComplete/finalReview.json
+++ b/messages/en/create/reviewAndComplete/finalReview.json
@@ -1,8 +1,8 @@
{
"title": "Review your CommunityRule",
- "description": "Here's what other people will see. Make sure everything looks good before you finalize everything. Once the rule is finalized, you must use one of your decision-making mechanisms to edit it again.",
+ "description": "Here's what other people will see. Make sure everything looks good before you publish. Once the rule is published, you must use one of your decision-making mechanisms to edit it again.",
"editPublishedTitle": "Edit your CommunityRule",
- "editPublishedDescription": "Update what others see on your public rule. Save & Exit or Finalize applies changes to your published CommunityRule.",
+ "editPublishedDescription": "Update what others see on your public rule. Save & Exit or Publish applies changes to your published CommunityRule.",
"ruleCardTitleFallback": "Your community",
"chipEditModal": {
"saveButton": "Save",
diff --git a/messages/en/create/reviewAndComplete/publish.json b/messages/en/create/reviewAndComplete/publish.json
index f428743..34362e9 100644
--- a/messages/en/create/reviewAndComplete/publish.json
+++ b/messages/en/create/reviewAndComplete/publish.json
@@ -1,6 +1,6 @@
{
- "finalizeBannerTitle": "Couldn't publish",
- "missingCommunityName": "Add a community name before finalizing.",
- "finalizeButtonPublishing": "Publishing…",
+ "publishBannerTitle": "Couldn't publish",
+ "missingCommunityName": "Add a community name before publishing.",
+ "publishButtonPublishing": "Publishing…",
"genericPublishFailed": "Something went wrong. Try again."
}
diff --git a/stories/pages/FinalReviewPage.stories.js b/stories/pages/FinalReviewPage.stories.js
index 03a0e03..2019c2b 100644
--- a/stories/pages/FinalReviewPage.stories.js
+++ b/stories/pages/FinalReviewPage.stories.js
@@ -8,7 +8,7 @@ export default {
docs: {
description: {
component:
- "Pre-finalize review: HeaderLockup + expanded Rule sections.",
+ "Pre-publish review: HeaderLockup + expanded Rule sections.",
},
},
},
diff --git a/tests/components/CompletedPage.test.tsx b/tests/components/CompletedPage.test.tsx
index 710ef06..aefdff6 100644
--- a/tests/components/CompletedPage.test.tsx
+++ b/tests/components/CompletedPage.test.tsx
@@ -84,7 +84,7 @@ describe("CompletedScreen", () => {
expect(screen.getByText("Fixture value title")).toBeInTheDocument();
});
- it("does not show post-finalize toast without celebrate query", () => {
+ it("does not show post-publish toast without celebrate query", () => {
render();
expect(
screen.queryByText(
@@ -98,7 +98,7 @@ describe("CompletedScreen", () => {
).not.toBeInTheDocument();
});
- it("shows post-finalize toast in status region when celebrate query is set", () => {
+ it("shows post-publish toast in status region when celebrate query is set", () => {
mockSearchParams({
[CREATE_FLOW_COMPLETED_CELEBRATE_QUERY]:
CREATE_FLOW_COMPLETED_CELEBRATE_VALUE,
diff --git a/tests/components/FinalReviewPage.test.tsx b/tests/components/FinalReviewPage.test.tsx
index 178e3f8..1b64789 100644
--- a/tests/components/FinalReviewPage.test.tsx
+++ b/tests/components/FinalReviewPage.test.tsx
@@ -79,7 +79,7 @@ describe("FinalReviewScreen", () => {
render();
expect(
screen.getByText(
- /Here's what other people will see. Make sure everything looks good before you finalize everything. Once the rule is finalized, you must use one of your decision-making mechanisms to edit it again./i,
+ /Here's what other people will see. Make sure everything looks good before you publish. Once the rule is published, you must use one of your decision-making mechanisms to edit it again./i,
),
).toBeInTheDocument();
});
@@ -1004,6 +1004,18 @@ function FinalReviewEditPublishedWithStateProbe({
}
describe("FinalReviewScreen — edit published title and description", () => {
+ it("renders edit-published lockup copy", () => {
+ render();
+ expect(
+ screen.getByRole("heading", { name: "Edit your CommunityRule" }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(
+ /Update what others see on your public rule. Save & Exit or Publish applies changes to your published CommunityRule./i,
+ ),
+ ).toBeInTheDocument();
+ });
+
it("does not expose click-to-edit title or description on default final review", () => {
render(
{
it("exposes confirmMembers for the community-size footer CTA", () => {
expect(messages.create.footer.confirmMembers).toBe("Confirm members");
});
+
+ it("exposes publishCommunityRule for the final-review footer CTA", () => {
+ expect(messages.create.footer.publishCommunityRule).toBe(
+ "Publish CommunityRule",
+ );
+ });
});
diff --git a/tests/unit/hooks/useCreateFlowFinalize.test.tsx b/tests/unit/hooks/useCreateFlowFinalize.test.tsx
index 965d505..7a18b7f 100644
--- a/tests/unit/hooks/useCreateFlowFinalize.test.tsx
+++ b/tests/unit/hooks/useCreateFlowFinalize.test.tsx
@@ -258,7 +258,7 @@ describe("useCreateFlowFinalize", () => {
});
});
- it("does not PATCH when a guest finalizes an already-published rule", async () => {
+ it("does not PATCH when a guest publishes an already-published rule", async () => {
const { result } = renderHook(() =>
useCreateFlowFinalize({
state: {