Tracks the workarounds in the stackql wrapper that exist because stackql exec does not signal failure through its exit code, prints informational messages on stderr, and prints null for an empty result. Upstream: stackql/stackql#801.
What the code does today
src/stackql.ts
rows() decides that a statement failed when stderr has output and stdout has none. That rule is wrong for every DDL statement and for REGISTRY PULL, which print their success message on stderr, so those cannot use it.
rows() treats a stdout of null as an empty result set.
pullProvider() ignores what REGISTRY PULL prints and confirms the pull with SHOW PROVIDERS.
src/snapshot.ts
countErrors() counts per repo HTTP failures inside a fan out by matching http response status code: <n> lines on stderr. The lines do not say which repo failed, so the source table records counts per status per org and an absent row can only be explained from other data (private, archived). A partial failure and a complete failure look the same to the wrapper except for the stdout rule above.
src/remediate.ts (Milestone 5)
- Every mutation is confirmed with a read of the new state. That stays regardless: CLAUDE.md requires apply = mutate, re-read, record.
Refactor once #801 lands
run() checks the exit code and throws StackQLError with stderr on non-zero. rows() becomes JSON.parse(stdout) with no stderr inspection and no null special case (if [] is adopted for empty results).
pullProvider() returns the version from the pull's own output, or keeps the SHOW PROVIDERS read if the message stays free text; either way it stops guessing which stream carries success.
- If the per repo failure lines gain the request path,
countErrors() records which repos were refused and the source table gets a refused column. Checks that today infer na from private or archived for an absent row (private_vuln_reporting, code_scanning) can then distinguish a refusal from a missing setting, and unknown becomes attributable.
- Drop the tests that pin the heuristics (
rows applies the error rule, query returns no rows when stackql prints null, pullProvider accepts success on stderr) and replace them with exit code tests.
Until then nothing here blocks the project. The cost is three heuristics and the lack of per repo error attribution.
Tracks the workarounds in the stackql wrapper that exist because
stackql execdoes not signal failure through its exit code, prints informational messages on stderr, and printsnullfor an empty result. Upstream: stackql/stackql#801.What the code does today
src/stackql.tsrows()decides that a statement failed when stderr has output and stdout has none. That rule is wrong for every DDL statement and forREGISTRY PULL, which print their success message on stderr, so those cannot use it.rows()treats a stdout ofnullas an empty result set.pullProvider()ignores whatREGISTRY PULLprints and confirms the pull withSHOW PROVIDERS.src/snapshot.tscountErrors()counts per repo HTTP failures inside a fan out by matchinghttp response status code: <n>lines on stderr. The lines do not say which repo failed, so thesourcetable records counts per status per org and an absent row can only be explained from other data (private, archived). A partial failure and a complete failure look the same to the wrapper except for the stdout rule above.src/remediate.ts(Milestone 5)Refactor once #801 lands
run()checks the exit code and throwsStackQLErrorwith stderr on non-zero.rows()becomesJSON.parse(stdout)with no stderr inspection and nonullspecial case (if[]is adopted for empty results).pullProvider()returns the version from the pull's own output, or keeps theSHOW PROVIDERSread if the message stays free text; either way it stops guessing which stream carries success.countErrors()records which repos were refused and thesourcetable gets arefusedcolumn. Checks that today infernafromprivateorarchivedfor an absent row (private_vuln_reporting,code_scanning) can then distinguish a refusal from a missing setting, andunknownbecomes attributable.rows applies the error rule,query returns no rows when stackql prints null,pullProvider accepts success on stderr) and replace them with exit code tests.Until then nothing here blocks the project. The cost is three heuristics and the lack of per repo error attribution.