Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions web/__tests__/project-root-reconciliation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,13 @@ describe('project-root expansion reconciliation boundary', () => {
expect(reconcileScript).toContain('reconcileFilesystemGrantsForProject')
expect(reconcileScript).toContain('hasBoundFilesystemAuthority')
expect(bootstrap).toContain("'forge_project_root_reconciler'")
expect(migration).toContain('GRANT UPDATE (status, error_message, updated_at) ON public.tasks TO forge_project_root_reconciler')
expect(migration).toContain('GRANT UPDATE (status, blocked_reason, metadata, updated_at) ON public.work_packages TO forge_project_root_reconciler')
expect(migration).toContain('GRANT UPDATE (id) ON public.work_package_local_projection_heads TO forge_project_root_reconciler')
expect(migration).not.toContain('GRANT SELECT ON public.projects, public.tasks, public.work_packages')
expect(migration).not.toContain('GRANT UPDATE (status, error_message, updated_at) ON public.tasks TO forge_project_root_reconciler')
expect(bootstrap).toContain('grant select on table public.projects, public.tasks, public.work_packages')
expect(bootstrap).toContain('grant update (status, error_message, updated_at) on table public.tasks to forge_project_root_reconciler')
expect(bootstrap).toContain('grant update (status, blocked_reason, metadata, updated_at) on table public.work_packages to forge_project_root_reconciler')
expect(migration).not.toContain('GRANT EXECUTE ON FUNCTION forge.advance_local_projection_head_v1')
expect(bootstrap).toContain('grant execute on function forge.advance_local_projection_head_v1(uuid,uuid,text,uuid,bigint,text,jsonb,bigint,text,text) to forge_project_root_reconciler')
expect(bootstrap).toContain('grant update (id) on table public.work_package_local_projection_heads to forge_project_root_reconciler')
expect(migration).toContain('public.project_root_reconciliation_operations')
expect(migration).toContain('REVOKE ALL ON FUNCTION forge.begin_project_root_reconciliation_v1')
Expand Down
12 changes: 0 additions & 12 deletions web/db/migrations/0027_epic_172_s4_packet_context.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7528,23 +7528,11 @@ GRANT EXECUTE ON FUNCTION forge.complete_project_root_reconciliation_generation_
GRANT EXECUTE ON FUNCTION forge.materialize_project_root_ref_expansion_v1(integer) TO forge_project_root_reconciler;
-- Canonical TypeScript S3 reconciliation needs these exact ordinary state
-- columns. It receives no insert/delete nor any protected journal authority.
GRANT SELECT ON public.projects, public.tasks, public.work_packages,
public.filesystem_mcp_grant_approvals, public.filesystem_mcp_current_decision_pointers,
public.project_filesystem_grant_decisions, public.project_filesystem_current_decision_pointers,
public.work_package_local_projection_heads TO forge_project_root_reconciler;
-- PostgreSQL requires UPDATE privilege to acquire FOR UPDATE locks. These are
-- immutable/key columns only; the reconciler never writes them.
GRANT UPDATE (id) ON public.projects, public.filesystem_mcp_grant_approvals,
public.project_filesystem_grant_decisions TO forge_project_root_reconciler;
GRANT UPDATE (project_id) ON public.project_filesystem_current_decision_pointers TO forge_project_root_reconciler;
GRANT UPDATE (work_package_id) ON public.filesystem_mcp_current_decision_pointers TO forge_project_root_reconciler;
-- PostgreSQL also requires an UPDATE privilege for the canonical helper's
-- FOR UPDATE lock on the current projection-head rows. The immutable key is
-- sufficient; this login receives no mutable head-column privilege.
GRANT UPDATE (id) ON public.work_package_local_projection_heads TO forge_project_root_reconciler;
GRANT UPDATE (status, error_message, updated_at) ON public.tasks TO forge_project_root_reconciler;
GRANT UPDATE (status, blocked_reason, metadata, updated_at) ON public.work_packages TO forge_project_root_reconciler;
GRANT EXECUTE ON FUNCTION forge.advance_local_projection_head_v1(uuid,uuid,text,uuid,bigint,text,jsonb,bigint,text,text) TO forge_project_root_reconciler;
-- The bootstrap fence temporarily gives the incoming owner CREATE on the two
-- containing schemas because PostgreSQL requires it for SET OWNER. The
-- finalizer revokes both grants before it verifies the permanent boundary.
Expand Down
7 changes: 7 additions & 0 deletions web/scripts/bootstrap-epic-172-s4-roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ async function main(): Promise<void> {
// canonical S3 helper. PostgreSQL requires an UPDATE privilege for
// that lock mode; grant only the immutable primary key column.
await admin`grant update (id) on table public.work_package_local_projection_heads to forge_project_root_reconciler`
await admin`grant select on table public.projects, public.tasks, public.work_packages, public.filesystem_mcp_grant_approvals, public.filesystem_mcp_current_decision_pointers, public.project_filesystem_grant_decisions, public.project_filesystem_current_decision_pointers, public.work_package_local_projection_heads to forge_project_root_reconciler`
await admin`grant update (id) on table public.projects, public.filesystem_mcp_grant_approvals, public.project_filesystem_grant_decisions to forge_project_root_reconciler`
await admin`grant update (project_id) on table public.project_filesystem_current_decision_pointers to forge_project_root_reconciler`
await admin`grant update (work_package_id) on table public.filesystem_mcp_current_decision_pointers to forge_project_root_reconciler`
await admin`grant update (status, error_message, updated_at) on table public.tasks to forge_project_root_reconciler`
await admin`grant update (status, blocked_reason, metadata, updated_at) on table public.work_packages to forge_project_root_reconciler`
await admin`grant execute on function forge.advance_local_projection_head_v1(uuid,uuid,text,uuid,bigint,text,jsonb,bigint,text,text) to forge_project_root_reconciler`
const [{ protectedOwnershipCount }] = await admin<{ protectedOwnershipCount: number }[]>`
select ((
select count(*) from pg_catalog.pg_class relation
Expand Down
Loading