I am going to attempt to go back to what I was doing a year or two ago, and note relevant POSIX changes (when they're simply updating to specify what we already do, or inconsequential changes that don't really affect anything, I'll ignore those).
One which just seems to have happened: tsort is getting a -w option added (as the only POSIX mandated option). When specified, cycles must be reported to stderr (of course, our -q option, as an extension, can override that), and the number of cycles detected, (up to some implementation defined maximum) is to be the exit code (more cycles than that and the maximum is used). That is, with -w, an exit status != 0 will be required if there are any cycles. They recommend setting the implementation defined maximum <= 125 (so exit codes 126 and 127 can have their traditional meanings, > 128 still can be used (when reported by the shell) for killed by a signal, 128 can be used for any other errors (like bad usage, files that cannot be opened, ...). Making the max < 125 would allow more "other" error exit possibilities if required, making it too small makes it more likely that being able to write code than checks that exactly N cycles exist in a particular graph (so one can check if any more come, or any vanish, to understand why) will be compromised (if N is ever >= the max, a test like that cannot be useful). The previous text required an exit code != 0 if any cycles (or anything else) was reported to stderr, which isn't what is often wanted, with cycles expected, to be reported, and otherwise ignored - leading to some implementations simply not reporting cycles at all. (I think our implementation (without -q) would report cycles to stderr, and still do exit(0).) Because the definition of the stderr section has altered, if -w is not given, messages written to stderr about cycles can be considered warnings instead of diagnostics, and so do not require a non-zero exit code, so (aside from needing to add, and support, -w, and exit with a code as it requires) we would be more conformant after the change than before. In addition, since there is now an option, standard option processing is to be required (including support for --) which wasn't actually required before (and perhaps technically wasn't even permitted). This again simplifies things for us, as our tsort already uses getopt(3) for option processing. kre