fix(provenance): address #143 review — page-stamp tests, confine is_agent, doc fixes

Resolves the open items from the latest PR #143 code review:

- test(page): cover the four agentSourceFields stamp sites (create, update,
  movePage, movePageToSpace) with agent + normal-user payload assertions;
  add findById({ includeIsAgent: true }) wiring guards to the JWT and collab
  auth-seam specs so a future drop of the option is caught.
- fix(privacy): drop `isAgent` from UserRepo.baseFields and gate it behind a
  new opt-in `findById({ includeIsAgent })`, requested only by the two auth
  seams that derive provenance — stops the flag leaking via the workspace
  member list and generic user payloads.
- docs: correct the agentSourceFields JSDoc and the two UPDATE-site comments
  to distinguish INSERT (omitted column → DB default 'user') from UPDATE
  (omitted column → existing value kept, Kysely writes only present keys).
- style(page): collapse three stray double blank lines left by an earlier edit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude_code
2026-06-24 02:04:23 +03:00
parent 7705d44fc6
commit 683b9d5de2
8 changed files with 282 additions and 16 deletions

View File

@@ -36,9 +36,6 @@ export class UserRepo {
'updatedAt',
'deletedAt',
'hasGeneratedPassword',
// AI agent identity flag — needed by the JWT strategy to derive a
// non-spoofable 'agent' provenance from the signed server-side identity.
'isAgent',
];
async findById(
@@ -48,6 +45,12 @@ export class UserRepo {
includePassword?: boolean;
includeUserMfa?: boolean;
includeScimExternalId?: boolean;
// Opt-in: `isAgent` is internal provenance state, not part of the generic
// user payload. Keeping it out of `baseFields` stops it from leaking into
// the workspace member list / `/users/me` (an enumeration leak). Only the
// JWT + collab auth seams opt in, because they derive a non-spoofable
// 'agent' provenance from the signed server-side identity.
includeIsAgent?: boolean;
trx?: KyselyTransaction;
},
): Promise<User> {
@@ -58,6 +61,7 @@ export class UserRepo {
.$if(opts?.includePassword, (qb) => qb.select('password'))
.$if(opts?.includeUserMfa, (qb) => qb.select(this.withUserMfa))
.$if(opts?.includeScimExternalId, (qb) => qb.select('scimExternalId'))
.$if(opts?.includeIsAgent, (qb) => qb.select('isAgent'))
.where('id', '=', userId)
.where('workspaceId', '=', workspaceId)
.executeTakeFirst();