@@ -582,20 +582,110 @@ func TestHarnessesLoadAndValidate(t *testing.T) {
582582 }
583583 t .Run (e .Name (), func (t * testing.T ) {
584584 harnessPath := filepath .Join (dir , "harness" , e .Name ())
585- h , err := harness .Load (harnessPath )
586- require .NoError (t , err , "Load should succeed" )
587585
588- err = h .ResolveRelativeTo (dir )
589- require .NoError (t , err , "ResolveRelativeTo should succeed" )
586+ t .Run ("Load" , func (t * testing.T ) {
587+ h , loadErr := harness .Load (harnessPath )
588+ require .NoError (t , loadErr , "Load should succeed" )
590589
591- err = h .ValidateFilesExist ()
592- require .NoError (t , err , "ValidateFilesExist should succeed" )
590+ // Top-level pre/post scripts serve as defaults even
591+ // without forge resolution (local dev without --forge).
592+ assert .NotEmpty (t , h .PreScript , "PreScript should be set at top level as default" )
593+ assert .NotEmpty (t , h .PostScript , "PostScript should be set at top level as default" )
594+ assert .NotNil (t , h .Forge , "Forge map should be present" )
595+ assert .Contains (t , h .Forge , "github" , "Forge should have a github key" )
596+
597+ resolveErr := h .ResolveRelativeTo (dir )
598+ require .NoError (t , resolveErr , "ResolveRelativeTo should succeed" )
599+
600+ existErr := h .ValidateFilesExist ()
601+ require .NoError (t , existErr , "ValidateFilesExist should succeed" )
602+ })
603+
604+ t .Run ("LoadWithOpts_github" , func (t * testing.T ) {
605+ h , loadErr := harness .LoadWithOpts (harnessPath , harness.LoadOpts {ForgePlatform : "github" })
606+ require .NoError (t , loadErr , "LoadWithOpts should succeed" )
607+
608+ assert .Nil (t , h .Forge , "Forge should be nil after resolution" )
609+ assert .NotEmpty (t , h .PreScript , "PreScript should be set after forge resolution" )
610+ assert .NotEmpty (t , h .PostScript , "PostScript should be set after forge resolution" )
611+ assert .NotEmpty (t , h .RunnerEnv , "RunnerEnv should be non-empty after merge" )
612+
613+ resolveErr := h .ResolveRelativeTo (dir )
614+ require .NoError (t , resolveErr , "ResolveRelativeTo should succeed" )
615+
616+ existErr := h .ValidateFilesExist ()
617+ require .NoError (t , existErr , "ValidateFilesExist should succeed" )
618+ })
593619 })
594620 loaded ++
595621 }
596622 assert .True (t , loaded >= 2 , "expected at least 2 harnesses, got %d" , loaded )
597623}
598624
625+ func TestHarnessForgeRunnerEnvMerge (t * testing.T ) {
626+ dir := t .TempDir ()
627+ err := WalkFullsendRepoAll (func (path string , content []byte ) error {
628+ dest := filepath .Join (dir , path )
629+ if mkErr := os .MkdirAll (filepath .Dir (dest ), 0o755 ); mkErr != nil {
630+ return mkErr
631+ }
632+ return os .WriteFile (dest , content , 0o644 )
633+ })
634+ require .NoError (t , err , "extracting scaffold" )
635+
636+ tests := []struct {
637+ file string
638+ topLevelKeys []string
639+ forgeGithubKeys []string
640+ }{
641+ {
642+ file : "triage.yaml" ,
643+ topLevelKeys : []string {"FULLSEND_OUTPUT_SCHEMA" },
644+ forgeGithubKeys : []string {"GITHUB_ISSUE_URL" , "GH_TOKEN" },
645+ },
646+ {
647+ file : "code.yaml" ,
648+ topLevelKeys : []string {"TARGET_BRANCH" },
649+ forgeGithubKeys : []string {"PUSH_TOKEN" , "PUSH_TOKEN_SOURCE" , "REPO_FULL_NAME" , "ISSUE_NUMBER" , "REPO_DIR" },
650+ },
651+ {
652+ file : "review.yaml" ,
653+ topLevelKeys : []string {"FULLSEND_OUTPUT_SCHEMA" },
654+ forgeGithubKeys : []string {"REVIEW_TOKEN" , "REPO_FULL_NAME" , "PR_NUMBER" , "GITHUB_PR_URL" },
655+ },
656+ {
657+ file : "fix.yaml" ,
658+ topLevelKeys : []string {"TARGET_BRANCH" , "TRIGGER_SOURCE" , "HUMAN_INSTRUCTION" , "FIX_ITERATION" , "REVIEW_BODY_FILE" , "PRE_AGENT_HEAD" , "FULLSEND_OUTPUT_SCHEMA" , "FULLSEND_OUTPUT_FILE" },
659+ forgeGithubKeys : []string {"PUSH_TOKEN" , "PUSH_TOKEN_SOURCE" , "REPO_FULL_NAME" , "PR_NUMBER" , "REPO_DIR" },
660+ },
661+ {
662+ file : "retro.yaml" ,
663+ topLevelKeys : []string {"FULLSEND_OUTPUT_SCHEMA" },
664+ forgeGithubKeys : []string {"ORIGINATING_URL" , "REPO_FULL_NAME" , "GH_TOKEN" },
665+ },
666+ {
667+ file : "prioritize.yaml" ,
668+ topLevelKeys : []string {"FULLSEND_OUTPUT_SCHEMA" },
669+ forgeGithubKeys : []string {"GITHUB_ISSUE_URL" , "GH_TOKEN" , "ORG" , "PROJECT_NUMBER" },
670+ },
671+ }
672+
673+ for _ , tt := range tests {
674+ t .Run (tt .file , func (t * testing.T ) {
675+ harnessPath := filepath .Join (dir , "harness" , tt .file )
676+ h , loadErr := harness .LoadWithOpts (harnessPath , harness.LoadOpts {ForgePlatform : "github" })
677+ require .NoError (t , loadErr )
678+
679+ for _ , key := range tt .topLevelKeys {
680+ assert .Contains (t , h .RunnerEnv , key , "merged RunnerEnv should contain top-level key %s" , key )
681+ }
682+ for _ , key := range tt .forgeGithubKeys {
683+ assert .Contains (t , h .RunnerEnv , key , "merged RunnerEnv should contain forge.github key %s" , key )
684+ }
685+ })
686+ }
687+ }
688+
599689func TestRepoMaintenanceWorkflowContent (t * testing.T ) {
600690 content , err := FullsendRepoFile (".github/workflows/repo-maintenance.yml" )
601691 require .NoError (t , err )
0 commit comments