Skip to content

feat(installations): add status audit log#13

Merged
rowan-stein merged 2 commits into
mainfrom
noa/issue-137
Apr 21, 2026
Merged

feat(installations): add status audit log#13
rowan-stein merged 2 commits into
mainfrom
noa/issue-137

Conversation

@casey-brooks
Copy link
Copy Markdown
Contributor

Summary

  • add installation status + audit log schema and storage helpers
  • enforce idempotency/retention and cursor pagination for audit entries
  • expose new Apps RPCs with validation and auth updates

Testing

  • go vet ./...
  • go test ./...
  • go build ./...

Issue

  • #137

@casey-brooks casey-brooks requested a review from noa-lucent April 20, 2026 22:49
@casey-brooks
Copy link
Copy Markdown
Contributor Author

Test & Lint Summary

  • go vet ./...
  • go test ./... -count=1 -json
  • go build ./...

Tests: 68 passed, 0 failed, 0 skipped
Lint: no errors

@casey-brooks
Copy link
Copy Markdown
Contributor Author

Test & Lint Summary

  • go vet ./...
  • go test ./... -count=1 -json > /tmp/apps-go-test.json
  • go build ./...

Tests: 68 passed, 0 failed, 0 skipped
Lint: no errors

Copy link
Copy Markdown

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

Implementation matches the spec (status clear-on-whitespace, append-only audit log with 24h idempotency, 1000-entry retention, newest-first pagination, and correct auth split between app identity vs org members).

Left a few minor follow-ups:

  • add DB-level constraint for level
  • consider avoiding hashtext advisory-lock collisions
  • add unit tests for audit-log page token format
  • strengthen the status-clear test to assert presence semantics

if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if resp.GetInstallation().GetStatus() != "" {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] This assertion uses GetStatus() which can’t distinguish an unset optional field from an explicitly empty string. Since the spec says whitespace clears the field (store NULL), it’d be better to assert the field is actually unset, e.g. if resp.GetInstallation().Status != nil { ... }.

return entries, nextToken, nil
}

func encodeAuditLogPageToken(entry InstallationAuditLogEntry) string {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] Can we add store-level unit tests for the audit-log page token (round-trip + invalid token cases), similar to pagination_test.go? This would protect against accidental changes to the token format.

id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
installation_id UUID NOT NULL REFERENCES app_installations (id) ON DELETE CASCADE,
message TEXT NOT NULL,
level TEXT NOT NULL,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] Consider adding a CHECK constraint for installation_audit_log_entries.level (e.g., level IN ('info','warning','error')). The converter currently panics on unknown levels, so it’d be good to enforce the invariant at the DB boundary too.

_ = tx.Rollback(ctx)
}()

if _, err := tx.Exec(ctx, "SELECT pg_advisory_xact_lock(hashtext($1))", input.InstallationID.String()); err != nil {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] Locking: advisory locks here make sense for idempotency/retention, but hashtext(uuid.String()) is only 32-bit and can collide across installations. Consider instead locking the installation row itself (SELECT 1 FROM app_installations WHERE id=$1 FOR UPDATE) or using a stronger lock key derivation to avoid cross-installation contention.

@rowan-stein rowan-stein merged commit 00b6c77 into main Apr 21, 2026
1 check passed
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.

3 participants