Warden plan

14 - member-wiki authoring & promotion governance lifecycle

← eval suite index


tier: destructive requires: [needs-postgres]


14 - member-wiki authoring & promotion governance lifecycle

What it proves

The member-authoring governance model (the shipped #141 data model + #143 backend + the Wave-II sponsor-at-creation / ownership-parity work) holds as a dedicated, granular regression floor — deeper than plan 13's bundled #132 step. Specifically:

Fast schema/route/ability greps fail early on a structural regression; the live-DB steps re-run the committed governance dbtests (routes.promotion, routes.authz, publish.member) as the durable floor.

Prerequisites

Step 1: reset to a clean pgvector schema (shared DB floor)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"
# shellcheck disable=SC1091
source "${WARDEN_ENV_FILE:?WARDEN_ENV_FILE not set — run this plan via .warden/run.sh}"

if psql "$DATABASE_URL" -q -X -c "DROP SCHEMA IF EXISTS public CASCADE; DROP SCHEMA IF EXISTS drizzle CASCADE; CREATE SCHEMA public; CREATE EXTENSION IF NOT EXISTS vector;" >/dev/null 2>&1; then
  warden_pass "reset robin_ci to a clean public schema with the vector extension"
else
  warden_fail "could not reset robin_ci schema — is greenlight-pg (:5433) up with pgvector?"
fi

Step 2: the standing model + governance routes + member abilities exist (static)

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"

# MG-1: the standing/authoring schema is present — the ownership discriminator,
# author + fork columns, the guardian-grant kind, and the promotion-requests
# table WITH its one-open-per-wiki unique index (the race guard #143 relies on).
SC=server/src/db/schema.ts
if grep -q "ownership: text('ownership')" "$SC" \
   && grep -q "authorId: text('author_id')" "$SC" \
   && grep -q "parentWikiId: text('parent_wiki_id')" "$SC" \
   && grep -q "wikiPromotionRequests" "$SC" \
   && grep -q "wiki_promotion_requests_one_open_per_wiki_uidx" "$SC"; then
  warden_pass "standing model persists: ownership/author_id/parent_wiki_id + wiki_promotion_requests (one-open unique idx)"
else
  warden_fail "the member-authoring schema regressed (ownership/author/parent/promotion-requests columns or the one-open unique index)"
fi

# MG-2: the governance routes exist on the wikis resource.
RT=server/src/modules/wikis/routes.ts
if grep -q "/:id/promotion-requests" "$RT" \
   && grep -q "/:id/promotion-requests/:reqId/accept" "$RT" \
   && grep -q "/:id/promotion-requests/:reqId/decline" "$RT" \
   && grep -q "/:id/sponsor" "$RT" \
   && grep -q "/:id/edit-proposals" "$RT"; then
  warden_pass "promotion-request / sponsor / accept / decline / edit-proposal routes are present (#143)"
else
  warden_fail "a member-authoring governance route is missing from wikis/routes.ts (#143)"
fi

# MG-3: the member role carries the authoring abilities — create own-ownership
# wiki, update own-authored member wiki, suggest against any workspace wiki.
AB=packages/permissions/src/ability.ts
if grep -q "ownership: 'member'" "$AB" && grep -Eq "'suggest'|\"suggest\"" "$AB"; then
  warden_pass "member ability rules present: create/update {ownership:'member'} + suggest (#143)"
else
  warden_fail "member authoring ability rules regressed in ability.ts — a member can no longer author/suggest (#143)"
fi

Step 3: promotion lifecycle + sponsor-at-creation, against a live DB

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"
# shellcheck disable=SC1091
source "${WARDEN_ENV_FILE:?WARDEN_ENV_FILE not set — run this plan via .warden/run.sh}"

# Proves: request → sponsor → accept flips ownership to company AND upgrades the
# sponsor grant to guardian atomically (author retained); a supplied sponsor is
# attached as a kind=sponsor guardian_grants row in the SAME create transaction;
# an omitted sponsor creates no grant; an edit proposal stores the full spliced
# body; an x-workspace-id create lands a Member Wiki in the targeted workspace.
if pnpm --filter @robin/server exec vitest run \
     src/modules/wikis/routes.promotion.dbtest.test.ts \
     >/tmp/warden-mw-promotion.log 2>&1; then
  warden_pass "#143/#132a promotion lifecycle + sponsor-at-creation dbtest passes against a live DB"
else
  tail -25 /tmp/warden-mw-promotion.log
  warden_fail "member-authoring promotion/sponsor dbtest FAILED — see /tmp/warden-mw-promotion.log"
fi

Step 4: negative-path authorization, against a live DB

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"
# shellcheck disable=SC1091
source "${WARDEN_ENV_FILE:?WARDEN_ENV_FILE not set — run this plan via .warden/run.sh}"

# Proves the governance gates hold from the deny side: outsider → 404 on
# promotion-request / edit-proposal reads; a workspace member → gets the rows;
# a member with no root membership → 403 (not silently written to root);
# accept/decline by a non-workspace-admin → 403; sponsor by a non-guardian → 403.
if pnpm --filter @robin/server exec vitest run \
     src/modules/wikis/routes.authz.dbtest.test.ts \
     >/tmp/warden-mw-authz.log 2>&1; then
  warden_pass "#143 member-authoring negative-path authz dbtest passes against a live DB"
else
  tail -25 /tmp/warden-mw-authz.log
  warden_fail "member-authoring authz dbtest FAILED — a governance gate regressed; see /tmp/warden-mw-authz.log"
fi

Step 5: publish guard + ownership/author/fork persistence, against a live DB

set -uo pipefail
source "$WARDEN_LIB/assert.sh"
cd "${PROJECT_ROOT:-$(git rev-parse --show-toplevel)}"
# shellcheck disable=SC1091
source "${WARDEN_ENV_FILE:?WARDEN_ENV_FILE not set — run this plan via .warden/run.sh}"

# Proves: publishing a Member Wiki is refused (promote first); a Company Wiki in
# a non-root workspace publishes; the root-workspace guard (D30) still wins; and
# a Member Wiki fork persists ownership=member, its author, and its parent.
if pnpm --filter @robin/server exec vitest run \
     src/services/publish.member.dbtest.test.ts \
     >/tmp/warden-mw-publish.log 2>&1; then
  warden_pass "#132 member-wiki publish guard + ownership/author/fork persistence dbtest passes against a live DB"
else
  tail -25 /tmp/warden-mw-publish.log
  warden_fail "member-wiki publish-guard/persistence dbtest FAILED — see /tmp/warden-mw-publish.log"
fi

Shape (note for the next author)

This plan is the dedicated governance-lifecycle floor for member authoring; plan 13 covers the bug-sweep's #132 slice (ownership parity across HTTP/MCP/Socrates) and intentionally overlaps only on the shared create-wiki-ownership / run dbtests, which plan 13 owns — this plan does not re-run those, it goes deep on promotion/sponsorship/publish/fork instead. Backend governance, no web surface, so no agent-browser E2E. Step 1 resets the shared robin_ci schema (a preceding plan leaves its own state); each suite pushes its schema and truncates in its own hooks.

Stays manual / not asserted here: