... ended up answering my own question in less time than it took to write it up. Solution (which might just be a workaround) is to force the content-type on the response to be application/octet-stream:

do inst.stream.SetAttribute("ContentDisposition","attachment; filename="""_inst.stream.GetAttribute("FileName")_"""")
do inst.stream.SetAttribute("ContentType","application/octet-stream")
set %response.Redirect = "%25CSP.StreamServer.cls?STREAMOID="_..Encrypt(inst.stream.GetStreamId())

@Eduard Lebedyuk it depends on the caller. In a CI process I could imagine doing different error handling for failed compilation vs. failed unit tests, this would be a way to signal those different modes of failure.

I've taken/seen approaches that are more shell-centric vs. more ObjectScript-centric which would be a driver for this being useful. With the package manager it's generally simpler to wrap things in <Invoke> or resource processors and then call IRIS with individual zpm commands (i.e., load then test) from CI. For some of my pre-package manager CI work we've had a big ObjectScript class that wraps all the build logic, running different sorts of tests, etc. In this case it would be useful to indicate the stage at which the failure occurred.

Regardless, $System.Process.Terminate is simpler to manage than "flag files" for sure, which would be the next best alternative. (IIRC in old days, maybe just pre-IRIS, there were Windows/Linux differences in $System.Process.Terminate's behavior, and that led us to use flag files.)

Hi @Steve Pisani - the same issue was reported via GitHub issues a little while back (https://github.com/intersystems/git-source-control/issues/137) but discussion trailed off and there wasn't any information there on the resolution.

You should always be able to upgrade zpm. I think the issue with <CLASS DOES NOT EXIST> error could be solved by running:

do ##class(%Studio.SourceControl.Interface).SourceControlClassSet("")

then reinstalling, then reenabling SourceControl.Git.Extension as the source control class for the namespace.

Ultimately something funky is going on with SQL stats collection. Given a bit more info it might be possible to mitigate the issue in the git-source-control package. Happy to connect sometime to discuss/troubleshoot.

From a diagnostic perspective, I think the things to do (which we would do on such a call) would be:
* Force single-process compilation: Do $System.OBJ.SetQualifiers("/nomulticompile")
* Running a fancy zbreak command:
zbreak *%objlasterror:"N":"$d(%objlasterror)#2":"s ^mtempdbg($i(^mtempdbg))=%objlasterror"
* Force single-process load of the package (zpm "install git-source-control -DThreads=0")

Then look at the contents of ^mtempdbg to figure out where our errant %Status is coming from and go from there.

@Michael Davidovich we'd generally set ^UnitTestRoot to the path in the cloned repo and then run tests right from there. Another workflow might be to have unit tests run in their own namespace that has all the same code/supporting data but is distinct from the namespace where you're doing dev work - in that case, it's safe to delete the unit tests from that second namespace. (But practically speaking we just use one namespace and don't delete.)

Re: best practices/shortcuts as an individual developer, I'd honestly just say embrace the automation and iterate in a feature branch with the tests running on commit to feature branches (not just main). It also helps to use the package manager to make a more modular codebase where each module has its own associated tests, so you're not rebuilding and testing everything every time.

In a case where you're (a) using the package manager and (b) being really strict about test coverage, combining the techniques in the article I linked earlier is a good approach - e.g., saying to run specific tests *and* measure test coverage. That helps answer "how well do the tests I'm currently writing cover the code I'm currently adding/changing?"

Of course, you can do this with the normal APIs for running unit tests with test coverage too. (TestCoverage.Manager also has DebugRunTestCase - think of it like %UnitTest.Manager but with test coverage built in.)