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: 10 additions & 0 deletions .changeset/unify-owner-compliance-writes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
---

PR 1 of 4 in the compliance-state unification initiative (issue #4247): owner-triggered
`evaluate_agent_quality` runs now write to canonical compliance tables
(`agent_compliance_status`, `agent_compliance_runs`, `agent_storyboard_status`) with
`triggered_by = 'owner_test'`, closing the 12-hour gap between owner tests and the
public `/api/registry/agents/:url/compliance` endpoint. Non-owner runs continue
writing to `agent_test_history` (deprecated in PR 3). Adds `'owner_test'` to both
`triggered_by` CHECK constraints via migration 471.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ server/node_modules
/dist/schemas/latest
/dist/schemas/v*
/dist/schemas/registry.*
/dist/schemas/*.js
/dist/schemas/*.js.map
/dist/schemas/*.d.ts
/dist/schemas/*.d.ts.map
/dist/compliance/latest
/dist/compliance/v*
/dist/protocol/latest.tgz
Expand Down
27 changes: 0 additions & 27 deletions dist/schemas/onboarding-openapi.d.ts

This file was deleted.

1 change: 0 additions & 1 deletion dist/schemas/onboarding-openapi.d.ts.map

This file was deleted.

135 changes: 0 additions & 135 deletions dist/schemas/onboarding-openapi.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/schemas/onboarding-openapi.js.map

This file was deleted.

2 changes: 1 addition & 1 deletion server/src/addie/config-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { loadRules, loadResponseStyle } from './rules/index.js';
* Format: YYYY.MM.N where N is incremented for multiple changes in a month
* Example: 2025.01.1, 2025.01.2, 2025.02.1
*/
export const CODE_VERSION = '2026.04.6';
export const CODE_VERSION = '2026.05.1';

// Types
export interface ConfigVersion {
Expand Down
61 changes: 60 additions & 1 deletion server/src/addie/mcp/member-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3560,8 +3560,67 @@ export function createMemberToolHandlers(
);
}

// Record result if the user has an org with this agent saved
// Record result when the user has an org context for this agent.
if (organizationId) {
// Write to canonical compliance tables when the calling org owns this agent.
// Mirrors resolveAgentOwnerOrg (registry-api.ts:4733) — joins organization_memberships
// to verify the acting user is still an active member of the owning org.
// Non-owner runs skip the canonical write and fall through to the legacy
// agent_test_history path below.
const workosUserId = memberContext?.workos_user?.workos_user_id;
let isAgentOwner = false;
if (workosUserId) {
try {
const ownerCheck = await query(
`SELECT 1 FROM member_profiles mp
JOIN organization_memberships om
ON om.workos_organization_id = mp.workos_organization_id
WHERE mp.workos_organization_id = $1
AND mp.agents @> $2::jsonb
AND om.workos_user_id = $3
LIMIT 1`,
[organizationId, JSON.stringify([{ url: resolved.resolvedUrl }]), workosUserId],
);
isAgentOwner = ownerCheck.rows.length > 0;
} catch (ownerCheckError) {
logger.warn({ ownerCheckError }, 'evaluate_agent_quality: owner check failed, skipping canonical write');
}
}

if (isAgentOwner) {
try {
const metadata = await complianceDb.getRegistryMetadata(resolved.resolvedUrl);
// Skip canonical write if the owner has opted out of compliance monitoring.
if (!metadata?.compliance_opt_out) {
const dbInput = {
...complianceResultToDbInput(
result,
resolved.resolvedUrl,
metadata?.lifecycle_stage ?? 'production',
'owner_test',
),
// Owner test runs are not dry runs — they update the live public record.
// (complianceResultToDbInput hard-codes dry_run: true; override here.)
dry_run: false,
// Known gap (deferred to follow-up): the canonical write doesn't
// carry the triggering org id. If two orgs both own the same
// agent URL (rare — staging vs prod orgs of one publisher), an
// owner_test from Org A surfaces in Org B's dashboard without
// attribution. Acceptable for the unblock; full fix adds
// `triggered_org_id` to agent_compliance_runs (#4247 PR 4).
};
await complianceDb.recordComplianceRun(dbInput);
// notifyComplianceChange intentionally omitted: owner test runs are
// exploratory; compliance-change notifications fire on heartbeat
// transitions only to prevent iteration-loop spam.
}
} catch (error) {
logger.warn({ error, agentUrl: resolved.resolvedUrl }, 'Could not write owner test result to canonical compliance state');
}
}

// Legacy write to agent_contexts + agent_test_history. Retained for
// backward compatibility until PR 3 migrates callers and drops the table.
try {
const context = await agentContextDb.getByOrgAndUrl(organizationId, resolved.resolvedUrl);
if (context) {
Expand Down
Loading
Loading