@@ -13,7 +13,7 @@ const testRoot = path.join(repoRoot, "test");
1313const workspacePackagePaths = [ "ui/package.json" ] ;
1414const MAX_SCAN_BYTES = 2 * 1024 * 1024 ;
1515const compareStrings = ( left , right ) => left . localeCompare ( right ) ;
16- const HELP_TEXT = `Usage: node scripts/audit-seams.mjs [--help]
16+ export const HELP_TEXT = `Usage: node scripts/audit-seams.mjs [--help]
1717
1818Audit repo seam inventory and emit JSON to stdout.
1919
@@ -22,7 +22,10 @@ Sections:
2222 overlapFiles Production files that touch multiple seam families
2323 optionalClusterStaticLeaks Optional extension/plugin clusters referenced from the static graph
2424 missingPackages Workspace packages whose deps are not mirrored at the root
25- seamTestInventory High-signal seam candidates with nearby-test gap signals
25+ seamTestInventory High-signal seam candidates with nearby-test gap signals,
26+ including cron orchestration seams for agent handoff,
27+ outbound/media delivery, heartbeat/followup handoff,
28+ and scheduler state crossings
2629
2730Notes:
2831 - Output is JSON only.
@@ -531,7 +534,135 @@ function stemFromRelativePath(relativePath) {
531534 return relativePath . replace ( / \. ( m | c ) ? [ j t ] s x ? $ / , "" ) ;
532535}
533536
534- function describeSeamKinds ( relativePath , source ) {
537+ function splitNameTokens ( name ) {
538+ return name
539+ . split ( / [ ^ a - z A - Z 0 - 9 ] + / )
540+ . map ( ( token ) => token . trim ( ) . toLowerCase ( ) )
541+ . filter ( Boolean ) ;
542+ }
543+
544+ function escapeForRegExp ( value ) {
545+ return value . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" ) ;
546+ }
547+
548+ function hasImportSource ( source , specifier ) {
549+ const escaped = escapeForRegExp ( specifier ) ;
550+ return new RegExp ( `from\\s+["']${ escaped } ["']|import\\s*\\(\\s*["']${ escaped } ["']\\s*\\)` ) . test (
551+ source ,
552+ ) ;
553+ }
554+
555+ function hasAnyImportSource ( source , specifiers ) {
556+ return specifiers . some ( ( specifier ) => hasImportSource ( source , specifier ) ) ;
557+ }
558+
559+ function isCronProductionPath ( relativePath ) {
560+ return relativePath . startsWith ( "src/cron/" ) && isProductionLikeFile ( relativePath ) ;
561+ }
562+
563+ function describeCronSeamKinds ( relativePath , source ) {
564+ if ( ! isCronProductionPath ( relativePath ) ) {
565+ return [ ] ;
566+ }
567+
568+ const seamKinds = [ ] ;
569+ const importsAgentRunner = hasAnyImportSource ( source , [
570+ "../../agents/cli-runner.js" ,
571+ "../../agents/pi-embedded.js" ,
572+ "../../agents/model-fallback.js" ,
573+ "../../agents/subagent-registry.js" ,
574+ "../../infra/agent-events.js" ,
575+ ] ) ;
576+ const importsOutboundDelivery = hasAnyImportSource ( source , [
577+ "../infra/outbound/deliver.js" ,
578+ "../../infra/outbound/deliver.js" ,
579+ "../infra/outbound/session-context.js" ,
580+ "../../infra/outbound/session-context.js" ,
581+ "../infra/outbound/identity.js" ,
582+ "../../infra/outbound/identity.js" ,
583+ "../cli/outbound-send-deps.js" ,
584+ "../../cli/outbound-send-deps.js" ,
585+ ] ) ;
586+ const importsHeartbeat = hasAnyImportSource ( source , [
587+ "../auto-reply/heartbeat.js" ,
588+ "../../auto-reply/heartbeat.js" ,
589+ "../infra/heartbeat-wake.js" ,
590+ "../../infra/heartbeat-wake.js" ,
591+ ] ) ;
592+ const importsFollowup = hasAnyImportSource ( source , [
593+ "./subagent-followup.js" ,
594+ "../../agents/subagent-registry.js" ,
595+ "../../agents/tools/agent-step.js" ,
596+ "../../gateway/call.js" ,
597+ ] ) ;
598+ const importsSchedulerModules =
599+ relativePath . startsWith ( "src/cron/service/" ) &&
600+ hasAnyImportSource ( source , [
601+ "./jobs.js" ,
602+ "./store.js" ,
603+ "./timer.js" ,
604+ "./state.js" ,
605+ "../schedule.js" ,
606+ "../store.js" ,
607+ "../run-log.js" ,
608+ ] ) ;
609+
610+ if (
611+ importsAgentRunner &&
612+ / \b r u n C l i A g e n t \b | \b r u n E m b e d d e d P i A g e n t \b | \b r u n W i t h M o d e l F a l l b a c k \b | \b r e g i s t e r A g e n t R u n C o n t e x t \b / . test (
613+ source ,
614+ )
615+ ) {
616+ seamKinds . push ( "cron-agent-handoff" ) ;
617+ }
618+
619+ if (
620+ importsOutboundDelivery &&
621+ / \b d e l i v e r O u t b o u n d P a y l o a d s \b | \b b u i l d O u t b o u n d S e s s i o n C o n t e x t \b | \b r e s o l v e A g e n t O u t b o u n d I d e n t i t y \b / . test (
622+ source ,
623+ )
624+ ) {
625+ seamKinds . push ( "cron-outbound-delivery" ) ;
626+ }
627+
628+ if (
629+ importsHeartbeat &&
630+ / \b s t r i p H e a r t b e a t T o k e n \b | \b H e a r t b e a t \b | \b h e a r t b e a t \b | \b n e x t - h e a r t b e a t \b / . test ( source )
631+ ) {
632+ seamKinds . push ( "cron-heartbeat-handoff" ) ;
633+ }
634+
635+ if (
636+ importsSchedulerModules &&
637+ / \b e n s u r e L o a d e d \b | \b p e r s i s t \b | \b a r m T i m e r \b | \b r u n M i s s e d J o b s \b | \b c o m p u t e J o b N e x t R u n A t M s \b | \b r e c o m p u t e N e x t R u n s \b | \b n e x t W a k e A t M s \b / . test (
638+ source ,
639+ )
640+ ) {
641+ seamKinds . push ( "cron-scheduler-state" ) ;
642+ }
643+
644+ if (
645+ importsOutboundDelivery &&
646+ / \b m e d i a U r l \b | \b m e d i a U r l s \b | \b f i l e n a m e \b | \b a u d i o A s V o i c e \b | \b d e l i v e r y P a y l o a d s \b | \b d e l i v e r y P a y l o a d H a s S t r u c t u r e d C o n t e n t \b / . test (
647+ source ,
648+ )
649+ ) {
650+ seamKinds . push ( "cron-media-delivery" ) ;
651+ }
652+
653+ if (
654+ importsFollowup &&
655+ / \b w a i t F o r D e s c e n d a n t S u b a g e n t S u m m a r y \b | \b r e a d D e s c e n d a n t S u b a g e n t F a l l b a c k R e p l y \b | \b e x p e c t s S u b a g e n t F o l l o w u p \b | \b c a l l G a t e w a y \b | \b l i s t D e s c e n d a n t R u n s F o r R e q u e s t e r \b / . test (
656+ source ,
657+ )
658+ ) {
659+ seamKinds . push ( "cron-followup-handoff" ) ;
660+ }
661+
662+ return seamKinds ;
663+ }
664+
665+ export function describeSeamKinds ( relativePath , source ) {
535666 const seamKinds = [ ] ;
536667 const isReplyDeliveryPath =
537668 / r e p l y - d e l i v e r y | r e p l y - d i s p a t c h e r | d e l i v e r - r e p l y | r e p l y \/ .* d e l i v e r y | m o n i t o r \/ (?: r e p l i e s | d e l i v e r | n a t i v e - c o m m a n d ) | o u t b o u n d \/ d e l i v e r | o u t b o u n d \/ m e s s a g e / . test (
@@ -565,11 +696,12 @@ function describeSeamKinds(relativePath, source) {
565696 }
566697 if (
567698 isReplyDeliveryPath &&
568- / b l o c k S t r e a m i n g E n a b l e d | d i r e c t l y S e n t B l o c k K e y s / . test ( source ) &&
699+ / b l o c k S t r e a m i n g E n a b l e d | d i r e c t l y S e n t B l o c k K e y s | r e s o l v e S e n d a b l e O u t b o u n d R e p l y P a r t s / . test ( source ) &&
569700 / \b m e d i a U r l \b | \b m e d i a U r l s \b / . test ( source )
570701 ) {
571702 seamKinds . push ( "streaming-media-handoff" ) ;
572703 }
704+ seamKinds . push ( ...describeCronSeamKinds ( relativePath , source ) ) ;
573705 return [ ...new Set ( seamKinds ) ] . toSorted ( compareStrings ) ;
574706}
575707
@@ -593,17 +725,6 @@ async function buildTestIndex(testFiles) {
593725 ) ;
594726}
595727
596- function splitNameTokens ( name ) {
597- return name
598- . split ( / [ ^ a - z A - Z 0 - 9 ] + / )
599- . map ( ( token ) => token . trim ( ) . toLowerCase ( ) )
600- . filter ( Boolean ) ;
601- }
602-
603- function escapeForRegExp ( value ) {
604- return value . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" ) ;
605- }
606-
607728function hasExecutableImportReference ( source , importPath ) {
608729 const escapedImportPath = escapeForRegExp ( importPath ) ;
609730 const suffix = String . raw `(?:\.[^"'\\\`]+)?` ;
@@ -697,7 +818,7 @@ function findRelatedTests(relativePath, testIndex) {
697818 } ) ;
698819}
699820
700- function determineSeamTestStatus ( seamKinds , relatedTestMatches ) {
821+ export function determineSeamTestStatus ( seamKinds , relatedTestMatches ) {
701822 if ( relatedTestMatches . length === 0 ) {
702823 return {
703824 status : "gap" ,
@@ -709,7 +830,13 @@ function determineSeamTestStatus(seamKinds, relatedTestMatches) {
709830 if (
710831 seamKinds . includes ( "reply-delivery-media" ) ||
711832 seamKinds . includes ( "streaming-media-handoff" ) ||
712- seamKinds . includes ( "tool-result-media" )
833+ seamKinds . includes ( "tool-result-media" ) ||
834+ seamKinds . includes ( "cron-agent-handoff" ) ||
835+ seamKinds . includes ( "cron-outbound-delivery" ) ||
836+ seamKinds . includes ( "cron-heartbeat-handoff" ) ||
837+ seamKinds . includes ( "cron-scheduler-state" ) ||
838+ seamKinds . includes ( "cron-media-delivery" ) ||
839+ seamKinds . includes ( "cron-followup-handoff" )
713840 ) {
714841 return {
715842 status : "partial" ,
@@ -765,22 +892,29 @@ async function buildSeamTestInventory() {
765892 } ) ;
766893}
767894
768- const args = new Set ( process . argv . slice ( 2 ) ) ;
769- if ( args . has ( "--help" ) || args . has ( "-h" ) ) {
770- process . stdout . write ( `${ HELP_TEXT } \n` ) ;
771- process . exit ( 0 ) ;
772- }
895+ export async function main ( argv = process . argv . slice ( 2 ) ) {
896+ const args = new Set ( argv ) ;
897+ if ( args . has ( "--help" ) || args . has ( "-h" ) ) {
898+ process . stdout . write ( `${ HELP_TEXT } \n` ) ;
899+ return ;
900+ }
773901
774- await collectWorkspacePackagePaths ( ) ;
775- const inventory = await collectCorePluginSdkImports ( ) ;
776- const optionalClusterStaticLeaks = await collectOptionalClusterStaticLeaks ( ) ;
777- const staticLeakClusters = new Set ( optionalClusterStaticLeaks . map ( ( entry ) => entry . cluster ) ) ;
778- const result = {
779- duplicatedSeamFamilies : buildDuplicatedSeamFamilies ( inventory ) ,
780- overlapFiles : buildOverlapFiles ( inventory ) ,
781- optionalClusterStaticLeaks : buildOptionalClusterStaticLeaks ( optionalClusterStaticLeaks ) ,
782- missingPackages : await buildMissingPackages ( { staticLeakClusters } ) ,
783- seamTestInventory : await buildSeamTestInventory ( ) ,
784- } ;
902+ await collectWorkspacePackagePaths ( ) ;
903+ const inventory = await collectCorePluginSdkImports ( ) ;
904+ const optionalClusterStaticLeaks = await collectOptionalClusterStaticLeaks ( ) ;
905+ const staticLeakClusters = new Set ( optionalClusterStaticLeaks . map ( ( entry ) => entry . cluster ) ) ;
906+ const result = {
907+ duplicatedSeamFamilies : buildDuplicatedSeamFamilies ( inventory ) ,
908+ overlapFiles : buildOverlapFiles ( inventory ) ,
909+ optionalClusterStaticLeaks : buildOptionalClusterStaticLeaks ( optionalClusterStaticLeaks ) ,
910+ missingPackages : await buildMissingPackages ( { staticLeakClusters } ) ,
911+ seamTestInventory : await buildSeamTestInventory ( ) ,
912+ } ;
785913
786- process . stdout . write ( `${ JSON . stringify ( result , null , 2 ) } \n` ) ;
914+ process . stdout . write ( `${ JSON . stringify ( result , null , 2 ) } \n` ) ;
915+ }
916+
917+ const entryFilePath = process . argv [ 1 ] ? path . resolve ( process . argv [ 1 ] ) : null ;
918+ if ( entryFilePath === fileURLToPath ( import . meta. url ) ) {
919+ await main ( ) ;
920+ }
0 commit comments