@papi-ai/shared
Advanced tools
+41
-1
@@ -120,2 +120,36 @@ /** Severity of a parsed finding. 'note' is unprefixed content we refuse to drop. */ | ||
| /** The trailing window, in days, that defines "this week" for WAB. */ | ||
| declare const WAB_WINDOW_DAYS = 7; | ||
| /** How many trailing weekly buckets the series returns by default. */ | ||
| declare const WAB_DEFAULT_WEEKS = 8; | ||
| /** One WAB week in milliseconds. */ | ||
| declare const WAB_WEEK_MS: number; | ||
| /** One trailing week of the series. `weekStart` is the ISO start of the 7-day bucket. */ | ||
| interface WabWeek { | ||
| weekStart: string; | ||
| /** Distinct owners who completed a cycle in this week, INCLUDING the PAPI owner. */ | ||
| total: number; | ||
| /** Distinct owners excluding the PAPI owner — the external-traction number. */ | ||
| external: number; | ||
| } | ||
| /** The only three columns the bucketing reads off a `cycles` row. */ | ||
| interface CycleCompletionRow { | ||
| user_id: string | null; | ||
| end_date: string | null; | ||
| updated_at: string | null; | ||
| } | ||
| /** | ||
| * The completion instant for a cycle: `end_date` when set (the release stamp), | ||
| * else `updated_at` (31 legacy cycles predate end_date — task-2753). | ||
| * Returns null when neither is a parseable timestamp. | ||
| */ | ||
| declare function wabCompletionMs(row: CycleCompletionRow): number | null; | ||
| /** | ||
| * Pure bucketing: distinct cycle-completers per trailing 7-day week. Pure by | ||
| * design (no Supabase, explicit `nowMs`) so the windowing is deterministically | ||
| * testable and so BOTH the dashboard and the server evaluator can call it. | ||
| * Returns `weeks` buckets oldest -> newest; the last element is the current window. | ||
| */ | ||
| declare function bucketWabSeries(rows: CycleCompletionRow[], ownerUserId: string | null, nowMs: number, weeks?: number): WabWeek[]; | ||
| /** Valid task statuses on the cycle board. */ | ||
@@ -138,2 +172,8 @@ type TaskStatus = 'Backlog' | 'In Cycle' | 'Ready' | 'In Progress' | 'In Review' | 'Done' | 'Blocked' | 'Cancelled' | 'Deferred'; | ||
| /** | ||
| * Independent deployments own their capacity policy and must not inherit the | ||
| * hosted SaaS project's Free/Pro/Team project-count ceilings. Both the MCP and | ||
| * dashboard creation doors call this shared rule so they cannot drift. | ||
| */ | ||
| declare function isSelfHostedDeployment(value: string | undefined): boolean; | ||
| /** | ||
| * Valid status transitions for tasks on the cycle board. | ||
@@ -291,2 +331,2 @@ * Each key maps to the set of statuses it can transition to. | ||
| export { CAPABILITY_KEYS, CAPABILITY_REGISTRY, type CapabilityDescriptor, type CapabilityKey, type CapabilityMap, type CapabilityStep, type ContributorGateDecision, type Discovery, type DiscoverySeverity, type DocDeletionBlockReason, type DocDeletionDecision, type DocDeletionInput, type EffortSize, type FindingDisposition, type FindingInput, type FindingKind, type FindingValidation, type FindingViolation, MIN_REASON_LENGTH, type ParsedFinding, RETIRED_DECISION_OUTCOMES, type ReviewStage, type ReviewVerdict, SENSITIVE_CHANGELOG_PATTERNS, TASK_STATUSES, type TaskComplexity, type TaskPriority, type TaskStatus, type TaskType, VALID_TRANSITIONS, contributorTeamUpsell, docDeletionBlockMessage, evaluateContributorGate, evaluateDocDeletion, findingKey, isCapabilityEnabled, isCapabilityKey, isLiveDecision, isNoneText, isSensitiveChangelogLine, isValidStatus, isValidTransition, parseDiscoveredIssues, splitFindings, validateFindings, validateTransition }; | ||
| export { CAPABILITY_KEYS, CAPABILITY_REGISTRY, type CapabilityDescriptor, type CapabilityKey, type CapabilityMap, type CapabilityStep, type ContributorGateDecision, type CycleCompletionRow, type Discovery, type DiscoverySeverity, type DocDeletionBlockReason, type DocDeletionDecision, type DocDeletionInput, type EffortSize, type FindingDisposition, type FindingInput, type FindingKind, type FindingValidation, type FindingViolation, MIN_REASON_LENGTH, type ParsedFinding, RETIRED_DECISION_OUTCOMES, type ReviewStage, type ReviewVerdict, SENSITIVE_CHANGELOG_PATTERNS, TASK_STATUSES, type TaskComplexity, type TaskPriority, type TaskStatus, type TaskType, VALID_TRANSITIONS, WAB_DEFAULT_WEEKS, WAB_WEEK_MS, WAB_WINDOW_DAYS, type WabWeek, bucketWabSeries, contributorTeamUpsell, docDeletionBlockMessage, evaluateContributorGate, evaluateDocDeletion, findingKey, isCapabilityEnabled, isCapabilityKey, isLiveDecision, isNoneText, isSelfHostedDeployment, isSensitiveChangelogLine, isValidStatus, isValidTransition, parseDiscoveredIssues, splitFindings, validateFindings, validateTransition, wabCompletionMs }; |
+43
-1
@@ -92,4 +92,40 @@ // src/findings.ts | ||
| // src/wab.ts | ||
| var WAB_WINDOW_DAYS = 7; | ||
| var WAB_DEFAULT_WEEKS = 8; | ||
| var WAB_WEEK_MS = WAB_WINDOW_DAYS * 24 * 60 * 60 * 1e3; | ||
| function wabCompletionMs(row) { | ||
| const ts = row.end_date ?? row.updated_at; | ||
| if (!ts) return null; | ||
| const ms = Date.parse(ts); | ||
| return Number.isNaN(ms) ? null : ms; | ||
| } | ||
| function bucketWabSeries(rows, ownerUserId, nowMs, weeks = WAB_DEFAULT_WEEKS) { | ||
| const buckets = Array.from({ length: weeks }, () => /* @__PURE__ */ new Set()); | ||
| for (const row of rows) { | ||
| if (!row.user_id) continue; | ||
| const ms = wabCompletionMs(row); | ||
| if (ms === null) continue; | ||
| const idx = Math.floor((nowMs - ms) / WAB_WEEK_MS); | ||
| if (idx < 0 || idx >= weeks) continue; | ||
| buckets[idx].add(row.user_id); | ||
| } | ||
| const out = []; | ||
| for (let idx = weeks - 1; idx >= 0; idx--) { | ||
| const owners = buckets[idx]; | ||
| const external = ownerUserId ? [...owners].filter((id) => id !== ownerUserId).length : owners.size; | ||
| out.push({ | ||
| weekStart: new Date(nowMs - (idx + 1) * WAB_WEEK_MS).toISOString(), | ||
| total: owners.size, | ||
| external | ||
| }); | ||
| } | ||
| return out; | ||
| } | ||
| // src/index.ts | ||
| var TASK_STATUSES = ["Backlog", "In Cycle", "Ready", "In Progress", "In Review", "Done", "Blocked", "Cancelled", "Deferred"]; | ||
| function isSelfHostedDeployment(value) { | ||
| return value === "1" || value?.toLowerCase() === "true"; | ||
| } | ||
| var VALID_TRANSITIONS = { | ||
@@ -257,2 +293,6 @@ "Backlog": ["In Cycle", "Ready", "In Progress", "Blocked", "Cancelled", "Deferred", "Done"], | ||
| VALID_TRANSITIONS, | ||
| WAB_DEFAULT_WEEKS, | ||
| WAB_WEEK_MS, | ||
| WAB_WINDOW_DAYS, | ||
| bucketWabSeries, | ||
| contributorTeamUpsell, | ||
@@ -267,2 +307,3 @@ docDeletionBlockMessage, | ||
| isNoneText, | ||
| isSelfHostedDeployment, | ||
| isSensitiveChangelogLine, | ||
@@ -274,3 +315,4 @@ isValidStatus, | ||
| validateFindings, | ||
| validateTransition | ||
| validateTransition, | ||
| wabCompletionMs | ||
| }; |
+1
-1
| { | ||
| "name": "@papi-ai/shared", | ||
| "version": "0.1.8", | ||
| "version": "0.1.9", | ||
| "description": "Shared types and business rules for PAPI — used by both MCP server and dashboard", | ||
@@ -5,0 +5,0 @@ "license": "Elastic-2.0", |
38195
9.59%641
14.26%