Skip to content
Open
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
2 changes: 2 additions & 0 deletions backend/src/api/public/v1/packages/getPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export async function getPackage(req: Request, res: Response): Promise<void> {
status: (pkg.stewardshipStatus ?? 'unassigned') as StewardshipStatus,
stewards: stewardshipSummary?.stewards ?? null,
lastActivityAt: stewardshipSummary?.lastActivityAt ?? null,
resolutionPath: pkg.stewardshipResolutionPath ?? null,
statusNote: pkg.stewardshipStatusNote ?? null,
},
history: {},
})
Expand Down
26 changes: 19 additions & 7 deletions backend/src/api/public/v1/packages/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,17 @@ components:

Steward:
type: object
required: [userId, name, role, assignedAt]
required: [userId, role, assignedAt]
properties:
userId:
type: string
description: LFID.
example: jdoe
description: Auth0 sub of the assigned steward.
example: abc123
name:
type: string
type:
- string
- 'null'
description: Display name of the steward. Null if not available.
example: Jonathan R.
role:
type: string
Expand Down Expand Up @@ -418,12 +421,12 @@ components:
- 'null'
stewardship:
type: object
description: Stewardship state. In v1 always unassigned with no stewards or activity.
description: Stewardship state.
properties:
status:
$ref: '#/components/schemas/StewardshipStatus'
stewards:
description: Assigned stewards or null. Null in v1.
description: Assigned stewards or null.
oneOf:
- type: array
items:
Expand All @@ -434,7 +437,16 @@ components:
- string
- 'null'
format: date-time
description: Null in v1.
resolutionPath:
type:
- string
- 'null'
description: Set on `escalated` status. Null for all other statuses.
Comment on lines +440 to +444
statusNote:
type:
- string
- 'null'
description: Free-text note for the current status.
history:
type: object
description: Package history data. Empty in v1.
Expand Down
17 changes: 17 additions & 0 deletions backend/src/api/public/v1/stewardships/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ components:
- stepped_down
- no_longer_critical
- 'null'
resolutionPath:
description: Set on `escalated` status. Null for all other statuses.
oneOf:
- $ref: '#/components/schemas/EscalationResolutionPath'
- type: 'null'
statusNote:
type:
- string
- 'null'
description: Free-text note for the current status. Set by escalate or updateStatus. Null on open.
example: Contacted maintainer, no response after 30 days.
createdAt:
type: string
format: date-time
Expand All @@ -115,6 +126,12 @@ components:
type: string
description: Auth0 sub of the assigned steward.
example: abc123
name:
type:
- string
- 'null'
description: Display name of the steward. Null if not available.
example: Jonathan R.
role:
type: string
enum: [lead, co_steward]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE stewardships
ADD COLUMN IF NOT EXISTS resolution_path TEXT,
ADD COLUMN IF NOT EXISTS status_note TEXT;
4 changes: 4 additions & 0 deletions services/libs/data-access-layer/src/osspckgs/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ export interface PackageDetailRow {
stewardshipId: string | null
stewardshipStatus: string | null
stewardshipLastStatusAt: Date | null
stewardshipResolutionPath: string | null
stewardshipStatusNote: string | null
// from package_repos + repos
repoUrl: string | null
repoMappingConfidence: number | null
Expand Down Expand Up @@ -319,6 +321,8 @@ export async function getPackageDetailByPurl(
s.id::text AS "stewardshipId",
s.status AS "stewardshipStatus",
s.last_status_at AS "stewardshipLastStatusAt",
s.resolution_path AS "stewardshipResolutionPath",
s.status_note AS "stewardshipStatusNote",
-- best repo link (highest confidence, prefer declared)
r.url AS "repoUrl",
pr.confidence AS "repoMappingConfidence",
Expand Down
27 changes: 21 additions & 6 deletions services/libs/data-access-layer/src/osspckgs/stewardships.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export interface StewardshipRecord {
openedAt: string | null
lastStatusAt: string | null
inactiveReason: string | null
resolutionPath: string | null
statusNote: string | null
createdAt: string
updatedAt: string
}
Expand All @@ -17,6 +19,7 @@ export interface StewardshipStewardRecord {
id: string
stewardshipId: string
userId: string
name: string | null
role: string
assignedAt: string
assignedBy: string | null
Expand Down Expand Up @@ -89,6 +92,7 @@ function mapStewardStewardRow(row: Record<string, unknown>): StewardshipStewardR
id: String(row.id),
stewardshipId: String(row.stewardship_id),
userId: String(row.user_id),
name: null,
role: String(row.role),
assignedAt: toIso(row.assigned_at),
assignedBy: row.assigned_by ? String(row.assigned_by) : null,
Expand All @@ -105,6 +109,8 @@ function mapStewardshipRow(row: Record<string, unknown>): StewardshipRecord {
openedAt: row.opened_at ? toIso(row.opened_at) : null,
lastStatusAt: row.last_status_at ? toIso(row.last_status_at) : null,
inactiveReason: row.inactive_reason ? String(row.inactive_reason) : null,
resolutionPath: row.resolution_path ? String(row.resolution_path) : null,
statusNote: row.status_note ? String(row.status_note) : null,
createdAt: toIso(row.created_at),
updatedAt: toIso(row.updated_at),
}
Expand All @@ -116,7 +122,7 @@ export async function getStewardshipById(
): Promise<StewardshipRecord | null> {
const row: Record<string, unknown> | null = await qx.selectOneOrNone(
`SELECT id, package_id, status, origin, version, opened_at, last_status_at,
inactive_reason, created_at, updated_at
inactive_reason, resolution_path, status_note, created_at, updated_at
FROM stewardships
WHERE id = $(id)`,
{ id },
Expand Down Expand Up @@ -151,12 +157,14 @@ export async function openStewardshipByPurl(
FROM pkg
ON CONFLICT (package_id) DO UPDATE
SET status = 'open',
opened_at = COALESCE(stewardships.opened_at, NOW()),
opened_at = NOW(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reopen resets original opened_at

Medium Severity

The stewardship upsert now sets opened_at to NOW() on conflict instead of keeping the first open time, so reopening or repeated open calls overwrite when the package was originally opened.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 23eb372. Configure here.

last_status_at = NOW(),
inactive_reason = NULL,
Comment on lines 158 to 162
resolution_path = NULL,
status_note = NULL,
updated_at = NOW()
RETURNING id, package_id, status, origin, version, opened_at,
last_status_at, inactive_reason, created_at, updated_at
last_status_at, inactive_reason, resolution_path, status_note, created_at, updated_at
),
_log AS (
INSERT INTO stewardship_activity (stewardship_id, actor_user_id, actor_type, activity_type, content)
Expand Down Expand Up @@ -288,10 +296,12 @@ export async function escalateStewardship(
SET status = 'escalated',
last_status_at = NOW(),
inactive_reason = NULL,
resolution_path = $(resolutionPath),
status_note = $(statusNote),
updated_at = NOW()
WHERE id = $(stewardshipId)
RETURNING id, package_id, status, origin, version, opened_at,
last_status_at, inactive_reason, created_at, updated_at
last_status_at, inactive_reason, resolution_path, status_note, created_at, updated_at
),
_log AS (
INSERT INTO stewardship_activity
Expand All @@ -304,6 +314,8 @@ export async function escalateStewardship(
`,
{
stewardshipId,
resolutionPath: data.resolutionPath,
statusNote: data.notes ?? null,
actorUserId: data.actorUserId,
content: `Escalated with resolution path: ${data.resolutionPath}`,
metadata: JSON.stringify({
Expand Down Expand Up @@ -353,11 +365,13 @@ export async function updateStewardshipStatus(
UPDATE stewardships
SET status = $(status),
last_status_at = NOW(),
inactive_reason = CASE WHEN $(status) = 'inactive' THEN $(inactiveReason) ELSE NULL END,
inactive_reason = CASE WHEN $(status) = 'inactive' THEN $(inactiveReason) ELSE inactive_reason END,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Inactive reason kept after reactivation

Medium Severity

updateStewardshipStatus leaves inactive_reason unchanged when the new status is not inactive, so moving back to active can still return a stale inactive reason on a non-inactive stewardship.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 23eb372. Configure here.

resolution_path = NULL,
status_note = $(statusNote),
Comment on lines 366 to +370
updated_at = NOW()
WHERE id = $(stewardshipId)
RETURNING id, package_id, status, origin, version, opened_at,
last_status_at, inactive_reason, created_at, updated_at
last_status_at, inactive_reason, resolution_path, status_note, created_at, updated_at
),
_log AS (
INSERT INTO stewardship_activity
Expand All @@ -372,6 +386,7 @@ export async function updateStewardshipStatus(
stewardshipId,
status: data.status,
inactiveReason: data.inactiveReason ?? null,
statusNote: data.notes ?? 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.

Status note cleared without notes

Medium Severity

In updateStewardshipStatus, status_note is always set from optional notes, so a status change without notes writes NULL and removes an existing note (e.g. after escalate).

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 23eb372. Configure here.

actorUserId: data.actorUserId,
content: `Status updated to ${data.status}`,
metadata: JSON.stringify({
Expand Down
Loading