feat(identity): project identity redesign — pure hash ID + display names (#72)#163
Conversation
- Change ResolveProjectSlug signature from (slug, gitRemote, error) to (id, displayName, gitRemote, error) - Git path: id = hash[:8] (pure hash, no dirName prefix) - Non-git path: id = hash[:6] (pure hash, no dirName prefix) - displayName = dirName in both cases - Add .engram-project anchor file support: auto-created on first call, overrides displayName (name field) and non-git id (id field) - Update identity_test.go: new 4-return-value assertions + T006 anchor tests - Update cmd/engram/main.go: resolveProject and OnProjectConnect log displayName
- Add migration 081_project_identity_pure_hash: strips dirName prefix from project IDs still in "dirName_hashN" format after migrations 078/079 - Algorithm: find rows where last _-segment is 6 or 8 lowercase hex chars, re-associate observations/issues, rename project to pure hash, preserve old slug in legacy_ids and dirName in display_name - Collision guard: if pure-hash row already exists, merge old row into it - Add display_name VARCHAR(255) column via ALTER TABLE ... ADD COLUMN IF NOT EXISTS - UpsertProject already accepts displayName parameter and stores it in the column (implemented in an earlier commit on this branch) - Add "strings" import to migrations.go (required for strings.LastIndex)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughПереход проектной идентификации с формата Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI (engram)
participant Proxy as Proxy.ResolveProjectSlug
participant Git as Git (remote)
participant FS as Filesystem (.engram-project)
participant DB as Database (migrations / projects)
participant API as Server (handlers)
participant UI as Frontend
CLI->>Proxy: ResolveProjectSlug(cwd)
Proxy->>Git: If git repo -> compute sha256(remote/relPath)
Proxy->>FS: read .engram-project (applyAnchorFile)
alt anchor exists or Git/non-git resolved
Proxy-->>CLI: return (id, displayName, gitRemote)
else anchor missing and non-git
Proxy->>FS: write .engram-project with id/name
Proxy-->>CLI: return (id, displayName, "")
end
DB->>DB: migration 081 adjusts projects.id -> pure-hash, updates FK, sets display_name, legacy_ids
UI->>API: listIssues / getObservations (may include project filter)
API->>DB: query projects / legacy_ids
API-->>UI: include project_display_name / project_names in JSON
UI->>UI: map project IDs -> display names for rendering
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.11.4)Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Code Review
This pull request redesigns project identity by transitioning from "dirName_hash" slugs to pure hash IDs, supported by a new display_name field and .engram-project anchor files for identity persistence. The changes span the database migration logic, Go and JavaScript identity resolution, and UI updates to display human-readable names. Feedback focuses on ensuring the migration covers all tables referencing project IDs, fixing a potential NULL handling bug in the PostgreSQL array_append call, optimizing N+1 database queries in the issue list handler, and addressing a UI regression where display names are missing in the aggregate observations view.
| }) | ||
| } | ||
| if project != "" { | ||
| resp["project_display_name"] = s.getProjectDisplayName(project) |
There was a problem hiding this comment.
The project_display_name is only included in the response when a specific project filter is applied. In the "All Projects" view, the UI will now display raw hashes for each observation instead of human-readable names, which is a regression in user experience. Consider populating a map of project IDs to display names for all observations in the list, similar to the implementation in handleListIssues.
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
internal/proxy/identity.go (1)
64-101: Обработка ошибок записи anchor-файла.Ошибка
os.WriteFileна строке 94 игнорируется молча. Хотя это приемлемо для best-effort поведения, стоит рассмотреть логирование для диагностики:if fileData, marshalErr := json.MarshalIndent(anchor, "", " "); marshalErr == nil { if writeErr := os.WriteFile(anchorPath, append(fileData, '\n'), 0644); writeErr != nil { // Опционально: log.Debug для диагностики } // ... }Также обратите внимание: логика
!storeIDна строке 96 означает, чтоgit addвыполняется только для git-репозиториев (гдеstoreID=false), что корректно.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/proxy/identity.go` around lines 64 - 101, In applyAnchorFile, don't ignore errors from os.WriteFile: capture the return (e.g., writeErr := os.WriteFile(...)) and log a diagnostic message when writeErr != nil (use the package's logger or log.Printf) including anchorPath and the error so failures to create the .engram-project file are visible; also consider capturing and logging the error returned by exec.Command("git", "-C", dir, "add", ".engram-project").Run() so git staging failures are visible for debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.engram-project:
- Around line 1-3: Поле "name" в файле .engram-project содержит
веточно-ориентированное значение "feat-project-identity" и переопределяет
displayName для репозитория; откройте файл .engram-project и замените значение
поля name на корректное имя проекта (реальное displayName), либо полностью
удалите файл если вы хотите использовать значение по умолчанию из
filepath.Base(repoDir); убедитесь, что после изменения функции/припроцессоры,
читающие поле name/displayName, корректно отображают новое значение.
In `@internal/db/gorm/migrations.go`:
- Around line 2541-2617: Migration "081_project_identity_pure_hash" in the
Migrate func does not update all foreign-key columns referring to projects; add
tx.Exec UPDATE statements similar to the existing ones to also update
sdk_sessions.project, vectors.project (used by idx_vectors_doc_type_project),
and issue_comments.author_project whenever you rename or merge an oldID into
newID inside the loop that processes rows; ensure you run these updates in both
the existing-merge branch (existingCount > 0) and the rename-in-place branch so
no orphaned references remain.
In `@internal/worker/handlers_data.go`:
- Around line 764-767: Ответ теперь оборачивает Observation в { observation,
project_display_name }, что ломает клиентов; чтобы сохранить обратную
совместимость, измените обработчик (в месте где вызывается writeJSON) чтобы
возвращать старую форму — сам Observation — (т.е. писать только obs), либо, если
project_display_name нужен, вернуть его через отдельное поле/эндпоинт; обновите
соответствующие символы: функцию writeJSON/обработчик в
internal/worker/handlers_data.go и согласуйте с клиентским helper-ом
fetchObservationById и типом Observation в ui/src/utils/api.ts (обновите типы и
parsing там, если вы всё же хотите официально менять shape ответа).
- Around line 1169-1180: getProjectDisplayName currently issues a raw DB query
without request context and ignores DB errors; change its signature to accept a
context.Context (e.g., getProjectDisplayName(ctx context.Context, projectID
string)), call s.store.GetDB().WithContext(ctx).Raw(...).Scan(&displayName) and
capture the result (res := ...). After Scan, check res.Error and ctx.Err(): if
ctx.Err() != nil return projectID (cancelled) and for other non-nil res.Error
log the error (use the service logger or appropriate logger on Service) and
return projectID; keep the existing fallback of returning projectID when
displayName == "". This ensures cancelled requests don’t continue work and DB
errors are surfaced/logged.
In `@internal/worker/handlers_issues.go`:
- Around line 66-75: The loop over issues currently calls
getProjectDisplayName(...) for every occurrence, causing an N+1 DB lookup;
instead first collect unique project IDs from issues (e.g., by appending
iss.SourceProject and iss.TargetProject into a set if non-empty) and then
iterate that set to call s.getProjectDisplayName once per unique ID and populate
projectNames; update the code around the projectNames map and the loop over
issues in handlers_issues.go to check/set uniqueness before invoking
getProjectDisplayName.
In `@ui/src/views/ObservationsView.vue`:
- Line 808: The friendly display name is only applied inside the obs.memory_type
branch, causing inconsistent project labels; update the other observation card
renderings to call shortProject(obs.project, obs.project === currentProject ?
currentProjectDisplayName : null) so they use the same display name logic as the
memory card. Locate uses of shortProject and places where observation cards
render obs.project (including the span currently using shortProject(...) and the
other card render blocks that render shortProject(obs.project)) and replace
those calls to pass the conditional second argument (currentProjectDisplayName
when obs.project === currentProject) so all cards show the same human-friendly
name.
---
Nitpick comments:
In `@internal/proxy/identity.go`:
- Around line 64-101: In applyAnchorFile, don't ignore errors from os.WriteFile:
capture the return (e.g., writeErr := os.WriteFile(...)) and log a diagnostic
message when writeErr != nil (use the package's logger or log.Printf) including
anchorPath and the error so failures to create the .engram-project file are
visible; also consider capturing and logging the error returned by
exec.Command("git", "-C", dir, "add", ".engram-project").Run() so git staging
failures are visible for debugging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 09d5e3ba-7a5f-4b4b-b1c7-dc919feabdef
📒 Files selected for processing (14)
.engram-projectcmd/engram/main.gointernal/db/gorm/migrations.gointernal/proxy/identity.gointernal/proxy/identity_test.gointernal/worker/handlers_data.gointernal/worker/handlers_issues.goplugin/engram/hooks/lib.jsplugin/engram/hooks/lib.test.jsui/src/composables/useIssues.tsui/src/utils/api.tsui/src/views/IssueDetailView.vueui/src/views/IssuesView.vueui/src/views/ObservationsView.vue
…an names Address review findings: - Update raw_events, indexed_sessions, user_prompts, injection_log, reasoning_traces alongside observations and issues - Set display_name for clean name-only projects (from migrations 078/079) - Handle COALESCE for NULL legacy_ids arrays
- Fix .engram-project name: change "feat-project-identity" to "engram"
- Fix /api/observations/{id} breaking change: flatten project_display_name
as an extra field on the observation object (embedded struct) instead of
a wrapper {observation, project_display_name} that broke fetchObservationById
- Fix getProjectDisplayName: add ctx context.Context param, use WithContext(ctx),
and handle DB errors (return empty string to trigger caller fallback)
- Fix N+1 query in handleListIssues: check projectNames map before calling
getProjectDisplayName to avoid duplicate DB queries for the same project ID
- Fix ObservationsView: use currentProjectDisplayName in regular observation
cards (was only used in memory-card branch)
- Fix migration rollback: add comment explaining why project ID changes are
not reversed (no reverse mapping was stored before migration)
Summary
Replaces
dirName_hashproject slug format with pure hash-based ID. Issue #72.Core Identity Change
ResolveProjectSlugreturnssha256(remote+relPath)[:8]as ID (e.g.67e398f8notengram_67e398f8).engram-projectanchor file for display names (auto-created, auto-staged in git)cmd/engram/main.goresolveProject + OnProjectConnect for 4-return signatureData Migration (081)
display_namecolumn to projects tablelegacy_idsfor backward compatibilityDisplay Names
project_display_namefor observations and issuesJS Hooks Alignment
lib.jsproduces pure hash IDs matching Go algorithmTests
Closes #72
Summary by CodeRabbit
Примечания к выпуску
Новые функции
Баг-фиксы / Улучшения
Миграция БД
Тесты