Skip to content

feat(review): actionable Project identity repair and honest stale-source grouping - #1545

Closed
kunickiaj wants to merge 1 commit into
graphite-base/1545from
ajk--feat_review_actionable_project_identity_repair_and_honest_stale-source_grouping
Closed

feat(review): actionable Project identity repair and honest stale-source grouping#1545
kunickiaj wants to merge 1 commit into
graphite-base/1545from
ajk--feat_review_actionable_project_identity_repair_and_honest_stale-source_grouping

Conversation

@kunickiaj

@kunickiaj kunickiaj commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Description

The recipient-policy review told users a Project needed repair and then gave them nothing usable. This makes those findings actionable, honest, or explicitly inert.

Before: blocked findings rendered a disabled Choose Project control and the message "No safe canonical Project choices are available for this source." Every one of the 5 live findings was a dead end. An earlier iteration shipped a Fix Project identity control that only printed git remote add instructions and reloaded — adding a remote cannot rewrite historical records, so it repaired nothing.

After:

  • Persisted repair. Choose Project → Save mapping writes a project_scope_mappings alias from the legacy source identity to a canonical Project, then refreshes the finding away. Reuses the Team setup picker pattern. Fail-closed on stale fingerprint, invalid/inactive target scope, foreign or conflicting mapping, and on a higher-priority wildcard that would keep the inserted row non-authoritative (matching explicitResolutionFor precedence, so a "success" cannot silently lose to a wildcard).
  • Explicit Space choice. Sources with evidence in multiple Spaces require picking both Project and Space instead of guessing or refusing.
  • Stale sources grouped, not hidden. Sources that own no live memories collapse into one informational row with privacy-safe labels. removableCount reports how many own deletable project_recipients rows; when it is 0 the cleanup control is not rendered at all (a hidden "Remove 0 records" still reaches assistive tech).
  • Optional cleanup. pruneStaleRecipientPolicySources deletes only inert legacy sharing rows, in one transaction, re-deriving every guard server-side, with a group fingerprint guard and per-item skip reasons. Never touches memories, sessions, or Spaces.
  • Ambiguous mappings are actionable. multiple_enforcement_boundaries now names the conflicting Spaces and offers Review Space mappings, which opens Advanced Project administration and focuses that Project's Space select.

Also fixes, all found while verifying against live data:

  • Projects search did not filter. Two guards blocked it: any open Advanced details panel deferred every render, and a focused Space select made loadProjectsData return before fetching. User-initiated loads (search, status filter, pagination) now always run and always paint; background refreshes keep the anti-jump behavior.
  • shared:default was labelled "Local folder (path hidden)" because its : fails the path-safety pattern. It is a synthetic compatibility bucket, now shown as Shared (default).
  • Removed the word "safe" from user copy, fixed cramped repair-card spacing, made the search/status filters sticky, surfaced Assign to Project… directly on path cards, and replaced repeated generic Legacy Team labels with the coordinator Team name.

Reviewer note on one rejected approach

An intermediate version classified any zero-memory Project holding a sharing row as stale. On real data that swept in ~20 canonical greenroom worktree Projects — all mislabelled greenroom — and offered to delete their recipient rows. Staleness is now decided only by the narrow noncanonical_project_identity repair path. Worth a close look during review.

Type of Change

  • 🚀 Feature (new functionality)
  • 🐛 Bug fix (fixes an issue)
  • 📚 Documentation (docs-only change)
  • 🔧 Maintenance (refactor, chore, CI, etc.)
  • 🧪 Testing (test-only changes)

Testing

  • Relevant checks pass locally (pnpm run tsc, pnpm run lint, pnpm run test)
  • Added/updated tests for changes
  • Manually verified changes work as expected

Full suite green: 222 files, 5558 tests. Verified against the live viewer on a real database:

  • Search with a details panel open and a Space select focused correctly narrows to "2 project identities found".
  • Review Space mappings opens Advanced Project administration and focuses the Space select.
  • Stale group reports 5 sources, removableCount: 0, no cleanup button, no raw local paths in the payload.
  • No browser console errors.

Checklist

  • Code follows project style (pnpm run lint passes for touched files)
  • Self-review completed
  • Documentation updated (if needed)
  • No new warnings introduced

kunickiaj commented Aug 29, 2026

Copy link
Copy Markdown
Owner Author

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-29T03:41:52.772218Z 6279c63 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6279c63450

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1423 to +1425
target.workspace_identity,
projection.project.canonicalIdentity,
sourceScopeId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject multi-Space repairs that create a global alias

When the source has memories in multiple Spaces, the selected sourceScopeId is stored on a single mapping, but the alias is keyed only by projection.project.canonicalIdentity; projection collapse therefore redirects every session for that source, including memories from the unselected Spaces. The repaired target consequently retains multiple scope boundaries and immediately becomes an ambiguous_scope_mapping finding even though this function returns applied. Either migrate only the selected Space's records or reject this repair shape instead of reporting success.

Useful? React with 👍 / 👎.

"DELETE FROM recipient_policy_authority_states WHERE canonical_project_identity = ?",
).run(item.canonicalProjectIdentity);
const deleted = db
.prepare("DELETE FROM project_recipients WHERE canonical_project_identity = ?")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve non-legacy recipient rows during pruning

When a stale identity contains a user-managed or invite-derived recipient row alongside legacy state, this unqualified delete removes every recipient assignment for the identity. Other write paths explicitly distinguish these rows through provenance and migration_state, while the confirmation promises to remove only inert legacy sharing records. Restrict both eligibility and deletion to the legacy-owned rows so cleanup cannot erase current policy configuration.

Useful? React with 👍 / 👎.

Comment on lines +1012 to +1014
removableCount: staleGroup.items.filter(
(item) => legacySharingRowsForIdentity(db, item.canonicalProjectIdentity).length > 0,
).length,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count every recipient row in cleanup totals

When one stale source has multiple project_recipients rows, removableCount increments once for the identity rather than once per deletable row. The UI nevertheless labels this value as a record count, and the prune result similarly reports one removed item even though the SQL deletes several rows, so the confirmation and completion notice underreport the destructive operation. Count the matching rows or label these values as source counts.

Useful? React with 👍 / 👎.

@kunickiaj

Copy link
Copy Markdown
Owner Author

Closing as superseded by the safer recipient-policy repair flow in #1561. The remaining persisted alias-mapping and stale-recipient cleanup work is not safe to merge from this stale branch: review identified multi-Space alias scope leakage, deletion of non-legacy recipient rows, and inaccurate cleanup counts. Any still-needed mapping or cleanup behavior should return as focused follow-up PRs against current main with those cases designed out.

@kunickiaj kunickiaj closed this Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant